Added settings menu with working currency editing

This commit is contained in:
Donkie
2024-01-07 11:11:57 +01:00
parent 9362d04dad
commit 77e781f1ea
7 changed files with 248 additions and 3 deletions

View File

@@ -10,7 +10,14 @@ import dataProvider from "./components/dataProvider";
import { useTranslation } from "react-i18next";
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
import { ColorModeContextProvider } from "./contexts/color-mode";
import { FileOutlined, HighlightOutlined, HomeOutlined, QuestionOutlined, UserOutlined } from "@ant-design/icons";
import {
FileOutlined,
HighlightOutlined,
HomeOutlined,
QuestionOutlined,
ToolOutlined,
UserOutlined,
} from "@ant-design/icons";
import { ConfigProvider } from "antd";
import React from "react";
import { Locale } from "antd/es/locale";
@@ -139,6 +146,14 @@ function App() {
icon: <UserOutlined />,
},
},
{
name: "settings",
list: "/settings",
meta: {
canDelete: false,
icon: <ToolOutlined />,
},
},
{
name: "help",
list: "/help",
@@ -202,6 +217,7 @@ function App() {
<Route path="edit/:id" element={<LoadableResourcePage resource="vendors" page="edit" />} />
<Route path="show/:id" element={<LoadableResourcePage resource="vendors" page="show" />} />
</Route>
<Route path="/settings" element={<LoadablePage name="settings" />} />
<Route path="/help" element={<LoadablePage name="help" />} />
<Route path="*" element={<ErrorComponent />} />
</Route>

View File

@@ -0,0 +1,5 @@
import React from "react";
export function ExtraFieldsSettings() {
return <>Extra Fields Settings Content</>;
}

View File

@@ -0,0 +1,79 @@
import React from "react";
import { useTranslate } from "@refinedev/core";
import { Button, Form, Input, message } from "antd";
import { useGetSettings, useSetSetting } from "./query";
export function GenericSettings() {
const settings = useGetSettings();
const setSetting = useSetSetting();
const [form] = Form.useForm();
const [messageApi, contextHolder] = message.useMessage();
const t = useTranslate();
// Set initial form values
React.useEffect(() => {
if (settings.data) {
form.setFieldsValue({
currency: JSON.parse(settings.data.currency.value),
});
}
}, [settings.data, form]);
// Popup message if setSetting is successful
React.useEffect(() => {
if (setSetting.isSuccess) {
messageApi.success(t("notifications.saveSuccessful"));
}
}, [setSetting.isSuccess, messageApi, t]);
// Handle form submit
const onFinish = (values: { currency: string }) => {
// Check if the currency has changed
if (settings.data?.currency.value !== JSON.stringify(values.currency)) {
setSetting.mutate({
key: "currency",
value: JSON.stringify(values.currency),
});
}
};
return (
<>
<Form
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
initialValues={{
currency: settings.data?.currency.value,
}}
onFinish={onFinish}
style={{
maxWidth: "600px",
margin: "0 auto",
}}
>
<Form.Item
label={t("settings.generic.currency.label")}
name="currency"
rules={[
{
required: true,
},
{
pattern: /^[A-Z]{3}$/,
},
]}
>
<Input />
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit" loading={settings.isFetching || setSetting.isLoading}>
{t("buttons.save")}
</Button>
</Form.Item>
</Form>
{contextHolder}
</>
);
}

View File

@@ -0,0 +1,64 @@
import React from "react";
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import { Content } from "antd/es/layout/layout";
import { Menu, theme } from "antd";
import { SolutionOutlined, ToolOutlined } from "@ant-design/icons";
import { useSavedState } from "../../utils/saveload";
import { GenericSettings } from "./GenericSettings";
import { ExtraFieldsSettings } from "./ExtraFieldsSettings";
dayjs.extend(utc);
const { useToken } = theme;
export const Settings: React.FC<IResourceComponentsProps> = () => {
const { token } = useToken();
const t = useTranslate();
const [current, setCurrent] = useSavedState("settings-tab", "generic");
return (
<>
<h1
style={{
color: token.colorText,
}}
>
{t("settings.header")}
</h1>
<Content
style={{
padding: "1em",
minHeight: 280,
margin: "0 auto",
backgroundColor: token.colorBgContainer,
borderRadius: token.borderRadiusLG,
color: token.colorText,
fontFamily: token.fontFamily,
fontSize: token.fontSizeLG,
lineHeight: 1.5,
}}
>
<Menu
mode="horizontal"
selectedKeys={[current]}
onClick={({ key }) => setCurrent(key)}
items={[
{ key: "generic", label: t("settings.generic.tab"), icon: <ToolOutlined /> },
{ key: "extra", label: t("settings.extra_fields.tab"), icon: <SolutionOutlined /> },
]}
style={{
marginBottom: "3em",
}}
/>
<main>
{current === "generic" && <GenericSettings />}
{current === "extra" && <ExtraFieldsSettings />}
</main>
</Content>
</>
);
};
export default Settings;

View File

@@ -0,0 +1,55 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
interface SettingResponseValue {
value: string;
is_set: boolean;
type: string;
}
interface SettingsResponse {
[key: string]: SettingResponseValue;
}
export function useGetSettings() {
const apiEndpoint = import.meta.env.VITE_APIURL;
return useQuery<SettingsResponse>({
queryKey: ["settings"],
queryFn: async () => {
const response = await fetch(`${apiEndpoint}/setting`);
return response.json();
},
});
}
export function useGetSetting(key: string) {
const apiEndpoint = import.meta.env.VITE_APIURL;
return useQuery<SettingResponseValue>({
queryKey: ["settings", key],
queryFn: async () => {
const response = await fetch(`${apiEndpoint}/setting/${key}`);
return response.json();
},
});
}
export function useSetSetting() {
const queryClient = useQueryClient();
const apiEndpoint = import.meta.env.VITE_APIURL;
return useMutation<SettingResponseValue, unknown, { key: string; value: unknown }>({
mutationFn: async ({ key, value }) => {
const response = await fetch(`${apiEndpoint}/setting/${key}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(value),
});
return response.json();
},
onSuccess: (_data, { key }) => {
// Invalidate and refetch
queryClient.invalidateQueries(["settings", key]);
},
});
}