Added settings menu with working currency editing
This commit is contained in:
@@ -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>
|
||||
|
||||
5
client/src/pages/settings/ExtraFieldsSettings.tsx
Normal file
5
client/src/pages/settings/ExtraFieldsSettings.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
export function ExtraFieldsSettings() {
|
||||
return <>Extra Fields Settings Content</>;
|
||||
}
|
||||
79
client/src/pages/settings/GenericSettings.tsx
Normal file
79
client/src/pages/settings/GenericSettings.tsx
Normal 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}
|
||||
</>
|
||||
);
|
||||
}
|
||||
64
client/src/pages/settings/index.tsx
Normal file
64
client/src/pages/settings/index.tsx
Normal 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;
|
||||
55
client/src/pages/settings/query.ts
Normal file
55
client/src/pages/settings/query.ts
Normal 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]);
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user