From fa689ea4aa4f6bd20ee27a23a5bbe9e00ad488ff Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 19 Feb 2026 19:16:26 +0700 Subject: [PATCH 1/2] Notification UI and house invitation --- messages/en.json | 34 +- messages/vi.json | 37 +- package.json | 1 + .../20260214113407_notification/migration.sql | 54 + prisma/schema.prisma | 53 +- project.inlang/settings.json | 4 +- src/components/Header.tsx | 57 +- src/components/Notification.tsx | 83 + .../form/house/user-invite-member-form.tsx | 2 +- .../house/current-user-invitation-list.tsx | 13 +- src/components/house/invite-user-dialog.tsx | 2 +- .../notification/notification-item.tsx | 59 + .../notification-type/invitation.tsx | 110 ++ src/components/user/add-new-user-dialog.tsx | 2 +- src/generated/prisma/browser.ts | 5 + src/generated/prisma/client.ts | 5 + src/generated/prisma/internal/class.ts | 14 +- .../prisma/internal/prismaNamespace.ts | 95 +- .../prisma/internal/prismaNamespaceBrowser.ts | 18 +- src/generated/prisma/models.ts | 1 + src/generated/prisma/models/Notification.ts | 1480 +++++++++++++++++ src/generated/prisma/models/User.ts | 170 ++ src/routeTree.gen.ts | 22 + .../(app)/(auth)/management/notifications.tsx | 59 + src/service/audit.api.ts | 31 +- src/service/house.api.ts | 101 +- src/service/house.schema.ts | 8 + src/service/notify.api.ts | 80 + src/service/notify.schema.ts | 6 + src/service/queries.ts | 15 + src/service/repository.ts | 12 +- src/styles.css | 16 + src/types/db.d.ts | 13 + src/types/enum.ts | 15 +- src/utils/helper.ts | 27 + 35 files changed, 2592 insertions(+), 112 deletions(-) create mode 100644 prisma/migrations/20260214113407_notification/migration.sql create mode 100644 src/components/Notification.tsx create mode 100644 src/components/notification/notification-item.tsx create mode 100644 src/components/notification/notification-type/invitation.tsx create mode 100644 src/generated/prisma/models/Notification.ts create mode 100644 src/routes/(app)/(auth)/management/notifications.tsx create mode 100644 src/service/notify.api.ts create mode 100644 src/service/notify.schema.ts diff --git a/messages/en.json b/messages/en.json index 28d3b6a..05ca731 100644 --- a/messages/en.json +++ b/messages/en.json @@ -13,7 +13,15 @@ "common_per_page": "Show", "common_select_page_size": "Select page size", "common_no_list": "Currently there is no data!", + "common_no_notify": "Không có thống báo", "common_is_required": "{field} is required.", + "common_time_ago_second": "{value} giây trước", + "common_time_ago_minute": "{value} phút trước", + "common_time_ago_hour": "{value} giờ trước", + "common_time_ago_day": "{value} ngày trước", + "common_time_ago_week": "{value} tuần trước", + "common_time_ago_month": "{value} tháng trước", + "common_time_ago_year": "{value} năm trước", "role_tags": [ { "match": { @@ -170,23 +178,42 @@ "houses_page_message_house_not_found": "House not found!", "houses_page_message_update_house_success": "Updated house successfully!", "houses_page_message_delete_house_success": "Delete house successfully!", + "houses_page_message_invite_member_success": "Invite member successfully!", + "houses_page_message_cancel_invitation_success": "Cancel invitation successfully!", "houses_page_house_active_btn": "Active", "houses_user_page_message_active_house_success": "Active \"{house}\" successfully!", "houses_user_page_block_action_title": "Action", "houses_user_page_action_invite_user": "Invite member", "houses_user_page_invite_label_to": "To", "houses_user_page_invite_label_status": "Status", + "invitation_not_found": "Invitation not found!", + "notification_page_notify_not_found": "Notification not found!", "invite_status": [ { "match": { "status=pending": "Pending", - "status=accept": "Accept", - "status=reject": "Reject", + "status=accepted": "Accept", + "status=rejected": "Reject", "status=expired": "Expired", "status=canceled": "Cancel" } } ], + "templates_title_notification": [ + { + "match": { + "title=INVITATION_HOUSE": "Invite to join house" + } + } + ], + "templates_message_notification": [ + { + "match": { + "message=INVITATION_HOUSE": "You have been invited to join house: {name}, do you accept?" + } + } + ], + "notification_page_message_invitation_success": "You have been invited to join house!", "backend_message": [ { "match": { @@ -197,7 +224,8 @@ "code=BANNED_USER": "Your account get banned, please contact administrator for more information!", "code=VALIDATION_ERROR": "Some field value invalid!", "code=USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": "User is not a member of the house", - "code=USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "This member has already been invited, waiting for the member to join!" + "code=USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "This member has already been invited, waiting for the member to join!", + "code=INVITATION_NOT_FOUND": "Invitation not found!" } } ] diff --git a/messages/vi.json b/messages/vi.json index c579f5c..eaf4c0f 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -13,7 +13,15 @@ "common_per_page": "Hiển thị", "common_select_page_size": "Chọn số lượng", "common_no_list": "Hiện tại chưa có dữ liệu nào!", + "common_no_notify": "Không có thống báo", "common_is_required": "{field} là bắt buộc.", + "common_time_ago_second": "{value} giây trước", + "common_time_ago_minute": "{value} phút trước", + "common_time_ago_hour": "{value} giờ trước", + "common_time_ago_day": "{value} ngày trước", + "common_time_ago_week": "{value} tuần trước", + "common_time_ago_month": "{value} tháng trước", + "common_time_ago_year": "{value} năm trước", "role_tags": [ { "match": { @@ -50,6 +58,8 @@ "ui_ban_btn": "Khóa", "ui_unban_btn": "Mở khóa", "ui_invite_btn": "Mời", + "ui_agree_btn": "Đồng ý", + "ui_reject_btn": "Từ chối", "ui_update_password_btn": "Đặt lại mật khẩu", "ui_change_role_btn": "Đặt lại quyền hạn", "ui_edit_user_btn": "Chỉnh sửa người dùng", @@ -172,23 +182,43 @@ "houses_page_message_house_not_found": "Không tìm thấy nhà này!", "houses_page_message_update_house_success": "Cập nhật nhà thành công!", "houses_page_message_delete_house_success": "Xóa nhà thành công!", + "houses_page_message_invite_member_success": "Mời thành viên thành công!", + "houses_page_message_cancel_invitation_success": "Hủy lời mời thành công!", "houses_page_house_active_btn": "Kích hoạt", "houses_user_page_message_active_house_success": "Kích hoạt \"{house}\" thành công!", "houses_user_page_block_action_title": "Hành động", "houses_user_page_action_invite_user": "Mời thành viên", "houses_user_page_invite_label_to": "Đến", "houses_user_page_invite_label_status": "Trạng thái", + "invitation_not_found": "Không tìm thấy lời mời!", + "notification_page_notify_not_found": "Không tìm thấy thống báo!", "invite_status": [ { "match": { "status=pending": "Đang chờ", - "status=accept": "Đồng ý", - "status=reject": "Không đồng ý", + "status=accepted": "Đồng ý", + "status=rejected": "Không đồng ý", "status=expired": "Hết hạn", "status=canceled": "Đã hủy" } } ], + "templates_title_notification": [ + { + "match": { + "title=INVITATION_HOUSE": "Mời tham gia nhà" + } + } + ], + "templates_message_notification": [ + { + "match": { + "message=INVITATION_HOUSE": "mời bạn tham gia nhà: {name}, bạn có đồng ý không?" + } + } + ], + "notification_page_message_invitation_success": "Bạn đã đồng ý tham gia nhà!", + "notification_page_message_invitation_rejected": "Bạn đã từ chối tham gia nhà!", "backend_message": [ { "match": { @@ -199,7 +229,8 @@ "code=BANNED_USER": "Bạn đã bị quản trị viên khóa tài khoản, hãy liên hệ quản trị viên để tìm hiểu thêm!", "code=VALIDATION_ERROR": "Có giá trị không hợp lệ!", "code=USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": "Người dùng này không phải thành viên nhà này", - "code=USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "Thành viên này đã được mời rồi, còn đang đợi thành viên đồng ý!" + "code=USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "Thành viên này đã được mời rồi, còn đang đợi thành viên đồng ý!", + "code=INVITATION_NOT_FOUND": "Không tìm thấy lời mời!" } } ] diff --git a/package.json b/package.json index 2498727..70089b1 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "format": "prettier", "check": "prettier --write . && eslint --fix", "post-cta-init": "npx create-db@latest", + "db:reset": "dotenv -e .env.local -- prisma migrate reset", "db:generate": "dotenv -e .env.local -- prisma generate", "db:push": "dotenv -e .env.local -- prisma db push", "db:migrate": "dotenv -e .env.local -- prisma migrate dev", diff --git a/prisma/migrations/20260214113407_notification/migration.sql b/prisma/migrations/20260214113407_notification/migration.sql new file mode 100644 index 0000000..f284c89 --- /dev/null +++ b/prisma/migrations/20260214113407_notification/migration.sql @@ -0,0 +1,54 @@ +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "audit" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "invitation" ALTER COLUMN "expiresAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "member" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "organization" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "session" ALTER COLUMN "expiresAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "setting" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ; + +-- AlterTable +ALTER TABLE "verification" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ, +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ; + +-- CreateTable +CREATE TABLE "notification" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "message" TEXT NOT NULL, + "type" TEXT NOT NULL DEFAULT 'system', + "link" TEXT, + "metadata" TEXT, + "createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + "readAt" TIMESTAMPTZ, + + CONSTRAINT "notification_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "notification_userId_idx" ON "notification"("userId"); + +-- AddForeignKey +ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 585f92e..10ec0c2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,11 +14,12 @@ model User { email String emailVerified Boolean @default(false) image String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz sessions Session[] accounts Account[] audit Audit[] + notification Notification[] role String? banned Boolean? @default(false) @@ -34,10 +35,10 @@ model User { model Session { id String @id @default(uuid()) - expiresAt DateTime + expiresAt DateTime @db.Timestamptz token String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz ipAddress String? userAgent String? userId String @@ -65,8 +66,8 @@ model Account { refreshTokenExpiresAt DateTime? scope String? password String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz @@index([userId]) @@map("account") @@ -77,8 +78,8 @@ model Verification { identifier String value String expiresAt DateTime - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz @@index([identifier]) @@map("verification") @@ -89,7 +90,7 @@ model Organization { name String slug String logo String? - createdAt DateTime + createdAt DateTime @db.Timestamptz metadata String? members Member[] invitations Invitation[] @@ -107,7 +108,7 @@ model Member { userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) role String @default("member") - createdAt DateTime + createdAt DateTime @db.Timestamptz @@index([organizationId]) @@index([userId]) @@ -121,8 +122,8 @@ model Invitation { email String role String? status String @default("pending") - expiresAt DateTime - createdAt DateTime @default(now()) + expiresAt DateTime @db.Timestamptz + createdAt DateTime @default(now()) @db.Timestamptz inviterId String user User @relation(fields: [inviterId], references: [id], onDelete: Cascade) @@ -138,8 +139,8 @@ model Setting { description String relation String @default("admin") - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz @@map("setting") } @@ -152,9 +153,29 @@ model Audit { recordId String oldValue String? newValue String? - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) @db.Timestamptz user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("audit") } + +model Notification { + id String @id @default(uuid()) + userId String + + title String + message String + type String @default("system") + + link String? + metadata String? + + createdAt DateTime @default(now()) @db.Timestamptz + readAt DateTime? @db.Timestamptz + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@map("notification") +} diff --git a/project.inlang/settings.json b/project.inlang/settings.json index 89c8bd3..87c83ef 100644 --- a/project.inlang/settings.json +++ b/project.inlang/settings.json @@ -3,8 +3,8 @@ "baseLocale": "en", "locales": ["en", "vi"], "modules": [ - "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", - "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js" ], "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" diff --git a/src/components/Header.tsx b/src/components/Header.tsx index a684b31..fe1b699 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,19 +1,7 @@ import { Separator } from '@base-ui/react/separator'; -import { m } from '@paraglide/messages'; -import { BellIcon } from '@phosphor-icons/react'; -import { Badge } from '@ui/badge'; -import { Button } from '@ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@ui/dropdown-menu'; import { SidebarTrigger } from '@ui/sidebar'; import { useAuth } from './auth/auth-provider'; +import Notification from './Notification'; import RouterBreadcrumb from './sidebar/router-breadcrumb'; export default function Header() { @@ -30,48 +18,7 @@ export default function Header() { /> -
- {session?.user && ( - - - - - - - {m.ui_label_notifications()} - - - - -
-

System

-

- 1 hour ago -

-
-
-
- - - - {m.ui_view_all_notifications()} - - -
-
- )} -
+
{session?.user && }
); diff --git a/src/components/Notification.tsx b/src/components/Notification.tsx new file mode 100644 index 0000000..dbee7cd --- /dev/null +++ b/src/components/Notification.tsx @@ -0,0 +1,83 @@ +import { notificationQueries } from '@/service/queries'; +import { formatTimeAgo } from '@/utils/helper'; +import { cn } from '@lib/utils'; +import { m } from '@paraglide/messages'; +import { BellIcon } from '@phosphor-icons/react'; +import { useQuery } from '@tanstack/react-query'; +import { Link } from '@tanstack/react-router'; +import { Button } from '@ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@ui/dropdown-menu'; +import { Item, ItemContent, ItemDescription, ItemTitle } from './ui/item'; + +const Notification = () => { + const { data: notifications } = useQuery(notificationQueries.topFive()); + + if (!notifications) return null; + + return ( + + + + + + + {m.ui_label_notifications()} + + + + {notifications.map((notify) => { + return ( + + + + + {m.templates_title_notification({ + title: notify.title as Parameters< + typeof m.templates_title_notification + >[0]['title'], + })} + + + {formatTimeAgo(new Date(notify.createdAt))} + + + + + ); + })} + + + + + + {m.ui_view_all_notifications()} + + + + + + ); +}; + +export default Notification; diff --git a/src/components/form/house/user-invite-member-form.tsx b/src/components/form/house/user-invite-member-form.tsx index 7e27c0d..c9891fd 100644 --- a/src/components/form/house/user-invite-member-form.tsx +++ b/src/components/form/house/user-invite-member-form.tsx @@ -38,7 +38,7 @@ const UserInviteMemberForm = ({ onSubmit }: FormProps) => { }); onSubmit(false); refetch(); - toast.success(m.houses_page_message_create_house_success(), { + toast.success(m.houses_page_message_invite_member_success(), { richColors: true, }); }, diff --git a/src/components/house/current-user-invitation-list.tsx b/src/components/house/current-user-invitation-list.tsx index b6f58e2..7aec2c8 100644 --- a/src/components/house/current-user-invitation-list.tsx +++ b/src/components/house/current-user-invitation-list.tsx @@ -1,3 +1,4 @@ +import useHasPermission from '@/hooks/use-has-permission'; import { cancelInvitation } from '@/service/house.api'; import { INVITE_STATUS } from '@/types/enum'; import { authClient } from '@lib/auth-client'; @@ -23,13 +24,17 @@ type InvitationListProps = { const CurrentUserInvitationList = ({ activeHouse }: InvitationListProps) => { const { refetch } = authClient.useActiveOrganization(); + const { hasPermission, isLoading } = useHasPermission( + 'invitation', + 'cancel', + true, + ); const { mutate: cancelInvitationMutation } = useMutation({ mutationFn: cancelInvitation, onSuccess: () => { refetch(); - // _setOpen(false); - toast.success(m.houses_page_message_delete_house_success(), { + toast.success(m.houses_page_message_cancel_invitation_success(), { richColors: true, }); }, @@ -66,7 +71,7 @@ const CurrentUserInvitationList = ({ activeHouse }: InvitationListProps) => { {activeHouse.invitations.length > 0 ? ( activeHouse.invitations.map((item) => ( - + @@ -87,7 +92,7 @@ const CurrentUserInvitationList = ({ activeHouse }: InvitationListProps) => {
- {item.status !== INVITE_STATUS.CANCELED && ( + {item.status === INVITE_STATUS.PENDING && hasPermission && ( + + + )} + + ); +}; + +export default NotificationInvitation; diff --git a/src/components/user/add-new-user-dialog.tsx b/src/components/user/add-new-user-dialog.tsx index 3861145..c42a642 100644 --- a/src/components/user/add-new-user-dialog.tsx +++ b/src/components/user/add-new-user-dialog.tsx @@ -25,7 +25,7 @@ const AddNewUserButton = () => { return ( - diff --git a/src/generated/prisma/browser.ts b/src/generated/prisma/browser.ts index 3cf601e..ea251ed 100644 --- a/src/generated/prisma/browser.ts +++ b/src/generated/prisma/browser.ts @@ -62,3 +62,8 @@ export type Setting = Prisma.SettingModel * */ export type Audit = Prisma.AuditModel +/** + * Model Notification + * + */ +export type Notification = Prisma.NotificationModel diff --git a/src/generated/prisma/client.ts b/src/generated/prisma/client.ts index fe60bf9..afc81bd 100644 --- a/src/generated/prisma/client.ts +++ b/src/generated/prisma/client.ts @@ -84,3 +84,8 @@ export type Setting = Prisma.SettingModel * */ export type Audit = Prisma.AuditModel +/** + * Model Notification + * + */ +export type Notification = Prisma.NotificationModel diff --git a/src/generated/prisma/internal/class.ts b/src/generated/prisma/internal/class.ts index 43b7647..3040677 100644 --- a/src/generated/prisma/internal/class.ts +++ b/src/generated/prisma/internal/class.ts @@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = { "clientVersion": "7.3.0", "engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735", "activeProvider": "postgresql", - "inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n name String\n email String\n emailVerified Boolean @default(false)\n image String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n sessions Session[]\n accounts Account[]\n audit Audit[]\n\n role String?\n banned Boolean? @default(false)\n banReason String?\n banExpires DateTime?\n\n members Member[]\n invitations Invitation[]\n\n @@unique([email])\n @@map(\"user\")\n}\n\nmodel Session {\n id String @id @default(uuid())\n expiresAt DateTime\n token String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n ipAddress String?\n userAgent String?\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n impersonatedBy String?\n\n activeOrganizationId String?\n\n @@unique([token])\n @@index([userId])\n @@map(\"session\")\n}\n\nmodel Account {\n id String @id @default(uuid())\n accountId String\n providerId String\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n accessToken String?\n refreshToken String?\n idToken String?\n accessTokenExpiresAt DateTime?\n refreshTokenExpiresAt DateTime?\n scope String?\n password String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"account\")\n}\n\nmodel Verification {\n id String @id @default(uuid())\n identifier String\n value String\n expiresAt DateTime\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([identifier])\n @@map(\"verification\")\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n slug String\n logo String?\n createdAt DateTime\n metadata String?\n members Member[]\n invitations Invitation[]\n\n color String? @default(\"#000000\")\n\n @@unique([slug])\n @@map(\"organization\")\n}\n\nmodel Member {\n id String @id @default(uuid())\n organizationId String\n organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n role String @default(\"member\")\n createdAt DateTime\n\n @@index([organizationId])\n @@index([userId])\n @@map(\"member\")\n}\n\nmodel Invitation {\n id String @id @default(uuid())\n organizationId String\n organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)\n email String\n role String?\n status String @default(\"pending\")\n expiresAt DateTime\n createdAt DateTime @default(now())\n inviterId String\n user User @relation(fields: [inviterId], references: [id], onDelete: Cascade)\n\n @@index([organizationId])\n @@index([email])\n @@map(\"invitation\")\n}\n\nmodel Setting {\n id String @id @default(uuid())\n key String @unique\n value String\n description String\n relation String @default(\"admin\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"setting\")\n}\n\nmodel Audit {\n id String @id @default(uuid())\n userId String\n action String\n tableName String\n recordId String\n oldValue String?\n newValue String?\n createdAt DateTime @default(now())\n\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@map(\"audit\")\n}\n", + "inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n name String\n email String\n emailVerified Boolean @default(false)\n image String?\n createdAt DateTime @default(now()) @db.Timestamptz\n updatedAt DateTime @updatedAt @db.Timestamptz\n sessions Session[]\n accounts Account[]\n audit Audit[]\n notification Notification[]\n\n role String?\n banned Boolean? @default(false)\n banReason String?\n banExpires DateTime?\n\n members Member[]\n invitations Invitation[]\n\n @@unique([email])\n @@map(\"user\")\n}\n\nmodel Session {\n id String @id @default(uuid())\n expiresAt DateTime @db.Timestamptz\n token String\n createdAt DateTime @default(now()) @db.Timestamptz\n updatedAt DateTime @updatedAt @db.Timestamptz\n ipAddress String?\n userAgent String?\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n impersonatedBy String?\n\n activeOrganizationId String?\n\n @@unique([token])\n @@index([userId])\n @@map(\"session\")\n}\n\nmodel Account {\n id String @id @default(uuid())\n accountId String\n providerId String\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n accessToken String?\n refreshToken String?\n idToken String?\n accessTokenExpiresAt DateTime?\n refreshTokenExpiresAt DateTime?\n scope String?\n password String?\n createdAt DateTime @default(now()) @db.Timestamptz\n updatedAt DateTime @updatedAt @db.Timestamptz\n\n @@index([userId])\n @@map(\"account\")\n}\n\nmodel Verification {\n id String @id @default(uuid())\n identifier String\n value String\n expiresAt DateTime\n createdAt DateTime @default(now()) @db.Timestamptz\n updatedAt DateTime @updatedAt @db.Timestamptz\n\n @@index([identifier])\n @@map(\"verification\")\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n slug String\n logo String?\n createdAt DateTime @db.Timestamptz\n metadata String?\n members Member[]\n invitations Invitation[]\n\n color String? @default(\"#000000\")\n\n @@unique([slug])\n @@map(\"organization\")\n}\n\nmodel Member {\n id String @id @default(uuid())\n organizationId String\n organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n role String @default(\"member\")\n createdAt DateTime @db.Timestamptz\n\n @@index([organizationId])\n @@index([userId])\n @@map(\"member\")\n}\n\nmodel Invitation {\n id String @id @default(uuid())\n organizationId String\n organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)\n email String\n role String?\n status String @default(\"pending\")\n expiresAt DateTime @db.Timestamptz\n createdAt DateTime @default(now()) @db.Timestamptz\n inviterId String\n user User @relation(fields: [inviterId], references: [id], onDelete: Cascade)\n\n @@index([organizationId])\n @@index([email])\n @@map(\"invitation\")\n}\n\nmodel Setting {\n id String @id @default(uuid())\n key String @unique\n value String\n description String\n relation String @default(\"admin\")\n\n createdAt DateTime @default(now()) @db.Timestamptz\n updatedAt DateTime @updatedAt @db.Timestamptz\n\n @@map(\"setting\")\n}\n\nmodel Audit {\n id String @id @default(uuid())\n userId String\n action String\n tableName String\n recordId String\n oldValue String?\n newValue String?\n createdAt DateTime @default(now()) @db.Timestamptz\n\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@map(\"audit\")\n}\n\nmodel Notification {\n id String @id @default(uuid())\n userId String\n\n title String\n message String\n type String @default(\"system\")\n\n link String?\n metadata String?\n\n createdAt DateTime @default(now()) @db.Timestamptz\n readAt DateTime? @db.Timestamptz\n\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@map(\"notification\")\n}\n", "runtimeDataModel": { "models": {}, "enums": {}, @@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = { } } -config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"accounts\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToUser\"},{\"name\":\"audit\",\"kind\":\"object\",\"type\":\"Audit\",\"relationName\":\"AuditToUser\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"banned\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"banReason\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"banExpires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"Member\",\"relationName\":\"MemberToUser\"},{\"name\":\"invitations\",\"kind\":\"object\",\"type\":\"Invitation\",\"relationName\":\"InvitationToUser\"}],\"dbName\":\"user\"},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userAgent\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"},{\"name\":\"impersonatedBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"activeOrganizationId\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"session\"},\"Account\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AccountToUser\"},{\"name\":\"accessToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"idToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accessTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"scope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"account\"},\"Verification\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"verification\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"slug\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"logo\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"Member\",\"relationName\":\"MemberToOrganization\"},{\"name\":\"invitations\",\"kind\":\"object\",\"type\":\"Invitation\",\"relationName\":\"InvitationToOrganization\"},{\"name\":\"color\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"organization\"},\"Member\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"MemberToOrganization\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"MemberToUser\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"member\"},\"Invitation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"InvitationToOrganization\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"inviterId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"InvitationToUser\"}],\"dbName\":\"invitation\"},\"Setting\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"relation\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"setting\"},\"Audit\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"action\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tableName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"recordId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"oldValue\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"newValue\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AuditToUser\"}],\"dbName\":\"audit\"}},\"enums\":{},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"accounts\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToUser\"},{\"name\":\"audit\",\"kind\":\"object\",\"type\":\"Audit\",\"relationName\":\"AuditToUser\"},{\"name\":\"notification\",\"kind\":\"object\",\"type\":\"Notification\",\"relationName\":\"NotificationToUser\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"banned\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"banReason\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"banExpires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"Member\",\"relationName\":\"MemberToUser\"},{\"name\":\"invitations\",\"kind\":\"object\",\"type\":\"Invitation\",\"relationName\":\"InvitationToUser\"}],\"dbName\":\"user\"},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userAgent\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"},{\"name\":\"impersonatedBy\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"activeOrganizationId\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"session\"},\"Account\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AccountToUser\"},{\"name\":\"accessToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"idToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accessTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenExpiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"scope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"account\"},\"Verification\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"verification\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"slug\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"logo\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"Member\",\"relationName\":\"MemberToOrganization\"},{\"name\":\"invitations\",\"kind\":\"object\",\"type\":\"Invitation\",\"relationName\":\"InvitationToOrganization\"},{\"name\":\"color\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"organization\"},\"Member\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"MemberToOrganization\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"MemberToUser\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"member\"},\"Invitation\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"InvitationToOrganization\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"inviterId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"InvitationToUser\"}],\"dbName\":\"invitation\"},\"Setting\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"relation\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"setting\"},\"Audit\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"action\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tableName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"recordId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"oldValue\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"newValue\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"AuditToUser\"}],\"dbName\":\"audit\"},\"Notification\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"message\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"link\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"readAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"NotificationToUser\"}],\"dbName\":\"notification\"}},\"enums\":{},\"types\":{}}") async function decodeBase64AsWasm(wasmBase64: string): Promise { const { Buffer } = await import('node:buffer') @@ -265,6 +265,16 @@ export interface PrismaClient< * ``` */ get audit(): Prisma.AuditDelegate; + + /** + * `prisma.notification`: Exposes CRUD operations for the **Notification** model. + * Example usage: + * ```ts + * // Fetch zero or more Notifications + * const notifications = await prisma.notification.findMany() + * ``` + */ + get notification(): Prisma.NotificationDelegate; } export function getPrismaClientClass(): PrismaClientConstructor { diff --git a/src/generated/prisma/internal/prismaNamespace.ts b/src/generated/prisma/internal/prismaNamespace.ts index c922939..7afbfe4 100644 --- a/src/generated/prisma/internal/prismaNamespace.ts +++ b/src/generated/prisma/internal/prismaNamespace.ts @@ -392,7 +392,8 @@ export const ModelName = { Member: 'Member', Invitation: 'Invitation', Setting: 'Setting', - Audit: 'Audit' + Audit: 'Audit', + Notification: 'Notification' } as const export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -408,7 +409,7 @@ export type TypeMap + fields: Prisma.NotificationFieldRefs + operations: { + findUnique: { + args: Prisma.NotificationFindUniqueArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.NotificationFindUniqueOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findFirst: { + args: Prisma.NotificationFindFirstArgs + result: runtime.Types.Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.NotificationFindFirstOrThrowArgs + result: runtime.Types.Utils.PayloadToResult + } + findMany: { + args: Prisma.NotificationFindManyArgs + result: runtime.Types.Utils.PayloadToResult[] + } + create: { + args: Prisma.NotificationCreateArgs + result: runtime.Types.Utils.PayloadToResult + } + createMany: { + args: Prisma.NotificationCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.NotificationCreateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + delete: { + args: Prisma.NotificationDeleteArgs + result: runtime.Types.Utils.PayloadToResult + } + update: { + args: Prisma.NotificationUpdateArgs + result: runtime.Types.Utils.PayloadToResult + } + deleteMany: { + args: Prisma.NotificationDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.NotificationUpdateManyArgs + result: BatchPayload + } + updateManyAndReturn: { + args: Prisma.NotificationUpdateManyAndReturnArgs + result: runtime.Types.Utils.PayloadToResult[] + } + upsert: { + args: Prisma.NotificationUpsertArgs + result: runtime.Types.Utils.PayloadToResult + } + aggregate: { + args: Prisma.NotificationAggregateArgs + result: runtime.Types.Utils.Optional + } + groupBy: { + args: Prisma.NotificationGroupByArgs + result: runtime.Types.Utils.Optional[] + } + count: { + args: Prisma.NotificationCountArgs + result: runtime.Types.Utils.Optional | number + } + } + } } } & { other: { @@ -1246,6 +1321,21 @@ export const AuditScalarFieldEnum = { export type AuditScalarFieldEnum = (typeof AuditScalarFieldEnum)[keyof typeof AuditScalarFieldEnum] +export const NotificationScalarFieldEnum = { + id: 'id', + userId: 'userId', + title: 'title', + message: 'message', + type: 'type', + link: 'link', + metadata: 'metadata', + createdAt: 'createdAt', + readAt: 'readAt' +} as const + +export type NotificationScalarFieldEnum = (typeof NotificationScalarFieldEnum)[keyof typeof NotificationScalarFieldEnum] + + export const SortOrder = { asc: 'asc', desc: 'desc' @@ -1428,6 +1518,7 @@ export type GlobalOmitConfig = { invitation?: Prisma.InvitationOmit setting?: Prisma.SettingOmit audit?: Prisma.AuditOmit + notification?: Prisma.NotificationOmit } /* Types for Logging */ diff --git a/src/generated/prisma/internal/prismaNamespaceBrowser.ts b/src/generated/prisma/internal/prismaNamespaceBrowser.ts index 3beb9e5..e1dc876 100644 --- a/src/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/src/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -59,7 +59,8 @@ export const ModelName = { Member: 'Member', Invitation: 'Invitation', Setting: 'Setting', - Audit: 'Audit' + Audit: 'Audit', + Notification: 'Notification' } as const export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -207,6 +208,21 @@ export const AuditScalarFieldEnum = { export type AuditScalarFieldEnum = (typeof AuditScalarFieldEnum)[keyof typeof AuditScalarFieldEnum] +export const NotificationScalarFieldEnum = { + id: 'id', + userId: 'userId', + title: 'title', + message: 'message', + type: 'type', + link: 'link', + metadata: 'metadata', + createdAt: 'createdAt', + readAt: 'readAt' +} as const + +export type NotificationScalarFieldEnum = (typeof NotificationScalarFieldEnum)[keyof typeof NotificationScalarFieldEnum] + + export const SortOrder = { asc: 'asc', desc: 'desc' diff --git a/src/generated/prisma/models.ts b/src/generated/prisma/models.ts index d7fa0ad..1bdae43 100644 --- a/src/generated/prisma/models.ts +++ b/src/generated/prisma/models.ts @@ -17,4 +17,5 @@ export type * from './models/Member.ts' export type * from './models/Invitation.ts' export type * from './models/Setting.ts' export type * from './models/Audit.ts' +export type * from './models/Notification.ts' export type * from './commonInputTypes.ts' \ No newline at end of file diff --git a/src/generated/prisma/models/Notification.ts b/src/generated/prisma/models/Notification.ts new file mode 100644 index 0000000..d19fe34 --- /dev/null +++ b/src/generated/prisma/models/Notification.ts @@ -0,0 +1,1480 @@ + +/* !!! This is code generated by Prisma. Do not edit directly. !!! */ +/* eslint-disable */ +// biome-ignore-all lint: generated file +// @ts-nocheck +/* + * This file exports the `Notification` model and its related types. + * + * 🟢 You can import this file directly. + */ +import type * as runtime from "@prisma/client/runtime/client" +import type * as $Enums from "../enums.ts" +import type * as Prisma from "../internal/prismaNamespace.ts" + +/** + * Model Notification + * + */ +export type NotificationModel = runtime.Types.Result.DefaultSelection + +export type AggregateNotification = { + _count: NotificationCountAggregateOutputType | null + _min: NotificationMinAggregateOutputType | null + _max: NotificationMaxAggregateOutputType | null +} + +export type NotificationMinAggregateOutputType = { + id: string | null + userId: string | null + title: string | null + message: string | null + type: string | null + link: string | null + metadata: string | null + createdAt: Date | null + readAt: Date | null +} + +export type NotificationMaxAggregateOutputType = { + id: string | null + userId: string | null + title: string | null + message: string | null + type: string | null + link: string | null + metadata: string | null + createdAt: Date | null + readAt: Date | null +} + +export type NotificationCountAggregateOutputType = { + id: number + userId: number + title: number + message: number + type: number + link: number + metadata: number + createdAt: number + readAt: number + _all: number +} + + +export type NotificationMinAggregateInputType = { + id?: true + userId?: true + title?: true + message?: true + type?: true + link?: true + metadata?: true + createdAt?: true + readAt?: true +} + +export type NotificationMaxAggregateInputType = { + id?: true + userId?: true + title?: true + message?: true + type?: true + link?: true + metadata?: true + createdAt?: true + readAt?: true +} + +export type NotificationCountAggregateInputType = { + id?: true + userId?: true + title?: true + message?: true + type?: true + link?: true + metadata?: true + createdAt?: true + readAt?: true + _all?: true +} + +export type NotificationAggregateArgs = { + /** + * Filter which Notification to aggregate. + */ + where?: Prisma.NotificationWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Notifications to fetch. + */ + orderBy?: Prisma.NotificationOrderByWithRelationInput | Prisma.NotificationOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: Prisma.NotificationWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Notifications from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Notifications. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned Notifications + **/ + _count?: true | NotificationCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: NotificationMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: NotificationMaxAggregateInputType +} + +export type GetNotificationAggregateType = { + [P in keyof T & keyof AggregateNotification]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType +} + + + + +export type NotificationGroupByArgs = { + where?: Prisma.NotificationWhereInput + orderBy?: Prisma.NotificationOrderByWithAggregationInput | Prisma.NotificationOrderByWithAggregationInput[] + by: Prisma.NotificationScalarFieldEnum[] | Prisma.NotificationScalarFieldEnum + having?: Prisma.NotificationScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: NotificationCountAggregateInputType | true + _min?: NotificationMinAggregateInputType + _max?: NotificationMaxAggregateInputType +} + +export type NotificationGroupByOutputType = { + id: string + userId: string + title: string + message: string + type: string + link: string | null + metadata: string | null + createdAt: Date + readAt: Date | null + _count: NotificationCountAggregateOutputType | null + _min: NotificationMinAggregateOutputType | null + _max: NotificationMaxAggregateOutputType | null +} + +type GetNotificationGroupByPayload = Prisma.PrismaPromise< + Array< + Prisma.PickEnumerable & + { + [P in ((keyof T) & (keyof NotificationGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : Prisma.GetScalarType + : Prisma.GetScalarType + } + > + > + + + +export type NotificationWhereInput = { + AND?: Prisma.NotificationWhereInput | Prisma.NotificationWhereInput[] + OR?: Prisma.NotificationWhereInput[] + NOT?: Prisma.NotificationWhereInput | Prisma.NotificationWhereInput[] + id?: Prisma.StringFilter<"Notification"> | string + userId?: Prisma.StringFilter<"Notification"> | string + title?: Prisma.StringFilter<"Notification"> | string + message?: Prisma.StringFilter<"Notification"> | string + type?: Prisma.StringFilter<"Notification"> | string + link?: Prisma.StringNullableFilter<"Notification"> | string | null + metadata?: Prisma.StringNullableFilter<"Notification"> | string | null + createdAt?: Prisma.DateTimeFilter<"Notification"> | Date | string + readAt?: Prisma.DateTimeNullableFilter<"Notification"> | Date | string | null + user?: Prisma.XOR +} + +export type NotificationOrderByWithRelationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + message?: Prisma.SortOrder + type?: Prisma.SortOrder + link?: Prisma.SortOrderInput | Prisma.SortOrder + metadata?: Prisma.SortOrderInput | Prisma.SortOrder + createdAt?: Prisma.SortOrder + readAt?: Prisma.SortOrderInput | Prisma.SortOrder + user?: Prisma.UserOrderByWithRelationInput +} + +export type NotificationWhereUniqueInput = Prisma.AtLeast<{ + id?: string + AND?: Prisma.NotificationWhereInput | Prisma.NotificationWhereInput[] + OR?: Prisma.NotificationWhereInput[] + NOT?: Prisma.NotificationWhereInput | Prisma.NotificationWhereInput[] + userId?: Prisma.StringFilter<"Notification"> | string + title?: Prisma.StringFilter<"Notification"> | string + message?: Prisma.StringFilter<"Notification"> | string + type?: Prisma.StringFilter<"Notification"> | string + link?: Prisma.StringNullableFilter<"Notification"> | string | null + metadata?: Prisma.StringNullableFilter<"Notification"> | string | null + createdAt?: Prisma.DateTimeFilter<"Notification"> | Date | string + readAt?: Prisma.DateTimeNullableFilter<"Notification"> | Date | string | null + user?: Prisma.XOR +}, "id"> + +export type NotificationOrderByWithAggregationInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + message?: Prisma.SortOrder + type?: Prisma.SortOrder + link?: Prisma.SortOrderInput | Prisma.SortOrder + metadata?: Prisma.SortOrderInput | Prisma.SortOrder + createdAt?: Prisma.SortOrder + readAt?: Prisma.SortOrderInput | Prisma.SortOrder + _count?: Prisma.NotificationCountOrderByAggregateInput + _max?: Prisma.NotificationMaxOrderByAggregateInput + _min?: Prisma.NotificationMinOrderByAggregateInput +} + +export type NotificationScalarWhereWithAggregatesInput = { + AND?: Prisma.NotificationScalarWhereWithAggregatesInput | Prisma.NotificationScalarWhereWithAggregatesInput[] + OR?: Prisma.NotificationScalarWhereWithAggregatesInput[] + NOT?: Prisma.NotificationScalarWhereWithAggregatesInput | Prisma.NotificationScalarWhereWithAggregatesInput[] + id?: Prisma.StringWithAggregatesFilter<"Notification"> | string + userId?: Prisma.StringWithAggregatesFilter<"Notification"> | string + title?: Prisma.StringWithAggregatesFilter<"Notification"> | string + message?: Prisma.StringWithAggregatesFilter<"Notification"> | string + type?: Prisma.StringWithAggregatesFilter<"Notification"> | string + link?: Prisma.StringNullableWithAggregatesFilter<"Notification"> | string | null + metadata?: Prisma.StringNullableWithAggregatesFilter<"Notification"> | string | null + createdAt?: Prisma.DateTimeWithAggregatesFilter<"Notification"> | Date | string + readAt?: Prisma.DateTimeNullableWithAggregatesFilter<"Notification"> | Date | string | null +} + +export type NotificationCreateInput = { + id?: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null + user: Prisma.UserCreateNestedOneWithoutNotificationInput +} + +export type NotificationUncheckedCreateInput = { + id?: string + userId: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null +} + +export type NotificationUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + user?: Prisma.UserUpdateOneRequiredWithoutNotificationNestedInput +} + +export type NotificationUncheckedUpdateInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + +export type NotificationCreateManyInput = { + id?: string + userId: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null +} + +export type NotificationUpdateManyMutationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + +export type NotificationUncheckedUpdateManyInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + userId?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + +export type NotificationListRelationFilter = { + every?: Prisma.NotificationWhereInput + some?: Prisma.NotificationWhereInput + none?: Prisma.NotificationWhereInput +} + +export type NotificationOrderByRelationAggregateInput = { + _count?: Prisma.SortOrder +} + +export type NotificationCountOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + message?: Prisma.SortOrder + type?: Prisma.SortOrder + link?: Prisma.SortOrder + metadata?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + readAt?: Prisma.SortOrder +} + +export type NotificationMaxOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + message?: Prisma.SortOrder + type?: Prisma.SortOrder + link?: Prisma.SortOrder + metadata?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + readAt?: Prisma.SortOrder +} + +export type NotificationMinOrderByAggregateInput = { + id?: Prisma.SortOrder + userId?: Prisma.SortOrder + title?: Prisma.SortOrder + message?: Prisma.SortOrder + type?: Prisma.SortOrder + link?: Prisma.SortOrder + metadata?: Prisma.SortOrder + createdAt?: Prisma.SortOrder + readAt?: Prisma.SortOrder +} + +export type NotificationCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.NotificationCreateWithoutUserInput[] | Prisma.NotificationUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.NotificationCreateOrConnectWithoutUserInput | Prisma.NotificationCreateOrConnectWithoutUserInput[] + createMany?: Prisma.NotificationCreateManyUserInputEnvelope + connect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] +} + +export type NotificationUncheckedCreateNestedManyWithoutUserInput = { + create?: Prisma.XOR | Prisma.NotificationCreateWithoutUserInput[] | Prisma.NotificationUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.NotificationCreateOrConnectWithoutUserInput | Prisma.NotificationCreateOrConnectWithoutUserInput[] + createMany?: Prisma.NotificationCreateManyUserInputEnvelope + connect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] +} + +export type NotificationUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.NotificationCreateWithoutUserInput[] | Prisma.NotificationUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.NotificationCreateOrConnectWithoutUserInput | Prisma.NotificationCreateOrConnectWithoutUserInput[] + upsert?: Prisma.NotificationUpsertWithWhereUniqueWithoutUserInput | Prisma.NotificationUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.NotificationCreateManyUserInputEnvelope + set?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + disconnect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + delete?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + connect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + update?: Prisma.NotificationUpdateWithWhereUniqueWithoutUserInput | Prisma.NotificationUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.NotificationUpdateManyWithWhereWithoutUserInput | Prisma.NotificationUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.NotificationScalarWhereInput | Prisma.NotificationScalarWhereInput[] +} + +export type NotificationUncheckedUpdateManyWithoutUserNestedInput = { + create?: Prisma.XOR | Prisma.NotificationCreateWithoutUserInput[] | Prisma.NotificationUncheckedCreateWithoutUserInput[] + connectOrCreate?: Prisma.NotificationCreateOrConnectWithoutUserInput | Prisma.NotificationCreateOrConnectWithoutUserInput[] + upsert?: Prisma.NotificationUpsertWithWhereUniqueWithoutUserInput | Prisma.NotificationUpsertWithWhereUniqueWithoutUserInput[] + createMany?: Prisma.NotificationCreateManyUserInputEnvelope + set?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + disconnect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + delete?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + connect?: Prisma.NotificationWhereUniqueInput | Prisma.NotificationWhereUniqueInput[] + update?: Prisma.NotificationUpdateWithWhereUniqueWithoutUserInput | Prisma.NotificationUpdateWithWhereUniqueWithoutUserInput[] + updateMany?: Prisma.NotificationUpdateManyWithWhereWithoutUserInput | Prisma.NotificationUpdateManyWithWhereWithoutUserInput[] + deleteMany?: Prisma.NotificationScalarWhereInput | Prisma.NotificationScalarWhereInput[] +} + +export type NotificationCreateWithoutUserInput = { + id?: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null +} + +export type NotificationUncheckedCreateWithoutUserInput = { + id?: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null +} + +export type NotificationCreateOrConnectWithoutUserInput = { + where: Prisma.NotificationWhereUniqueInput + create: Prisma.XOR +} + +export type NotificationCreateManyUserInputEnvelope = { + data: Prisma.NotificationCreateManyUserInput | Prisma.NotificationCreateManyUserInput[] + skipDuplicates?: boolean +} + +export type NotificationUpsertWithWhereUniqueWithoutUserInput = { + where: Prisma.NotificationWhereUniqueInput + update: Prisma.XOR + create: Prisma.XOR +} + +export type NotificationUpdateWithWhereUniqueWithoutUserInput = { + where: Prisma.NotificationWhereUniqueInput + data: Prisma.XOR +} + +export type NotificationUpdateManyWithWhereWithoutUserInput = { + where: Prisma.NotificationScalarWhereInput + data: Prisma.XOR +} + +export type NotificationScalarWhereInput = { + AND?: Prisma.NotificationScalarWhereInput | Prisma.NotificationScalarWhereInput[] + OR?: Prisma.NotificationScalarWhereInput[] + NOT?: Prisma.NotificationScalarWhereInput | Prisma.NotificationScalarWhereInput[] + id?: Prisma.StringFilter<"Notification"> | string + userId?: Prisma.StringFilter<"Notification"> | string + title?: Prisma.StringFilter<"Notification"> | string + message?: Prisma.StringFilter<"Notification"> | string + type?: Prisma.StringFilter<"Notification"> | string + link?: Prisma.StringNullableFilter<"Notification"> | string | null + metadata?: Prisma.StringNullableFilter<"Notification"> | string | null + createdAt?: Prisma.DateTimeFilter<"Notification"> | Date | string + readAt?: Prisma.DateTimeNullableFilter<"Notification"> | Date | string | null +} + +export type NotificationCreateManyUserInput = { + id?: string + title: string + message: string + type?: string + link?: string | null + metadata?: string | null + createdAt?: Date | string + readAt?: Date | string | null +} + +export type NotificationUpdateWithoutUserInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + +export type NotificationUncheckedUpdateWithoutUserInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + +export type NotificationUncheckedUpdateManyWithoutUserInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + title?: Prisma.StringFieldUpdateOperationsInput | string + message?: Prisma.StringFieldUpdateOperationsInput | string + type?: Prisma.StringFieldUpdateOperationsInput | string + link?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + metadata?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + readAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null +} + + + +export type NotificationSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + title?: boolean + message?: boolean + type?: boolean + link?: boolean + metadata?: boolean + createdAt?: boolean + readAt?: boolean + user?: boolean | Prisma.UserDefaultArgs +}, ExtArgs["result"]["notification"]> + +export type NotificationSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + title?: boolean + message?: boolean + type?: boolean + link?: boolean + metadata?: boolean + createdAt?: boolean + readAt?: boolean + user?: boolean | Prisma.UserDefaultArgs +}, ExtArgs["result"]["notification"]> + +export type NotificationSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ + id?: boolean + userId?: boolean + title?: boolean + message?: boolean + type?: boolean + link?: boolean + metadata?: boolean + createdAt?: boolean + readAt?: boolean + user?: boolean | Prisma.UserDefaultArgs +}, ExtArgs["result"]["notification"]> + +export type NotificationSelectScalar = { + id?: boolean + userId?: boolean + title?: boolean + message?: boolean + type?: boolean + link?: boolean + metadata?: boolean + createdAt?: boolean + readAt?: boolean +} + +export type NotificationOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "title" | "message" | "type" | "link" | "metadata" | "createdAt" | "readAt", ExtArgs["result"]["notification"]> +export type NotificationInclude = { + user?: boolean | Prisma.UserDefaultArgs +} +export type NotificationIncludeCreateManyAndReturn = { + user?: boolean | Prisma.UserDefaultArgs +} +export type NotificationIncludeUpdateManyAndReturn = { + user?: boolean | Prisma.UserDefaultArgs +} + +export type $NotificationPayload = { + name: "Notification" + objects: { + user: Prisma.$UserPayload + } + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: string + userId: string + title: string + message: string + type: string + link: string | null + metadata: string | null + createdAt: Date + readAt: Date | null + }, ExtArgs["result"]["notification"]> + composites: {} +} + +export type NotificationGetPayload = runtime.Types.Result.GetResult + +export type NotificationCountArgs = + Omit & { + select?: NotificationCountAggregateInputType | true + } + +export interface NotificationDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['Notification'], meta: { name: 'Notification' } } + /** + * Find zero or one Notification that matches the filter. + * @param {NotificationFindUniqueArgs} args - Arguments to find a Notification + * @example + * // Get one Notification + * const notification = await prisma.notification.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find one Notification that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {NotificationFindUniqueOrThrowArgs} args - Arguments to find a Notification + * @example + * // Get one Notification + * const notification = await prisma.notification.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Notification that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationFindFirstArgs} args - Arguments to find a Notification + * @example + * // Get one Notification + * const notification = await prisma.notification.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions> + + /** + * Find the first Notification that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationFindFirstOrThrowArgs} args - Arguments to find a Notification + * @example + * // Get one Notification + * const notification = await prisma.notification.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Find zero or more Notifications that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all Notifications + * const notifications = await prisma.notification.findMany() + * + * // Get first 10 Notifications + * const notifications = await prisma.notification.findMany({ take: 10 }) + * + * // Only select the `id` + * const notificationWithIdOnly = await prisma.notification.findMany({ select: { id: true } }) + * + */ + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>> + + /** + * Create a Notification. + * @param {NotificationCreateArgs} args - Arguments to create a Notification. + * @example + * // Create one Notification + * const Notification = await prisma.notification.create({ + * data: { + * // ... data to create a Notification + * } + * }) + * + */ + create(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Create many Notifications. + * @param {NotificationCreateManyArgs} args - Arguments to create many Notifications. + * @example + * // Create many Notifications + * const notification = await prisma.notification.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Create many Notifications and returns the data saved in the database. + * @param {NotificationCreateManyAndReturnArgs} args - Arguments to create many Notifications. + * @example + * // Create many Notifications + * const notification = await prisma.notification.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many Notifications and only return the `id` + * const notificationWithIdOnly = await prisma.notification.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "createManyAndReturn", GlobalOmitOptions>> + + /** + * Delete a Notification. + * @param {NotificationDeleteArgs} args - Arguments to delete one Notification. + * @example + * // Delete one Notification + * const Notification = await prisma.notification.delete({ + * where: { + * // ... filter to delete one Notification + * } + * }) + * + */ + delete(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Update one Notification. + * @param {NotificationUpdateArgs} args - Arguments to update one Notification. + * @example + * // Update one Notification + * const notification = await prisma.notification.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + /** + * Delete zero or more Notifications. + * @param {NotificationDeleteManyArgs} args - Arguments to filter Notifications to delete. + * @example + * // Delete a few Notifications + * const { count } = await prisma.notification.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more Notifications. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many Notifications + * const notification = await prisma.notification.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more Notifications and returns the data updated in the database. + * @param {NotificationUpdateManyAndReturnArgs} args - Arguments to update many Notifications. + * @example + * // Update many Notifications + * const notification = await prisma.notification.updateManyAndReturn({ + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * + * // Update zero or more Notifications and only return the `id` + * const notificationWithIdOnly = await prisma.notification.updateManyAndReturn({ + * select: { id: true }, + * where: { + * // ... provide filter here + * }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + updateManyAndReturn(args: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "updateManyAndReturn", GlobalOmitOptions>> + + /** + * Create or update one Notification. + * @param {NotificationUpsertArgs} args - Arguments to update or create a Notification. + * @example + * // Update or create a Notification + * const notification = await prisma.notification.upsert({ + * create: { + * // ... data to create a Notification + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the Notification we want to update + * } + * }) + */ + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__NotificationClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions> + + + /** + * Count the number of Notifications. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationCountArgs} args - Arguments to filter Notifications to count. + * @example + * // Count the number of Notifications + * const count = await prisma.notification.count({ + * where: { + * // ... the filter for the Notifications we want to count + * } + * }) + **/ + count( + args?: Prisma.Subset, + ): Prisma.PrismaPromise< + T extends runtime.Types.Utils.Record<'select', any> + ? T['select'] extends true + ? number + : Prisma.GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a Notification. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Prisma.Subset): Prisma.PrismaPromise> + + /** + * Group by Notification. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {NotificationGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends NotificationGroupByArgs, + HasSelectOrTake extends Prisma.Or< + Prisma.Extends<'skip', Prisma.Keys>, + Prisma.Extends<'take', Prisma.Keys> + >, + OrderByArg extends Prisma.True extends HasSelectOrTake + ? { orderBy: NotificationGroupByArgs['orderBy'] } + : { orderBy?: NotificationGroupByArgs['orderBy'] }, + OrderFields extends Prisma.ExcludeUnderscoreKeys>>, + ByFields extends Prisma.MaybeTupleToUnion, + ByValid extends Prisma.Has, + HavingFields extends Prisma.GetHavingFields, + HavingValid extends Prisma.Has, + ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, + InputErrors extends ByEmpty extends Prisma.True + ? `Error: "by" must not be empty.` + : HavingValid extends Prisma.False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Prisma.Keys + ? 'orderBy' extends Prisma.Keys + ? ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends Prisma.True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetNotificationGroupByPayload : Prisma.PrismaPromise +/** + * Fields of the Notification model + */ +readonly fields: NotificationFieldRefs; +} + +/** + * The delegate class that acts as a "Promise-like" for Notification. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ +export interface Prisma__NotificationClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + user = {}>(args?: Prisma.Subset>): Prisma.Prisma__UserClient, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise +} + + + + +/** + * Fields of the Notification model + */ +export interface NotificationFieldRefs { + readonly id: Prisma.FieldRef<"Notification", 'String'> + readonly userId: Prisma.FieldRef<"Notification", 'String'> + readonly title: Prisma.FieldRef<"Notification", 'String'> + readonly message: Prisma.FieldRef<"Notification", 'String'> + readonly type: Prisma.FieldRef<"Notification", 'String'> + readonly link: Prisma.FieldRef<"Notification", 'String'> + readonly metadata: Prisma.FieldRef<"Notification", 'String'> + readonly createdAt: Prisma.FieldRef<"Notification", 'DateTime'> + readonly readAt: Prisma.FieldRef<"Notification", 'DateTime'> +} + + +// Custom InputTypes +/** + * Notification findUnique + */ +export type NotificationFindUniqueArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter, which Notification to fetch. + */ + where: Prisma.NotificationWhereUniqueInput +} + +/** + * Notification findUniqueOrThrow + */ +export type NotificationFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter, which Notification to fetch. + */ + where: Prisma.NotificationWhereUniqueInput +} + +/** + * Notification findFirst + */ +export type NotificationFindFirstArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter, which Notification to fetch. + */ + where?: Prisma.NotificationWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Notifications to fetch. + */ + orderBy?: Prisma.NotificationOrderByWithRelationInput | Prisma.NotificationOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Notifications. + */ + cursor?: Prisma.NotificationWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Notifications from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Notifications. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Notifications. + */ + distinct?: Prisma.NotificationScalarFieldEnum | Prisma.NotificationScalarFieldEnum[] +} + +/** + * Notification findFirstOrThrow + */ +export type NotificationFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter, which Notification to fetch. + */ + where?: Prisma.NotificationWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Notifications to fetch. + */ + orderBy?: Prisma.NotificationOrderByWithRelationInput | Prisma.NotificationOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for Notifications. + */ + cursor?: Prisma.NotificationWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Notifications from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Notifications. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of Notifications. + */ + distinct?: Prisma.NotificationScalarFieldEnum | Prisma.NotificationScalarFieldEnum[] +} + +/** + * Notification findMany + */ +export type NotificationFindManyArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter, which Notifications to fetch. + */ + where?: Prisma.NotificationWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of Notifications to fetch. + */ + orderBy?: Prisma.NotificationOrderByWithRelationInput | Prisma.NotificationOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing Notifications. + */ + cursor?: Prisma.NotificationWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` Notifications from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` Notifications. + */ + skip?: number + distinct?: Prisma.NotificationScalarFieldEnum | Prisma.NotificationScalarFieldEnum[] +} + +/** + * Notification create + */ +export type NotificationCreateArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * The data needed to create a Notification. + */ + data: Prisma.XOR +} + +/** + * Notification createMany + */ +export type NotificationCreateManyArgs = { + /** + * The data used to create many Notifications. + */ + data: Prisma.NotificationCreateManyInput | Prisma.NotificationCreateManyInput[] + skipDuplicates?: boolean +} + +/** + * Notification createManyAndReturn + */ +export type NotificationCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelectCreateManyAndReturn | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * The data used to create many Notifications. + */ + data: Prisma.NotificationCreateManyInput | Prisma.NotificationCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationIncludeCreateManyAndReturn | null +} + +/** + * Notification update + */ +export type NotificationUpdateArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * The data needed to update a Notification. + */ + data: Prisma.XOR + /** + * Choose, which Notification to update. + */ + where: Prisma.NotificationWhereUniqueInput +} + +/** + * Notification updateMany + */ +export type NotificationUpdateManyArgs = { + /** + * The data used to update Notifications. + */ + data: Prisma.XOR + /** + * Filter which Notifications to update + */ + where?: Prisma.NotificationWhereInput + /** + * Limit how many Notifications to update. + */ + limit?: number +} + +/** + * Notification updateManyAndReturn + */ +export type NotificationUpdateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelectUpdateManyAndReturn | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * The data used to update Notifications. + */ + data: Prisma.XOR + /** + * Filter which Notifications to update + */ + where?: Prisma.NotificationWhereInput + /** + * Limit how many Notifications to update. + */ + limit?: number + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationIncludeUpdateManyAndReturn | null +} + +/** + * Notification upsert + */ +export type NotificationUpsertArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * The filter to search for the Notification to update in case it exists. + */ + where: Prisma.NotificationWhereUniqueInput + /** + * In case the Notification found by the `where` argument doesn't exist, create a new Notification with this data. + */ + create: Prisma.XOR + /** + * In case the Notification was found with the provided `where` argument, update it with this data. + */ + update: Prisma.XOR +} + +/** + * Notification delete + */ +export type NotificationDeleteArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + /** + * Filter which Notification to delete. + */ + where: Prisma.NotificationWhereUniqueInput +} + +/** + * Notification deleteMany + */ +export type NotificationDeleteManyArgs = { + /** + * Filter which Notifications to delete + */ + where?: Prisma.NotificationWhereInput + /** + * Limit how many Notifications to delete. + */ + limit?: number +} + +/** + * Notification without action + */ +export type NotificationDefaultArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null +} diff --git a/src/generated/prisma/models/User.ts b/src/generated/prisma/models/User.ts index 2dda63a..833934a 100644 --- a/src/generated/prisma/models/User.ts +++ b/src/generated/prisma/models/User.ts @@ -233,6 +233,7 @@ export type UserWhereInput = { sessions?: Prisma.SessionListRelationFilter accounts?: Prisma.AccountListRelationFilter audit?: Prisma.AuditListRelationFilter + notification?: Prisma.NotificationListRelationFilter members?: Prisma.MemberListRelationFilter invitations?: Prisma.InvitationListRelationFilter } @@ -252,6 +253,7 @@ export type UserOrderByWithRelationInput = { sessions?: Prisma.SessionOrderByRelationAggregateInput accounts?: Prisma.AccountOrderByRelationAggregateInput audit?: Prisma.AuditOrderByRelationAggregateInput + notification?: Prisma.NotificationOrderByRelationAggregateInput members?: Prisma.MemberOrderByRelationAggregateInput invitations?: Prisma.InvitationOrderByRelationAggregateInput } @@ -274,6 +276,7 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{ sessions?: Prisma.SessionListRelationFilter accounts?: Prisma.AccountListRelationFilter audit?: Prisma.AuditListRelationFilter + notification?: Prisma.NotificationListRelationFilter members?: Prisma.MemberListRelationFilter invitations?: Prisma.InvitationListRelationFilter }, "id" | "email"> @@ -327,6 +330,7 @@ export type UserCreateInput = { sessions?: Prisma.SessionCreateNestedManyWithoutUserInput accounts?: Prisma.AccountCreateNestedManyWithoutUserInput audit?: Prisma.AuditCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput members?: Prisma.MemberCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput } @@ -346,6 +350,7 @@ export type UserUncheckedCreateInput = { sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput } @@ -365,6 +370,7 @@ export type UserUpdateInput = { sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput members?: Prisma.MemberUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput } @@ -384,6 +390,7 @@ export type UserUncheckedUpdateInput = { sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput } @@ -571,6 +578,20 @@ export type UserUpdateOneRequiredWithoutAuditNestedInput = { update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutAuditInput> } +export type UserCreateNestedOneWithoutNotificationInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutNotificationInput + connect?: Prisma.UserWhereUniqueInput +} + +export type UserUpdateOneRequiredWithoutNotificationNestedInput = { + create?: Prisma.XOR + connectOrCreate?: Prisma.UserCreateOrConnectWithoutNotificationInput + upsert?: Prisma.UserUpsertWithoutNotificationInput + connect?: Prisma.UserWhereUniqueInput + update?: Prisma.XOR, Prisma.UserUncheckedUpdateWithoutNotificationInput> +} + export type UserCreateWithoutSessionsInput = { id?: string name: string @@ -585,6 +606,7 @@ export type UserCreateWithoutSessionsInput = { banExpires?: Date | string | null accounts?: Prisma.AccountCreateNestedManyWithoutUserInput audit?: Prisma.AuditCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput members?: Prisma.MemberCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput } @@ -603,6 +625,7 @@ export type UserUncheckedCreateWithoutSessionsInput = { banExpires?: Date | string | null accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput } @@ -637,6 +660,7 @@ export type UserUpdateWithoutSessionsInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput members?: Prisma.MemberUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput } @@ -655,6 +679,7 @@ export type UserUncheckedUpdateWithoutSessionsInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput } @@ -673,6 +698,7 @@ export type UserCreateWithoutAccountsInput = { banExpires?: Date | string | null sessions?: Prisma.SessionCreateNestedManyWithoutUserInput audit?: Prisma.AuditCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput members?: Prisma.MemberCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput } @@ -691,6 +717,7 @@ export type UserUncheckedCreateWithoutAccountsInput = { banExpires?: Date | string | null sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput } @@ -725,6 +752,7 @@ export type UserUpdateWithoutAccountsInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput members?: Prisma.MemberUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput } @@ -743,6 +771,7 @@ export type UserUncheckedUpdateWithoutAccountsInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput } @@ -762,6 +791,7 @@ export type UserCreateWithoutMembersInput = { sessions?: Prisma.SessionCreateNestedManyWithoutUserInput accounts?: Prisma.AccountCreateNestedManyWithoutUserInput audit?: Prisma.AuditCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput } @@ -780,6 +810,7 @@ export type UserUncheckedCreateWithoutMembersInput = { sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput } @@ -814,6 +845,7 @@ export type UserUpdateWithoutMembersInput = { sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput } @@ -832,6 +864,7 @@ export type UserUncheckedUpdateWithoutMembersInput = { sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput } @@ -850,6 +883,7 @@ export type UserCreateWithoutInvitationsInput = { sessions?: Prisma.SessionCreateNestedManyWithoutUserInput accounts?: Prisma.AccountCreateNestedManyWithoutUserInput audit?: Prisma.AuditCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput members?: Prisma.MemberCreateNestedManyWithoutUserInput } @@ -868,6 +902,7 @@ export type UserUncheckedCreateWithoutInvitationsInput = { sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput } @@ -902,6 +937,7 @@ export type UserUpdateWithoutInvitationsInput = { sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput members?: Prisma.MemberUpdateManyWithoutUserNestedInput } @@ -920,6 +956,7 @@ export type UserUncheckedUpdateWithoutInvitationsInput = { sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput } @@ -937,6 +974,7 @@ export type UserCreateWithoutAuditInput = { banExpires?: Date | string | null sessions?: Prisma.SessionCreateNestedManyWithoutUserInput accounts?: Prisma.AccountCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationCreateNestedManyWithoutUserInput members?: Prisma.MemberCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput } @@ -955,6 +993,7 @@ export type UserUncheckedCreateWithoutAuditInput = { banExpires?: Date | string | null sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput + notification?: Prisma.NotificationUncheckedCreateNestedManyWithoutUserInput members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput } @@ -989,6 +1028,7 @@ export type UserUpdateWithoutAuditInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUpdateManyWithoutUserNestedInput members?: Prisma.MemberUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput } @@ -1007,6 +1047,99 @@ export type UserUncheckedUpdateWithoutAuditInput = { banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput + notification?: Prisma.NotificationUncheckedUpdateManyWithoutUserNestedInput + members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput + invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput +} + +export type UserCreateWithoutNotificationInput = { + id?: string + name: string + email: string + emailVerified?: boolean + image?: string | null + createdAt?: Date | string + updatedAt?: Date | string + role?: string | null + banned?: boolean | null + banReason?: string | null + banExpires?: Date | string | null + sessions?: Prisma.SessionCreateNestedManyWithoutUserInput + accounts?: Prisma.AccountCreateNestedManyWithoutUserInput + audit?: Prisma.AuditCreateNestedManyWithoutUserInput + members?: Prisma.MemberCreateNestedManyWithoutUserInput + invitations?: Prisma.InvitationCreateNestedManyWithoutUserInput +} + +export type UserUncheckedCreateWithoutNotificationInput = { + id?: string + name: string + email: string + emailVerified?: boolean + image?: string | null + createdAt?: Date | string + updatedAt?: Date | string + role?: string | null + banned?: boolean | null + banReason?: string | null + banExpires?: Date | string | null + sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput + accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput + audit?: Prisma.AuditUncheckedCreateNestedManyWithoutUserInput + members?: Prisma.MemberUncheckedCreateNestedManyWithoutUserInput + invitations?: Prisma.InvitationUncheckedCreateNestedManyWithoutUserInput +} + +export type UserCreateOrConnectWithoutNotificationInput = { + where: Prisma.UserWhereUniqueInput + create: Prisma.XOR +} + +export type UserUpsertWithoutNotificationInput = { + update: Prisma.XOR + create: Prisma.XOR + where?: Prisma.UserWhereInput +} + +export type UserUpdateToOneWithWhereWithoutNotificationInput = { + where?: Prisma.UserWhereInput + data: Prisma.XOR +} + +export type UserUpdateWithoutNotificationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + role?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + banned?: Prisma.NullableBoolFieldUpdateOperationsInput | boolean | null + banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput + accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput + audit?: Prisma.AuditUpdateManyWithoutUserNestedInput + members?: Prisma.MemberUpdateManyWithoutUserNestedInput + invitations?: Prisma.InvitationUpdateManyWithoutUserNestedInput +} + +export type UserUncheckedUpdateWithoutNotificationInput = { + id?: Prisma.StringFieldUpdateOperationsInput | string + name?: Prisma.StringFieldUpdateOperationsInput | string + email?: Prisma.StringFieldUpdateOperationsInput | string + emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean + image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string + role?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + banned?: Prisma.NullableBoolFieldUpdateOperationsInput | boolean | null + banReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null + banExpires?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null + sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput + accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput + audit?: Prisma.AuditUncheckedUpdateManyWithoutUserNestedInput members?: Prisma.MemberUncheckedUpdateManyWithoutUserNestedInput invitations?: Prisma.InvitationUncheckedUpdateManyWithoutUserNestedInput } @@ -1020,6 +1153,7 @@ export type UserCountOutputType = { sessions: number accounts: number audit: number + notification: number members: number invitations: number } @@ -1028,6 +1162,7 @@ export type UserCountOutputTypeSelect = { + where?: Prisma.NotificationWhereInput +} + /** * UserCountOutputType without action */ @@ -1093,6 +1235,7 @@ export type UserSelect accounts?: boolean | Prisma.User$accountsArgs audit?: boolean | Prisma.User$auditArgs + notification?: boolean | Prisma.User$notificationArgs members?: boolean | Prisma.User$membersArgs invitations?: boolean | Prisma.User$invitationsArgs _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs @@ -1145,6 +1288,7 @@ export type UserInclude accounts?: boolean | Prisma.User$accountsArgs audit?: boolean | Prisma.User$auditArgs + notification?: boolean | Prisma.User$notificationArgs members?: boolean | Prisma.User$membersArgs invitations?: boolean | Prisma.User$invitationsArgs _count?: boolean | Prisma.UserCountOutputTypeDefaultArgs @@ -1158,6 +1302,7 @@ export type $UserPayload[] accounts: Prisma.$AccountPayload[] audit: Prisma.$AuditPayload[] + notification: Prisma.$NotificationPayload[] members: Prisma.$MemberPayload[] invitations: Prisma.$InvitationPayload[] } @@ -1570,6 +1715,7 @@ export interface Prisma__UserClient = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> accounts = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> audit = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> + notification = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> members = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> invitations = {}>(args?: Prisma.Subset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions> | Null> /** @@ -2071,6 +2217,30 @@ export type User$auditArgs = { + /** + * Select specific fields to fetch from the Notification + */ + select?: Prisma.NotificationSelect | null + /** + * Omit specific fields from the Notification + */ + omit?: Prisma.NotificationOmit | null + /** + * Choose, which related nodes to fetch as well + */ + include?: Prisma.NotificationInclude | null + where?: Prisma.NotificationWhereInput + orderBy?: Prisma.NotificationOrderByWithRelationInput | Prisma.NotificationOrderByWithRelationInput[] + cursor?: Prisma.NotificationWhereUniqueInput + take?: number + skip?: number + distinct?: Prisma.NotificationScalarFieldEnum | Prisma.NotificationScalarFieldEnum[] +} + /** * User.members */ diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 6afa5db..6d391c9 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -20,6 +20,7 @@ import { Route as appauthAccountRouteRouteImport } from './routes/(app)/(auth)/a import { Route as appauthManagementIndexRouteImport } from './routes/(app)/(auth)/management/index' import { Route as appauthKanriIndexRouteImport } from './routes/(app)/(auth)/kanri/index' import { Route as appauthAccountIndexRouteImport } from './routes/(app)/(auth)/account/index' +import { Route as appauthManagementNotificationsRouteImport } from './routes/(app)/(auth)/management/notifications' import { Route as appauthManagementHousesRouteImport } from './routes/(app)/(auth)/management/houses' import { Route as appauthManagementDashboardRouteImport } from './routes/(app)/(auth)/management/dashboard' import { Route as appauthKanriUsersRouteImport } from './routes/(app)/(auth)/kanri/users' @@ -83,6 +84,12 @@ const appauthAccountIndexRoute = appauthAccountIndexRouteImport.update({ path: '/', getParentRoute: () => appauthAccountRouteRoute, } as any) +const appauthManagementNotificationsRoute = + appauthManagementNotificationsRouteImport.update({ + id: '/notifications', + path: '/notifications', + getParentRoute: () => appauthManagementRouteRoute, + } as any) const appauthManagementHousesRoute = appauthManagementHousesRouteImport.update({ id: '/houses', path: '/houses', @@ -147,6 +154,7 @@ export interface FileRoutesByFullPath { '/kanri/users': typeof appauthKanriUsersRoute '/management/dashboard': typeof appauthManagementDashboardRoute '/management/houses': typeof appauthManagementHousesRoute + '/management/notifications': typeof appauthManagementNotificationsRoute '/account/': typeof appauthAccountIndexRoute '/kanri/': typeof appauthKanriIndexRoute '/management/': typeof appauthManagementIndexRoute @@ -164,6 +172,7 @@ export interface FileRoutesByTo { '/kanri/users': typeof appauthKanriUsersRoute '/management/dashboard': typeof appauthManagementDashboardRoute '/management/houses': typeof appauthManagementHousesRoute + '/management/notifications': typeof appauthManagementNotificationsRoute '/account': typeof appauthAccountIndexRoute '/kanri': typeof appauthKanriIndexRoute '/management': typeof appauthManagementIndexRoute @@ -187,6 +196,7 @@ export interface FileRoutesById { '/(app)/(auth)/kanri/users': typeof appauthKanriUsersRoute '/(app)/(auth)/management/dashboard': typeof appauthManagementDashboardRoute '/(app)/(auth)/management/houses': typeof appauthManagementHousesRoute + '/(app)/(auth)/management/notifications': typeof appauthManagementNotificationsRoute '/(app)/(auth)/account/': typeof appauthAccountIndexRoute '/(app)/(auth)/kanri/': typeof appauthKanriIndexRoute '/(app)/(auth)/management/': typeof appauthManagementIndexRoute @@ -209,6 +219,7 @@ export interface FileRouteTypes { | '/kanri/users' | '/management/dashboard' | '/management/houses' + | '/management/notifications' | '/account/' | '/kanri/' | '/management/' @@ -226,6 +237,7 @@ export interface FileRouteTypes { | '/kanri/users' | '/management/dashboard' | '/management/houses' + | '/management/notifications' | '/account' | '/kanri' | '/management' @@ -248,6 +260,7 @@ export interface FileRouteTypes { | '/(app)/(auth)/kanri/users' | '/(app)/(auth)/management/dashboard' | '/(app)/(auth)/management/houses' + | '/(app)/(auth)/management/notifications' | '/(app)/(auth)/account/' | '/(app)/(auth)/kanri/' | '/(app)/(auth)/management/' @@ -338,6 +351,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof appauthAccountIndexRouteImport parentRoute: typeof appauthAccountRouteRoute } + '/(app)/(auth)/management/notifications': { + id: '/(app)/(auth)/management/notifications' + path: '/notifications' + fullPath: '/management/notifications' + preLoaderRoute: typeof appauthManagementNotificationsRouteImport + parentRoute: typeof appauthManagementRouteRoute + } '/(app)/(auth)/management/houses': { id: '/(app)/(auth)/management/houses' path: '/houses' @@ -443,6 +463,7 @@ const appauthKanriRouteRouteWithChildren = interface appauthManagementRouteRouteChildren { appauthManagementDashboardRoute: typeof appauthManagementDashboardRoute appauthManagementHousesRoute: typeof appauthManagementHousesRoute + appauthManagementNotificationsRoute: typeof appauthManagementNotificationsRoute appauthManagementIndexRoute: typeof appauthManagementIndexRoute } @@ -450,6 +471,7 @@ const appauthManagementRouteRouteChildren: appauthManagementRouteRouteChildren = { appauthManagementDashboardRoute: appauthManagementDashboardRoute, appauthManagementHousesRoute: appauthManagementHousesRoute, + appauthManagementNotificationsRoute: appauthManagementNotificationsRoute, appauthManagementIndexRoute: appauthManagementIndexRoute, } diff --git a/src/routes/(app)/(auth)/management/notifications.tsx b/src/routes/(app)/(auth)/management/notifications.tsx new file mode 100644 index 0000000..fb6f1be --- /dev/null +++ b/src/routes/(app)/(auth)/management/notifications.tsx @@ -0,0 +1,59 @@ +import Pagination from '@/components/Pagination'; +import { m } from '@/paraglide/messages'; +import { notificationQueries } from '@/service/queries'; +import NotificationItem from '@components/notification/notification-item'; +import { BellRingingIcon } from '@phosphor-icons/react'; +import { useQuery } from '@tanstack/react-query'; +import { createFileRoute } from '@tanstack/react-router'; +import { Card, CardHeader, CardTitle } from '@ui/card'; +import { useState } from 'react'; + +export const Route = createFileRoute('/(app)/(auth)/management/notifications')({ + component: RouteComponent, +}); + +function RouteComponent() { + const [page, setPage] = useState(1); + const { data, isLoading } = useQuery( + notificationQueries.list({ + page, + limit: 10, + }), + ); + + if (isLoading) return null; + + return ( +
+
+ + + + + {m.ui_label_notifications()} + + + + {data && data.result.length > 0 ? ( +
+ {data.result.map((notify) => { + return ; + })} + +
+ ) : ( +
+ {m.common_no_notify()} +
+ )} +
+
+ ); +} diff --git a/src/service/audit.api.ts b/src/service/audit.api.ts index 2f8090f..7c5b15c 100644 --- a/src/service/audit.api.ts +++ b/src/service/audit.api.ts @@ -34,23 +34,24 @@ export const getAllAudit = createServerFn({ method: 'GET' }) }, ], }; - const [auditlog, total]: [AuditWithUser[], number] = await Promise.all([ - await prisma.audit.findMany({ - where, - orderBy: { createdAt: 'desc' }, - include: { - user: { - select: { - id: true, - name: true, + const [auditlog, total]: [AuditWithUser[], number] = + await prisma.$transaction([ + prisma.audit.findMany({ + where, + orderBy: { createdAt: 'desc' }, + include: { + user: { + select: { + id: true, + name: true, + }, }, }, - }, - take: data.limit, - skip, - }), - await prisma.audit.count({ where }), - ]); + take: data.limit, + skip, + }), + prisma.audit.count({ where }), + ]); const totalPage = Math.ceil(+total / data.limit); diff --git a/src/service/house.api.ts b/src/service/house.api.ts index a58ac4e..bd05561 100644 --- a/src/service/house.api.ts +++ b/src/service/house.api.ts @@ -1,19 +1,20 @@ import { prisma } from '@/db'; import { OrganizationWhereInput } from '@/generated/prisma/models'; -import { DB_TABLE, LOG_ACTION } from '@/types/enum'; +import { DB_TABLE, LOG_ACTION, NOTIFICATION_TYPE } from '@/types/enum'; import { auth } from '@lib/auth'; import { parseError } from '@lib/errors'; import { authMiddleware } from '@lib/middleware'; import { createServerFn } from '@tanstack/react-start'; import { getRequestHeaders } from '@tanstack/react-start/server'; import { + actionInvitationSchema, baseHouse, houseCreateBESchema, houseEditBESchema, houseListSchema, invitationCreateBESchema, } from './house.schema'; -import { createAuditLog } from './repository'; +import { createAuditLog, createNotification } from './repository'; export const getAllHouse = createServerFn({ method: 'GET' }) .middleware([authMiddleware]) @@ -266,12 +267,31 @@ export const invitationMember = createServerFn({ method: 'POST' }) organizationId: data.houseId, }; + const cuser = await prisma.user.findUnique({ + where: { email: data.email }, + }); + const chouse = await prisma.organization.findUnique({ + where: { id: data.houseId }, + }); + const result = await auth.api.createInvitation({ body, headers, }); - if (result) { + if (result && cuser && chouse) { + await createNotification({ + type: NOTIFICATION_TYPE.INVITATION, + userId: cuser.id, + title: 'INVITATION_HOUSE', + message: 'INVITATION_HOUSE', + link: result.id, + metadata: JSON.stringify({ + house: chouse, + }), + readAt: null, + }); + await createAuditLog({ action: LOG_ACTION.CREATE, tableName: DB_TABLE.INVITATION, @@ -302,6 +322,81 @@ export const cancelInvitation = createServerFn({ method: 'POST' }) }, headers, }); + + if (result) { + const notification = await prisma.notification.findFirst({ + where: { link: data.id }, + }); + + if (notification) { + await prisma.notification.update({ + where: { id: notification.id }, + data: { link: null }, + }); + } + } + + return result; + } catch (error) { + console.error(error); + const { message, code } = parseError(error); + throw { message, code }; + } + }); + +export const acceptInvitation = createServerFn({ method: 'POST' }) + .middleware([authMiddleware]) + .inputValidator(actionInvitationSchema) + .handler(async ({ data }) => { + try { + const result = await prisma.invitation.update({ + where: { id: data.id }, + data: { status: 'accepted' }, + }); + + if (result) { + const notify = await prisma.notification.update({ + where: { id: data.notificationId }, + data: { link: null }, + }); + + await auth.api.addMember({ + body: { + userId: notify.userId, + organizationId: result.organizationId, + role: (result.role as 'admin' | 'owner' | 'member') || 'member', + }, + }); + } + + return result; + } catch (error) { + console.error(error); + const { message, code } = parseError(error); + throw { message, code }; + } + }); + +export const rejectInvitation = createServerFn({ method: 'POST' }) + .middleware([authMiddleware]) + .inputValidator(actionInvitationSchema) + .handler(async ({ data }) => { + try { + const headers = getRequestHeaders(); + const result = await auth.api.rejectInvitation({ + body: { + invitationId: data.id, + }, + headers, + }); + + if (result) { + await prisma.notification.update({ + where: { id: data.notificationId }, + data: { link: null }, + }); + } + return result; } catch (error) { console.error(error); diff --git a/src/service/house.schema.ts b/src/service/house.schema.ts index f98fd96..e8feb3b 100644 --- a/src/service/house.schema.ts +++ b/src/service/house.schema.ts @@ -49,6 +49,10 @@ export const RoleHouseEnum = z.enum( m.users_page_message_role_select(), ); +const baseInvitation = z.object({ + id: z.string().nonempty(m.invitation_not_found()), +}); + const invitationCreateSchema = z.object({ email: z .string() @@ -63,3 +67,7 @@ export const invitationCreateFESchema = invitationCreateSchema.extend({ export const invitationCreateBESchema = invitationCreateSchema.extend({ role: RoleHouseEnum, }); + +export const actionInvitationSchema = baseInvitation.extend({ + notificationId: z.string().nonempty(m.notification_page_notify_not_found()), +}); diff --git a/src/service/notify.api.ts b/src/service/notify.api.ts new file mode 100644 index 0000000..f3fd88c --- /dev/null +++ b/src/service/notify.api.ts @@ -0,0 +1,80 @@ +import { prisma } from '@/db'; +import { parseError } from '@lib/errors'; +import { authMiddleware } from '@lib/middleware'; +import { createServerFn } from '@tanstack/react-start'; +import { notificationListSchema } from './notify.schema'; + +export const getTopFiveNotification = createServerFn({ method: 'GET' }) + .middleware([authMiddleware]) + .handler(async ({ context: { user } }) => { + try { + const list = await prisma.notification.findMany({ + where: { + userId: user.id, + }, + orderBy: { + createdAt: 'asc', + }, + take: 5, + }); + + return list; + } catch (error) { + console.error(error); + const { message, code } = parseError(error); + throw { message, code }; + } + }); + +export const getAllNotifications = createServerFn({ method: 'GET' }) + .middleware([authMiddleware]) + .inputValidator(notificationListSchema) + .handler(async ({ data, context: { user } }) => { + try { + const skip = (data.page - 1) * data.limit; + + const [list, total]: [NotificationWithUser[], number] = + await prisma.$transaction([ + prisma.notification.findMany({ + where: { + userId: user.id, + }, + include: { + user: { + select: { + id: true, + name: true, + email: true, + image: true, + }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + take: data.limit, + skip, + }), + prisma.notification.count({ + where: { + userId: user.id, + }, + }), + ]); + + const totalPage = Math.ceil(+total / data.limit); + + return { + result: list, + pagination: { + currentPage: data.page, + totalPage, + totalItem: total, + }, + }; + } catch (error) { + console.error(error); + const { message, code } = parseError(error); + throw { message, code }; + } + }); diff --git a/src/service/notify.schema.ts b/src/service/notify.schema.ts new file mode 100644 index 0000000..8144e0e --- /dev/null +++ b/src/service/notify.schema.ts @@ -0,0 +1,6 @@ +import z from 'zod'; + +export const notificationListSchema = z.object({ + page: z.coerce.number().min(1).default(1), + limit: z.coerce.number().min(10).max(100).default(10), +}); diff --git a/src/service/queries.ts b/src/service/queries.ts index 25ec7ea..e977bb2 100644 --- a/src/service/queries.ts +++ b/src/service/queries.ts @@ -2,6 +2,7 @@ import { getSession } from '@lib/auth/session'; import { queryOptions } from '@tanstack/react-query'; import { getAllAudit } from './audit.api'; import { getAllHouse, getCurrentUserHouses } from './house.api'; +import { getAllNotifications, getTopFiveNotification } from './notify.api'; import { getAdminSettings, getCurrentUserLanguage, @@ -75,3 +76,17 @@ export const housesQueries = { queryFn: () => getCurrentUserHouses(), }), }; + +export const notificationQueries = { + all: ['notification'], + list: (params: { page: number; limit: number }) => + queryOptions({ + queryKey: [...notificationQueries.all, 'list', params], + queryFn: () => getAllNotifications({ data: params }), + }), + topFive: () => + queryOptions({ + queryKey: [...notificationQueries.all, 'topFive'], + queryFn: () => getTopFiveNotification(), + }), +}; diff --git a/src/service/repository.ts b/src/service/repository.ts index 3f67e49..5c9af90 100644 --- a/src/service/repository.ts +++ b/src/service/repository.ts @@ -1,5 +1,5 @@ import { prisma } from '@/db'; -import { Audit, Setting } from '@/generated/prisma/client'; +import { Audit, Notification, Setting } from '@/generated/prisma/client'; type AdminSettingValue = Pick; @@ -61,3 +61,13 @@ export const getInitialOrganization = async (userId: string) => { return organization; }; + +export const createNotification = async ( + data: Omit, +) => { + await prisma.notification.create({ + data: { + ...data, + }, + }); +}; diff --git a/src/styles.css b/src/styles.css index 8b9d0bc..5e505bb 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5,6 +5,22 @@ @custom-variant dark (&:is(.dark *)); +@theme { + --animate-bell-ring: bell-ring 4s .7s ease-in-out infinite; + + @keyframes bell-ring { + 0% { transform: rotate(0deg); } + 10% { transform: rotate(15deg); } + 20% { transform: rotate(-15deg); } + 30% { transform: rotate(10deg); } + 40% { transform: rotate(-10deg); } + 50% { transform: rotate(5deg); } + 60% { transform: rotate(-5deg); } + 70% { transform: rotate(0deg); } + 100% { transform: rotate(0deg); } + } +} + body { @apply m-0; font-family: var(--font-sans); diff --git a/src/types/db.d.ts b/src/types/db.d.ts index 0e5b9d0..c510ced 100644 --- a/src/types/db.d.ts +++ b/src/types/db.d.ts @@ -36,6 +36,19 @@ declare global { }; }; + type NotificationWithUser = Prisma.NotificationGetPayload<{ + include: { + user: { + select: { + id: true; + name: true; + email: true; + image: true; + }; + }; + }; + }>; + type ReturnError = Error & { code: string; message: string; diff --git a/src/types/enum.ts b/src/types/enum.ts index 690e506..670ca46 100644 --- a/src/types/enum.ts +++ b/src/types/enum.ts @@ -38,10 +38,21 @@ export type ROLE_NAME = (typeof ROLE_NAME)[keyof typeof ROLE_NAME]; export const INVITE_STATUS = { PENDING: 'pending', - ACCEPT: 'accept', - REJECT: 'reject', + ACCEPT: 'accepted', + REJECT: 'rejected', CANCELED: 'canceled', EXPIRED: 'expired', } as const; export type INVITE_STATUS = (typeof INVITE_STATUS)[keyof typeof INVITE_STATUS]; + +export const NOTIFICATION_TYPE = { + SYSTEM: 'system', + ERROR: 'error', + INVITATION: 'invitation', + HOUSE: 'house', + EXPIRED: 'expired', +} as const; + +export type NOTIFICATION_TYPE = + (typeof NOTIFICATION_TYPE)[keyof typeof NOTIFICATION_TYPE]; diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 5faffb3..aae0d1e 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -1,3 +1,5 @@ +import { m } from '@paraglide/messages'; + export function jsonSupport(jsonSTR: string) { try { const data = JSON.parse(jsonSTR); @@ -36,3 +38,28 @@ export function slugify(text: string) { .trim() .replace(/\s+/g, '-'); } + +const UNITS = [ + { limit: 60, divisor: 1, key: 'common_time_ago_second' }, + { limit: 60, divisor: 60, key: 'common_time_ago_minute' }, + { limit: 24, divisor: 60 * 60, key: 'common_time_ago_hour' }, + { limit: 30, divisor: 60 * 60 * 24, key: 'common_time_ago_day' }, + { limit: 12, divisor: 60 * 60 * 24 * 30, key: 'common_time_ago_month' }, + { limit: Infinity, divisor: 60 * 60 * 24 * 365, key: 'common_time_ago_year' }, +] as const; + +export function formatTimeAgo(input: Date | string): string { + const date = typeof input === 'string' ? new Date(input) : input; + const diffSec = Math.floor((Date.now() - date.getTime()) / 1000); + + if (diffSec < 10) return m.common_time_ago_second({ value: 1 }); + + for (const unit of UNITS) { + const value = Math.floor(diffSec / unit.divisor); + if (value < unit.limit) { + return m[unit.key]({ value }); + } + } + + return ''; +} -- 2.49.1 From ab745e6a2fc6f733e7d5dbf6db39a07f73c12002 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 21 Feb 2026 22:34:29 +0700 Subject: [PATCH 2/2] Added SSE function and add readAt for notification --- package.json | 3 +- pnpm-lock.yaml | 27 ++++++ prisma/data.ts | 11 +++ .../migration.sql | 5 +- prisma/schema.prisma | 3 +- prisma/seed.ts | 15 +++- src/components/Notification.tsx | 87 +++++++++++++------ src/generated/prisma/internal/class.ts | 2 +- src/hooks/use-sse.ts | 24 +++++ src/lib/notification.ts | 52 +++++++++++ src/routeTree.gen.ts | 21 +++++ .../(app)/(auth)/management/notifications.tsx | 1 + src/routes/(app)/route.tsx | 17 +++- src/routes/api/notify.ts | 48 ++++++++++ src/service/notify.api.ts | 55 +++++++++--- src/service/repository.ts | 7 ++ src/store/useNotificationStore.ts | 14 +++ 17 files changed, 349 insertions(+), 43 deletions(-) rename prisma/migrations/{20260214113407_notification => 20260221093213_notification}/migration.sql (91%) create mode 100644 src/hooks/use-sse.ts create mode 100644 src/lib/notification.ts create mode 100644 src/routes/api/notify.ts create mode 100644 src/store/useNotificationStore.ts diff --git a/package.json b/package.json index 70089b1..0096267 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,8 @@ "tailwindcss": "^4.0.6", "tw-animate-css": "^1.3.6", "vite-tsconfig-paths": "^6.0.5", - "zod": "^4.3.6" + "zod": "^4.3.6", + "zustand": "^5.0.11" }, "devDependencies": { "@inlang/paraglide-js": "2.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b66164..c7b2a32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,6 +104,9 @@ importers: zod: specifier: ^4.3.6 version: 4.3.6 + zustand: + specifier: ^5.0.11 + version: 5.0.11(@types/react@19.2.10)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: '@inlang/paraglide-js': specifier: 2.10.0 @@ -4866,6 +4869,24 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@5.0.11: + resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: '@acemir/cssom@0.9.31': {} @@ -9690,3 +9711,9 @@ snapshots: zod@3.25.76: {} zod@4.3.6: {} + + zustand@5.0.11(@types/react@19.2.10)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + optionalDependencies: + '@types/react': 19.2.10 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) diff --git a/prisma/data.ts b/prisma/data.ts index adef299..9e00b35 100644 --- a/prisma/data.ts +++ b/prisma/data.ts @@ -15,3 +15,14 @@ export const settingsData = [ description: 'The keywords of the site', }, ]; + +export const userData = [ + { + name: 'Raysam', + email: 'raysam024@gmail.com', + }, + { + name: 'Raysam', + email: 'juines.liu@gmail.com', + }, +]; diff --git a/prisma/migrations/20260214113407_notification/migration.sql b/prisma/migrations/20260221093213_notification/migration.sql similarity index 91% rename from prisma/migrations/20260214113407_notification/migration.sql rename to prisma/migrations/20260221093213_notification/migration.sql index f284c89..3a269e3 100644 --- a/prisma/migrations/20260214113407_notification/migration.sql +++ b/prisma/migrations/20260221093213_notification/migration.sql @@ -48,7 +48,10 @@ CREATE TABLE "notification" ( ); -- CreateIndex -CREATE INDEX "notification_userId_idx" ON "notification"("userId"); +CREATE INDEX "notification_userId_readAt_idx" ON "notification"("userId", "readAt"); + +-- CreateIndex +CREATE INDEX "notification_readAt_idx" ON "notification"("readAt"); -- AddForeignKey ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 10ec0c2..a88dabb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -176,6 +176,7 @@ model Notification { user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@index([userId]) + @@index([userId, readAt]) + @@index([readAt]) @@map("notification") } diff --git a/prisma/seed.ts b/prisma/seed.ts index bf6e5b6..fe80209 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,7 +1,7 @@ import { auth } from '@lib/auth'; import { PrismaPg } from '@prisma/adapter-pg'; import { PrismaClient } from '../src/generated/prisma/client.js'; -import { settingsData } from './data.js'; +import { settingsData, userData } from './data.js'; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL!, @@ -32,6 +32,19 @@ async function main() { } console.log('---------------Created admin user-----------------'); + + userData.map(async (user) => { + await auth.api.createUser({ + body: { + email: user.email, + password: 'Th@m!S@m!040390', + name: user.name, + role: 'user', + }, + }); + }); + + console.log('---------------Created member user-----------------'); await prisma.setting.deleteMany(); const listSettings = [ diff --git a/src/components/Notification.tsx b/src/components/Notification.tsx index dbee7cd..e6351e7 100644 --- a/src/components/Notification.tsx +++ b/src/components/Notification.tsx @@ -1,9 +1,11 @@ +import { updateReadedNotification } from '@/service/notify.api'; import { notificationQueries } from '@/service/queries'; +import useNotificationStore from '@/store/useNotificationStore'; import { formatTimeAgo } from '@/utils/helper'; import { cn } from '@lib/utils'; import { m } from '@paraglide/messages'; import { BellIcon } from '@phosphor-icons/react'; -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { Link } from '@tanstack/react-router'; import { Button } from '@ui/button'; import { @@ -15,15 +17,42 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@ui/dropdown-menu'; +import { useEffect, useState } from 'react'; +import { toast } from 'sonner'; import { Item, ItemContent, ItemDescription, ItemTitle } from './ui/item'; const Notification = () => { - const { data: notifications } = useQuery(notificationQueries.topFive()); + const [open, _setOpen] = useState(false); + const { hasNew, setHasNew } = useNotificationStore((state) => state); + const { data } = useQuery(notificationQueries.topFive()); - if (!notifications) return null; + const { mutate: updateReaded } = useMutation({ + mutationFn: () => updateReadedNotification(), + onError: (error: ReturnError) => { + const code = error.code as Parameters< + typeof m.backend_message + >[0]['code']; + toast.error(m.backend_message({ code }), { + richColors: true, + }); + }, + }); + + const onOpenNotification = (isOpen: boolean) => { + _setOpen(isOpen); + updateReaded(); + }; + + useEffect(() => { + if (data) { + setHasNew(data.hasNewNotify); + } + }, [data]); + + if (!data) return null; return ( - +