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:
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import {ColumnDef} from "@tanstack/react-table";
|
||||
import {NotificationLogWithRelations} from "@/db/services/notification-log";
|
||||
import {getNotificationChannelIcon} from "@/components/wrappers/dashboard/admin/notifications/helpers";
|
||||
import {humanReadableDate} from "@/utils/date-formatting";
|
||||
import {CheckCircle2, XCircle} from "lucide-react";
|
||||
import {Badge} from "@/components/ui/badge";
|
||||
import {NotificationLogModal} from "@/components/wrappers/dashboard/admin/notifications/logs/notification-log-modal";
|
||||
|
||||
|
||||
export function notificationLogsColumns(): ColumnDef<NotificationLogWithRelations>[] {
|
||||
return [
|
||||
{
|
||||
accessorKey: "success",
|
||||
header: "Status",
|
||||
cell: ({row}) => {
|
||||
const status = row.original.success ? "delivered" : "failed";
|
||||
return(
|
||||
<Badge variant="outline" className={`gap-1.5 ${getStatusColor(status)}`}>
|
||||
{getStatusIcon(row.original.success)}
|
||||
<span className="capitalize">{status}</span>
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "channel",
|
||||
header: "Channel",
|
||||
cell: ({row}) => {
|
||||
const channel = row.original.channel
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="flex h-10 w-10 items-center justify-center rounded-md bg-secondary border border-border">
|
||||
{getNotificationChannelIcon(channel?.provider ?? "")}
|
||||
</div>
|
||||
{channel?.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "sentAt",
|
||||
header: "Sent At",
|
||||
cell: ({row}) => {
|
||||
const sentAt = humanReadableDate(row.original.sentAt)
|
||||
return (
|
||||
<div>
|
||||
{sentAt}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "policy",
|
||||
header: "Event Kinds",
|
||||
cell: ({row}) => {
|
||||
const eventKinds = row.original.policy?.eventKinds ?? [];
|
||||
return (
|
||||
<div>
|
||||
{eventKinds.map((eventKind, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<Badge>{eventKind}</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "details",
|
||||
header: "Details",
|
||||
cell: ({row}) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NotificationLogModal notificationLog={row.original} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
const getStatusIcon = (status: boolean) => {
|
||||
switch (status) {
|
||||
case true:
|
||||
return <CheckCircle2 className="h-4 w-4"/>
|
||||
case false:
|
||||
return <XCircle className="h-4 w-4"/>
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "delivered":
|
||||
return "bg-green-100 dark:bg-green-100/10"
|
||||
case "failed":
|
||||
return "bg-red-100 dark:bg-red-100/10"
|
||||
case "pending":
|
||||
return "bg-orange-100 dark:bg-orange-100/10"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
"use client"
|
||||
import {Eye} from "lucide-react";
|
||||
|
||||
import {useState} from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger
|
||||
} from "@/components/ui/dialog";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Separator} from "@/components/ui/separator";
|
||||
import {NotificationLogWithRelations} from "@/db/services/notification-log";
|
||||
|
||||
type NotificationLogModalProps = {
|
||||
notificationLog: NotificationLogWithRelations;
|
||||
}
|
||||
|
||||
export const NotificationLogModal = ({notificationLog}: NotificationLogModalProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" onClick={() => setOpen(true)} className="relative">
|
||||
<Eye/>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Notification details</DialogTitle>
|
||||
<DialogDescription>
|
||||
Details of the notification sent
|
||||
</DialogDescription>
|
||||
<Separator className="mt-3 mb-3"/>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div>
|
||||
<span className="font-semibold">Title:</span>{" "}
|
||||
<span>{notificationLog.content.title}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold">Message:</span>{" "}
|
||||
<span>{notificationLog.content.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notificationLog.payload && (
|
||||
<>
|
||||
<Separator className="mb-3" />
|
||||
<div className="text-sm font-mono bg-gray-100 dark:bg-gray-800 dark:text-gray-200 p-4 rounded-md overflow-auto">
|
||||
{JSON.stringify(notificationLog.payload, null, 2)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</DialogHeader>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client"
|
||||
import {DataTable} from "@/components/wrappers/common/table/data-table";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {
|
||||
notificationLogsColumns,
|
||||
} from "@/components/wrappers/dashboard/admin/notifications/logs/columns";
|
||||
import {NotificationLogWithRelations} from "@/db/services/notification-log";
|
||||
|
||||
|
||||
type NotificationsLogsListProps = {
|
||||
notificationLogs: NotificationLogWithRelations[]
|
||||
}
|
||||
|
||||
export const NotificationLogsList = (props: NotificationsLogsListProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
enableSelect={false}
|
||||
columns={notificationLogsColumns()}
|
||||
data={props.notificationLogs}
|
||||
enablePagination
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user