162 lines
3.8 KiB
TypeScript
162 lines
3.8 KiB
TypeScript
import {
|
|
ActionIcon,
|
|
Box,
|
|
Card,
|
|
Group,
|
|
LoadingOverlay,
|
|
Text,
|
|
TextInput,
|
|
} from "@mantine/core";
|
|
import { useForm, zodResolver } from "@mantine/form";
|
|
import { IconAt, IconMinus, IconPlus } from "@tabler/icons-react";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import { z } from "zod";
|
|
import { getConfig, upsertConfig } from "../../apis/config";
|
|
import { IConfig } from "../../system/type";
|
|
import { useConfirmStore } from "../../lib/zustand/use-confirm";
|
|
import { useDisclosure } from "@mantine/hooks";
|
|
|
|
const schema = z.object({
|
|
email: z
|
|
.string({ message: "Email is required" })
|
|
.email({ message: "Invalid email address" }),
|
|
});
|
|
|
|
const MailInput = ({
|
|
initValue,
|
|
onDelete,
|
|
onAdd,
|
|
}: {
|
|
initValue?: string;
|
|
onDelete?: (data: string) => void;
|
|
onAdd?: (data: string) => Promise<void>;
|
|
}) => {
|
|
const form = useForm({
|
|
initialValues: {
|
|
email: initValue || "",
|
|
},
|
|
validate: zodResolver(schema),
|
|
});
|
|
|
|
return (
|
|
<form
|
|
onSubmit={form.onSubmit(
|
|
onAdd
|
|
? async (values) => {
|
|
await onAdd(values.email);
|
|
form.reset();
|
|
}
|
|
: () => {}
|
|
)}
|
|
className="flex items-start gap-2 w-full"
|
|
>
|
|
<TextInput
|
|
{...form.getInputProps("email")}
|
|
leftSection={<IconAt size={14} />}
|
|
placeholder="Enter email"
|
|
className="flex-1"
|
|
size="xs"
|
|
/>
|
|
<ActionIcon
|
|
onClick={initValue && onDelete ? () => onDelete(initValue) : undefined}
|
|
type={!initValue ? "submit" : "button"}
|
|
color={initValue ? "red" : "blue"}
|
|
variant="light"
|
|
>
|
|
{initValue ? <IconMinus size={14} /> : <IconPlus size={14} />}
|
|
</ActionIcon>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
export default function MailsConfig() {
|
|
const [config, setConfig] = useState<null | IConfig>(null);
|
|
const { setConfirm } = useConfirmStore();
|
|
const [opened, { open, close }] = useDisclosure(false);
|
|
useEffect(() => {
|
|
fetchConfig();
|
|
}, []);
|
|
|
|
const mails = useMemo(() => {
|
|
if (!config) return [];
|
|
|
|
return config?.value?.split(", ").length > 0
|
|
? config?.value.split(",")
|
|
: [];
|
|
}, [config]);
|
|
|
|
const fetchConfig = async () => {
|
|
const response = await getConfig("MAIL_SCRAP_REPORT");
|
|
|
|
if (!response || ![200, 201].includes(response.data?.status_code)) return;
|
|
|
|
setConfig(response.data.data);
|
|
};
|
|
|
|
const handleDelete = (mail: string) => {
|
|
setConfirm({
|
|
message: "Are you want to delete: " + mail,
|
|
title: "Delete",
|
|
handleOk: async () => {
|
|
open();
|
|
const newMails = mails.filter((item) => item !== mail);
|
|
|
|
if (!config) return;
|
|
|
|
const response = await upsertConfig({
|
|
...(config as IConfig),
|
|
value: newMails.join(", "),
|
|
});
|
|
|
|
if (response) {
|
|
fetchConfig();
|
|
}
|
|
close();
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleAdd = async (mail: string) => {
|
|
const newMails = [...mails, mail];
|
|
|
|
open();
|
|
const response = await upsertConfig({
|
|
...(config as IConfig),
|
|
value: newMails.join(", "),
|
|
});
|
|
|
|
if (response) {
|
|
fetchConfig();
|
|
}
|
|
close();
|
|
};
|
|
|
|
return (
|
|
<Card withBorder shadow="sm" radius="md" w={400}>
|
|
<Card.Section withBorder inheritPadding py="xs">
|
|
<Group justify="space-between">
|
|
<Text fw={500}>Mails</Text>
|
|
</Group>
|
|
</Card.Section>
|
|
|
|
<Card.Section p="md">
|
|
<Box className="flex flex-col gap-2">
|
|
{mails.length > 0 &&
|
|
mails.map((mail) => {
|
|
return (
|
|
<MailInput
|
|
onDelete={handleDelete}
|
|
key={mail}
|
|
initValue={mail}
|
|
/>
|
|
);
|
|
})}
|
|
<MailInput onAdd={handleAdd} />
|
|
</Box>
|
|
</Card.Section>
|
|
|
|
<LoadingOverlay visible={opened} />
|
|
</Card>
|
|
);
|
|
}
|