update settings
parent
e6b9a6d0e8
commit
8544f21b94
11
src/App.tsx
11
src/App.tsx
|
@ -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 ? (
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue