Added password input
This commit is contained in:
parent
4fbcd5581d
commit
dc4379b482
|
|
@ -1,88 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { UserFormData } from "@/features/dashboard/users/formSchemas/userFormDataSchema";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
Modal,
|
|
||||||
ScrollArea,
|
|
||||||
Text,
|
|
||||||
Stack,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import deleteUser from "@/features/dashboard/users/actions/deleteUser";
|
|
||||||
import { showNotification } from "@/utils/notifications";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
data: UserFormData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DeleteModal(props: Props) {
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [isSubmitting, setSubmitting] = useState(false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the modal. It won't close if a submission is in progress.
|
|
||||||
*/
|
|
||||||
const closeModal = () => {
|
|
||||||
if (isSubmitting) return;
|
|
||||||
router.replace("?");
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmAction = () => {
|
|
||||||
setSubmitting(true)
|
|
||||||
deleteUser(props.data.id)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.success){
|
|
||||||
showNotification(response.message);
|
|
||||||
router.replace("?")
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
showNotification(response.message, "error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//TODO: Handle Error
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setSubmitting(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal opened onClose={closeModal} title={`Delete confirmation`}>
|
|
||||||
<Text size="sm">
|
|
||||||
Are you sure you want to delete user{" "}
|
|
||||||
<Text span fw={700}>
|
|
||||||
{props.data.name}
|
|
||||||
</Text>
|
|
||||||
? This action is irreversible.
|
|
||||||
</Text>
|
|
||||||
{/* Buttons */}
|
|
||||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={closeModal}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="subtle"
|
|
||||||
// leftSection={<TbDeviceFloppy size={20} />}
|
|
||||||
type="submit"
|
|
||||||
color="red"
|
|
||||||
loading={isSubmitting}
|
|
||||||
onClick={confirmAction}
|
|
||||||
>
|
|
||||||
Delete User
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import DeleteModal from "./DeleteModal";
|
|
||||||
|
|
||||||
export default DeleteModal;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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} />
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import DetailModal from "./DetailModal";
|
|
||||||
|
|
||||||
export default DetailModal;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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} />
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import EditModal from "./EditModal";
|
|
||||||
|
|
||||||
export default EditModal;
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useForm } from "@mantine/form";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
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("?")
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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("?")
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (response.errors) {
|
|
||||||
form.setErrors(response.errors);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showNotification(response.message, "error");
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import FormModal from "./FormModal";
|
|
||||||
|
|
||||||
export default FormModal;
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
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,74 +0,0 @@
|
||||||
"use client";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import {
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import columns, { UserRow } from "./columns";
|
|
||||||
import { Table, Text } from "@mantine/core";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
users: UserRow[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function UsersTable({ users }: Props) {
|
|
||||||
const table = useReactTable({
|
|
||||||
data: users,
|
|
||||||
columns,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
defaultColumn: {
|
|
||||||
cell: (props) => <Text>{props.getValue() as React.ReactNode}</Text>,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
import { createColumnHelper } from "@tanstack/react-table";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { ActionIcon, Anchor, Avatar, Badge, Flex, Group, Text, Tooltip } from "@mantine/core";
|
|
||||||
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
|
|
||||||
import stringToColorHex from "@/utils/stringToColorHex";
|
|
||||||
|
|
||||||
export interface UserRow {
|
|
||||||
id: string,
|
|
||||||
name: string | null,
|
|
||||||
email: string | null,
|
|
||||||
photoUrl: string | null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<UserRow>()
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
columnHelper.display({
|
|
||||||
id: "sequence",
|
|
||||||
header: "#",
|
|
||||||
cell: props => props.row.index + 1,
|
|
||||||
size: 1
|
|
||||||
}),
|
|
||||||
|
|
||||||
columnHelper.accessor('name', {
|
|
||||||
header: "Name",
|
|
||||||
cell: (props) => <Group>
|
|
||||||
<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()}`} size="sm" component={Link}>{props.getValue()}</Anchor>
|
|
||||||
}),
|
|
||||||
|
|
||||||
columnHelper.display({
|
|
||||||
id: "status",
|
|
||||||
header: "Status",
|
|
||||||
cell: (props) => <Badge color="green">Active</Badge>
|
|
||||||
}),
|
|
||||||
|
|
||||||
columnHelper.display({
|
|
||||||
id: 'actions',
|
|
||||||
header: "Actions",
|
|
||||||
size: 10,
|
|
||||||
meta: {
|
|
||||||
className: "w-fit"
|
|
||||||
},
|
|
||||||
cell: (props) => <Flex gap="xs">
|
|
||||||
|
|
||||||
{/* Detail */}
|
|
||||||
<Tooltip label="Detail">
|
|
||||||
<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={`?edit=${props.row.original.id}`}>
|
|
||||||
<TbPencil />
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* Delete */}
|
|
||||||
<Tooltip label="Delete">
|
|
||||||
<ActionIcon variant="light" color="red" component={Link} href={`?delete=${props.row.original.id}`}>
|
|
||||||
<TbTrash />
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue";
|
import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue";
|
||||||
import prisma from "@/db";
|
import prisma from "@/db";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
import userFormDataSchema, { UserFormData } from "../formSchemas/userFormSchema";
|
import userFormDataSchema, {
|
||||||
|
UserFormData,
|
||||||
|
} from "../formSchemas/userFormSchema";
|
||||||
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
||||||
import unauthorized from "@/modules/dashboard/utils/unauthorized";
|
import unauthorized from "@/modules/dashboard/utils/unauthorized";
|
||||||
import DashboardError from "@/modules/dashboard/errors/DashboardError";
|
import DashboardError from "@/modules/dashboard/errors/DashboardError";
|
||||||
import handleCatch from "@/modules/dashboard/utils/handleCatch";
|
import handleCatch from "@/modules/dashboard/utils/handleCatch";
|
||||||
import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction";
|
import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction";
|
||||||
|
import hashPassword from "@/modules/auth/utils/hashPassword";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upserts a user based on the provided UserFormData.
|
* Upserts a user based on the provided UserFormData.
|
||||||
|
|
@ -35,31 +38,36 @@ export default async function upsertUser(
|
||||||
if (!validatedFields.success) {
|
if (!validatedFields.success) {
|
||||||
throw new DashboardError({
|
throw new DashboardError({
|
||||||
errorCode: "INVALID_FORM_DATA",
|
errorCode: "INVALID_FORM_DATA",
|
||||||
formErrors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors)
|
formErrors: mapObjectToFirstValue(
|
||||||
})
|
validatedFields.error.flatten().fieldErrors
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const userData = {
|
const userData = {
|
||||||
id: validatedFields.data.id ? validatedFields.data.id : undefined,
|
id: validatedFields.data.id ? validatedFields.data.id : undefined,
|
||||||
name: validatedFields.data.name,
|
name: validatedFields.data.name,
|
||||||
photoProfile: validatedFields.data.photoProfileUrl ?? "",
|
photoProfile: validatedFields.data.photoProfileUrl ?? "",
|
||||||
email: validatedFields.data.email
|
email: validatedFields.data.email,
|
||||||
};
|
};
|
||||||
|
const passwordHash = await hashPassword(validatedFields.data.password!);
|
||||||
|
|
||||||
// Database operation
|
// Database operation
|
||||||
if (isInsert) {
|
if (isInsert) {
|
||||||
if (await prisma.user.findFirst({
|
if (
|
||||||
|
await prisma.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
email: userData.email
|
email: userData.email,
|
||||||
}
|
},
|
||||||
})){
|
})
|
||||||
|
) {
|
||||||
throw new DashboardError({
|
throw new DashboardError({
|
||||||
errorCode: "INVALID_FORM_DATA",
|
errorCode: "INVALID_FORM_DATA",
|
||||||
formErrors: {
|
formErrors: {
|
||||||
email: "The user is already exists"
|
email: "The user is already exists",
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
await prisma.user.create({ data: { ...userData, passwordHash } });
|
||||||
}
|
|
||||||
await prisma.user.create({ data: userData });
|
|
||||||
} else {
|
} else {
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: { id: validatedFields.data.id! },
|
where: { id: validatedFields.data.id! },
|
||||||
|
|
@ -78,6 +86,6 @@ export default async function upsertUser(
|
||||||
}.`,
|
}.`,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleCatch(error)
|
return handleCatch(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export interface UserFormData {
|
export interface UserFormData {
|
||||||
id: string;
|
id: string | undefined;
|
||||||
name: string;
|
name: string;
|
||||||
photoProfileUrl: string;
|
photoProfileUrl: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
password: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const userFormDataSchema = z.object({
|
const userFormDataSchema = z.object({
|
||||||
id: z.string().nullable(),
|
id: z.string().optional(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
photoProfileUrl: z.union([z.string(), z.null()]),
|
photoProfileUrl: z.union([z.string(), z.null()]),
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
|
password: z.string().min(8).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default userFormDataSchema;
|
export default userFormDataSchema;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
Alert,
|
Alert,
|
||||||
Center,
|
Center,
|
||||||
Avatar,
|
Avatar,
|
||||||
|
PasswordInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, zodResolver } from "@mantine/form";
|
import { useForm, zodResolver } from "@mantine/form";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
@ -55,6 +56,7 @@ export default function UserFormModal(props: ModalProps) {
|
||||||
email: "",
|
email: "",
|
||||||
name: "",
|
name: "",
|
||||||
photoProfileUrl: "",
|
photoProfileUrl: "",
|
||||||
|
password: "",
|
||||||
},
|
},
|
||||||
validate: zodResolver(userFormDataSchema),
|
validate: zodResolver(userFormDataSchema),
|
||||||
validateInputOnChange: false,
|
validateInputOnChange: false,
|
||||||
|
|
@ -128,7 +130,7 @@ export default function UserFormModal(props: ModalProps) {
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
title={props.title}
|
title={props.title}
|
||||||
scrollAreaComponent={ScrollArea.Autosize}
|
scrollAreaComponent={ScrollArea.Autosize}
|
||||||
size="xl"
|
size="md"
|
||||||
>
|
>
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
<Stack mt="sm" gap="lg" px="lg">
|
<Stack mt="sm" gap="lg" px="lg">
|
||||||
|
|
@ -137,7 +139,7 @@ export default function UserFormModal(props: ModalProps) {
|
||||||
<Skeleton visible={isFetching}>
|
<Skeleton visible={isFetching}>
|
||||||
<Center>
|
<Center>
|
||||||
<Avatar
|
<Avatar
|
||||||
color={stringToColorHex(form.values.id)}
|
color={stringToColorHex(form.values.id ?? "")}
|
||||||
src={form.values.photoProfileUrl}
|
src={form.values.photoProfileUrl}
|
||||||
size={120}
|
size={120}
|
||||||
>
|
>
|
||||||
|
|
@ -178,6 +180,16 @@ export default function UserFormModal(props: ModalProps) {
|
||||||
/>
|
/>
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
{!form.values.id && !isFetching && (
|
||||||
|
<PasswordInput
|
||||||
|
label="Password"
|
||||||
|
readOnly={props.readonly}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
{...form.getInputProps("password")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user