form
This commit is contained in:
1 parent
31cc9cd294
commit
1808db5cd7
3 files changed
+139
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import { useId } from "react";
|
||||
import { Modal, Stack, Typography, Box } from "@mui/material";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { Button } from "@/Components/v2/inputs";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ConfirmDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
description?: string;
|
||||
onConfirm: () => void;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
isLoading?: boolean;
|
||||
confirmColor?: "error" | "primary" | "secondary" | "success" | "warning" | "info";
|
||||
}
|
||||
|
||||
export const ConfirmDialog = ({
|
||||
open,
|
||||
onClose,
|
||||
title,
|
||||
description,
|
||||
onConfirm,
|
||||
confirmText,
|
||||
cancelText,
|
||||
isLoading = false,
|
||||
confirmColor = "error",
|
||||
}: ConfirmDialogProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const titleId = useId();
|
||||
const descriptionId = useId();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={description ? descriptionId : undefined}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Stack
|
||||
gap={2}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
minWidth: 400,
|
||||
maxWidth: 500,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: 1,
|
||||
borderColor: theme.palette.divider,
|
||||
borderRadius: 1,
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
id={titleId}
|
||||
variant="h6"
|
||||
fontWeight={600}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
{description && (
|
||||
<Typography
|
||||
id={descriptionId}
|
||||
color="text.secondary"
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
gap: 2,
|
||||
mt: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={onClose}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{cancelText ?? t("common.buttons.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color={confirmColor}
|
||||
onClick={onConfirm}
|
||||
loading={isLoading}
|
||||
>
|
||||
{confirmText ?? t("common.buttons.confirm")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { profileSchema, type ProfileFormData } from "@/Validation/profile";
|
||||
|
||||
export const useProfileForm = () => {
|
||||
return {
|
||||
resolver: zodResolver(profileSchema),
|
||||
defaults: {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
profileImage: null,
|
||||
deleteProfileImage: false,
|
||||
} as ProfileFormData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const NAME_PATTERN = /^[A-Za-z'-]+$/;
|
||||
|
||||
export const profileSchema = z.object({
|
||||
firstName: z
|
||||
.string()
|
||||
.min(1, "First name is required")
|
||||
.max(50, "First name must be 50 characters or less")
|
||||
.regex(NAME_PATTERN, "First name can only contain letters, hyphens, and apostrophes"),
|
||||
lastName: z
|
||||
.string()
|
||||
.min(1, "Last name is required")
|
||||
.max(50, "Last name must be 50 characters or less")
|
||||
.regex(NAME_PATTERN, "Last name can only contain letters, hyphens, and apostrophes"),
|
||||
profileImage: z.instanceof(File).optional().nullable(),
|
||||
deleteProfileImage: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type ProfileFormData = z.infer<typeof profileSchema>;
|
||||
Reference in new issue
Block a user