🎉 feat: initialization
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { Button } from "@workspace/ui/components/button";
|
||||
import { Switch } from "@workspace/ui/components/switch";
|
||||
import { ConfirmButton } from "@workspace/ui/composed/confirm-button";
|
||||
import {
|
||||
ProTable,
|
||||
type ProTableActions,
|
||||
} from "@workspace/ui/composed/pro-table/pro-table";
|
||||
import {
|
||||
createAnnouncement,
|
||||
deleteAnnouncement,
|
||||
getAnnouncementList,
|
||||
updateAnnouncement,
|
||||
} from "@workspace/ui/services/admin/announcement";
|
||||
import { format } from "date-fns";
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import NoticeForm from "./notice-form";
|
||||
|
||||
export default function Page() {
|
||||
const { t } = useTranslation("announcement");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ref = useRef<ProTableActions>(null);
|
||||
|
||||
return (
|
||||
<ProTable<API.Announcement, { enable: boolean; search: string }>
|
||||
action={ref}
|
||||
actions={{
|
||||
render(row) {
|
||||
return [
|
||||
<NoticeForm<API.Announcement>
|
||||
initialValues={row}
|
||||
key="edit"
|
||||
loading={loading}
|
||||
onSubmit={async (values) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await updateAnnouncement({
|
||||
...row,
|
||||
...values,
|
||||
});
|
||||
toast.success(t("updateSuccess", "Updated successfully"));
|
||||
ref.current?.refresh();
|
||||
setLoading(false);
|
||||
return true;
|
||||
} catch {
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
title={t("editAnnouncement", "Edit Announcement")}
|
||||
trigger={t("edit", "Edit")}
|
||||
/>,
|
||||
<ConfirmButton
|
||||
cancelText={t("cancel", "Cancel")}
|
||||
confirmText={t("confirm", "Confirm")}
|
||||
description={t(
|
||||
"deleteDescription",
|
||||
"This action cannot be undone."
|
||||
)}
|
||||
key="delete"
|
||||
onConfirm={async () => {
|
||||
await deleteAnnouncement({
|
||||
id: row.id,
|
||||
});
|
||||
toast.success(t("deleteSuccess", "Deleted successfully"));
|
||||
ref.current?.refresh();
|
||||
}}
|
||||
title={t("confirmDelete", "Confirm Delete")}
|
||||
trigger={
|
||||
<Button variant="destructive">{t("delete", "Delete")}</Button>
|
||||
}
|
||||
/>,
|
||||
];
|
||||
},
|
||||
batchRender(rows) {
|
||||
return [
|
||||
<ConfirmButton
|
||||
cancelText={t("cancel", "Cancel")}
|
||||
confirmText={t("confirm", "Confirm")}
|
||||
description={t(
|
||||
"deleteDescription",
|
||||
"This action cannot be undone."
|
||||
)}
|
||||
key="delete"
|
||||
onConfirm={async () => {
|
||||
for (const element of rows) {
|
||||
await deleteAnnouncement({
|
||||
id: element.id!,
|
||||
});
|
||||
}
|
||||
toast.success(t("deleteSuccess", "Deleted successfully"));
|
||||
ref.current?.refresh();
|
||||
}}
|
||||
title={t("confirmDelete", "Confirm Delete")}
|
||||
trigger={
|
||||
<Button variant="destructive">{t("delete", "Delete")}</Button>
|
||||
}
|
||||
/>,
|
||||
];
|
||||
},
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "show",
|
||||
header: t("show", "Show"),
|
||||
cell: ({ row }) => (
|
||||
<Switch
|
||||
defaultChecked={row.getValue("show")}
|
||||
onCheckedChange={async (checked) => {
|
||||
await updateAnnouncement({
|
||||
...row.original,
|
||||
show: checked,
|
||||
});
|
||||
ref.current?.refresh();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "pinned",
|
||||
header: t("pinned", "Pinned"),
|
||||
cell: ({ row }) => (
|
||||
<Switch
|
||||
defaultChecked={row.getValue("pinned")}
|
||||
onCheckedChange={async (checked) => {
|
||||
await updateAnnouncement({
|
||||
...row.original,
|
||||
pinned: checked,
|
||||
});
|
||||
ref.current?.refresh();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "popup",
|
||||
header: t("popup", "Popup"),
|
||||
cell: ({ row }) => (
|
||||
<Switch
|
||||
defaultChecked={row.getValue("popup")}
|
||||
onCheckedChange={async (checked) => {
|
||||
await updateAnnouncement({
|
||||
...row.original,
|
||||
popup: checked,
|
||||
});
|
||||
ref.current?.refresh();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: t("title", "Title"),
|
||||
},
|
||||
{
|
||||
accessorKey: "content",
|
||||
header: t("content", "Content"),
|
||||
},
|
||||
{
|
||||
accessorKey: "updated_at",
|
||||
header: t("updatedAt", "Updated At"),
|
||||
cell: ({ row }) =>
|
||||
format(row.getValue("updated_at"), "yyyy-MM-dd HH:mm:ss"),
|
||||
},
|
||||
]}
|
||||
header={{
|
||||
title: t("announcementList", "Announcement List"),
|
||||
toolbar: (
|
||||
<NoticeForm<API.CreateAnnouncementRequest>
|
||||
loading={loading}
|
||||
onSubmit={async (values) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await createAnnouncement(values);
|
||||
toast.success(t("createSuccess", "Created successfully"));
|
||||
ref.current?.refresh();
|
||||
setLoading(false);
|
||||
return true;
|
||||
} catch {
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
title={t("createAnnouncement", "Create Announcement")}
|
||||
trigger={t("create", "Create")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
params={[
|
||||
{
|
||||
key: "enable",
|
||||
placeholder: t("enable", "Enable"),
|
||||
options: [
|
||||
{ label: t("show", "Show"), value: "false" },
|
||||
{ label: t("hide", "Hide"), value: "true" },
|
||||
],
|
||||
},
|
||||
{ key: "search" },
|
||||
]}
|
||||
request={async (pagination, filter) => {
|
||||
const { data } = await getAnnouncementList({
|
||||
...pagination,
|
||||
...filter,
|
||||
});
|
||||
return {
|
||||
list: data.data?.list || [],
|
||||
total: data.data?.total || 0,
|
||||
};
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Button } from "@workspace/ui/components/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@workspace/ui/components/form";
|
||||
import { Input } from "@workspace/ui/components/input";
|
||||
import { ScrollArea } from "@workspace/ui/components/scroll-area";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@workspace/ui/components/sheet";
|
||||
import { MarkdownEditor } from "@workspace/ui/composed/editor/markdown";
|
||||
import { Icon } from "@workspace/ui/composed/icon";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
title: z.string(),
|
||||
content: z.string().optional(),
|
||||
});
|
||||
|
||||
interface AnnouncementFormProps<T> {
|
||||
onSubmit: (data: T) => Promise<boolean> | boolean;
|
||||
initialValues?: T;
|
||||
loading?: boolean;
|
||||
trigger: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default function AnnouncementForm<T extends Record<string, any>>({
|
||||
onSubmit,
|
||||
initialValues,
|
||||
loading,
|
||||
trigger,
|
||||
title,
|
||||
}: AnnouncementFormProps<T>) {
|
||||
const { t } = useTranslation("announcement");
|
||||
const [open, setOpen] = useState(false);
|
||||
const form = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
title: "",
|
||||
content: "",
|
||||
...initialValues,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
form?.reset(initialValues);
|
||||
}, [form, initialValues]);
|
||||
|
||||
async function handleSubmit(data: { [x: string]: any }) {
|
||||
const bool = await onSubmit(data as T);
|
||||
if (bool) setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Sheet onOpenChange={setOpen} open={open}>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
{trigger}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="w-[800px] max-w-full md:max-w-screen-md">
|
||||
<SheetHeader>
|
||||
<SheetTitle>{title}</SheetTitle>
|
||||
</SheetHeader>
|
||||
<ScrollArea className="h-[calc(100vh-48px-36px-36px-env(safe-area-inset-top))] px-6">
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-4 pt-4"
|
||||
id="notice-form"
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("form.title", "Title")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t("form.titlePlaceholder", "Enter title")}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="content"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("form.content", "Content")}</FormLabel>
|
||||
<FormControl>
|
||||
<MarkdownEditor
|
||||
onChange={(value) => {
|
||||
form.setValue(field.name, value || "");
|
||||
}}
|
||||
value={field.value}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</ScrollArea>
|
||||
<SheetFooter className="flex-row justify-end gap-2 pt-3">
|
||||
<Button
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
variant="outline"
|
||||
>
|
||||
{t("form.cancel", "Cancel")}
|
||||
</Button>
|
||||
<Button disabled={loading} onClick={form.handleSubmit(handleSubmit)}>
|
||||
{loading && (
|
||||
<Icon className="mr-2 animate-spin" icon="mdi:loading" />
|
||||
)}{" "}
|
||||
{t("form.confirm", "Confirm")}
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user