refactor(ui): refactor emails state using zustand store

This commit is contained in:
Fu Diwei 2024-12-11 16:42:23 +08:00
parent 83ba3d4450
commit b744363736
21 changed files with 141 additions and 166 deletions

View File

@ -1,4 +1,4 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { z } from "zod"; import { z } from "zod";
@ -10,10 +10,8 @@ import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { PbErrorData } from "@/domain/base"; import { type PbErrorData } from "@/domain/base";
import { EmailsSetting } from "@/domain/settings"; import { useContactStore } from "@/stores/contact";
import { update } from "@/repository/settings";
import { useConfigContext } from "@/providers/config";
type EmailsEditProps = { type EmailsEditProps = {
className?: string; className?: string;
@ -21,10 +19,7 @@ type EmailsEditProps = {
}; };
const EmailsEdit = ({ className, trigger }: EmailsEditProps) => { const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
const { const { emails, setEmails, fetchEmails } = useContactStore();
config: { emails },
setEmails,
} = useConfigContext();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
@ -40,30 +35,21 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
}, },
}); });
useEffect(() => {
fetchEmails();
}, []);
const onSubmit = async (data: z.infer<typeof formSchema>) => { const onSubmit = async (data: z.infer<typeof formSchema>) => {
if ((emails.content as EmailsSetting).emails.includes(data.email)) { if (emails.includes(data.email)) {
form.setError("email", { form.setError("email", {
message: "common.errmsg.email_duplicate", message: "common.errmsg.email_duplicate",
}); });
return; return;
} }
// 保存到 config
const newEmails = [...(emails.content as EmailsSetting).emails, data.email];
try { try {
const resp = await update({ await setEmails([...emails, data.email]);
...emails,
name: "emails",
content: {
emails: newEmails,
},
});
// 更新本地状态
setEmails(resp);
// 关闭弹窗
form.reset(); form.reset();
form.clearErrors(); form.clearErrors();

View File

@ -8,7 +8,7 @@ import { Switch } from "@/components/ui/switch";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { NotifyChannels, NotifyChannelBark } from "@/domain/settings"; import { NotifyChannels, NotifyChannelBark } from "@/domain/settings";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
import Show from "@/components/Show"; import Show from "@/components/Show";
@ -96,7 +96,7 @@ const Bark = () => {
const handleSaveClick = async () => { const handleSaveClick = async () => {
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -160,7 +160,7 @@ const Bark = () => {
setBark(newData); setBark(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -9,7 +9,7 @@ import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { NotifyChannelDingTalk, NotifyChannels } from "@/domain/settings"; import { NotifyChannelDingTalk, NotifyChannels } from "@/domain/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import Show from "@/components/Show"; import Show from "@/components/Show";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
@ -96,7 +96,7 @@ const DingTalk = () => {
const handleSaveClick = async () => { const handleSaveClick = async () => {
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -160,7 +160,7 @@ const DingTalk = () => {
setDingtalk(newData); setDingtalk(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -9,7 +9,7 @@ import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { NotifyChannelEmail, NotifyChannels } from "@/domain/settings"; import { NotifyChannelEmail, NotifyChannels } from "@/domain/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import Show from "@/components/Show"; import Show from "@/components/Show";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
@ -119,7 +119,7 @@ const Mail = () => {
const handleSaveClick = async () => { const handleSaveClick = async () => {
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -183,7 +183,7 @@ const Mail = () => {
setMail(newData); setMail(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -5,7 +5,7 @@ import { Label } from "@/components/ui/label";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { NotifyChannelLark, NotifyChannels } from "@/domain/settings"; import { NotifyChannelLark, NotifyChannels } from "@/domain/settings";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -92,7 +92,7 @@ const Lark = () => {
const handleSaveClick = async () => { const handleSaveClick = async () => {
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -156,7 +156,7 @@ const Lark = () => {
setLark(newData); setLark(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -6,7 +6,7 @@ import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { defaultNotifyTemplate, NotifyTemplates, NotifyTemplate as NotifyTemplateT } from "@/domain/settings"; import { defaultNotifyTemplate, NotifyTemplates, NotifyTemplate as NotifyTemplateT } from "@/domain/settings";
import { getSetting, update } from "@/repository/settings"; import { get, save } from "@/repository/settings";
const NotifyTemplate = () => { const NotifyTemplate = () => {
const [id, setId] = useState(""); const [id, setId] = useState("");
@ -17,7 +17,7 @@ const NotifyTemplate = () => {
useEffect(() => { useEffect(() => {
const featchData = async () => { const featchData = async () => {
const resp = await getSetting("templates"); const resp = await get("templates");
if (resp.content) { if (resp.content) {
setTemplates((resp.content as NotifyTemplates).notifyTemplates); setTemplates((resp.content as NotifyTemplates).notifyTemplates);
@ -50,7 +50,7 @@ const NotifyTemplate = () => {
}; };
const handleSaveClick = async () => { const handleSaveClick = async () => {
const resp = await update({ const resp = await save({
id: id, id: id,
content: { content: {
notifyTemplates: templates, notifyTemplates: templates,

View File

@ -9,7 +9,7 @@ import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { isValidURL } from "@/utils/url"; import { isValidURL } from "@/utils/url";
import { NotifyChannels, NotifyChannelServerChan } from "@/domain/settings"; import { NotifyChannels, NotifyChannelServerChan } from "@/domain/settings";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
import Show from "@/components/Show"; import Show from "@/components/Show";
@ -103,7 +103,7 @@ const ServerChan = () => {
return; return;
} }
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -167,7 +167,7 @@ const ServerChan = () => {
setServerChan(newData); setServerChan(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -8,7 +8,7 @@ import { Switch } from "@/components/ui/switch";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { NotifyChannels, NotifyChannelTelegram } from "@/domain/settings"; import { NotifyChannels, NotifyChannelTelegram } from "@/domain/settings";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
import Show from "@/components/Show"; import Show from "@/components/Show";
@ -96,7 +96,7 @@ const Telegram = () => {
const handleSaveClick = async () => { const handleSaveClick = async () => {
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -160,7 +160,7 @@ const Telegram = () => {
setTelegram(newData); setTelegram(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -9,7 +9,7 @@ import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { isValidURL } from "@/utils/url"; import { isValidURL } from "@/utils/url";
import { NotifyChannels, NotifyChannelWebhook } from "@/domain/settings"; import { NotifyChannels, NotifyChannelWebhook } from "@/domain/settings";
import { update } from "@/repository/settings"; import { save } from "@/repository/settings";
import { useNotifyContext } from "@/providers/notify"; import { useNotifyContext } from "@/providers/notify";
import { notifyTest } from "@/api/notify"; import { notifyTest } from "@/api/notify";
import Show from "@/components/Show"; import Show from "@/components/Show";
@ -103,7 +103,7 @@ const Webhook = () => {
return; return;
} }
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {
@ -167,7 +167,7 @@ const Webhook = () => {
setWebhook(newData); setWebhook(newData);
try { try {
const resp = await update({ const resp = await save({
...config, ...config,
name: "notifyChannels", name: "notifyChannels",
content: { content: {

View File

@ -1,5 +1,4 @@
import { memo } from "react"; import { memo, useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import z from "zod"; import z from "zod";
@ -16,8 +15,7 @@ import EmailsEdit from "@/components/certimate/EmailsEdit";
import StringList from "@/components/certimate/StringList"; import StringList from "@/components/certimate/StringList";
import { accessProvidersMap } from "@/domain/access"; import { accessProvidersMap } from "@/domain/access";
import { EmailsSetting } from "@/domain/settings"; import { useContactStore } from "@/stores/contact";
import { useConfigContext } from "@/providers/config"; import { useConfigContext } from "@/providers/config";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { TooltipFast } from "@/components/ui/tooltip"; import { TooltipFast } from "@/components/ui/tooltip";
@ -36,8 +34,13 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
const { updateNode } = useWorkflowStore(useShallow(selectState)); const { updateNode } = useWorkflowStore(useShallow(selectState));
const { const {
config: { accesses, emails }, config: { accesses },
} = useConfigContext(); } = useConfigContext();
const { emails, fetchEmails } = useContactStore();
useEffect(() => {
fetchEmails();
}, []);
const { t } = useTranslation(); const { t } = useTranslation();
@ -141,7 +144,7 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectLabel>{t("domain.application.form.email.list")}</SelectLabel> <SelectLabel>{t("domain.application.form.email.list")}</SelectLabel>
{(emails.content as EmailsSetting).emails.map((item) => ( {emails.map((item) => (
<SelectItem key={item} value={item}> <SelectItem key={item} value={item}>
<div>{item}</div> <div>{item}</div>
</SelectItem> </SelectItem>

View File

@ -1,10 +1,11 @@
export type Setting<T> = { import { type BaseModel } from "pocketbase";
id?: string;
name?: string;
content?: T;
};
export type EmailsSetting = { export interface Settings<T> extends BaseModel {
name: string;
content: T;
}
export type EmailsSettingsContent = {
emails: string[]; emails: string[];
}; };

View File

@ -1,7 +1,7 @@
{ {
"access.page.title": "授权管理", "access.page.title": "授权管理",
"access.nodata": "暂无授权信息,请先新建", "access.nodata": "暂无授权信息,请先新建授权",
"access.action.add": "新建授权", "access.action.add": "新建授权",
"access.action.edit": "编辑授权", "access.action.edit": "编辑授权",

View File

@ -1,7 +1,7 @@
{ {
"workflow.page.title": "工作流", "workflow.page.title": "工作流",
"workflow.nodata": "暂无工作流,请先新建", "workflow.nodata": "暂无工作流,请先新建工作流",
"workflow.action.create": "新建工作流", "workflow.action.create": "新建工作流",
"workflow.action.edit": "编辑工作流", "workflow.action.edit": "编辑工作流",

View File

@ -7,7 +7,7 @@ import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase"; import { ClientResponseError } from "pocketbase";
import AccessEditDialog from "@/components/certimate/AccessEditDialog"; import AccessEditDialog from "@/components/certimate/AccessEditDialog";
import { Access as AccessType, accessProvidersMap } from "@/domain/access"; import { accessProvidersMap, type Access as AccessType } from "@/domain/access";
import { remove as removeAccess } from "@/repository/access"; import { remove as removeAccess } from "@/repository/access";
import { useConfigContext } from "@/providers/config"; import { useConfigContext } from "@/providers/config";
@ -120,10 +120,10 @@ const AccessList = () => {
try { try {
const startIndex = (page - 1) * pageSize; const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize; const endIndex = startIndex + pageSize;
const items = configContext.config.accesses.slice(startIndex, endIndex); const items = configContext.config?.accesses?.slice(startIndex, endIndex) ?? [];
setTableData(items); setTableData(items);
setTableTotal(configContext.config.accesses.length); setTableTotal(configContext.config?.accesses?.length ?? 0);
} catch (err) { } catch (err) {
if (err instanceof ClientResponseError && err.isAbort) { if (err instanceof ClientResponseError && err.isAbort) {
return; return;

View File

@ -12,14 +12,14 @@ import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
import { SSLProvider as SSLProviderType, SSLProviderSetting, Setting } from "@/domain/settings"; import { SSLProvider as SSLProviderType, SSLProviderSetting, Settings } from "@/domain/settings";
import { getSetting, update } from "@/repository/settings"; import { get, save } from "@/repository/settings";
import { produce } from "immer"; import { produce } from "immer";
type SSLProviderContext = { type SSLProviderContext = {
setting: Setting<SSLProviderSetting>; setting: Settings<SSLProviderSetting>;
onSubmit: (data: Setting<SSLProviderSetting>) => void; onSubmit: (data: Settings<SSLProviderSetting>) => void;
setConfig: (config: Setting<SSLProviderSetting>) => void; setConfig: (config: Settings<SSLProviderSetting>) => void;
}; };
const Context = createContext({} as SSLProviderContext); const Context = createContext({} as SSLProviderContext);
@ -31,19 +31,18 @@ export const useSSLProviderContext = () => {
const SSLProvider = () => { const SSLProvider = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [config, setConfig] = useState<Setting<SSLProviderSetting>>({ const [config, setConfig] = useState<Settings<SSLProviderSetting>>({
id: "",
content: { content: {
provider: "letsencrypt", provider: "letsencrypt",
config: {}, config: {},
}, },
}); } as Settings<SSLProviderSetting>);
const { toast } = useToast(); const { toast } = useToast();
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const setting = await getSetting<SSLProviderSetting>("ssl-provider"); const setting = await get<SSLProviderSetting>("ssl-provider");
if (setting) { if (setting) {
setConfig(setting); setConfig(setting);
@ -74,10 +73,10 @@ const SSLProvider = () => {
return ""; return "";
}; };
const onSubmit = async (data: Setting<SSLProviderSetting>) => { const onSubmit = async (data: Settings<SSLProviderSetting>) => {
try { try {
console.log(data); console.log(data);
const resp = await update({ ...data }); const resp = await save({ ...data });
setConfig(resp); setConfig(resp);
toast({ toast({
title: t("common.text.operation_succeeded"), title: t("common.text.operation_succeeded"),

View File

@ -1,22 +1,17 @@
import { createContext, ReactNode, useCallback, useContext, useEffect, useReducer } from "react"; import { createContext, useCallback, useContext, useEffect, useReducer, type ReactNode } from "react";
import { Access } from "@/domain/access"; import { type Access as AccessType } from "@/domain/access";
import { EmailsSetting, Setting } from "@/domain/settings"; import { list as listAccess } from "@/repository/access";
import { list } from "@/repository/access";
import { getEmails } from "@/repository/settings";
import { configReducer } from "./reducer"; import { configReducer } from "./reducer";
export type ConfigData = { export type ConfigData = {
accesses: Access[]; accesses: AccessType[];
emails: Setting<EmailsSetting>;
}; };
export type ConfigContext = { export type ConfigContext = {
config: ConfigData; config: ConfigData;
setEmails: (email: Setting<EmailsSetting>) => void; addAccess: (access: AccessType) => void;
addAccess: (access: Access) => void; updateAccess: (access: AccessType) => void;
updateAccess: (access: Access) => void;
deleteAccess: (id: string) => void; deleteAccess: (id: string) => void;
}; };
@ -24,45 +19,28 @@ const Context = createContext({} as ConfigContext);
export const useConfigContext = () => useContext(Context); export const useConfigContext = () => useContext(Context);
interface ConfigProviderProps { export const ConfigProvider = ({ children }: { children: ReactNode }) => {
children: ReactNode;
}
export const ConfigProvider = ({ children }: ConfigProviderProps) => {
const [config, dispatchConfig] = useReducer(configReducer, { const [config, dispatchConfig] = useReducer(configReducer, {
accesses: [], accesses: [],
emails: { content: { emails: [] } },
}); });
useEffect(() => { useEffect(() => {
const featchData = async () => { const featchData = async () => {
const data = await list(); const data = await listAccess();
dispatchConfig({ type: "SET_ACCESSES", payload: data }); dispatchConfig({ type: "SET_ACCESSES", payload: data });
}; };
featchData(); featchData();
}, []); }, []);
useEffect(() => {
const featchEmails = async () => {
const emails = await getEmails();
dispatchConfig({ type: "SET_EMAILS", payload: emails });
};
featchEmails();
}, []);
const setEmails = useCallback((emails: Setting<EmailsSetting>) => {
dispatchConfig({ type: "SET_EMAILS", payload: emails });
}, []);
const deleteAccess = useCallback((id: string) => { const deleteAccess = useCallback((id: string) => {
dispatchConfig({ type: "DELETE_ACCESS", payload: id }); dispatchConfig({ type: "DELETE_ACCESS", payload: id });
}, []); }, []);
const addAccess = useCallback((access: Access) => { const addAccess = useCallback((access: AccessType) => {
dispatchConfig({ type: "ADD_ACCESS", payload: access }); dispatchConfig({ type: "ADD_ACCESS", payload: access });
}, []); }, []);
const updateAccess = useCallback((access: Access) => { const updateAccess = useCallback((access: AccessType) => {
dispatchConfig({ type: "UPDATE_ACCESS", payload: access }); dispatchConfig({ type: "UPDATE_ACCESS", payload: access });
}, []); }, []);
@ -71,15 +49,13 @@ export const ConfigProvider = ({ children }: ConfigProviderProps) => {
value={{ value={{
config: { config: {
accesses: config.accesses, accesses: config.accesses,
emails: config.emails,
}, },
setEmails,
addAccess, addAccess,
updateAccess, updateAccess,
deleteAccess, deleteAccess,
}} }}
> >
{children && children} {children}
</Context.Provider> </Context.Provider>
); );
}; };

View File

@ -1,14 +1,11 @@
import { Access } from "@/domain/access"; import { Access } from "@/domain/access";
import { EmailsSetting, Setting } from "@/domain/settings";
import { ConfigData } from "./"; import { ConfigData } from "./";
type Action = type Action =
| { type: "ADD_ACCESS"; payload: Access } | { type: "ADD_ACCESS"; payload: Access }
| { type: "DELETE_ACCESS"; payload: string } | { type: "DELETE_ACCESS"; payload: string }
| { type: "UPDATE_ACCESS"; payload: Access } | { type: "UPDATE_ACCESS"; payload: Access }
| { type: "SET_ACCESSES"; payload: Access[] } | { type: "SET_ACCESSES"; payload: Access[] };
| { type: "SET_EMAILS"; payload: Setting<EmailsSetting> }
| { type: "ADD_EMAIL"; payload: string };
export const configReducer = (state: ConfigData, action: Action): ConfigData => { export const configReducer = (state: ConfigData, action: Action): ConfigData => {
switch (action.type) { switch (action.type) {
@ -36,23 +33,6 @@ export const configReducer = (state: ConfigData, action: Action): ConfigData =>
accesses: state.accesses.map((access) => (access.id === action.payload.id ? action.payload : access)), accesses: state.accesses.map((access) => (access.id === action.payload.id ? action.payload : access)),
}; };
} }
case "SET_EMAILS": {
return {
...state,
emails: action.payload,
};
}
case "ADD_EMAIL": {
return {
...state,
emails: {
...state.emails,
content: {
emails: [...(state.emails.content as EmailsSetting).emails, action.payload],
},
},
};
}
default: default:
return state; return state;
} }

View File

@ -1,13 +1,13 @@
import { ReactNode, useContext, createContext, useEffect, useReducer, useCallback } from "react"; import { ReactNode, useContext, createContext, useEffect, useReducer, useCallback } from "react";
import { NotifyChannel, NotifyChannels, Setting } from "@/domain/settings"; import { NotifyChannel, NotifyChannels, Settings } from "@/domain/settings";
import { getSetting } from "@/repository/settings"; import { get } from "@/repository/settings";
import { notifyReducer } from "./reducer"; import { notifyReducer } from "./reducer";
export type NotifyContext = { export type NotifyContext = {
config: Setting<NotifyChannels>; config: Settings<NotifyChannels>;
setChannel: (data: { channel: string; data: NotifyChannel }) => void; setChannel: (data: { channel: string; data: NotifyChannel }) => void;
setChannels: (data: Setting<NotifyChannels>) => void; setChannels: (data: Settings<NotifyChannels>) => void;
initChannels: () => void; initChannels: () => void;
}; };
@ -27,7 +27,7 @@ export const NotifyProvider = ({ children }: NotifyProviderProps) => {
}, []); }, []);
const featchData = async () => { const featchData = async () => {
const chanels = await getSetting<NotifyChannels>("notifyChannels"); const chanels = await get<NotifyChannels>("notifyChannels");
dispatchNotify({ dispatchNotify({
type: "SET_CHANNELS", type: "SET_CHANNELS",
payload: chanels, payload: chanels,
@ -45,7 +45,7 @@ export const NotifyProvider = ({ children }: NotifyProviderProps) => {
}); });
}, []); }, []);
const setChannels = useCallback((setting: Setting<NotifyChannels>) => { const setChannels = useCallback((setting: Settings<NotifyChannels>) => {
dispatchNotify({ dispatchNotify({
type: "SET_CHANNELS", type: "SET_CHANNELS",
payload: setting, payload: setting,

View File

@ -1,4 +1,4 @@
import { NotifyChannel, NotifyChannels, Setting } from "@/domain/settings"; import { NotifyChannel, NotifyChannels, Settings } from "@/domain/settings";
type Action = type Action =
| { | {
@ -10,10 +10,10 @@ type Action =
} }
| { | {
type: "SET_CHANNELS"; type: "SET_CHANNELS";
payload: Setting<NotifyChannels>; payload: Settings<NotifyChannels>;
}; };
export const notifyReducer = (state: Setting<NotifyChannels>, action: Action) => { export const notifyReducer = (state: Settings<NotifyChannels>, action: Action) => {
switch (action.type) { switch (action.type) {
case "SET_CHANNEL": { case "SET_CHANNEL": {
const channel = action.payload.channel; const channel = action.payload.channel;

View File

@ -1,36 +1,22 @@
import { EmailsSetting, Setting } from "@/domain/settings"; import { Settings } from "@/domain/settings";
import { getPocketBase } from "./pocketbase"; import { getPocketBase } from "./pocketbase";
export const getEmails = async () => { export const get = async <T>(name: string) => {
try { try {
const resp = await getPocketBase().collection("settings").getFirstListItem<Setting<EmailsSetting>>("name='emails'"); const resp = await getPocketBase().collection("settings").getFirstListItem<Settings<T>>(`name='${name}'`);
return resp; return resp;
} catch (e) { } catch {
return { return {
content: { emails: [] },
};
}
};
export const getSetting = async <T>(name: string) => {
try {
const resp = await getPocketBase().collection("settings").getFirstListItem<Setting<T>>(`name='${name}'`);
return resp;
} catch (e) {
const rs: Setting<T> = {
name: name, name: name,
}; content: {} as T,
return rs; } as Settings<T>;
} }
}; };
export const update = async <T>(setting: Setting<T>) => { export const save = async <T>(record: Settings<T>) => {
const pb = getPocketBase(); if (record.id) {
let resp: Setting<T>; return await getPocketBase().collection("settings").update<Settings<T>>(record.id, record);
if (setting.id) {
resp = await pb.collection("settings").update(setting.id, setting);
} else {
resp = await pb.collection("settings").create(setting);
} }
return resp;
return await getPocketBase().collection("settings").create<Settings<T>>(record);
}; };

View File

@ -0,0 +1,44 @@
import { create } from "zustand";
import { produce } from "immer";
import { type EmailsSettingsContent, type Settings } from "@/domain/settings";
import { get as getSettings, save as saveSettings } from "@/repository/settings";
export interface ContactState {
emails: string[];
setEmails: (emails: string[]) => void;
fetchEmails: () => Promise<void>;
}
export const useContactStore = create<ContactState>((set) => {
let settings: Settings<EmailsSettingsContent>;
return {
emails: [],
setEmails: async (emails: string[]) => {
settings ??= await getSettings<EmailsSettingsContent>("emails");
settings = await saveSettings<EmailsSettingsContent>({
...settings,
content: {
...settings.content,
emails: emails,
},
});
set(
produce((state: ContactState) => {
state.emails = settings.content.emails;
})
);
},
fetchEmails: async () => {
settings = await getSettings<EmailsSettingsContent>("emails");
set({
emails: settings.content.emails?.sort() ?? [],
});
},
};
});