Added User Edit Feature
This commit is contained in:
parent
e9db884fc5
commit
764f677637
|
|
@ -29,6 +29,7 @@
|
|||
"client-only": "^0.0.1",
|
||||
"clsx": "^2.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mantine-form-zod-resolver": "^1.1.0",
|
||||
"next": "14.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ dependencies:
|
|||
jsonwebtoken:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
mantine-form-zod-resolver:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(@mantine/form@7.4.2)(zod@3.22.4)
|
||||
next:
|
||||
specifier: 14.1.0
|
||||
version: 14.1.0(react-dom@18.2.0)(react@18.2.0)(sass@1.70.0)
|
||||
|
|
@ -2421,6 +2424,17 @@ packages:
|
|||
semver: 6.3.1
|
||||
dev: false
|
||||
|
||||
/mantine-form-zod-resolver@1.1.0(@mantine/form@7.4.2)(zod@3.22.4):
|
||||
resolution: {integrity: sha512-hidTuYq6agSF5XbkcVVcr0mkGs9ki/x8OC9ldZMxGLVGja6bdl+x4k1hCNrigCG90DBoMDnu0bo3hprGBBlUZA==}
|
||||
engines: {node: '>=16.6.0'}
|
||||
peerDependencies:
|
||||
'@mantine/form': '>=7.0.0'
|
||||
zod: '>=3.0.0'
|
||||
dependencies:
|
||||
'@mantine/form': 7.4.2(react@18.2.0)
|
||||
zod: 3.22.4
|
||||
dev: false
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
|
||||
// TODO: Implement Delete Modal
|
||||
|
||||
export default function DeleteModal() {
|
||||
return (
|
||||
<div>DeleteModal</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import DeleteModal from "./DeleteModal";
|
||||
|
||||
export default DeleteModal;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react'
|
||||
import { FormModal } from '..'
|
||||
import { UserFormData } from '@/features/dashboard/users/formSchemas/userFormDataSchema'
|
||||
|
||||
interface Props {
|
||||
data: UserFormData
|
||||
}
|
||||
|
||||
export default function DetailModal(props: Props) {
|
||||
return <FormModal readonly modalTitle='Detail User' data={props.data} />
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import DetailModal from "./DetailModal";
|
||||
|
||||
export default DetailModal;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react'
|
||||
import { FormModal } from '..'
|
||||
import { UserFormData } from '@/features/dashboard/users/formSchemas/userFormDataSchema'
|
||||
|
||||
interface Props {
|
||||
data: UserFormData
|
||||
}
|
||||
|
||||
export default function EditModal(props: Props) {
|
||||
return <FormModal modalTitle='Edit User' data={props.data} />
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import EditModal from "./EditModal";
|
||||
|
||||
export default EditModal;
|
||||
154
src/app/dashboard/(auth)/users/_modals/FormModal/FormModal.tsx
Normal file
154
src/app/dashboard/(auth)/users/_modals/FormModal/FormModal.tsx
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
Modal,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { TbDeviceFloppy } from "react-icons/tb";
|
||||
import editUser from "@/features/dashboard/users/actions/editUser";
|
||||
import userFormDataSchema, {
|
||||
UserFormData,
|
||||
} from "@/features/dashboard/users/formSchemas/userFormDataSchema";
|
||||
import { showNotification } from "@/utils/notifications";
|
||||
import stringToColorHex from "@/utils/stringToColorHex";
|
||||
import { zodResolver } from "@mantine/form";
|
||||
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
modalTitle: string;
|
||||
data: UserFormData;
|
||||
}
|
||||
|
||||
export default function FormModal(props: Props) {
|
||||
const router = useRouter();
|
||||
|
||||
const [isSubmitting, setSubmitting] = useState(false);
|
||||
|
||||
const form = useForm<UserFormData>({
|
||||
initialValues: props.data,
|
||||
validate: zodResolver(userFormDataSchema),
|
||||
});
|
||||
|
||||
/**
|
||||
* Closes the modal. It won't close if a submission is in progress.
|
||||
*/
|
||||
const closeModal = () => {
|
||||
if (isSubmitting) return;
|
||||
router
|
||||
.replace("?")
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the form submission.
|
||||
*
|
||||
* @param data The data from the form.
|
||||
*/
|
||||
const handleSubmit = (data: UserFormData) => {
|
||||
setSubmitting(true);
|
||||
editUser(data)
|
||||
.then((response) => {
|
||||
if (response.success) {
|
||||
showNotification(response.message);
|
||||
router
|
||||
.replace("?")
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
return;
|
||||
} else {
|
||||
if (response.errors) {
|
||||
form.setErrors(response.errors);
|
||||
return;
|
||||
}
|
||||
showNotification(response.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
//TODO: Handle Error
|
||||
})
|
||||
.finally(() => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened
|
||||
onClose={closeModal}
|
||||
title={<Title order={3}>{props.modalTitle}</Title>}
|
||||
scrollAreaComponent={ScrollArea.Autosize}
|
||||
>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack mt="sm" gap="lg" px="lg">
|
||||
{/* Avatar */}
|
||||
<Center>
|
||||
<Avatar
|
||||
color={stringToColorHex(props.data.id)}
|
||||
src={props.data.photoProfileUrl}
|
||||
size={120}
|
||||
>
|
||||
{props.data.name?.[0].toUpperCase()}
|
||||
</Avatar>
|
||||
</Center>
|
||||
|
||||
{/* ID */}
|
||||
<TextInput
|
||||
label="ID"
|
||||
readOnly
|
||||
variant="filled"
|
||||
{...form.getInputProps("id")}
|
||||
/>
|
||||
|
||||
{/* Name */}
|
||||
<TextInput
|
||||
data-autofocus
|
||||
label="Name"
|
||||
readOnly={props.readonly}
|
||||
disabled={isSubmitting}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
|
||||
{/* Email */}
|
||||
<TextInput
|
||||
label="Email"
|
||||
readOnly={props.readonly}
|
||||
disabled={isSubmitting}
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
|
||||
{/* Buttons */}
|
||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={closeModal}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
{!props.readonly && (
|
||||
<Button
|
||||
variant="filled"
|
||||
leftSection={<TbDeviceFloppy size={20} />}
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import FormModal from "./FormModal";
|
||||
|
||||
export default FormModal;
|
||||
4
src/app/dashboard/(auth)/users/_modals/index.ts
Normal file
4
src/app/dashboard/(auth)/users/_modals/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { default as DeleteModal } from "./DeleteModal"
|
||||
export { default as DetailModal } from "./DetailModal"
|
||||
export { default as EditModal } from "./EditModal"
|
||||
export { default as FormModal } from "./FormModal"
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
"use client";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
flexRender,
|
||||
|
|
@ -7,8 +7,7 @@ import {
|
|||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import columns, { UserRow } from "./columns";
|
||||
import { Table, TableThead, Text } from "@mantine/core";
|
||||
import { showErrorNotification } from "@/utils/notifications";
|
||||
import { Table, Text } from "@mantine/core";
|
||||
|
||||
interface Props {
|
||||
users: UserRow[]
|
||||
|
|
@ -26,40 +25,42 @@ export default function UsersTable({users}: Props) {
|
|||
});
|
||||
|
||||
return (
|
||||
<Table verticalSpacing="sm" horizontalSpacing="xs">
|
||||
{/* Thead */}
|
||||
<Table.Thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<Table.Tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<Table.Th key={header.id} style={{maxWidth: `${header.column.columnDef.maxSize}px`, width: `${header.getSize()}`}}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</Table.Th>
|
||||
))}
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Thead>
|
||||
<>
|
||||
<Table verticalSpacing="xs" horizontalSpacing="xs">
|
||||
{/* Thead */}
|
||||
<Table.Thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<Table.Tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<Table.Th key={header.id} style={{maxWidth: `${header.column.columnDef.maxSize}px`, width: `${header.getSize()}`}}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</Table.Th>
|
||||
))}
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Thead>
|
||||
|
||||
{/* TBody */}
|
||||
<Table.Tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<Table.Tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<Table.Td key={cell.id} style={{maxWidth: `${cell.column.columnDef.maxSize}px`}}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</Table.Td>
|
||||
))}
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
{/* TBody */}
|
||||
<Table.Tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<Table.Tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<Table.Td key={cell.id} style={{maxWidth: `${cell.column.columnDef.maxSize}px`}}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</Table.Td>
|
||||
))}
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ const columns = [
|
|||
columnHelper.accessor('name', {
|
||||
header: "Name",
|
||||
cell: (props) => <Group>
|
||||
<Avatar color={stringToColorHex(props.row.original.id)} src={props.row.original.photoUrl}>{props.getValue()?.[0].toUpperCase()}</Avatar>
|
||||
<Text>{props.getValue()}</Text>
|
||||
<Avatar color={stringToColorHex(props.row.original.id)} src={props.row.original.photoUrl} size={26}>{props.getValue()?.[0].toUpperCase()}</Avatar>
|
||||
<Text size="sm" fw={500}>{props.getValue()}</Text>
|
||||
</Group>
|
||||
}),
|
||||
|
||||
columnHelper.accessor('email', {
|
||||
header: "Email",
|
||||
cell: (props) => <Anchor href={`mailto:${props.getValue()}`} component={Link}>{props.getValue()}</Anchor>
|
||||
cell: (props) => <Anchor href={`mailto:${props.getValue()}`} size="sm" component={Link}>{props.getValue()}</Anchor>
|
||||
}),
|
||||
|
||||
columnHelper.display({
|
||||
|
|
@ -51,21 +51,21 @@ const columns = [
|
|||
|
||||
{/* Detail */}
|
||||
<Tooltip label="Detail">
|
||||
<ActionIcon variant="light" color="green" component={Link} href={`/dashboard/users/detail/${props.row.original.id}`}>
|
||||
<ActionIcon variant="light" color="green" component={Link} href={`?detail=${props.row.original.id}`}>
|
||||
<TbEye />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
{/* Edit */}
|
||||
<Tooltip label="Edit">
|
||||
<ActionIcon variant="light" color="yellow" component={Link} href={`/dashboard/users/edit/${props.row.original.id}`}>
|
||||
<ActionIcon variant="light" color="yellow" component={Link} href={`?edit=${props.row.original.id}`}>
|
||||
<TbPencil />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
{/* Delete */}
|
||||
<Tooltip label="Delete">
|
||||
<ActionIcon variant="light" color="red">
|
||||
<ActionIcon variant="light" color="red" component={Link} href={`?delete=${props.row.original.id}`}>
|
||||
<TbTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -4,19 +4,55 @@ import UsersTable from "./_tables/UsersTable/UsersTable";
|
|||
import checkPermission from "@/features/auth/tools/checkPermission";
|
||||
import { redirect } from "next/navigation";
|
||||
import getUsers from "@/features/dashboard/users/data/getUsers";
|
||||
import { DeleteModal, DetailModal, EditModal } from "./_modals";
|
||||
import getUserDetailById from "@/features/dashboard/users/data/getUserDetailById";
|
||||
|
||||
export default async function UsersPage() {
|
||||
interface Props {
|
||||
searchParams: {
|
||||
detail?: string;
|
||||
edit?: string;
|
||||
delete?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function UsersPage({searchParams}: Props) {
|
||||
|
||||
// Check for permission and return error component if not permitted
|
||||
if (!await checkPermission("authenticated-only")) return <div>Error</div>
|
||||
|
||||
const users = await getUsers()
|
||||
|
||||
/**
|
||||
* Renders the appropriate modal based on the search parameters.
|
||||
*
|
||||
* @returns A modal component or null.
|
||||
*/
|
||||
const renderModal = async () => {
|
||||
if (searchParams.detail){
|
||||
const userDetail = await getUserDetailById(searchParams.detail)
|
||||
return <DetailModal data={userDetail} />
|
||||
}
|
||||
|
||||
if (searchParams.edit){
|
||||
const userDetail = await getUserDetailById(searchParams.edit)
|
||||
return <EditModal data={userDetail} />
|
||||
}
|
||||
|
||||
if (searchParams.delete){
|
||||
return <DeleteModal />
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack className="flex flex-col">
|
||||
<Title order={1}>Users</Title>
|
||||
<Card>
|
||||
<UsersTable users={users} />
|
||||
</Card>
|
||||
|
||||
{await renderModal()}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
|
|||
|
||||
import "./globals.css";
|
||||
import "@mantine/core/styles.css";
|
||||
import '@mantine/notifications/styles.css';
|
||||
|
||||
import { ColorSchemeScript, MantineProvider } from "@mantine/core";
|
||||
import { AuthContextProvider } from "@/features/auth/contexts/AuthContext";
|
||||
|
|
|
|||
6
src/config/cookie.ts
Normal file
6
src/config/cookie.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const cookieConfig = {
|
||||
notificationKey: "n",
|
||||
notificationMaxAge: 10,
|
||||
} as const;
|
||||
|
||||
export default cookieConfig;
|
||||
69
src/features/dashboard/users/actions/editUser.ts
Normal file
69
src/features/dashboard/users/actions/editUser.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"use server";
|
||||
|
||||
import prisma from "@/db";
|
||||
import userFormDataSchema, { UserFormData } from "../formSchemas/userFormDataSchema";
|
||||
import checkPermission from "@/features/auth/tools/checkPermission";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue";
|
||||
|
||||
/**
|
||||
* Edits user data in the database based on the provided form data.
|
||||
*
|
||||
* @param formData The user data to be updated.
|
||||
* @returns A promise that resolves to an object indicating the success or failure of the operation.
|
||||
*/
|
||||
export default async function editUser(formData: UserFormData) {
|
||||
|
||||
// Check user permission
|
||||
if (!await checkPermission("authenticated-only")) return {
|
||||
success: false,
|
||||
message: "Unauthorized"
|
||||
}
|
||||
|
||||
// Validate form data
|
||||
const validatedFields = userFormDataSchema.safeParse(formData);
|
||||
if (!validatedFields.success){
|
||||
return {
|
||||
success: false,
|
||||
message: "Invalid Form Data",
|
||||
errors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors)
|
||||
} as const
|
||||
}
|
||||
|
||||
// Check for valid ID
|
||||
if (!validatedFields.data.id){
|
||||
return {
|
||||
success: false,
|
||||
message: "Invalid Form Data",
|
||||
errors: {
|
||||
id: "Invalid ID"
|
||||
}
|
||||
} as const
|
||||
}
|
||||
|
||||
// Update user data in the database
|
||||
try {
|
||||
await prisma.user.update({
|
||||
where: { id: validatedFields.data.id },
|
||||
data: {
|
||||
email: validatedFields.data.email,
|
||||
name: validatedFields.data.name,
|
||||
}
|
||||
});
|
||||
|
||||
// Revalidate the cache
|
||||
revalidatePath(".");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `User ${validatedFields.data.name} has been successfully updated`
|
||||
};
|
||||
} catch (error) {
|
||||
// Consider handling specific database errors here
|
||||
console.error('Error updating user data', error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Error updating user data"
|
||||
};
|
||||
}
|
||||
}
|
||||
45
src/features/dashboard/users/data/getUserDetailById.ts
Normal file
45
src/features/dashboard/users/data/getUserDetailById.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import "server-only"
|
||||
import prisma from "@/db";
|
||||
import { notFound } from "next/navigation";
|
||||
import checkPermission from "@/features/auth/tools/checkPermission";
|
||||
import { unauthorized } from "@/BaseError";
|
||||
|
||||
/**
|
||||
* Retrieves detailed information of a user by their ID.
|
||||
*
|
||||
* @param id The unique identifier of the user.
|
||||
* @returns The user's detailed information or an error response.
|
||||
*/
|
||||
export default async function getUserDetailById(id: string){
|
||||
|
||||
// Check user permission
|
||||
if (!checkPermission("authenticated-only")) return unauthorized();
|
||||
|
||||
// Retrieve user data from the database
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
photoProfile: {
|
||||
select: {
|
||||
path: true
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Check if user exists
|
||||
if (!user) return notFound();
|
||||
|
||||
// Format user data
|
||||
const formattedUser = {
|
||||
id: user.id,
|
||||
email: user.email ?? "",
|
||||
name: user.name ?? "",
|
||||
photoProfileUrl: user.photoProfile?.path ?? ""
|
||||
}
|
||||
|
||||
return formattedUser;
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ const getUsers = async () => {
|
|||
},
|
||||
name: true,
|
||||
},
|
||||
where: {},
|
||||
})
|
||||
|
||||
const result = users.map((user) => ({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export interface UserFormData {
|
||||
id: string;
|
||||
name: string;
|
||||
photoProfileUrl: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
const userFormDataSchema = z.object({
|
||||
id: z.string().nullable(),
|
||||
name: z.string(),
|
||||
photoProfileUrl: z.union([z.string(), z.null()]),
|
||||
email: z.string().email(),
|
||||
});
|
||||
|
||||
export default userFormDataSchema;
|
||||
16
src/utils/mapObjectToFirstValue.ts
Normal file
16
src/utils/mapObjectToFirstValue.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Maps each key of an object to the first value in its corresponding array.
|
||||
*
|
||||
* @template T The object type, where each key has an array of strings as its value.
|
||||
* @param someObject The object to be transformed.
|
||||
* @returns An object with the same keys, but each key maps to the first string in the original array.
|
||||
*/
|
||||
const mapObjectToFirstValue = <T extends { [key: string]: string[] }>(
|
||||
someObject: T
|
||||
): { [K in keyof T]: string } =>
|
||||
Object.entries(someObject).reduce((prev, [k, v]) => {
|
||||
prev[k as keyof T] = v[0];
|
||||
return prev;
|
||||
}, {} as { [K in keyof T]: string });
|
||||
|
||||
export default mapObjectToFirstValue;
|
||||
|
|
@ -1,9 +1,34 @@
|
|||
import cookieConfig from "@/config/cookie";
|
||||
import { NotificationData, notifications } from "@mantine/notifications";
|
||||
|
||||
export type NotificationType = "success" | "error";
|
||||
|
||||
/**
|
||||
* Shows an error notification. This function is deprecated and should be replaced by `showNotification`.
|
||||
*
|
||||
* @deprecated
|
||||
* @param [message="Error"] The message to be displayed in the notification.
|
||||
* @param notificationData Optional additional data for the notification.
|
||||
*/
|
||||
export const showErrorNotification = (message: string = "Error", notificationData?: NotificationData) => {
|
||||
notifications.show({
|
||||
message,
|
||||
color: "red",
|
||||
...notificationData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a notification with configurable type and message.
|
||||
*
|
||||
* @param message The message to be displayed in the notification.
|
||||
* @param [type="success"] The type of the notification, either "success" or "error".
|
||||
* @param notificationData Optional additional data for the notification.
|
||||
*/
|
||||
export const showNotification = (message: string, type: NotificationType = "success", notificationData?: NotificationData) => {
|
||||
notifications.show({
|
||||
message,
|
||||
color: type === "error" ? "red" : "green",
|
||||
...notificationData
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user