mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Working on notification system.
This commit is contained in:
+45
@@ -0,0 +1,45 @@
|
||||
"use client"
|
||||
import {NotificationChannel, NotificationChannelWith} from "@/db/schema/09_notification-channel";
|
||||
import {CardsWithPagination} from "@/components/wrappers/common/cards-with-pagination";
|
||||
import {NotifierCard} from "@/components/wrappers/dashboard/common/notifier/notifier-card/notifier-card";
|
||||
import {useState} from "react";
|
||||
import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
|
||||
import {OrganizationWithMembers} from "@/db/schema/03_organization";
|
||||
|
||||
type NotificationChannelsSectionProps = {
|
||||
notificationChannels: NotificationChannelWith[]
|
||||
organizations: OrganizationWithMembers[]
|
||||
}
|
||||
|
||||
export const NotificationChannelsSection = ({
|
||||
organizations,
|
||||
notificationChannels
|
||||
}: NotificationChannelsSectionProps) => {
|
||||
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||
|
||||
const hasNotifiers = notificationChannels.length > 0;
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
{hasNotifiers ? (
|
||||
<div className="h-full">
|
||||
<CardsWithPagination
|
||||
data={notificationChannels}
|
||||
cardItem={NotifierCard}
|
||||
cardsPerPage={8}
|
||||
numberOfColumns={2}
|
||||
adminView={true}
|
||||
organizations={organizations}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyStatePlaceholder
|
||||
text="No notification channels configured yet"
|
||||
onClick={() => setIsAddModalOpen(true)}
|
||||
className="h-full"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import {useRouter} from "next/navigation";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import {Form, FormControl, FormField, FormItem, useZodForm} from "@/components/ui/form";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
import {OrganizationWithMembers} from "@/db/schema/03_organization";
|
||||
import {NotificationChannelWith} from "@/db/schema/09_notification-channel";
|
||||
import {
|
||||
NotificationChannelsOrganizationSchema,
|
||||
NotificationChannelsOrganizationType
|
||||
} from "@/components/wrappers/dashboard/admin/notifications/channels/organization/notification-channels-organization.schema";
|
||||
import {MultiSelect} from "@/components/wrappers/common/multiselect/multi-select";
|
||||
import {
|
||||
updateNotificationChannelsOrganizationAction
|
||||
} from "@/components/wrappers/dashboard/admin/notifications/channels/organization/notification-channels-organization.action";
|
||||
import {toast} from "sonner";
|
||||
|
||||
|
||||
type NotifierChannelOrganisationFormProps = {
|
||||
organizations?: OrganizationWithMembers[];
|
||||
defaultValues?: NotificationChannelWith
|
||||
};
|
||||
|
||||
export const NotifierChannelOrganisationForm = ({
|
||||
organizations,
|
||||
defaultValues,
|
||||
}: NotifierChannelOrganisationFormProps) => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
console.log("defaultValues", defaultValues);
|
||||
console.log("organizations", organizations);
|
||||
|
||||
const defaultOrganizationIds = defaultValues?.organizations?.map(organization => organization.organizationId) ?? []
|
||||
|
||||
|
||||
const form = useZodForm({
|
||||
schema: NotificationChannelsOrganizationSchema,
|
||||
// @ts-ignore
|
||||
defaultValues: {
|
||||
organizations: defaultOrganizationIds
|
||||
},
|
||||
});
|
||||
|
||||
const formatOrganizationsList = (organizations: OrganizationWithMembers[]) => {
|
||||
return organizations
|
||||
.map((organization) => ({
|
||||
value: organization.id,
|
||||
label: `${organization.name}`,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
const mutationUpdateNotificationChannelOrganizations = useMutation({
|
||||
mutationFn: async (values: NotificationChannelsOrganizationType) => {
|
||||
|
||||
const payload = {
|
||||
data: values.organizations,
|
||||
notificationChannelId: defaultValues?.id ?? ""
|
||||
};
|
||||
|
||||
const result = await updateNotificationChannelsOrganizationAction(payload)
|
||||
const inner = result?.data;
|
||||
|
||||
if (inner?.success) {
|
||||
toast.success(inner.actionSuccess?.message);
|
||||
router.refresh();
|
||||
} else {
|
||||
toast.error(inner?.actionError?.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={async (values) => {
|
||||
await mutationUpdateNotificationChannelOrganizations.mutateAsync(values);
|
||||
}}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`organizations`}
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<MultiSelect
|
||||
options={formatOrganizationsList(organizations ?? [])}
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value ?? []}
|
||||
placeholder="Select organization(s)"
|
||||
variant="inverted"
|
||||
animation={0}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
<ButtonWithLoading isPending={mutationUpdateNotificationChannelOrganizations.isPending}>
|
||||
Save
|
||||
</ButtonWithLoading>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
"use server"
|
||||
import {userAction} from "@/lib/safe-actions/actions";
|
||||
import {z} from "zod";
|
||||
import {ServerActionResult} from "@/types/action-type";
|
||||
import {db} from "@/db";
|
||||
import {and, eq, inArray} from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {NotificationChannelWith} from "@/db/schema/09_notification-channel";
|
||||
|
||||
|
||||
export const updateNotificationChannelsOrganizationAction = userAction
|
||||
.schema(
|
||||
z.object({
|
||||
data: z.array(z.string()),
|
||||
notificationChannelId: z.string(),
|
||||
})
|
||||
)
|
||||
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<null>> => {
|
||||
try {
|
||||
const organizationsIds = parsedInput.data;
|
||||
|
||||
const notificationChannel = await db.query.notificationChannel.findFirst({
|
||||
where: eq(drizzleDb.schemas.notificationChannel.id, parsedInput.notificationChannelId),
|
||||
with: {
|
||||
organizations: true,
|
||||
}
|
||||
}) as NotificationChannelWith;
|
||||
|
||||
|
||||
if (!notificationChannel) {
|
||||
return {
|
||||
success: false,
|
||||
actionError: {
|
||||
message: "Notification channel not found.",
|
||||
status: 404,
|
||||
cause: "not_found",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const existingItemIds = notificationChannel.organizations.map((organization) => organization.organizationId);
|
||||
|
||||
const organizationsToAdd = organizationsIds.filter((id) => !existingItemIds.includes(id));
|
||||
const organizationsToRemove = existingItemIds.filter((id) => !organizationsIds.includes(id));
|
||||
|
||||
if (organizationsToAdd.length > 0) {
|
||||
for (const organizationToAdd of organizationsToAdd) {
|
||||
await db.insert(drizzleDb.schemas.organizationNotificationChannel).values({
|
||||
organizationId: organizationToAdd,
|
||||
notificationChannelId: parsedInput.notificationChannelId
|
||||
});
|
||||
}
|
||||
}
|
||||
if (organizationsToRemove.length > 0) {
|
||||
await db.delete(drizzleDb.schemas.organizationNotificationChannel).where(and(inArray(drizzleDb.schemas.organizationNotificationChannel.organizationId, organizationsToRemove), eq(drizzleDb.schemas.organizationNotificationChannel.notificationChannelId, parsedInput.notificationChannelId))).execute();
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
value: null,
|
||||
actionSuccess: {
|
||||
message: "Notification channel organizations has been successfully updated.",
|
||||
messageParams: {notificationChannelId: parsedInput.notificationChannelId},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating notification channel:", error);
|
||||
return {
|
||||
success: false,
|
||||
actionError: {
|
||||
message: "Failed to update notification channel.",
|
||||
status: 500,
|
||||
cause: "server_error",
|
||||
messageParams: {message: "Error updating the notification channel"},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import {z} from "zod";
|
||||
|
||||
export const NotificationChannelsOrganizationSchema = z.object({
|
||||
organizations: z.array(z.string())
|
||||
});
|
||||
|
||||
export type NotificationChannelsOrganizationType = z.infer<typeof NotificationChannelsOrganizationSchema>;
|
||||
Reference in New Issue
Block a user