From 77e781f1ea9a4e2841c8acdce6130602101d9dc4 Mon Sep 17 00:00:00 2001 From: Donkie Date: Sun, 7 Jan 2024 11:11:57 +0100 Subject: [PATCH] Added settings menu with working currency editing --- client/public/locales/en/common.json | 15 +++- client/public/locales/sv/common.json | 15 +++- client/src/App.tsx | 18 ++++- .../pages/settings/ExtraFieldsSettings.tsx | 5 ++ client/src/pages/settings/GenericSettings.tsx | 79 +++++++++++++++++++ client/src/pages/settings/index.tsx | 64 +++++++++++++++ client/src/pages/settings/query.ts | 55 +++++++++++++ 7 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 client/src/pages/settings/ExtraFieldsSettings.tsx create mode 100644 client/src/pages/settings/GenericSettings.tsx create mode 100644 client/src/pages/settings/index.tsx create mode 100644 client/src/pages/settings/query.ts diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 23e3f97..23d06e0 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -41,7 +41,8 @@ "deleteError": "Error when deleting {{resource}} (status code: {{statusCode}})", "editSuccess": "Successfully edited {{resource}}", "editError": "Error when editing {{resource}} (status code: {{statusCode}})", - "importProgress": "Importing: {{processed}}/{{total}}" + "importProgress": "Importing: {{processed}}/{{total}}", + "saveSuccessful": "Save successful!" }, "kofi": "Tip me on Ko-fi", "loading": "Loading", @@ -252,6 +253,18 @@ "table": { "actions": "Actions" }, + "settings": { + "header": "Settings", + "generic": { + "tab": "Generic", + "currency": { + "label": "Currency" + } + }, + "extra_fields": { + "tab": "Extra Fields" + } + }, "documentTitle": { "default": "Spoolman", "suffix": " | Spoolman", diff --git a/client/public/locales/sv/common.json b/client/public/locales/sv/common.json index ba75663..a21903b 100644 --- a/client/public/locales/sv/common.json +++ b/client/public/locales/sv/common.json @@ -41,7 +41,8 @@ "deleteError": "Det gick inte att ta bort {{resource}} (statuskod: {{statusCode}})", "editSuccess": "Lyckades ändra {{resource}}", "editError": "Det gick inte att ändra {{resource}} (statuskod: {{statusCode}})", - "importProgress": "Importerar: {{processed}}/{{total}}" + "importProgress": "Importerar: {{processed}}/{{total}}", + "saveSuccessful": "Sparning lyckades!" }, "loading": "Laddar", "version": "Version", @@ -240,6 +241,18 @@ "table": { "actions": "Åtgärder" }, + "settings": { + "header": "Inställningar", + "generic": { + "tab": "Allmänt", + "currency": { + "label": "Valuta" + } + }, + "extra_fields": { + "tab": "Extra Fält" + } + }, "documentTitle": { "default": "Spoolman", "suffix": " | Spoolman", diff --git a/client/src/App.tsx b/client/src/App.tsx index bc2c03e..550fbcb 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -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: , }, }, + { + name: "settings", + list: "/settings", + meta: { + canDelete: false, + icon: , + }, + }, { name: "help", list: "/help", @@ -202,6 +217,7 @@ function App() { } /> } /> + } /> } /> } /> diff --git a/client/src/pages/settings/ExtraFieldsSettings.tsx b/client/src/pages/settings/ExtraFieldsSettings.tsx new file mode 100644 index 0000000..95bbec5 --- /dev/null +++ b/client/src/pages/settings/ExtraFieldsSettings.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function ExtraFieldsSettings() { + return <>Extra Fields Settings Content; +} diff --git a/client/src/pages/settings/GenericSettings.tsx b/client/src/pages/settings/GenericSettings.tsx new file mode 100644 index 0000000..f0e1adc --- /dev/null +++ b/client/src/pages/settings/GenericSettings.tsx @@ -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 ( + <> +
+ + + + + + + +
+ {contextHolder} + + ); +} diff --git a/client/src/pages/settings/index.tsx b/client/src/pages/settings/index.tsx new file mode 100644 index 0000000..575f067 --- /dev/null +++ b/client/src/pages/settings/index.tsx @@ -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 = () => { + const { token } = useToken(); + const t = useTranslate(); + const [current, setCurrent] = useSavedState("settings-tab", "generic"); + + return ( + <> +

+ {t("settings.header")} +

+ + setCurrent(key)} + items={[ + { key: "generic", label: t("settings.generic.tab"), icon: }, + { key: "extra", label: t("settings.extra_fields.tab"), icon: }, + ]} + style={{ + marginBottom: "3em", + }} + /> +
+ {current === "generic" && } + {current === "extra" && } +
+ + + ); +}; + +export default Settings; diff --git a/client/src/pages/settings/query.ts b/client/src/pages/settings/query.ts new file mode 100644 index 0000000..63ce94a --- /dev/null +++ b/client/src/pages/settings/query.ts @@ -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({ + 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({ + 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({ + 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]); + }, + }); +}