diff --git a/apps/dashboard/src/app/(internal)/arrangementer/[id]/notification-page.tsx b/apps/dashboard/src/app/(internal)/arrangementer/[id]/notification-page.tsx new file mode 100644 index 0000000000..9505bfc024 --- /dev/null +++ b/apps/dashboard/src/app/(internal)/arrangementer/[id]/notification-page.tsx @@ -0,0 +1,88 @@ +"use client" + +import { GenericTable } from "@/components/GenericTable" +import { DateTooltip } from "@/components/DateTooltip" +import { mapNotificationPayloadTypeToLabel, mapNotificationTypeToLabel, type Notification } from "@dotkomonline/rpc" +import { Anchor, Box, Button, Skeleton, Stack, Title } from "@mantine/core" +import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table" +import Link from "next/link" +import { type FC, useMemo } from "react" +import { useNotificationsByPayloadQuery } from "../queries" +import { useEventContext } from "./provider" +import { openCreateEventNotificationModal } from "../components/create-event-notification-modal" + +export const NotificationsPage: FC = () => { + const { event } = useEventContext() + const { notifications, isLoading } = useNotificationsByPayloadQuery("EVENT", event.id) + + const columnHelper = createColumnHelper() + + const columns = useMemo( + () => [ + columnHelper.accessor((notification) => notification.title, { + id: "title", + header: () => "Tittel", + sortingFn: "alphanumeric", + cell: (info) => ( + + {info.getValue()} + + ), + }), + columnHelper.accessor((notification) => notification.shortDescription, { + id: "shortDescription", + header: () => "Kort beskrivelse", + cell: (info) => info.getValue(), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.type, { + id: "type", + header: () => "Type", + cell: (info) => mapNotificationTypeToLabel(info.getValue()), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.payloadType, { + id: "payloadType", + header: () => "Payload type", + cell: (info) => mapNotificationPayloadTypeToLabel(info.getValue()), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.createdAt, { + id: "createdAt", + header: () => "Opprettet", + cell: (info) => , + sortingFn: "datetime", + }), + ], + [columnHelper] + ) + + const tableOptions = useMemo( + () => ({ + data: notifications, + getCoreRowModel: getCoreRowModel(), + columns, + }), + [notifications, columns] + ) + + const table = useReactTable(tableOptions) + + return ( + + + + Opprett varsling + + + + + Varslinger + + + + + ) +} diff --git a/apps/dashboard/src/app/(internal)/arrangementer/[id]/page.tsx b/apps/dashboard/src/app/(internal)/arrangementer/[id]/page.tsx index d6ea9b2f5c..2fe785eefc 100644 --- a/apps/dashboard/src/app/(internal)/arrangementer/[id]/page.tsx +++ b/apps/dashboard/src/app/(internal)/arrangementer/[id]/page.tsx @@ -16,6 +16,7 @@ import { IconSelector, IconTrash, IconUser, + IconBell, } from "@tabler/icons-react" import { useRouter, useSearchParams } from "next/navigation" import { useDeleteEventMutation } from "../mutations" @@ -27,6 +28,7 @@ import { FeedbackPage } from "./feedback-page" import { PaymentPage } from "./payment-page" import { useEventContext } from "./provider" import { SelectionsPage } from "./selections-page" +import { NotificationsPage } from "./notification-page" const SIDEBAR_LINKS = [ { @@ -65,6 +67,12 @@ const SIDEBAR_LINKS = [ slug: "betaling", component: PaymentPage, }, + { + icon: IconBell, + label: "Varslinger", + slug: "varslinger", + component: NotificationsPage, + }, ] export default function EventWithAttendancesPage() { diff --git a/apps/dashboard/src/app/(internal)/arrangementer/components/create-event-notification-modal.tsx b/apps/dashboard/src/app/(internal)/arrangementer/components/create-event-notification-modal.tsx new file mode 100644 index 0000000000..7fc473b488 --- /dev/null +++ b/apps/dashboard/src/app/(internal)/arrangementer/components/create-event-notification-modal.tsx @@ -0,0 +1,42 @@ +import { useNotificationWriteForm } from "@/app/(internal)/varslinger/write-form" +import { type ContextModalProps, modals } from "@mantine/modals" +import type { FC } from "react" +import { useCreateEventNotificationMutation } from "../mutations" + +interface CreateEventNotificationModalProps { + eventId: string +} + +export const CreateEventNotificationModal: FC> = ({ + context, + id, + innerProps: { eventId }, +}) => { + const close = () => context.closeModal(id) + const create = useCreateEventNotificationMutation(eventId) + + const FormComponent = useNotificationWriteForm({ + defaultValues: { + recipientIds: [], + taskId: null, + payloadType: "EVENT", + payload: eventId, + }, + onSubmit: (data) => { + create.mutate(data) + close() + }, + }) + + return +} + +export const openCreateEventNotificationModal = + ({ eventId }: CreateEventNotificationModalProps) => + () => + modals.openContextModal({ + modal: "event/notification/create", + title: "Legg inn ny varsling", + size: "lg", + innerProps: { eventId }, + }) diff --git a/apps/dashboard/src/app/(internal)/arrangementer/mutations.ts b/apps/dashboard/src/app/(internal)/arrangementer/mutations.ts index 510a52708b..5c5154dc26 100644 --- a/apps/dashboard/src/app/(internal)/arrangementer/mutations.ts +++ b/apps/dashboard/src/app/(internal)/arrangementer/mutations.ts @@ -542,6 +542,37 @@ export const useUpdateAttendeeReservedMutation = () => { ) } +export const useCreateEventNotificationMutation = (eventId: string) => { + const trpc = useTRPC() + const queryClient = useQueryClient() + const notification = useQueryNotification() + return useMutation( + trpc.notification.create.mutationOptions({ + onMutate: () => { + notification.loading({ + title: "Lager varsling...", + message: "Varslingen blir opprettet.", + }) + }, + onSuccess: async (data) => { + await queryClient.invalidateQueries({ + queryKey: trpc.notification.findManyByPayload.queryKey({ payloadType: "EVENT", payload: eventId }), + }) + notification.complete({ + title: "Varsling opprettet", + message: `Varslingen "${data.title}" har blitt opprettet.`, + }) + }, + onError: (err) => { + notification.fail({ + title: "Feil oppsto", + message: `En feil oppsto under opprettelse av varslingen: ${err.toString()}.`, + }) + }, + }) + ) +} + export const useEventFileUploadMutation = () => { const trpc = useTRPC() diff --git a/apps/dashboard/src/app/(internal)/arrangementer/queries.ts b/apps/dashboard/src/app/(internal)/arrangementer/queries.ts index 17edbdf5c3..8ec1dab900 100644 --- a/apps/dashboard/src/app/(internal)/arrangementer/queries.ts +++ b/apps/dashboard/src/app/(internal)/arrangementer/queries.ts @@ -4,6 +4,7 @@ import { type SkipToken, useInfiniteQuery, useQuery } from "@tanstack/react-quer import { useTRPC } from "@/lib/trpc-client" import type { Pageable } from "@dotkomonline/utils" import { useMemo } from "react" +import type { NotificationPayloadType } from "@dotkomonline/rpc" interface UseEventAllQueryProps { filter: EventFilterQuery @@ -90,3 +91,10 @@ export const useFeedbackAnswersGetQuery = (formId: FeedbackFormId | SkipToken) = const trpc = useTRPC() return useQuery(trpc.event.feedback.getAllAnswers.queryOptions(formId)) } + +export const useNotificationsByPayloadQuery = (payloadType: NotificationPayloadType, payload: string) => { + const trpc = useTRPC() + const { data, ...query } = useQuery(trpc.notification.findManyByPayload.queryOptions({ payloadType, payload })) + + return { notifications: useMemo(() => data?.items ?? [], [data]), ...query } +} diff --git a/apps/dashboard/src/app/(internal)/grupper/[id]/group-notification-page.tsx b/apps/dashboard/src/app/(internal)/grupper/[id]/group-notification-page.tsx new file mode 100644 index 0000000000..a9ca2ffed6 --- /dev/null +++ b/apps/dashboard/src/app/(internal)/grupper/[id]/group-notification-page.tsx @@ -0,0 +1,88 @@ +"use client" + +import { GenericTable } from "@/components/GenericTable" +import { DateTooltip } from "@/components/DateTooltip" +import { mapNotificationPayloadTypeToLabel, mapNotificationTypeToLabel, type Notification } from "@dotkomonline/rpc" +import { Anchor, Box, Button, Skeleton, Stack, Title } from "@mantine/core" +import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table" +import Link from "next/link" +import { type FC, useMemo } from "react" +import { useNotificationsByPayloadQuery } from "../../arrangementer/queries" +import { openCreateGroupNotificationModal } from "../modals/create-group-notification-modal" +import { useGroupDetailsContext } from "./provider" + +export const GroupNotificationPage: FC = () => { + const { group } = useGroupDetailsContext() + const { notifications, isLoading } = useNotificationsByPayloadQuery("GROUP", group.slug) + + const columnHelper = createColumnHelper() + + const columns = useMemo( + () => [ + columnHelper.accessor((notification) => notification.title, { + id: "title", + header: () => "Tittel", + sortingFn: "alphanumeric", + cell: (info) => ( + + {info.getValue()} + + ), + }), + columnHelper.accessor((notification) => notification.shortDescription, { + id: "shortDescription", + header: () => "Kort beskrivelse", + cell: (info) => info.getValue(), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.type, { + id: "type", + header: () => "Type", + cell: (info) => mapNotificationTypeToLabel(info.getValue()), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.payloadType, { + id: "payloadType", + header: () => "Payload type", + cell: (info) => mapNotificationPayloadTypeToLabel(info.getValue()), + sortingFn: "alphanumeric", + }), + columnHelper.accessor((notification) => notification.createdAt, { + id: "createdAt", + header: () => "Opprettet", + cell: (info) => , + sortingFn: "datetime", + }), + ], + [columnHelper] + ) + + const tableOptions = useMemo( + () => ({ + data: notifications, + getCoreRowModel: getCoreRowModel(), + columns, + }), + [notifications, columns] + ) + + const table = useReactTable(tableOptions) + + return ( + + + + Opprett varsling + + + + + Varslinger + + + + + ) +} diff --git a/apps/dashboard/src/app/(internal)/grupper/[id]/page.tsx b/apps/dashboard/src/app/(internal)/grupper/[id]/page.tsx index bebaf5b84d..e74ae6bb58 100644 --- a/apps/dashboard/src/app/(internal)/grupper/[id]/page.tsx +++ b/apps/dashboard/src/app/(internal)/grupper/[id]/page.tsx @@ -1,10 +1,11 @@ "use client" import { Box, CloseButton, Group, Tabs, Title } from "@mantine/core" -import { IconCircles, IconListDetails, IconUsers, IconWheelchair } from "@tabler/icons-react" +import { IconCircles, IconListDetails, IconBell, IconUsers, IconWheelchair } from "@tabler/icons-react" import { useRouter, useSearchParams } from "next/navigation" import { GroupEditCard } from "./edit-card" import { GroupEventPage } from "./group-event-page" +import { GroupNotificationPage } from "./group-notification-page" import { GroupMembersPage } from "./members-page" import { useGroupDetailsContext } from "./provider" import { GroupRolesPage } from "./roles-page" @@ -34,6 +35,12 @@ const SIDEBAR_LINKS = [ slug: "arrangementer", component: GroupEventPage, }, + { + icon: IconBell, + label: "Varslinger", + slug: "varslinger", + component: GroupNotificationPage, + }, ] as const export default function GroupDetailsPage() { diff --git a/apps/dashboard/src/app/(internal)/grupper/modals/create-group-notification-modal.tsx b/apps/dashboard/src/app/(internal)/grupper/modals/create-group-notification-modal.tsx new file mode 100644 index 0000000000..e6cf17a0b9 --- /dev/null +++ b/apps/dashboard/src/app/(internal)/grupper/modals/create-group-notification-modal.tsx @@ -0,0 +1,88 @@ +import { useFormBuilder } from "@/components/forms/Form" +import { createRichTextInput } from "@/components/forms/RichTextInput/RichTextInput" +import { createSelectInput } from "@/components/forms/SelectInput" +import { createTextInput } from "@/components/forms/TextInput" +import { mapNotificationTypeToLabel, NotificationTypeSchema, NotificationWriteSchema } from "@dotkomonline/rpc" +import { getActiveGroupMembership } from "@dotkomonline/types" +import { type ContextModalProps, modals } from "@mantine/modals" +import type { FC } from "react" +import { useGroupAllQuery, useGroupMembersAllQuery } from "../queries" +import { useCreateGroupNotificationMutation } from "../mutations" + +interface CreateGroupNotificationModalProps { + groupSlug: string +} + +export const CreateGroupNotificationModal: FC> = ({ + context, + id, + innerProps: { groupSlug }, +}) => { + const close = () => context.closeModal(id) + const create = useCreateGroupNotificationMutation(groupSlug) + const { members } = useGroupMembersAllQuery(groupSlug) + const { groups } = useGroupAllQuery() + + const recipientIds = Array.from(members.entries()) + .filter(([, member]) => getActiveGroupMembership(member, groupSlug) !== null) + .map(([userId]) => userId) + + const FormComponent = useFormBuilder({ + schema: NotificationWriteSchema, + defaultValues: { + recipientIds, + taskId: null, + payloadType: "GROUP", + payload: groupSlug, + }, + onSubmit: (data) => { + create.mutate(data) + close() + }, + label: "Legg inn ny varsling", + fields: { + title: createTextInput({ + label: "Tittel", + placeholder: "Juleball Påmeldingen er åpen!", + required: true, + }), + shortDescription: createTextInput({ + label: "Kort beskrivelse", + placeholder: "En kort beskrivelse av varslingen", + required: true, + }), + content: createRichTextInput({ + label: "Innhold", + required: true, + }), + type: createSelectInput({ + data: Object.values(NotificationTypeSchema.Values).map((type) => ({ + value: type, + label: mapNotificationTypeToLabel(type), + })), + label: "Type", + placeholder: "Velg type", + required: true, + }), + actorGroupId: createSelectInput({ + label: "Ansvarlig gruppe", + placeholder: "Velg gruppe", + data: groups.map((group) => ({ value: group.slug, label: group.abbreviation })), + searchable: true, + required: true, + }), + }, + }) + + return +} + +export const openCreateGroupNotificationModal = + ({ groupSlug }: CreateGroupNotificationModalProps) => + () => + modals.openContextModal({ + modal: "group/notification/create", + title: "Legg inn ny varsling", + size: "lg", + innerProps: { groupSlug }, + }) diff --git a/apps/dashboard/src/app/(internal)/grupper/mutations.ts b/apps/dashboard/src/app/(internal)/grupper/mutations.ts index 0ed32f1176..47c133cb65 100644 --- a/apps/dashboard/src/app/(internal)/grupper/mutations.ts +++ b/apps/dashboard/src/app/(internal)/grupper/mutations.ts @@ -282,6 +282,37 @@ export const useLinkGroupMutation = () => { ) } +export const useCreateGroupNotificationMutation = (groupSlug: string) => { + const trpc = useTRPC() + const queryClient = useQueryClient() + const notification = useQueryNotification() + return useMutation( + trpc.notification.create.mutationOptions({ + onMutate: () => { + notification.loading({ + title: "Lager varsling...", + message: "Varslingen blir opprettet.", + }) + }, + onSuccess: async (data) => { + await queryClient.invalidateQueries({ + queryKey: trpc.notification.findManyByPayload.queryKey({ payloadType: "GROUP", payload: groupSlug }), + }) + notification.complete({ + title: "Varsling opprettet", + message: `Varslingen "${data.title}" har blitt opprettet.`, + }) + }, + onError: (err) => { + notification.fail({ + title: "Feil oppsto", + message: `En feil oppsto under opprettelse av varslingen: ${err.toString()}.`, + }) + }, + }) + ) +} + export const useGroupFileUploadMutation = () => { const trpc = useTRPC() diff --git a/apps/dashboard/src/app/ModalProvider.tsx b/apps/dashboard/src/app/ModalProvider.tsx index af022f4645..0c89a62db2 100644 --- a/apps/dashboard/src/app/ModalProvider.tsx +++ b/apps/dashboard/src/app/ModalProvider.tsx @@ -25,7 +25,9 @@ import type { FC, PropsWithChildren } from "react" import { QRCodeScannedModal } from "@/app/(internal)/arrangementer/components/qr-code-scanned-modal" import { NotifyAttendeesModal } from "@/app/(internal)/arrangementer/components/notify-attendees-modal" import { CreateGroupMemberModal } from "@/app/(internal)/grupper/modals/create-group-member-modal" +import { CreateGroupNotificationModal } from "@/app/(internal)/grupper/modals/create-group-notification-modal" import { CreateNotificationModal } from "@/app/(internal)/varslinger/modals/create-notification" +import { CreateEventNotificationModal } from "./(internal)/arrangementer/components/create-event-notification-modal" const modals = { "event/attendance/attendee/create": ManualCreateUserAttendModal, @@ -52,6 +54,8 @@ const modals = { "user/membership/update": EditMembershipModal, "image/upload": UploadImageModal, "notification/create": CreateNotificationModal, + "event/notification/create": CreateEventNotificationModal, + "group/notification/create": CreateGroupNotificationModal, } as const export const ModalProvider: FC = ({ children }) => ( diff --git a/apps/rpc/src/modules/core.ts b/apps/rpc/src/modules/core.ts index a608bd7cd6..73942fbc7b 100644 --- a/apps/rpc/src/modules/core.ts +++ b/apps/rpc/src/modules/core.ts @@ -192,7 +192,12 @@ export async function createServiceLayer( const contestRepository = getContestRepository() const notificationRepository = getNotificationRepository() - const notificationService = getNotificationService(notificationRepository, userRepository, attendanceRepository, eventEmitter) + const notificationService = getNotificationService( + notificationRepository, + userRepository, + attendanceRepository, + eventEmitter + ) const membershipService = getMembershipService() const emailService = isAmazonSesEmailFeatureEnabled(configuration) ? getEmailService(clients.sesClient, clients.sqsClient, configuration) diff --git a/apps/rpc/src/modules/event/event-service.ts b/apps/rpc/src/modules/event/event-service.ts index ab1f88f92b..dab19b7a10 100644 --- a/apps/rpc/src/modules/event/event-service.ts +++ b/apps/rpc/src/modules/event/event-service.ts @@ -128,7 +128,11 @@ export function getEventService( afterEvent.attendanceId !== null && afterEvent.hostingGroups.length > 0 ) { - const recipients = await notificationService.retrieveIntendedRecipientIds(handle, "EVENT_UPDATE", afterEvent.id) + const recipients = await notificationService.retrieveIntendedRecipientIds( + handle, + "EVENT_UPDATE", + afterEvent.id + ) await notificationService.create( handle, recipients, diff --git a/apps/rpc/src/modules/mark/personal-mark-service.ts b/apps/rpc/src/modules/mark/personal-mark-service.ts index 8e6f9955ee..fcb085b637 100644 --- a/apps/rpc/src/modules/mark/personal-mark-service.ts +++ b/apps/rpc/src/modules/mark/personal-mark-service.ts @@ -73,16 +73,7 @@ export function getPersonalMarkService( const title = mark.weight > 1 ? `Du har fått ${mark.weight} prikker` : `Du har fått en prikk` const description = `${mark.title} med grunn "${mark.details}"` - await notificationService.create( - handle, - [userId], - "NEW_MARK", - title, - description, - null, - "USER", - userId - ) + await notificationService.create(handle, [userId], "NEW_MARK", title, description, null, "USER", userId) await this.sendReceivedMarkEmail(handle, personalMark) return personalMark diff --git a/apps/rpc/src/modules/notification/notification-repository.ts b/apps/rpc/src/modules/notification/notification-repository.ts index 23dfe3ffeb..87252cfd10 100644 --- a/apps/rpc/src/modules/notification/notification-repository.ts +++ b/apps/rpc/src/modules/notification/notification-repository.ts @@ -2,6 +2,7 @@ import type { DBHandle } from "@dotkomonline/db" import { type Notification, type NotificationId, + type NotificationPayloadType, type NotificationWrite, type NotificationRecipientId, type NotificationRecipient, @@ -37,6 +38,12 @@ export interface NotificationRepository { recipientId: NotificationRecipientId, userId: UserId ): Promise + findManyByPayload( + handle: DBHandle, + payloadType: NotificationPayloadType, + payload: string, + page: Pageable + ): Promise findAllForUser(handle: DBHandle, userId: UserId, page: Pageable): Promise getUnreadCountForUser(handle: DBHandle, userId: UserId): Promise markAsRead(handle: DBHandle, notificationId: NotificationId, userId: UserId): Promise @@ -149,6 +156,21 @@ export function getNotificationRepository(): NotificationRepository { return result.count }, + async findManyByPayload(handle, payloadType, payload, page) { + const results = await handle.notification.findMany({ + ...pageQuery(page), + where: { payloadType, payload }, + include: { + actorGroup: { + include: { + roles: true, + }, + }, + }, + }) + return parseOrReport(NotificationSchema.array(), results) + }, + async findAllForUser(handle, userId, page) { const userNotifications = await handle.notificationRecipient.findMany({ where: { userId }, diff --git a/apps/rpc/src/modules/notification/notification-router.ts b/apps/rpc/src/modules/notification/notification-router.ts index 1bb82a9e04..b344ad1630 100644 --- a/apps/rpc/src/modules/notification/notification-router.ts +++ b/apps/rpc/src/modules/notification/notification-router.ts @@ -6,6 +6,7 @@ import { procedure, t } from "../../trpc" import { BasePaginateInputSchema, PaginateInputSchema } from "@dotkomonline/utils" import { NotificationDTOSchema, + NotificationPayloadTypeSchema, NotificationSchema, NotificationWriteSchema, UserNotificationDTOSchema, @@ -145,15 +146,35 @@ const findNotificationsProcedure = procedure export type OnNewNotificationInput = inferProcedureInput export type OnNewNotificationOutput = inferProcedureOutput -const onNewNotificationProcedure = procedure +const onNewNotificationProcedure = procedure.use(withAuthentication()).subscription(async function* ({ ctx, signal }) { + for await (const [data] of on(ctx.eventEmitter, "notification:new", { signal })) { + const { userId, notification } = data as { userId: string; notification: Notification } + if (userId !== ctx.principal.subject) { + continue + } + yield notification + } +}) + +export type FindNotificationsByPayloadInput = inferProcedureInput +export type FindNotificationsByPayloadOutput = inferProcedureOutput +const findNotificationsByPayloadProcedure = procedure + .input( + BasePaginateInputSchema.extend({ + payloadType: NotificationPayloadTypeSchema, + payload: z.string(), + }) + ) + .output(z.object({ items: z.array(NotificationDTOSchema), nextCursor: NotificationSchema.shape.id.optional() })) .use(withAuthentication()) - .subscription(async function* ({ ctx, signal }) { - for await (const [data] of on(ctx.eventEmitter, "notification:new", { signal })) { - const { userId, notification } = data as { userId: string; notification: Notification } - if (userId !== ctx.principal.subject) { - continue - } - yield notification + .use(withAuthorization(isCommitteeMember())) + .use(withDatabaseTransaction()) + .query(async ({ input, ctx }) => { + const { payloadType, payload, ...page } = input + const items = await ctx.notificationService.findManyByPayload(ctx.handle, payloadType, payload, page) + return { + items, + nextCursor: items.at(-1)?.id, } }) @@ -168,4 +189,5 @@ export const notificationRouter = t.router({ markAllAsRead: markAllAsReadProcedure, findMany: findNotificationsProcedure, onNewNotification: onNewNotificationProcedure, + findManyByPayload: findNotificationsByPayloadProcedure, }) diff --git a/apps/rpc/src/modules/notification/notification-service.ts b/apps/rpc/src/modules/notification/notification-service.ts index 70093839c3..70a5d2578b 100644 --- a/apps/rpc/src/modules/notification/notification-service.ts +++ b/apps/rpc/src/modules/notification/notification-service.ts @@ -34,6 +34,12 @@ export interface NotificationService { payloadType?: NotificationPayloadType, payload?: string | null ): Promise + findManyByPayload( + handle: DBHandle, + payloadType: NotificationPayloadType, + payload: string, + page: Pageable + ): Promise update( handle: DBHandle, notificationId: NotificationId, @@ -100,6 +106,10 @@ export function getNotificationService( return users.map((u) => u.id) }, + async findManyByPayload(handle, payloadType, payload, page) { + return await notificationRepository.findManyByPayload(handle, payloadType, payload, page) + }, + async update(handle, notificationId, notificationData) { return await notificationRepository.update(handle, notificationId, notificationData) }, diff --git a/apps/rpc/src/modules/task/task-executor.ts b/apps/rpc/src/modules/task/task-executor.ts index f52dd50830..3c2c89d2dd 100644 --- a/apps/rpc/src/modules/task/task-executor.ts +++ b/apps/rpc/src/modules/task/task-executor.ts @@ -122,7 +122,6 @@ export function getLocalTaskExecutor( handle, payload as InferTaskData ) - } // NOTE: If you have done everything correctly, TypeScript should SCREAM "Unreachable code detected" below. We