Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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 { slugify } from "@dotkomonline/utils"
import Link from "next/link"
import { type FC, useMemo } from "react"
import { useNotificationsByPayloadQuery } from "../queries"
Expand All @@ -13,7 +14,9 @@ import { openCreateEventNotificationModal } from "../components/create-event-not

export const NotificationsPage: FC = () => {
const { event } = useEventContext()
const { notifications, isLoading } = useNotificationsByPayloadQuery("EVENT", event.id)
const attendanceId = event.attendanceId ?? undefined
const eventPath = `${slugify(event.title)}/${event.id}`
const { notifications, isLoading } = useNotificationsByPayloadQuery("EVENT", eventPath)

const columnHelper = createColumnHelper<Notification>()

Expand Down Expand Up @@ -73,7 +76,10 @@ export const NotificationsPage: FC = () => {
<Stack gap="lg">
<Box>
<Title order={3}>Opprett varsling</Title>
<Button mt="md" onClick={openCreateEventNotificationModal({ eventId: event.id })}>
<Button
mt="md"
onClick={openCreateEventNotificationModal({ eventId: event.id, eventPath, attendanceId })}
>
Legg til ny varsling
</Button>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
"use client"

import { useNotificationWriteForm } from "@/app/(internal)/varslinger/write-form"
import { useTRPC } from "@/lib/trpc-client"
import type { AttendanceId } from "@dotkomonline/types"
import { type ContextModalProps, modals } from "@mantine/modals"
import { useQuery } from "@tanstack/react-query"
import { skipToken } from "@tanstack/react-query"
import type { FC } from "react"
import { useCreateEventNotificationMutation } from "../mutations"

interface CreateEventNotificationModalProps {
eventId: string
eventPath: string
attendanceId: AttendanceId | undefined
}

export const CreateEventNotificationModal: FC<ContextModalProps<CreateEventNotificationModalProps>> = ({
context,
id,
innerProps: { eventId },
innerProps: { eventId, eventPath, attendanceId },
}) => {
const close = () => context.closeModal(id)
const create = useCreateEventNotificationMutation(eventId)
const trpc = useTRPC()

const { data: attendeeUserIds = [] } = useQuery(
trpc.event.attendance.getAttendeeUserIds.queryOptions(attendanceId ?? skipToken)
)
const FormComponent = useNotificationWriteForm({
defaultValues: {
recipientIds: [],
taskId: null,
payloadType: "EVENT",
payload: eventId,
payload: eventPath,
actorGroupId: null,
},
onSubmit: (data) => {
create.mutate(data)
create.mutate({ ...data, recipientIds: attendeeUserIds })
close()
},
})
Expand All @@ -32,11 +45,11 @@ export const CreateEventNotificationModal: FC<ContextModalProps<CreateEventNotif
}

export const openCreateEventNotificationModal =
({ eventId }: CreateEventNotificationModalProps) =>
({ eventId, eventPath, attendanceId }: CreateEventNotificationModalProps) =>
() =>
modals.openContextModal({
modal: "event/notification/create",
title: "Legg inn ny varsling",
size: "lg",
innerProps: { eventId },
innerProps: { eventId, eventPath, attendanceId },
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { mapNotificationTypeToLabel, NotificationTypeSchema, NotificationWriteSc
import { getActiveGroupMembership } from "@dotkomonline/types"
import { type ContextModalProps, modals } from "@mantine/modals"
import type { FC } from "react"
import { useGroupAllQuery, useGroupMembersAllQuery } from "../queries"
import { useGroupMembersAllQuery } from "../queries"
import { useCreateGroupNotificationMutation } from "../mutations"

interface CreateGroupNotificationModalProps {
Expand All @@ -21,7 +21,6 @@ export const CreateGroupNotificationModal: FC<ContextModalProps<CreateGroupNotif
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)
Expand All @@ -34,6 +33,7 @@ export const CreateGroupNotificationModal: FC<ContextModalProps<CreateGroupNotif
taskId: null,
payloadType: "GROUP",
payload: groupSlug,
actorGroupId: groupSlug,
},
onSubmit: (data) => {
create.mutate(data)
Expand All @@ -43,7 +43,7 @@ export const CreateGroupNotificationModal: FC<ContextModalProps<CreateGroupNotif
fields: {
title: createTextInput({
label: "Tittel",
placeholder: "Juleball Påmeldingen er åpen!",
placeholder: "Nytt oppmøtested!",
required: true,
}),
shortDescription: createTextInput({
Expand All @@ -64,13 +64,6 @@ export const CreateGroupNotificationModal: FC<ContextModalProps<CreateGroupNotif
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,
}),
},
})

Expand Down
10 changes: 7 additions & 3 deletions apps/dashboard/src/app/(internal)/varslinger/[id]/edit-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ export const NotificationEditCard: FC = () => {
const FormComponent = useNotificationWriteForm({
label: "Oppdater varsling",
onSubmit: (data) => {
const { ...notificationData } = data
edit.mutate({ id: notification.id, input: notificationData })
edit.mutate({ id: notification.id, input: data })
},
defaultValues: {
...notification,
shortDescription: notification.shortDescription ?? "",
payload: notification.payload ?? "",
recipientIds: [],
},
defaultValues: { ...notification },
})
return <FormComponent />
}
13 changes: 10 additions & 3 deletions apps/dashboard/src/app/(internal)/varslinger/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
"use client"

import { Box, CloseButton, Group, Tabs, Title } from "@mantine/core"
import { IconPhoto } from "@tabler/icons-react"
import { IconListDetails, IconUsers } from "@tabler/icons-react"
import { useRouter, useSearchParams } from "next/navigation"
import { NotificationEditCard } from "./edit-card"
import { NotificationRecipientsPage } from "./recipients-page"
import { useNotificationDetailsContext } from "./provider"

const SIDEBAR_LINKS = [
{
icon: IconPhoto,
icon: IconListDetails,
label: "Info",
slug: "info",
component: NotificationEditCard,
},
{
icon: IconUsers,
label: "Mottakere",
slug: "mottakere",
component: NotificationRecipientsPage,
},
] as const

export default function NotificationDetailsPage() {
Expand All @@ -25,7 +32,7 @@ export default function NotificationDetailsPage() {
const handleTabChange = (value: string | null) => {
const params = new URLSearchParams(searchParams.toString())
params.set("tab", value ?? SIDEBAR_LINKS[0].slug)
router.replace(`/varsler/${notification.id}?${params.toString()}`)
router.replace(`/varslinger/${notification.id}?${params.toString()}`)
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"use client"

import { GenericTable } from "@/components/GenericTable"
import { DateTooltip } from "@/components/DateTooltip"
import { RecipientPickerInput } from "@/components/forms/RecipientPickerInput"
import { Box, Button, Skeleton, Stack, Title } from "@mantine/core"
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table"
import { type FC, useMemo, useState } from "react"
import { useNotificationRecipientsQuery } from "../queries"
import { useAddNotificationRecipientsMutation } from "../mutations"
import { useNotificationDetailsContext } from "./provider"

type Recipient = {
id: string
readAt: Date | null
userId: string
user: { id: string; name: string | null }
}

export const NotificationRecipientsPage: FC = () => {
const { notification } = useNotificationDetailsContext()
const { recipients, isLoading } = useNotificationRecipientsQuery(notification.id)
const addRecipients = useAddNotificationRecipientsMutation(notification.id)
const [recipientIds, setRecipientIds] = useState<string[]>([])

const columnHelper = createColumnHelper<Recipient>()

const columns = useMemo(
() => [
columnHelper.accessor((r) => r.user.name ?? r.userId, {
id: "name",
header: () => "Navn",
cell: (info) => info.getValue(),
sortingFn: "alphanumeric",
}),
columnHelper.accessor((r) => r.readAt, {
id: "readAt",
header: () => "Lest",
cell: (info) => {
const value = info.getValue()
return value ? <DateTooltip date={value} /> : "Ulest"
},
sortingFn: "datetime",
}),
],
[columnHelper]
)

const table = useReactTable({
data: recipients,
getCoreRowModel: getCoreRowModel(),
columns,
})

return (
<Stack gap="lg">
<Box>
<Title order={3} mb="sm">
Legg til mottakere
</Title>
<RecipientPickerInput value={recipientIds} onChange={setRecipientIds} />
<Button
mt="md"
disabled={recipientIds.length === 0 || addRecipients.isPending}
onClick={() => {
addRecipients.mutate(
{ notificationId: notification.id, recipientIds },
{ onSuccess: () => setRecipientIds([]) }
)
}}
>
Legg til {recipientIds.length > 0 ? `${recipientIds.length} mottaker(e)` : "mottakere"}
</Button>
</Box>

<Box>
<Title order={3} mb="sm">
Nåværende mottakere ({recipients.length})
</Title>
<Skeleton visible={isLoading}>
<GenericTable table={table} />
</Skeleton>
</Box>
</Stack>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const AllNotificationsTable = ({ notifications }: AllNotificationsTablePr
header: () => "Tittel",
sortingFn: "alphanumeric",
cell: (info) => (
<Anchor component={Link} size="sm" href={`/varsler/${info.row.original.id}`}>
<Anchor component={Link} size="sm" href={`/varslinger/${info.row.original.id}`}>
{info.getValue()}
</Anchor>
),
Expand Down
17 changes: 17 additions & 0 deletions apps/dashboard/src/app/(internal)/varslinger/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ export const useCreateNotificationMutation = () => {
)
}

export const useAddNotificationRecipientsMutation = (notificationId: string) => {
const trpc = useTRPC()
const queryClient = useQueryClient()
const notification = useQueryNotification()
return useMutation(
trpc.notification.addRecipients.mutationOptions({
onSuccess: async () => {
await queryClient.invalidateQueries(trpc.notification.getRecipients.queryOptions(notificationId))
notification.complete({ title: "Mottakere lagt til", message: "Mottakerne er lagt til varslingen." })
},
onError: (err) => {
notification.fail({ title: "Feil oppsto", message: err.toString() })
},
})
)
}

export const useEditNotificationMutation = () => {
const trpc = useTRPC()
const queryClient = useQueryClient()
Expand Down
8 changes: 7 additions & 1 deletion apps/dashboard/src/app/(internal)/varslinger/queries.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { useTRPC } from "@/lib/trpc-client"
import type { Pageable } from "@dotkomonline/utils"
import { useInfiniteQuery } from "@tanstack/react-query"
import { useInfiniteQuery, useQuery } from "@tanstack/react-query"
import { useMemo } from "react"

export const useNotificationRecipientsQuery = (notificationId: string) => {
const trpc = useTRPC()
const { data, ...query } = useQuery(trpc.notification.getRecipients.queryOptions(notificationId))
return { recipients: data ?? [], ...query }
}

export const useNotificationAllInfiniteQuery = (page?: Pageable) => {
const trpc = useTRPC()

Expand Down
Loading
Loading