update settings

main
alex 2024-09-04 22:56:58 +02:00
parent e6b9a6d0e8
commit 8544f21b94
4 changed files with 195 additions and 150 deletions

View File

@ -2,6 +2,8 @@ import { ConfigProvider, Layout, theme } from "antd";
import DashboardLayout from "./core/components/DashboardLayout"; import DashboardLayout from "./core/components/DashboardLayout";
import { import {
darkMode, darkMode,
primaryColor,
setPrimaryColor,
setUserAuthenticated, setUserAuthenticated,
userAuthenticated, userAuthenticated,
} from "./core/reducers/appSlice"; } from "./core/reducers/appSlice";
@ -19,6 +21,7 @@ function App() {
const isDarkMode = useSelector(darkMode); const isDarkMode = useSelector(darkMode);
const uAuthenticated = useSelector(userAuthenticated); const uAuthenticated = useSelector(userAuthenticated);
const primColor = useSelector(primaryColor);
console.info( console.info(
"\n %c LMS %c v1.0.0 %c \n", "\n %c LMS %c v1.0.0 %c \n",
@ -36,12 +39,13 @@ function App() {
(async () => { (async () => {
try { try {
const response = await myFetch({ const response = await myFetch({
url: "/user", url: "/app",
method: "GET", method: "GET",
}); });
if (response) { if (response) {
dispatch(setUserAuthenticated(true)) dispatch(setUserAuthenticated(true));
dispatch(setPrimaryColor(`#${response.Organization.PrimaryColor}`));
} }
} catch (error) {} } catch (error) {}
})(); })();
@ -58,6 +62,9 @@ function App() {
<ConfigProvider <ConfigProvider
theme={{ theme={{
algorithm: isDarkMode ? darkAlgorithm : defaultAlgorithm, algorithm: isDarkMode ? darkAlgorithm : defaultAlgorithm,
token: {
colorPrimary: primColor,
},
}} }}
> >
{uAuthenticated == null ? ( {uAuthenticated == null ? (

View File

@ -5,6 +5,7 @@ export const appSlice = createSlice({
initialState: { initialState: {
darkMode: false, darkMode: false,
userAuthenticated: null, userAuthenticated: null,
primaryColor: "#1677FF",
}, },
reducers: { reducers: {
setDarkMode: (state, action) => { setDarkMode: (state, action) => {
@ -12,14 +13,19 @@ export const appSlice = createSlice({
}, },
setUserAuthenticated: (state, action) => { setUserAuthenticated: (state, action) => {
state.userAuthenticated = action.payload; state.userAuthenticated = action.payload;
} },
setPrimaryColor: (state, action) => {
state.primaryColor = action.payload;
},
}, },
selectors: { selectors: {
darkMode: (state) => state.darkMode, darkMode: (state) => state.darkMode,
userAuthenticated: (state) => state.userAuthenticated, userAuthenticated: (state) => state.userAuthenticated,
primaryColor: (state) => state.primaryColor,
}, },
}) });
export const { setDarkMode, setUserAuthenticated } = appSlice.actions; export const { setDarkMode, setUserAuthenticated, setPrimaryColor } =
appSlice.actions;
export const { darkMode, userAuthenticated } = appSlice.selectors; export const { darkMode, userAuthenticated, primaryColor } = appSlice.selectors;

View File

@ -1,24 +1,35 @@
import { createApi } from '@reduxjs/toolkit/query/react'; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQueryWithErrorHandling } from 'core/helper/api'; import { baseQueryWithErrorHandling } from "core/helper/api";
import { TeamMember, OrganizationSettings } from 'core/types/organization'; import { TeamMember, OrganizationSettings } from "core/types/organization";
export const organizationApi = createApi({ export const organizationApi = createApi({
reducerPath: 'organizationApi', reducerPath: "organizationApi",
baseQuery: baseQueryWithErrorHandling, baseQuery: baseQueryWithErrorHandling,
endpoints: (builder) => ({ endpoints: (builder) => ({
getTeam: builder.query<TeamMember[], undefined>({ getTeam: builder.query<TeamMember[], undefined>({
query: () => ({ query: () => ({
url: 'organization/team/members', url: "organization/team/members",
method: 'GET', method: "GET",
}), }),
}), }),
getOrganizationSettings: builder.query<OrganizationSettings, undefined>({ getOrganizationSettings: builder.query<OrganizationSettings, undefined>({
query: () => ({ query: () => ({
url: 'organization/settings', url: "organization/settings",
method: 'GET', method: "GET",
}),
}),
updateOrganizationSettings: builder.mutation({
query: ({ primaryColor, companyName }) => ({
url: "organization/settings",
method: "PATCH",
body: { PrimaryColor: primaryColor, CompanyName: companyName },
}), }),
}), }),
}), }),
}); });
export const { useGetTeamQuery, useGetOrganizationSettingsQuery } = organizationApi; export const {
useGetTeamQuery,
useGetOrganizationSettingsQuery,
useUpdateOrganizationSettingsMutation,
} = organizationApi;

View File

@ -1,4 +1,4 @@
import { Button, Card, Divider, Flex, Form, Input, Typography } from "antd"; import { Button, Card, Flex, Form, Input } from "antd";
import HeaderBar from "../../core/components/Header"; import HeaderBar from "../../core/components/Header";
import MyBanner from "../../shared/components/MyBanner"; import MyBanner from "../../shared/components/MyBanner";
import { MyContainer } from "../../shared/components/MyContainer"; import { MyContainer } from "../../shared/components/MyContainer";
@ -6,14 +6,19 @@ import { SaveOutlined } from "@ant-design/icons";
import MyUpload from "shared/components/MyUpload"; import MyUpload from "shared/components/MyUpload";
import { Constants } from "core/utils/utils"; import { Constants } from "core/utils/utils";
import ColorPicker from "antd/es/color-picker"; import ColorPicker from "antd/es/color-picker";
import { useGetOrganizationSettingsQuery } from "core/services/organization"; import {
useGetOrganizationSettingsQuery,
useUpdateOrganizationSettingsMutation,
} from "core/services/organization";
import MyErrorResult from "shared/components/MyResult"; import MyErrorResult from "shared/components/MyResult";
import { useForm } from "antd/es/form/Form"; import { useForm } from "antd/es/form/Form";
import { useEffect } from "react"; import { useEffect, useRef } from "react";
import { AggregationColor } from "antd/es/color-picker/color"; import { AggregationColor } from "antd/es/color-picker/color";
import { useDispatch } from "react-redux";
import { setPrimaryColor } from "core/reducers/appSlice";
type FieldType = { type FieldType = {
primaryColor: string; primaryColor: string | AggregationColor;
companyName: string; companyName: string;
subdomain: string; subdomain: string;
}; };
@ -27,9 +32,29 @@ export default function Settings() {
); );
const [form] = useForm<FieldType>(); const [form] = useForm<FieldType>();
const dispatch = useDispatch();
const debounceRef = useRef<null | NodeJS.Timeout>(null);
const currentPrimaryColor = useRef<string>();
const [updateOrganizationSettings, { isLoading: isUpdateSettingsLoading }] =
useUpdateOrganizationSettingsMutation();
const handleSave = (values: FieldType) => { const handleSave = (values: FieldType) => {
console.log(values); const hexColor =
typeof values.primaryColor === "string"
? values.primaryColor
: values.primaryColor.toHexString().split("#")[1];
try {
updateOrganizationSettings({
primaryColor: hexColor,
companyName: values.companyName,
});
currentPrimaryColor.current = hexColor;
} catch (error) {
console.error(error);
}
}; };
useEffect(() => { useEffect(() => {
@ -39,9 +64,21 @@ export default function Settings() {
companyName: data.CompanyName, companyName: data.CompanyName,
subdomain: data.Subdomain, subdomain: data.Subdomain,
}); });
currentPrimaryColor.current = data.PrimaryColor;
} }
}, [data]); }, [data]);
useEffect(() => {
return () => {
dispatch(setPrimaryColor(currentPrimaryColor.current));
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
};
}, []);
if (error) return <MyErrorResult />; if (error) return <MyErrorResult />;
return ( return (
@ -49,8 +86,7 @@ export default function Settings() {
<MyBanner title="Settings" subtitle="MANAGE" headerBar={<HeaderBar />} /> <MyBanner title="Settings" subtitle="MANAGE" headerBar={<HeaderBar />} />
<MyContainer> <MyContainer>
<Flex vertical gap={16} style={{ paddingBottom: 16 }}> <Form form={form} onFinish={handleSave} layout="vertical">
<Form form={form} onFinish={handleSave}>
<Card <Card
loading={isLoading} loading={isLoading}
styles={{ styles={{
@ -66,48 +102,37 @@ export default function Settings() {
shape="circle" shape="circle"
size="large" size="large"
htmlType="submit" htmlType="submit"
loading={isUpdateSettingsLoading}
/> />
} }
> >
<Flex vertical gap={2}> <Flex wrap gap={12}>
<Flex gap={32}> <Form.Item<FieldType> name="primaryColor" label="Primary color">
<Flex vertical>
<Typography.Text style={{ fontSize: 16 }}>
Primary Color
</Typography.Text>
<Form.Item name="primaryColor">
<ColorPicker <ColorPicker
defaultValue="#1677ff"
size="small" size="small"
showText showText
format="hex" onChange={(color: AggregationColor) => {
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
debounceRef.current = setTimeout(() => {
dispatch(setPrimaryColor(color.toHexString()));
}, 600);
}}
/> />
</Form.Item> </Form.Item>
</Flex> <Form.Item<FieldType> name="companyName" label="Company name">
<Flex vertical>
<Typography.Text style={{ fontSize: 16 }}>
Company name
</Typography.Text>
<Form.Item name="companyName">
<Input defaultValue="Jannex" /> <Input defaultValue="Jannex" />
</Form.Item> </Form.Item>
</Flex> <Form.Item<FieldType> name="subdomain" label="Subdomain">
<Flex vertical>
<Typography.Text style={{ fontSize: 16 }}>
Subdomain
</Typography.Text>
<Form.Item name="subdomain">
<Input <Input
addonBefore="https://" addonBefore="https://"
addonAfter=". jannex.de" addonAfter=". jannex.de"
defaultValue="mysite" defaultValue="mysite"
/> />
</Form.Item> </Form.Item>
</Flex> <Form.Item label="Logo">
<Flex vertical>
<Typography.Text style={{ fontSize: 16 }}>
Logo
</Typography.Text>
<MyUpload <MyUpload
action={`/changeCompanyLogo`} action={`/changeCompanyLogo`}
onChange={(info) => { onChange={(info) => {
@ -133,12 +158,10 @@ export default function Settings() {
}} }}
/> />
</MyUpload> </MyUpload>
</Form.Item>
</Flex> </Flex>
</Flex>
<Flex vertical> <Form.Item label="Thumbnail">
<Typography.Text style={{ fontSize: 16 }}>
Thumbnail
</Typography.Text>
<MyUpload <MyUpload
action={`/changeCompanyLogo`} action={`/changeCompanyLogo`}
onChange={(info) => { onChange={(info) => {
@ -165,11 +188,9 @@ export default function Settings() {
}} }}
/> />
</MyUpload> </MyUpload>
</Flex> </Form.Item>
</Flex>
</Card> </Card>
</Form> </Form>
</Flex>
</MyContainer> </MyContainer>
</> </>
); );