Working on UX/UI of the notification system.

This commit is contained in:
charlesgauthereau
2025-11-22 19:36:31 +01:00
parent e886bdd58d
commit eb471c7cdb
5 changed files with 164 additions and 52 deletions
@@ -1,31 +1,72 @@
// import Link from "next/link";
// import {cn} from "@/lib/utils";
// import {Plus} from "lucide-react";
//
// type EmptyStatePlaceholderProps = {
// url?: string;
// text: string;
// className?: string;
// }
//
// export const EmptyStatePlaceholder = ({url, text, className}: EmptyStatePlaceholderProps) => {
// return (
// <div className={cn("",className)}>{url ?
// <Link
// href={url}
// className={cn(
// "h-full",
// "flex flex-col items-center justify-center w-full rounded-2xl border border-dashed border-muted p-6 lg:p-10",
// "hover:bg-muted/50 transition-colors text-muted-foreground hover:text-primary text-center space-y-2"
// )}
// >
// <Plus className="w-5 h-5 lg:w-6 lg:h-6"/>
// <span className="text-sm lg:text-base font-medium">{text}</span>
// </Link>
// :
// <div className="flex h-full flex-col items-center justify-center py-12 text-center">
// <p className="text-lg text-muted-foreground">{text}</p>
// </div>
// }
// </div>
//
// )
// }
import Link from "next/link";
import {cn} from "@/lib/utils";
import {Plus} from "lucide-react";
type EmptyStatePlaceholderProps = {
url?: string;
onClick?: () => void;
text: string;
}
className?: string;
};
export const EmptyStatePlaceholder = ({url, text}: EmptyStatePlaceholderProps) => {
return (
<>{url ?
<Link
href={url}
className={cn(
"flex flex-col items-center justify-center w-full rounded-2xl border border-dashed border-muted p-6 lg:p-10",
"hover:bg-muted/50 transition-colors text-muted-foreground hover:text-primary text-center space-y-2"
)}
>
<Plus className="w-5 h-5 lg:w-6 lg:h-6"/>
<span className="text-sm lg:text-base font-medium">{text}</span>
</Link>
:
<div className="flex flex-col items-center justify-center py-12 text-center">
<p className="text-lg text-muted-foreground">{text}</p>
export const EmptyStatePlaceholder = ({
url,
onClick,
text,
className,
}: EmptyStatePlaceholderProps) => {
const content = (
<div
className={cn(
"flex h-full flex-col items-center justify-center w-full rounded-2xl border border-dashed border-muted p-6 lg:p-10",
"hover:bg-muted/50 transition-colors text-muted-foreground hover:text-primary text-center space-y-4 cursor-pointer",
onClick || url ? "cursor-pointer" : "cursor-default"
)}
{...(onClick ? {onClick} : url ? {asChild: true} : {})}
>
<Plus className="w-5 h-5 lg:w-6 lg:h-6"/>
<div>
<p className="text-sm ">{text}</p>
</div>
}
</>
</div>
);
)
}
return (
<div className={cn("", className)}>
{url ? <Link href={url}>{content}</Link> : content}
</div>
);
};
@@ -14,17 +14,22 @@ import {NotificationChannel} from "@/db/schema/09_notification-channel";
type OrganizationNotifierAddModalProps = {
notificationChannel?: NotificationChannel
organization?: OrganizationWithMembers;
open: boolean;
onOpenChangeAction: (open: boolean) => void;
}
export const NotifierAddEditModal = ({organization, notificationChannel}: OrganizationNotifierAddModalProps) => {
export const NotifierAddEditModal = ({
organization,
notificationChannel,
open,
onOpenChangeAction
}: OrganizationNotifierAddModalProps) => {
const isCreate = !Boolean(notificationChannel);
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<Dialog open={open} onOpenChange={onOpenChangeAction}>
<DialogTrigger asChild>
{isCreate ?
<Button>
@@ -49,7 +54,7 @@ export const NotifierAddEditModal = ({organization, notificationChannel}: Organi
<NotifierForm
defaultValues={notificationChannel}
organization={organization}
onSuccessAction={() => setOpen(false)}
onSuccessAction={() => onOpenChangeAction(false)}
/>
</DialogContent>
</Dialog>
@@ -8,6 +8,7 @@ import {
updateNotificationChannelAction
} from "@/components/wrappers/dashboard/common/notifier/notifier-form/notifier-form.action";
import {toast} from "sonner";
import {useState} from "react";
export type EditNotifierButtonProps = {
notificationChannel: NotificationChannel;
@@ -15,6 +16,8 @@ export type EditNotifierButtonProps = {
export const EditNotifierButton = ({notificationChannel}: EditNotifierButtonProps) => {
const router = useRouter();
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const mutation = useMutation({
mutationFn: async (value: boolean) => {
@@ -49,6 +52,8 @@ export const EditNotifierButton = ({notificationChannel}: EditNotifierButtonProp
/>
<NotifierAddEditModal
notificationChannel={notificationChannel}
open={isAddModalOpen}
onOpenChangeAction={setIsAddModalOpen}
/>
</>
);
@@ -45,7 +45,6 @@ export const NotifierForm = ({onSuccessAction, organization, defaultValues}: Not
});
const mutationCreateOrganisation = useMutation({
mutationFn: async (values: NotificationChannelFormType) => {
@@ -61,11 +60,11 @@ export const NotifierForm = ({onSuccessAction, organization, defaultValues}: Not
if (inner?.success) {
toast.success(inner.actionSuccess?.message);
// onSuccessAction?.();
onSuccessAction?.();
router.refresh();
} else {
toast.error(inner?.actionError?.message);
// onSuccessAction?.();
onSuccessAction?.();
}
}
});
@@ -134,7 +133,7 @@ export const NotifierForm = ({onSuccessAction, organization, defaultValues}: Not
<div className="flex gap-4 justify-end">
{defaultValues && (
<NotifierTestChannelButton notificationChannel={defaultValues} />
<NotifierTestChannelButton notificationChannel={defaultValues}/>
)}
@@ -1,41 +1,103 @@
// import {OrganizationWithMembers} from "@/db/schema/03_organization";
// import {NotificationChannel} 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 {NotifierAddEditModal} from "@/components/wrappers/dashboard/common/notifier/notifier-add-edit-modal";
// import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
//
//
// export type OrganizationNotifiersTabProps = {
// organization: OrganizationWithMembers;
// notificationChannels: NotificationChannel[];
// };
//
// export const OrganizationNotifiersTab = ({organization, notificationChannels}: OrganizationNotifiersTabProps) => {
//
// return (
// <div className="flex flex-col gap-y-4 h-full py-4">
// <EmptyStatePlaceholder className="h-full" text="No project available"/>
//
// <div className="flex flex-row gap-y-4 justify-between ">
// <div>
// <div className="flex items-center gap-3 mb-1">
// <h3 className="text-xl font-semibold text-balance">Notification Settings</h3>
// </div>
// <p className="text-muted-foreground leading-relaxed">
// Configure how and when you receive alerts about your services and infrastructure.
// </p>
// </div>
// <div>
// <NotifierAddEditModal organization={organization}/>
// </div>
// </div>
// <div className=" h-full">
// <CardsWithPagination data={notificationChannels} cardItem={NotifierCard} cardsPerPage={8}
// numberOfColumns={2} organization={organization}/>
// </div>
// </div>
// );
// };
import {OrganizationWithMembers} from "@/db/schema/03_organization";
import {Bell} from "lucide-react";
import {NotificationChannel} 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 {NotifierAddEditModal} from "@/components/wrappers/dashboard/common/notifier/notifier-add-edit-modal";
import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
import {useState} from "react";
import {cn} from "@/lib/utils";
export type OrganizationNotifiersTabProps = {
organization: OrganizationWithMembers;
notificationChannels: NotificationChannel[];
};
export const OrganizationNotifiersTab = ({organization ,notificationChannels}: OrganizationNotifiersTabProps) => {
export const OrganizationNotifiersTab = ({
organization,
notificationChannels,
}: OrganizationNotifiersTabProps) => {
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
// const hasNotifiers = 0;
const hasNotifiers = notificationChannels.length > 0;
return (
<div className="flex flex-col gap-y-4 h-full py-4">
<div className="flex flex-col gap-y-4 ">
<div>
<div className="flex items-center gap-3 mb-2">
<div
className="flex h-10 w-10 items-center justify-center rounded-lg bg-card border border-border">
<Bell className="h-5 w-5 text-foreground"/>
</div>
<h1 className="text-3xl font-semibold text-balance">Notification Settings</h1>
<div className="flex flex-col gap-y-6 h-full py-4">
<div className="h-full flex flex-col gap-y-6">
<div className={cn("hidden flex-row justify-between items-start", hasNotifiers && "flex")}>
<div className="max-w-2xl">
<h3 className="text-xl font-semibold text-balance mb-1">
Notification Settings
</h3>
<p className="text-muted-foreground leading-relaxed">
Configure how and when you receive alerts about your services and
infrastructure.
</p>
</div>
<p className="text-muted-foreground leading-relaxed">
Configure how and when you receive alerts about your services and infrastructure.
</p>
<NotifierAddEditModal
organization={organization}
open={isAddModalOpen}
onOpenChangeAction={setIsAddModalOpen}
/>
</div>
<div>
<NotifierAddEditModal organization={organization}/>
</div>
</div>
<div className=" h-full">
<CardsWithPagination data={notificationChannels} cardItem={NotifierCard} cardsPerPage={8} numberOfColumns={2} organization={organization}/>
{hasNotifiers ? (
<div className="h-full">
<CardsWithPagination
data={notificationChannels}
cardItem={NotifierCard}
cardsPerPage={8}
numberOfColumns={2}
organization={organization}
/>
</div>
) : (
<EmptyStatePlaceholder
text="No notification channels configured yet"
onClick={() => setIsAddModalOpen(true)}
className="h-full"
/>
)}
</div>
</div>
);
};
};