From b4749e7091649815f2918b81d3c79b0216ece5d2 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:18:47 +0100 Subject: [PATCH] feat: add user management (#134) --- apps/nextjs/package.json | 4 +- .../[locale]/manage/users/[userId]/page.tsx | 36 ++++ .../users/_components/user-list.component.tsx | 106 ++++++++++++ .../stepper-navigation.component.tsx | 74 ++++++++ .../create/_components/stepper.component.tsx | 156 +++++++++++++++++ .../app/[locale]/manage/users/create/page.tsx | 16 ++ .../src/app/[locale]/manage/users/page.tsx | 18 ++ packages/api/src/router/user.ts | 44 ++++- packages/translation/src/lang/en.ts | 46 +++++ packages/ui/package.json | 3 +- packages/ui/src/styles.css | 1 + pnpm-lock.yaml | 158 +++++++++++++----- 12 files changed, 614 insertions(+), 48 deletions(-) create mode 100644 apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx create mode 100644 apps/nextjs/src/app/[locale]/manage/users/_components/user-list.component.tsx create mode 100644 apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper-navigation.component.tsx create mode 100644 apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper.component.tsx create mode 100644 apps/nextjs/src/app/[locale]/manage/users/create/page.tsx create mode 100644 apps/nextjs/src/app/[locale]/manage/users/page.tsx diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 44ab2eed8..82d8265a2 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -47,9 +47,9 @@ "postcss-preset-mantine": "^1.13.0", "react": "18.2.0", "react-dom": "18.2.0", + "sass": "^1.71.0", "superjson": "2.2.1", - "use-deep-compare-effect": "^1.8.1", - "sass": "^1.71.0" + "use-deep-compare-effect": "^1.8.1" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx new file mode 100644 index 000000000..696dca8cb --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx @@ -0,0 +1,36 @@ +import { notFound } from "next/navigation"; + +import { getScopedI18n } from "@homarr/translation/server"; +import { Title } from "@homarr/ui"; + +import { api } from "~/trpc/server"; + +interface Props { + params: { + userId: string; + }; +} + +export async function generateMetadata({ params }: Props) { + const user = await api.user.getById({ + userId: params.userId, + }); + const t = await getScopedI18n("management.page.user.edit"); + const metaTitle = `${t("metaTitle", { username: user?.name })} • Homarr`; + + return { + title: metaTitle, + }; +} + +export default async function EditUserPage({ params }: Props) { + const user = await api.user.getById({ + userId: params.userId, + }); + + if (!user) { + notFound(); + } + + return Edit User {user.name}!; +} diff --git a/apps/nextjs/src/app/[locale]/manage/users/_components/user-list.component.tsx b/apps/nextjs/src/app/[locale]/manage/users/_components/user-list.component.tsx new file mode 100644 index 000000000..79446ddd5 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/_components/user-list.component.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { useMemo } from "react"; +import Link from "next/link"; +import type { MRT_ColumnDef } from "mantine-react-table"; +import { MantineReactTable, useMantineReactTable } from "mantine-react-table"; + +import type { RouterOutputs } from "@homarr/api"; +import { clientApi } from "@homarr/api/client"; +import { useScopedI18n } from "@homarr/translation/client"; +import { + ActionIcon, + Button, + Flex, + Group, + IconCheck, + IconEdit, + IconTrash, + Text, + ThemeIcon, + Title, + Tooltip, +} from "@homarr/ui"; + +interface UserListComponentProps { + initialUserList: RouterOutputs["user"]["getAll"]; +} + +export const UserListComponent = ({ + initialUserList, +}: UserListComponentProps) => { + const t = useScopedI18n("management.page.user.list"); + const { data, isLoading } = clientApi.user.getAll.useQuery(undefined, { + initialData: initialUserList, + }); + + const columns = useMemo< + MRT_ColumnDef[] + >( + () => [ + { + accessorKey: "name", + header: "Name", + }, + { + accessorKey: "email", + header: "Email", + Cell: ({ renderedCellValue, row }) => ( + + {row.original.email ? renderedCellValue : -} + {row.original.emailVerified && ( + + + + )} + + ), + }, + ], + [], + ); + + const table = useMantineReactTable({ + columns, + data, + enableRowSelection: true, + enableColumnOrdering: true, + enableGlobalFilter: false, + enableRowActions: true, + enableDensityToggle: false, + enableFullScreenToggle: false, + getRowId: (row) => row.id, + renderRowActions: ({ row }) => ( + + + + + + + + + + + + + ), + renderTopToolbarCustomActions: () => ( + + ), + state: { + isLoading: isLoading, + }, + }); + + return ( + <> + {t("title")} + + + ); +}; diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper-navigation.component.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper-navigation.component.tsx new file mode 100644 index 000000000..8d45c1442 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper-navigation.component.tsx @@ -0,0 +1,74 @@ +import Link from "next/link"; + +import { useI18n } from "@homarr/translation/client"; +import { + Button, + Card, + Group, + IconArrowBackUp, + IconArrowLeft, + IconArrowRight, + IconRotate, +} from "@homarr/ui"; + +interface StepperNavigationComponentProps { + hasPrevious: boolean; + hasNext: boolean; + isComplete: boolean; + isLoadingNextStep: boolean; + prevStep: () => void; + nextStep: () => void; + reset: () => void; +} + +export const StepperNavigationComponent = ({ + hasNext, + hasPrevious, + isComplete, + isLoadingNextStep, + nextStep, + prevStep, + reset, +}: StepperNavigationComponentProps) => { + const t = useI18n(); + return ( + + {!isComplete ? ( + + + + + ) : ( + + + + + )} + + ); +}; diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper.component.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper.component.tsx new file mode 100644 index 000000000..33fda941b --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/create/_components/stepper.component.tsx @@ -0,0 +1,156 @@ +"use client"; + +import { useState } from "react"; + +import { clientApi } from "@homarr/api/client"; +import { useForm, zodResolver } from "@homarr/form"; +import { useScopedI18n } from "@homarr/translation/client"; +import { + Avatar, + Card, + IconUserCheck, + Stack, + Stepper, + Text, + TextInput, + Title, +} from "@homarr/ui"; +import { z } from "@homarr/validation"; + +import { StepperNavigationComponent } from "./stepper-navigation.component"; + +export const UserCreateStepperComponent = () => { + const t = useScopedI18n("management.page.user.create"); + + const stepperMax = 4; + const [active, setActive] = useState(0); + const nextStep = () => + setActive((current) => (current < stepperMax ? current + 1 : current)); + const prevStep = () => + setActive((current) => (current > 0 ? current - 1 : current)); + const hasNext = active < stepperMax; + const hasPrevious = active > 0; + + const { mutateAsync, isPending } = clientApi.user.create.useMutation(); + + const generalForm = useForm({ + initialValues: { + username: "", + email: undefined, + }, + validate: zodResolver( + z.object({ + username: z.string().min(1), + email: z.string().email().or(z.string().length(0).optional()), + }), + ), + validateInputOnBlur: true, + validateInputOnChange: true, + }); + + const allForms = [generalForm]; + + const canNavigateToNextStep = allForms[active]?.isValid() ?? true; + + const controlledGoToNextStep = async () => { + if (active + 1 === stepperMax) { + await mutateAsync({ + name: generalForm.values.username, + email: generalForm.values.email, + }); + } + nextStep(); + }; + + const reset = () => { + setActive(0); + allForms.forEach((form) => { + form.reset(); + }); + }; + + return ( + <> + {t("title")} + + +
+ + + + + + + +
+
+ + Step 2 + + + 3 + + + + + {generalForm.values.username} + + {generalForm.values.username} + + + + + + + + + {t("step.completed.title")} + + + +
+ + + ); +}; diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx new file mode 100644 index 000000000..1d610e4c4 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx @@ -0,0 +1,16 @@ +import { getScopedI18n } from "@homarr/translation/server"; + +import { UserCreateStepperComponent } from "./_components/stepper.component"; + +export async function generateMetadata() { + const t = await getScopedI18n("management.page.user.create"); + const metaTitle = `${t("metaTitle")} • Homarr`; + + return { + title: metaTitle, + }; +} + +export default function CreateUserPage() { + return ; +} diff --git a/apps/nextjs/src/app/[locale]/manage/users/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/page.tsx new file mode 100644 index 000000000..6de37296c --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/page.tsx @@ -0,0 +1,18 @@ +import { getScopedI18n } from "@homarr/translation/server"; + +import { api } from "~/trpc/server"; +import { UserListComponent } from "./_components/user-list.component"; + +export async function generateMetadata() { + const t = await getScopedI18n("management.page.user.list"); + const metaTitle = `${t("metaTitle")} • Homarr`; + + return { + title: metaTitle, + }; +} + +export default async function UsersPage() { + const userList = await api.user.getAll(); + return ; +} diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index 2c9ed3b3c..2781a9b7a 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -3,8 +3,9 @@ import "server-only"; import { TRPCError } from "@trpc/server"; import { createSalt, hashPassword } from "@homarr/auth"; -import { createId, schema } from "@homarr/db"; -import { validation } from "@homarr/validation"; +import { createId, db, eq, schema } from "@homarr/db"; +import { users } from "@homarr/db/schema/sqlite"; +import { validation, z } from "@homarr/validation"; import { createTRPCRouter, publicProcedure } from "../trpc"; @@ -36,4 +37,43 @@ export const userRouter = createTRPCRouter({ salt, }); }), + getAll: publicProcedure.query(async () => { + return db.query.users.findMany({ + columns: { + id: true, + name: true, + email: true, + emailVerified: true, + image: true, + }, + }); + }), + getById: publicProcedure + .input(z.object({ userId: z.string() })) + .query(async ({ input }) => { + return db.query.users.findFirst({ + columns: { + id: true, + name: true, + email: true, + emailVerified: true, + image: true, + }, + where: eq(users.id, input.userId), + }); + }), + create: publicProcedure + .input( + z.object({ + name: z.string(), + email: z.string().email().or(z.string().length(0).optional()), + }), + ) + .mutation(async ({ input }) => { + await db.insert(users).values({ + id: createId(), + name: input.name, + email: input.email, + }); + }), }); diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts index 66d291f38..1c1dc6417 100644 --- a/packages/translation/src/lang/en.ts +++ b/packages/translation/src/lang/en.ts @@ -147,6 +147,8 @@ export default { saveChanges: "Save changes", cancel: "Cancel", confirm: "Confirm", + previous: "Previous", + next: "Next", }, multiSelect: { placeholder: "Pick one or more values", @@ -394,6 +396,50 @@ export default { }, }, }, + user: { + list: { + metaTitle: "Manage users", + title: "Users", + }, + edit: { + metaTitle: "Edit user {username}", + }, + create: { + metaTitle: "Create user", + title: "Create new user", + step: { + personalInformation: { + label: "Personal information", + field: { + username: { + label: "Username", + }, + email: { + label: "E-Mail", + }, + }, + }, + preferences: { + label: "Preferences", + description: "Coming soon", + }, + permissions: { + label: "Permissions", + description: "Coming soon", + }, + review: { + label: "Review", + }, + completed: { + title: "User created", + }, + }, + buttons: { + createAnother: "Create another user", + return: "Return to the user list", + }, + }, + }, }, }, } as const; diff --git a/packages/ui/package.json b/packages/ui/package.json index 4e119f19b..291319541 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -37,6 +37,7 @@ "dependencies": { "@mantine/core": "^7.5.3", "@mantine/dates": "^7.5.3", - "@tabler/icons-react": "^2.47.0" + "@tabler/icons-react": "^2.47.0", + "mantine-react-table": "2.0.0-alpha.17" } } diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css index 4a09a3d8b..2e6fce46e 100644 --- a/packages/ui/src/styles.css +++ b/packages/ui/src/styles.css @@ -1,2 +1,3 @@ @import "@mantine/core/styles.css"; @import "@mantine/dates/styles.css"; +@import "mantine-react-table/styles.css"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58e90a510..bd09167b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,7 +113,7 @@ importers: version: 5.21.7(@tanstack/react-query@5.21.7)(react@18.2.0) '@tanstack/react-query-next-experimental': specifier: 5.21.7 - version: 5.21.7(@tanstack/react-query@5.21.7)(next@14.1.1-canary.62)(react@18.2.0) + version: 5.21.7(@tanstack/react-query@5.21.7)(next@14.1.1-canary.63)(react@18.2.0) '@tiptap/extension-link': specifier: ^2.2.3 version: 2.2.3(@tiptap/core@2.2.3)(@tiptap/pm@2.2.3) @@ -128,7 +128,7 @@ importers: version: 11.0.0-next-beta.289(@trpc/server@11.0.0-next-beta.289) '@trpc/next': specifier: next - version: 11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/react-query@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(next@14.1.1-canary.62)(react-dom@18.2.0)(react@18.2.0) + version: 11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/react-query@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(next@14.1.1-canary.63)(react-dom@18.2.0)(react@18.2.0) '@trpc/react-query': specifier: next version: 11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(react-dom@18.2.0)(react@18.2.0) @@ -146,7 +146,7 @@ importers: version: 7.5.3(@mantine/core@7.5.3)(@mantine/hooks@7.5.3)(react-dom@18.2.0)(react@18.2.0) next: specifier: ^14.1.1-canary.62 - version: 14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) + version: 14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) postcss-preset-mantine: specifier: ^1.13.0 version: 1.13.0(postcss@8.4.35) @@ -262,10 +262,10 @@ importers: version: 0.9.1 next: specifier: ^14.1.1-canary.62 - version: 14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) + version: 14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) next-auth: specifier: 5.0.0-beta.13 - version: 5.0.0-beta.13(next@14.1.1-canary.62)(react@18.2.0) + version: 5.0.0-beta.13(next@14.1.1-canary.63)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -495,6 +495,9 @@ importers: '@tabler/icons-react': specifier: ^2.47.0 version: 2.47.0(react@18.2.0) + mantine-react-table: + specifier: 2.0.0-alpha.17 + version: 2.0.0-alpha.17(@mantine/core@7.5.3)(@mantine/dates@7.5.3)(@mantine/hooks@7.5.3)(@tabler/icons-react@2.47.0)(clsx@2.0.0)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0) devDependencies: '@homarr/eslint-config': specifier: workspace:^0.2.0 @@ -1670,8 +1673,8 @@ packages: - supports-color dev: false - /@next/env@14.1.1-canary.62: - resolution: {integrity: sha512-lW88VAxTGm6VeXWxHGCHnTQ3ndF9onShjpMibJVqRNsG49AAzzrk4LKb0+ILLWimIuRo8kYtX9/WNJMvzoMxgg==} + /@next/env@14.1.1-canary.63: + resolution: {integrity: sha512-yMhY/WajNMysbcdPkSNtGsQW6CBBqi/63C7nC404XVPTAVhsRQH6L23ls450aCxaUyoO9b5kvDUjmNKKiotdeQ==} dev: false /@next/eslint-plugin-next@14.1.0: @@ -1680,8 +1683,8 @@ packages: glob: 10.3.10 dev: false - /@next/swc-darwin-arm64@14.1.1-canary.62: - resolution: {integrity: sha512-o8yJK9TmweAP9tGm6sLyWD0sSB+n+3thRiHD0Chm+tsJN9vjHhACj11O6UIgPLI7vu+bbBPt3xrU+4wxIYexUQ==} + /@next/swc-darwin-arm64@14.1.1-canary.63: + resolution: {integrity: sha512-xoOv/29gn5SH3soiDOJWOkJJ98Z7rkKKNoPbzPfKvPgfcwAyeAl55SjraNfdlFiLE67Mp+AKauJiKtL6xJclOg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -1689,8 +1692,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@14.1.1-canary.62: - resolution: {integrity: sha512-R7/opKdbsm1du+LnFNgJPYLvguMMlOF3R0d7xAurAg7MBUwWUh2f50tHcTDrVzNbdZEhk1jhlcmS/lrCxv3uYA==} + /@next/swc-darwin-x64@14.1.1-canary.63: + resolution: {integrity: sha512-Rv9WDLjk+L1X1nvBgkO2HK39ujoe6tohcX+KW2rS+XXPRMcmmxPdIa4D20PYSsYa7AX9riJhRibBgpHuNXR4rQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -1698,8 +1701,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@14.1.1-canary.62: - resolution: {integrity: sha512-hKysZWKQ5uuUcxhJafPJFfFz/6nrH2t6fAZfxV4skB+FxikMLUNMETeu6A2gRtXwHhqttlinD2BNlxHlEx5UoQ==} + /@next/swc-linux-arm64-gnu@14.1.1-canary.63: + resolution: {integrity: sha512-6Vla/RHWgxmU+KQXcPevnHfE4f98mazNSLGg5e2U2JLbBXxbzTz6u5WpOw1oAsEe6uH6CxrpnxIpTlnEK06vKw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1707,8 +1710,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@14.1.1-canary.62: - resolution: {integrity: sha512-2nVWU4ihwVypYltvukvRXuoH2Y8l38etaOdwmN1LcllKJ5Hu1DmXDFq2bmFML6q6HBs9LsX0YEHxfeVLd6pLXg==} + /@next/swc-linux-arm64-musl@14.1.1-canary.63: + resolution: {integrity: sha512-F0d9UDjvOs4TEvQ4/puM/fNNvq9B3LKgTXPqrLx932EfX2Di+C50D7O6qcb20HhAn60nahjbvRKDzAEm4P7IJQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1716,8 +1719,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@14.1.1-canary.62: - resolution: {integrity: sha512-SFqP1ZVUQqWmdX3PtNgel62sffV5wZHbtW/J4E4P/QrBaNbGQlziw34tFvlssjOGRG3ZzuDGCgqtVr0l4/QvfQ==} + /@next/swc-linux-x64-gnu@14.1.1-canary.63: + resolution: {integrity: sha512-Nwuz+gkZytJFKg6wnKPEBC0d4HsD7/QgVePSyibjtbYrq13yqk/dHNkdEr9sksGI7zr8TN8lfreb1VEgqN9zeQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1725,8 +1728,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@14.1.1-canary.62: - resolution: {integrity: sha512-adlHzPGxVc9PuorODAWLPZtjjrJVFIEmMAVvwLJy9vz4T/c+CrTfgWxUy+uIx5Xr+zw0GU3+E4D0FqqTKS6FDw==} + /@next/swc-linux-x64-musl@14.1.1-canary.63: + resolution: {integrity: sha512-4ZaUnhzdTtU94OV6zldIRy9YinqIugpt38uKD5+GtpyBdj3OPZEdKzvbPkQTAlXuxnqNZwuM/k1D5LLS9A1ZAg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1734,8 +1737,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@14.1.1-canary.62: - resolution: {integrity: sha512-gm5GnQz9I2yWst7RZqHf5frtfmjp+SP+FRzuTbCwWo04vOZqt/nrDOFha3O8VHfH/H+R1mECV6MiCR3vW64S/g==} + /@next/swc-win32-arm64-msvc@14.1.1-canary.63: + resolution: {integrity: sha512-OQ7AwsOLuxlt7Wl+E1sYeXmljmQqOdYHA3cXc0vI1X7QC6bxhQ49yFS5VZzFN6cz+zx0uTXIA4IAZQz6VI+vdw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -1743,8 +1746,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@14.1.1-canary.62: - resolution: {integrity: sha512-zB/UXYimoYSmqAq3o4EH4vOFeAFaXdrwqpt7ptow2kLRt2w3hahp6dd4om/qhopd2fraezxdMxvuUoKKDCtzXw==} + /@next/swc-win32-ia32-msvc@14.1.1-canary.63: + resolution: {integrity: sha512-ll67730wgehzZ4zoYayXg0TRRjGw+Oq+MLp7u6rodwUA93Lu9GxDibyh9dHT+mpGdwSroo7zVgvk4aXp30cmYA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -1752,8 +1755,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@14.1.1-canary.62: - resolution: {integrity: sha512-wjDYA9PiricK8de+/xWQUc6q29KfA9sLUcxWfMzQQqBWKkx4AZsKJpQla8mf4ueJFKiWyy5wSjpUUqtQYObk4Q==} + /@next/swc-win32-x64-msvc@14.1.1-canary.63: + resolution: {integrity: sha512-Pvg+crGlW8QvT2r5y/Dom2M++5TxJ7pBiwHkL7kD9x21RNc8QwEEyAJjxFYGzecKBbZs3HtcRbKEkVDZsHrq4w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1997,6 +2000,13 @@ packages: resolution: {integrity: sha512-4w5evLh+7FUUiA1GucvGj2ReX2TvOjEr4ejXdwL/bsjoSkof6r1gQmzqI+VHrE2CpJpB3al7bCTulOkFa/RcyA==} dev: false + /@tanstack/match-sorter-utils@8.11.8: + resolution: {integrity: sha512-3VPh0SYMGCa5dWQEqNab87UpCMk+ANWHDP4ALs5PeEW9EpfTAbrezzaOk/OiM52IESViefkoAOYuxdoa04p6aA==} + engines: {node: '>=12'} + dependencies: + remove-accents: 0.4.2 + dev: false + /@tanstack/query-core@5.21.7: resolution: {integrity: sha512-z0NSWFsM75esVmkxeuDWeyo9Wv4CZ/WsLMZgu1Zz164S6Oc/57NMia88dTu/d51wdVowMTAcDMQgRmiWmyPMxQ==} dev: false @@ -2016,7 +2026,7 @@ packages: react: 18.2.0 dev: false - /@tanstack/react-query-next-experimental@5.21.7(@tanstack/react-query@5.21.7)(next@14.1.1-canary.62)(react@18.2.0): + /@tanstack/react-query-next-experimental@5.21.7(@tanstack/react-query@5.21.7)(next@14.1.1-canary.63)(react@18.2.0): resolution: {integrity: sha512-5XgnH2JK0iH28eNmjYicX9b1lGq01XkZWQ0DFI8jCHypnXLbmD7Uq/Kh2IQ+OS6G1o3CK4RG5p8tPDNzD1vdAw==} peerDependencies: '@tanstack/react-query': ^5.21.7 @@ -2024,7 +2034,7 @@ packages: react: ^18.0.0 dependencies: '@tanstack/react-query': 5.21.7(react@18.2.0) - next: 14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) + next: 14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) react: 18.2.0 dev: false @@ -2037,6 +2047,38 @@ packages: react: 18.2.0 dev: false + /@tanstack/react-table@8.11.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-NEwvIq4iSiDQozEyvbdiSdCOiLa+g5xHmdEnvwDb98FObcK6YkBOkRrs/CNqrKdDy+/lqoIllIWHk+M80GW6+g==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/table-core': 8.11.8 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/react-virtual@3.0.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-tiqKW/e2MJVCr7/pRUXulpkyxllaOclkHNfhKTo4pmHjJIqnhMfwIjc1Q1R0Un3PI3kQywywu/791c8z9u0qeA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/table-core@8.11.8: + resolution: {integrity: sha512-DECHvtq4YW4U/gqg6etup7ydt/RB1Bi1pJaMpHUXl65ooW1d71Nv7BzD66rUdHrBSNdyiW3PLTPUQlpXjAgDeA==} + engines: {node: '>=12'} + dev: false + + /@tanstack/virtual-core@3.0.0: + resolution: {integrity: sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==} + dev: false + /@testing-library/react-hooks@8.0.1(react@17.0.2): resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==} engines: {node: '>=12'} @@ -2330,7 +2372,7 @@ packages: '@trpc/server': 11.0.0-next-beta.289 dev: false - /@trpc/next@11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/react-query@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(next@14.1.1-canary.62)(react-dom@18.2.0)(react@18.2.0): + /@trpc/next@11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/react-query@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(next@14.1.1-canary.63)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-AKCrcbtHh/zFrld6lMG0RC37d/aac4ZisLDjJcViMnEmJXCo0J5nhoZa6f+G9N683NdMWZVmY2rmJidw9IX3QQ==} peerDependencies: '@tanstack/react-query': ^5.0.0 @@ -2350,7 +2392,7 @@ packages: '@trpc/client': 11.0.0-next-beta.289(@trpc/server@11.0.0-next-beta.289) '@trpc/react-query': 11.0.0-next-beta.289(@tanstack/react-query@5.21.7)(@trpc/client@11.0.0-next-beta.289)(@trpc/server@11.0.0-next-beta.289)(react-dom@18.2.0)(react@18.2.0) '@trpc/server': 11.0.0-next-beta.289 - next: 14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) + next: 14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -5720,6 +5762,32 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /mantine-react-table@2.0.0-alpha.17(@mantine/core@7.5.3)(@mantine/dates@7.5.3)(@mantine/hooks@7.5.3)(@tabler/icons-react@2.47.0)(clsx@2.0.0)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-DnB1+c/IiTH2ptVqSEoaiBZ003wa8J5AlYK//apkhZE18M45lJ1iw12LCl1X5kRWx36P2D6O1yw8QW7SZZfQqQ==} + engines: {node: '>=16'} + peerDependencies: + '@mantine/core': ^7.3 + '@mantine/dates': ^7.3 + '@mantine/hooks': ^7.3 + '@tabler/icons-react': '>=2.23.0' + clsx: '>=2' + dayjs: '>=1.11' + react: '>=18.0' + react-dom: '>=18.0' + dependencies: + '@mantine/core': 7.5.3(@mantine/hooks@7.5.3)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) + '@mantine/dates': 7.5.3(@mantine/core@7.5.3)(@mantine/hooks@7.5.3)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0) + '@mantine/hooks': 7.5.3(react@18.2.0) + '@tabler/icons-react': 2.47.0(react@18.2.0) + '@tanstack/match-sorter-utils': 8.11.8 + '@tanstack/react-table': 8.11.8(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-virtual': 3.0.4(react-dom@18.2.0)(react@18.2.0) + clsx: 2.0.0 + dayjs: 1.11.10 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /markdown-it@14.0.0: resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} hasBin: true @@ -5907,7 +5975,7 @@ packages: engines: {node: '>= 0.4.0'} dev: true - /next-auth@5.0.0-beta.13(next@14.1.1-canary.62)(react@18.2.0): + /next-auth@5.0.0-beta.13(next@14.1.1-canary.63)(react@18.2.0): resolution: {integrity: sha512-2m2Gq69WQ0YXcHCCpHn2y5z1bxSlqD/XOuAgrdtz49/VIAdTFFeYZz97RYqf6xMF8VGmoG32VUnJ6LzaHk6Fwg==} peerDependencies: '@simplewebauthn/browser': ^9.0.1 @@ -5924,7 +5992,7 @@ packages: optional: true dependencies: '@auth/core': 0.27.0 - next: 14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) + next: 14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0) react: 18.2.0 dev: false @@ -5940,8 +6008,8 @@ packages: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true - /next@14.1.1-canary.62(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0): - resolution: {integrity: sha512-Qjb/nzmExeYmpOLnrR8LKJo8v9X2H7iWfPhS7fHPxpYC+Aj/XrbWplH6YllMaJR/VHRKeeXDqWEvCqr+q4+8zQ==} + /next@14.1.1-canary.63(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.0): + resolution: {integrity: sha512-2gDV0kHY34eOsUJMQWV+B5SFPzdunNxRGNaPzlEIpvdz6fS23r7ozgeKI9XhtA6cHUF2OXEF6mjl40dxSG8EOw==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -5955,7 +6023,7 @@ packages: sass: optional: true dependencies: - '@next/env': 14.1.1-canary.62 + '@next/env': 14.1.1-canary.63 '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001587 @@ -5966,15 +6034,15 @@ packages: sass: 1.71.0 styled-jsx: 5.1.1(@babel/core@7.23.9)(react@18.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 14.1.1-canary.62 - '@next/swc-darwin-x64': 14.1.1-canary.62 - '@next/swc-linux-arm64-gnu': 14.1.1-canary.62 - '@next/swc-linux-arm64-musl': 14.1.1-canary.62 - '@next/swc-linux-x64-gnu': 14.1.1-canary.62 - '@next/swc-linux-x64-musl': 14.1.1-canary.62 - '@next/swc-win32-arm64-msvc': 14.1.1-canary.62 - '@next/swc-win32-ia32-msvc': 14.1.1-canary.62 - '@next/swc-win32-x64-msvc': 14.1.1-canary.62 + '@next/swc-darwin-arm64': 14.1.1-canary.63 + '@next/swc-darwin-x64': 14.1.1-canary.63 + '@next/swc-linux-arm64-gnu': 14.1.1-canary.63 + '@next/swc-linux-arm64-musl': 14.1.1-canary.63 + '@next/swc-linux-x64-gnu': 14.1.1-canary.63 + '@next/swc-linux-x64-musl': 14.1.1-canary.63 + '@next/swc-win32-arm64-msvc': 14.1.1-canary.63 + '@next/swc-win32-ia32-msvc': 14.1.1-canary.63 + '@next/swc-win32-x64-msvc': 14.1.1-canary.63 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -6895,6 +6963,10 @@ packages: rc: 1.2.8 dev: true + /remove-accents@0.4.2: + resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} + dev: false + /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true