mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-26 16:30:57 +01:00
feat: add user management (#134)
This commit is contained in:
@@ -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",
|
||||
|
||||
36
apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx
Normal file
36
apps/nextjs/src/app/[locale]/manage/users/[userId]/page.tsx
Normal file
@@ -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 <Title>Edit User {user.name}!</Title>;
|
||||
}
|
||||
@@ -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<RouterOutputs["user"]["getAll"][number]>[]
|
||||
>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "Name",
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: "Email",
|
||||
Cell: ({ renderedCellValue, row }) => (
|
||||
<Group>
|
||||
{row.original.email ? renderedCellValue : <Text>-</Text>}
|
||||
{row.original.emailVerified && (
|
||||
<ThemeIcon radius="xl" size="sm">
|
||||
<IconCheck size="1rem" />
|
||||
</ThemeIcon>
|
||||
)}
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const table = useMantineReactTable({
|
||||
columns,
|
||||
data,
|
||||
enableRowSelection: true,
|
||||
enableColumnOrdering: true,
|
||||
enableGlobalFilter: false,
|
||||
enableRowActions: true,
|
||||
enableDensityToggle: false,
|
||||
enableFullScreenToggle: false,
|
||||
getRowId: (row) => row.id,
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Edit">
|
||||
<ActionIcon
|
||||
component={Link}
|
||||
href={`/manage/users/${row.original.id}`}
|
||||
>
|
||||
<IconEdit size="1rem" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Delete">
|
||||
<ActionIcon color="red">
|
||||
<IconTrash size="1rem" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
renderTopToolbarCustomActions: () => (
|
||||
<Button component={Link} href="/manage/users/create">
|
||||
Create New User
|
||||
</Button>
|
||||
),
|
||||
state: {
|
||||
isLoading: isLoading,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title mb="md">{t("title")}</Title>
|
||||
<MantineReactTable table={table} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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 (
|
||||
<Card shadow="md" withBorder>
|
||||
{!isComplete ? (
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Button
|
||||
leftSection={<IconArrowLeft size="1rem" />}
|
||||
disabled={!hasPrevious || isLoadingNextStep}
|
||||
onClick={prevStep}
|
||||
>
|
||||
{t("common.action.previous")}
|
||||
</Button>
|
||||
<Button
|
||||
rightSection={<IconArrowRight size="1rem" />}
|
||||
disabled={!hasNext || isLoadingNextStep}
|
||||
loading={isLoadingNextStep}
|
||||
onClick={nextStep}
|
||||
>
|
||||
{t("common.action.next")}
|
||||
</Button>
|
||||
</Group>
|
||||
) : (
|
||||
<Group justify="end" wrap="nowrap">
|
||||
<Button
|
||||
variant="light"
|
||||
leftSection={<IconRotate size="1rem" />}
|
||||
onClick={reset}
|
||||
>
|
||||
{t("management.page.user.create.buttons.createAnother")}
|
||||
</Button>
|
||||
<Button
|
||||
leftSection={<IconArrowBackUp size="1rem" />}
|
||||
component={Link}
|
||||
href="/manage/users"
|
||||
>
|
||||
{t("management.page.user.create.buttons.return")}
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -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 (
|
||||
<>
|
||||
<Title mb="md">{t("title")}</Title>
|
||||
<Stepper
|
||||
active={active}
|
||||
onStepClick={setActive}
|
||||
allowNextStepsSelect={false}
|
||||
mb="md"
|
||||
>
|
||||
<Stepper.Step
|
||||
label={t("step.personalInformation.label")}
|
||||
allowStepSelect={false}
|
||||
allowStepClick={false}
|
||||
color={!generalForm.isValid() ? "red" : undefined}
|
||||
>
|
||||
<form>
|
||||
<Card p="xl">
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
label={t("step.personalInformation.field.username.label")}
|
||||
variant="filled"
|
||||
withAsterisk
|
||||
{...generalForm.getInputProps("username")}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={t("step.personalInformation.field.email.label")}
|
||||
variant="filled"
|
||||
{...generalForm.getInputProps("email")}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
</form>
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
label={t("step.preferences.label")}
|
||||
description={t("step.preferences.description")}
|
||||
allowStepSelect={false}
|
||||
allowStepClick={false}
|
||||
>
|
||||
Step 2
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
label={t("step.permissions.label")}
|
||||
description={t("step.permissions.description")}
|
||||
allowStepSelect={false}
|
||||
allowStepClick={false}
|
||||
>
|
||||
3
|
||||
</Stepper.Step>
|
||||
<Stepper.Step
|
||||
label={t("step.review.label")}
|
||||
allowStepSelect={false}
|
||||
allowStepClick={false}
|
||||
>
|
||||
<Card p="xl">
|
||||
<Stack maw={300} align="center" mx="auto">
|
||||
<Avatar size="xl">{generalForm.values.username}</Avatar>
|
||||
<Text tt="uppercase" fw="bolder" size="xl">
|
||||
{generalForm.values.username}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stepper.Step>
|
||||
<Stepper.Completed>
|
||||
<Card p="xl">
|
||||
<Stack align="center" maw={300} mx="auto">
|
||||
<IconUserCheck size="3rem" />
|
||||
<Title order={2}>{t("step.completed.title")}</Title>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stepper.Completed>
|
||||
</Stepper>
|
||||
<StepperNavigationComponent
|
||||
hasNext={hasNext && canNavigateToNextStep}
|
||||
hasPrevious={hasPrevious}
|
||||
isComplete={active === stepperMax}
|
||||
isLoadingNextStep={isPending}
|
||||
nextStep={controlledGoToNextStep}
|
||||
prevStep={prevStep}
|
||||
reset={reset}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
16
apps/nextjs/src/app/[locale]/manage/users/create/page.tsx
Normal file
16
apps/nextjs/src/app/[locale]/manage/users/create/page.tsx
Normal file
@@ -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 <UserCreateStepperComponent />;
|
||||
}
|
||||
18
apps/nextjs/src/app/[locale]/manage/users/page.tsx
Normal file
18
apps/nextjs/src/app/[locale]/manage/users/page.tsx
Normal file
@@ -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 <UserListComponent initialUserList={userList} />;
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
@import "@mantine/core/styles.css";
|
||||
@import "@mantine/dates/styles.css";
|
||||
@import "mantine-react-table/styles.css";
|
||||
|
||||
158
pnpm-lock.yaml
generated
158
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user