diff --git a/src/core/components/AppRoutes/index.tsx b/src/core/components/AppRoutes/index.tsx index ec3c6a4..a2f22c6 100644 --- a/src/core/components/AppRoutes/index.tsx +++ b/src/core/components/AppRoutes/index.tsx @@ -12,8 +12,8 @@ import PageNotFound from "../../../features/PageNotFound"; import LessonPage from "../../../features/Lessons/LessonPage"; import LessonPageEditor from "../../../features/Lessons/LessonPageEditor"; import TeamCreateUser from "features/Team/CreateUser"; -import AccountSettings from "features/AccountSettings"; import Board from "features/Board"; +import UserProfile from "features/UserProfile"; export default function AppRoutes() { return ( @@ -94,7 +94,7 @@ export default function AppRoutes() { path={Constants.ROUTE_PATHS.ACCOUNT_SETTINGS} element={ - + } /> diff --git a/src/core/services/userProfile.ts b/src/core/services/userProfile.ts new file mode 100644 index 0000000..79aa09f --- /dev/null +++ b/src/core/services/userProfile.ts @@ -0,0 +1,18 @@ +import { createApi } from "@reduxjs/toolkit/query/react"; +import { baseQueryWithErrorHandling } from "core/helper/api"; +import { UserProfile } from "core/types/userProfile"; + +export const userProfileApi = createApi({ + reducerPath: "userProfileApi", + baseQuery: baseQueryWithErrorHandling, + endpoints: (builder) => ({ + getUserProfile: builder.query({ + query: () => ({ + url: "user/profile", + method: "GET", + }), + }), + }), +}); + +export const { useGetUserProfileQuery } = userProfileApi; diff --git a/src/core/services/websocketService.ts b/src/core/services/websocketService.ts index 1719850..cad25e9 100644 --- a/src/core/services/websocketService.ts +++ b/src/core/services/websocketService.ts @@ -33,6 +33,7 @@ import { deleteTeamMember, updateTeamMemberRole, } from "features/Team/teamSlice"; +import { setProfilePictureUrl } from "features/UserProfile/userProfileSlice"; interface WebSocketMessage { Cmd: number; @@ -276,6 +277,9 @@ export function WebSocketMessageHandler( ); } break; + case WebSocketReceivedMessagesCmds.UserProfilePictureUpdated: + dispatch(setProfilePictureUrl(Body)); + break; default: console.error("Unknown message type:", Cmd); } diff --git a/src/core/store/store.tsx b/src/core/store/store.tsx index 89f601b..6c5a7b5 100644 --- a/src/core/store/store.tsx +++ b/src/core/store/store.tsx @@ -8,6 +8,8 @@ import { organizationApi } from "core/services/organization"; import { teamSlice } from "features/Team/teamSlice"; import { lessonsSlice } from "features/Lessons/lessonsSlice"; import { lessonPageSlice } from "features/Lessons/LessonPage/lessonPageSlice"; +import { userProfileApi } from "core/services/userProfile"; +import { userProfileSlice } from "features/UserProfile/userProfileSlice"; const makeStore = (/* preloadedState */) => { const store = configureStore({ @@ -21,12 +23,15 @@ const makeStore = (/* preloadedState */) => { [lessonPageSlice.reducerPath]: lessonPageSlice.reducer, [organizationApi.reducerPath]: organizationApi.reducer, [teamSlice.reducerPath]: teamSlice.reducer, + [userProfileApi.reducerPath]: userProfileApi.reducer, + [userProfileSlice.reducerPath]: userProfileSlice.reducer, }, // preloadedState, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat( lessonsApi.middleware, - organizationApi.middleware + organizationApi.middleware, + userProfileApi.middleware ), }); diff --git a/src/core/types/userProfile.ts b/src/core/types/userProfile.ts new file mode 100644 index 0000000..1bde012 --- /dev/null +++ b/src/core/types/userProfile.ts @@ -0,0 +1,7 @@ +export interface UserProfile { + ProfilePictureUrl: string; + FirstName: string; + LastName: string; + Email: string; + RoleId: string; +} diff --git a/src/core/utils/utils.ts b/src/core/utils/utils.ts index bce0439..5cb1f4c 100644 --- a/src/core/utils/utils.ts +++ b/src/core/utils/utils.ts @@ -18,7 +18,7 @@ export const Constants = { ORGANIZATION_TEAM_CREATE_USER: "/team/create-user", ORGANIZATION_ROLES: "/roles", ORGANIZATION_SETTINGS: "/settings", - ACCOUNT_SETTINGS: "/account", + ACCOUNT_SETTINGS: "/user-profile", WHATS_NEW: "/whats-new", SUGGEST_FEATURE: "/suggest-feature", CONTACT_SUPPORT: "/contact-support", diff --git a/src/core/utils/webSocket.ts b/src/core/utils/webSocket.ts index 9a33933..8839833 100644 --- a/src/core/utils/webSocket.ts +++ b/src/core/utils/webSocket.ts @@ -19,6 +19,7 @@ enum WebSocketReceivedMessagesCmds { LessonContentUpdated = 14, LessonContentUpdatedPosition = 15, LessonContentFileUpdated = 16, + UserProfilePictureUpdated = 17, } export { WebSocketSendMessagesCmds, WebSocketReceivedMessagesCmds }; diff --git a/src/features/AccountSettings/index.tsx b/src/features/AccountSettings/index.tsx deleted file mode 100644 index 7be651f..0000000 --- a/src/features/AccountSettings/index.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import { - Avatar, - Button, - Card, - Descriptions, - Divider, - Flex, - Form, - Input, - Typography, -} from "antd"; -import HeaderBar from "../../core/components/Header"; -import MyBanner from "../../shared/components/MyBanner"; -import { MyContainer } from "../../shared/components/MyContainer"; -import { SaveOutlined } from "@ant-design/icons"; -import MyUpload from "shared/components/MyUpload"; -import { Constants } from "core/utils/utils"; -import ColorPicker from "antd/es/color-picker"; -import MyMiddleCard from "shared/components/MyMiddleCard"; -import Meta from "antd/es/card/Meta"; - -export default function AccountSettings({ isAdmin }: { isAdmin?: boolean }) { - function AdminWrapper({ children }: { children: React.ReactNode }) { - if (!isAdmin) { - return <>{children}; - } - - return ( -
- {children} -
- ); - } - - function TextItem({ value, name }: { value: string; name: string }) { - if (!isAdmin) { - return <>{value}; - } - - return ( - - - - ); - } - - return ( - <> - } - /> - - - - - - } - title="Jorg Kreith" - description="Lead" - /> - - {/* - - - - - First name - Jorg - - - Email - julian@xx.com - - - - - Last name - Kreth - - - - */} - - - , - }, - { - key: "2", - label: "Last name", - children: , - }, - { - key: "3", - label: "Email", - children: , - }, - ]} - /> - - - - - - ); -} - -/* -// TODO: sessions table - - - - // Todoo - -*/ - -function TitleText({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} - -function ValueText({ children }: { children: React.ReactNode }) { - return {children}; -} diff --git a/src/features/PageNotFound/index.tsx b/src/features/PageNotFound/index.tsx index b749b15..21a3b6e 100644 --- a/src/features/PageNotFound/index.tsx +++ b/src/features/PageNotFound/index.tsx @@ -1,7 +1,21 @@ +import { Button, Result } from "antd"; +import { Constants } from "core/utils/utils"; +import { Link } from "react-router-dom"; +import { MyCenteredContainer } from "shared/components/MyContainer"; + export default function PageNotFound() { return ( - <> -

PageNotFound

- + + + + + } + /> + ); -} \ No newline at end of file +} diff --git a/src/features/Settings/index.tsx b/src/features/Settings/index.tsx index 1437c8c..351abe5 100644 --- a/src/features/Settings/index.tsx +++ b/src/features/Settings/index.tsx @@ -199,7 +199,7 @@ function MediaCard({ }} > Company Logo Banner{children}; + } + + return ( +
+ {children} +
+ ); + } + + function TextItem({ value, name }: { value: string; name: string }) { + if (!isAdmin) { + return <>{value}; + } + + return ( + + + + ); + } + + useEffect(() => { + if (!data) return; + + dispatch(setProfilePictureUrl(data.ProfilePictureUrl)); + dispatch(setFirstName(data.FirstName)); + dispatch(setLastName(data.LastName)); + dispatch(setEmail(data.Email)); + dispatch(setRoleId(data.RoleId)); + }, [data]); + + useEffect(() => { + addWebSocketReconnectListener(refetch); + + return () => removeWebSocketReconnectListener(refetch); + }, []); + + return ( + <> + } + /> + + + {error ? ( + + ) : ( + + + { + if (info.file.status === "done") { + success("Profile picture updated successfully"); + + dispatch(setProfilePictureUrl(info.file.response.Data)); + } + }} + imgCropProps={{ + aspect: 1 / 1, + children: <>, + }} + > + {dataProfilePictureUrl === "" ? ( + + {dataFirstName.charAt(0).toUpperCase()} + + ) : ( + + )} + + } + title={`${dataFirstName} ${dataLastName}`} + description={tmpRoleNames[dataRoleId]} + /> + + + + + + ), + }, + { + key: "2", + label: "Last name", + children: ( + + ), + }, + { + key: "3", + label: "Email", + children: , + }, + ]} + /> + + + + )} + + + ); +} + +/* +// TODO: sessions table + + + + // Todoo + +*/ + +function TitleText({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +function ValueText({ children }: { children: React.ReactNode }) { + return {children}; +} diff --git a/src/features/UserProfile/userProfileSlice.ts b/src/features/UserProfile/userProfileSlice.ts new file mode 100644 index 0000000..1a991b9 --- /dev/null +++ b/src/features/UserProfile/userProfileSlice.ts @@ -0,0 +1,47 @@ +import { createSlice } from "@reduxjs/toolkit"; + +export const userProfileSlice = createSlice({ + name: "userProfile", + initialState: { + profilePictureUrl: "", + firstName: "", + lastName: "", + email: "", + roleId: "", + }, + reducers: { + setProfilePictureUrl: (state, action) => { + state.profilePictureUrl = action.payload; + }, + setFirstName: (state, action) => { + state.firstName = action.payload; + }, + setLastName: (state, action) => { + state.lastName = action.payload; + }, + setEmail: (state, action) => { + state.email = action.payload; + }, + setRoleId: (state, action) => { + state.roleId = action.payload; + }, + }, + selectors: { + profilePictureUrl: (state) => state.profilePictureUrl, + firstName: (state) => state.firstName, + lastName: (state) => state.lastName, + email: (state) => state.email, + roleId: (state) => state.roleId, + }, +}); + +export const { + setEmail, + setFirstName, + setLastName, + setProfilePictureUrl, + setRoleId, +} = userProfileSlice.actions; + +export const { profilePictureUrl, firstName, lastName, email, roleId } = + userProfileSlice.selectors; diff --git a/src/shared/components/MyUpload/index.tsx b/src/shared/components/MyUpload/index.tsx index e99fa34..03a60bd 100644 --- a/src/shared/components/MyUpload/index.tsx +++ b/src/shared/components/MyUpload/index.tsx @@ -42,29 +42,39 @@ export default function MyUpload({ ? accept.join(",") : (accept as string); - const MyUpload = () => ( - { - if (onChange) onChange(info); - }} - beforeUpload={beforeUpload} - > - {children} - - ); - if (fileType === "video") { - return ; + return ( + { + if (onChange) onChange(info); + }} + beforeUpload={beforeUpload} + > + {children} + + ); } return ( - + { + if (onChange) onChange(info); + }} + beforeUpload={beforeUpload} + > + {children} + ); }