From a29d82bfee25767ede61a0d37b388999393c2f02 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Fri, 6 Feb 2026 10:50:30 -0800 Subject: [PATCH] team management + profile + content management integrations + minor fixes --- .env | 2 +- src/api/auth.api.ts | 24 +- src/api/courses.api.ts | 193 ++++ src/api/http.ts | 101 +- src/api/team.api.ts | 13 + src/api/users.api.ts | 3 + src/app/AppRoutes.tsx | 26 +- src/components/topbar/Topbar.tsx | 32 +- src/pages/DashboardPage.tsx | 16 +- src/pages/ProfilePage.tsx | 308 ++++++ src/pages/auth/LoginPage.tsx | 6 +- .../content-management/AddNewPracticePage.tsx | 933 ++++++++++++++++++ .../ContentOverviewPage.tsx | 40 +- .../content-management/CourseCategoryPage.tsx | 73 ++ src/pages/content-management/CoursesPage.tsx | 609 +++++++++++- .../PracticeQuestionsPage.tsx | 477 +++++++++ .../SubCourseContentPage.tsx | 838 ++++++++++++++++ .../content-management/SubCoursesPage.tsx | 537 ++++++++++ src/pages/team/TeamManagementPage.tsx | 374 +++++++ src/pages/team/TeamMemberDetailPage.tsx | 333 +++++++ src/pages/user-management/UserDetailPage.tsx | 408 +++++--- src/pages/user-management/UsersListPage.tsx | 353 ++++--- src/types/auth.types.ts | 23 +- src/types/course.types.ts | 435 ++++++++ src/types/team.types.ts | 41 + src/types/user.types.ts | 72 +- 26 files changed, 5898 insertions(+), 372 deletions(-) create mode 100644 src/api/courses.api.ts create mode 100644 src/api/team.api.ts create mode 100644 src/pages/ProfilePage.tsx create mode 100644 src/pages/content-management/AddNewPracticePage.tsx create mode 100644 src/pages/content-management/CourseCategoryPage.tsx create mode 100644 src/pages/content-management/PracticeQuestionsPage.tsx create mode 100644 src/pages/content-management/SubCourseContentPage.tsx create mode 100644 src/pages/content-management/SubCoursesPage.tsx create mode 100644 src/pages/team/TeamManagementPage.tsx create mode 100644 src/pages/team/TeamMemberDetailPage.tsx create mode 100644 src/types/course.types.ts create mode 100644 src/types/team.types.ts diff --git a/.env b/.env index a56e68e..0c0e1f5 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VITE_API_BASE_URL=http://195.35.29.82:8080/api/v1 +VITE_API_BASE_URL=http://localhost:8080/api/v1 diff --git a/src/api/auth.api.ts b/src/api/auth.api.ts index d819eba..ee09b26 100644 --- a/src/api/auth.api.ts +++ b/src/api/auth.api.ts @@ -1,22 +1,22 @@ -import http from "./http"; -import type { LoginRequest, LoginResponse, LoginResponseData } from "../types/auth.types"; +import http from "./http" +import type { LoginRequest, LoginResponse, LoginResponseData } from "../types/auth.types" export interface LoginResult { - accessToken: string; - refreshToken: string; - role: string; - user_id: number; + accessToken: string + refreshToken: string + role: string + memberId: number } export const login = async (payload: LoginRequest): Promise => { - const res = await http.post("/auth/customer-login", payload); + const res = await http.post("/team/login", payload) - const data: LoginResponseData = res.data.data; + const data: LoginResponseData = res.data.data return { accessToken: data.access_token, refreshToken: data.refresh_token, - role: data.role, - user_id: data.user_id, - }; -}; + role: data.team_role, + memberId: data.member_id, + } +} diff --git a/src/api/courses.api.ts b/src/api/courses.api.ts new file mode 100644 index 0000000..7bf5ce7 --- /dev/null +++ b/src/api/courses.api.ts @@ -0,0 +1,193 @@ +import http from "./http" +import type { + GetCourseCategoriesResponse, + GetCoursesResponse, + CreateCourseRequest, + UpdateCourseRequest, + GetSubCoursesResponse, + CreateSubCourseRequest, + UpdateSubCourseRequest, + UpdateSubCourseStatusRequest, + GetSubCourseVideosResponse, + CreateSubCourseVideoRequest, + UpdateSubCourseVideoRequest, + GetPracticesResponse, + CreatePracticeRequest, + UpdatePracticeRequest, + UpdatePracticeStatusRequest, + GetPracticeQuestionsResponse, + CreatePracticeQuestionRequest, + UpdatePracticeQuestionRequest, + GetProgramsResponse, + GetLevelsResponse, + GetModulesResponse, + UpdateProgramStatusRequest, + CreateProgramRequest, + UpdateProgramRequest, + CreateLevelRequest, + UpdateLevelRequest, + UpdateLevelStatusRequest, + CreateModuleRequest, + UpdateModuleRequest, + UpdateModuleStatusRequest, + GetQuestionSetsResponse, + CreateQuestionSetRequest, + CreateQuestionSetResponse, + AddQuestionToSetRequest, + CreateQuestionRequest, + CreateQuestionResponse, + CreateVimeoVideoRequest, +} from "../types/course.types" + +export const getCourseCategories = () => + http.get("/course-management/categories") + +export const getCoursesByCategory = (categoryId: number) => + http.get(`/course-management/categories/${categoryId}/courses`) + +export const createCourse = (data: CreateCourseRequest) => + http.post("/course-management/courses", data) + +export const deleteCourse = (courseId: number) => + http.delete(`/course-management/courses/${courseId}`) + +export const updateCourseStatus = (courseId: number, isActive: boolean) => + http.put(`/course-management/courses/${courseId}`, { is_active: isActive }) + +export const updateCourse = (courseId: number, data: UpdateCourseRequest) => + http.put(`/course-management/courses/${courseId}`, data) + +// SubCourse APIs (New Hierarchy) +export const getSubCoursesByCourse = (courseId: number) => + http.get(`/course-management/courses/${courseId}/sub-courses`) + +export const createSubCourse = (data: CreateSubCourseRequest) => + http.post("/course-management/sub-courses", data) + +export const updateSubCourse = (subCourseId: number, data: UpdateSubCourseRequest) => + http.patch(`/course-management/sub-courses/${subCourseId}`, data) + +export const updateSubCourseStatus = (subCourseId: number, data: UpdateSubCourseStatusRequest) => + http.patch(`/course-management/sub-courses/${subCourseId}`, data) + +export const deleteSubCourse = (subCourseId: number) => + http.delete(`/course-management/sub-courses/${subCourseId}`) + +// SubCourse Video APIs +export const getVideosBySubCourse = (subCourseId: number) => + http.get(`/course-management/sub-courses/${subCourseId}/videos`) + +export const createSubCourseVideo = (data: CreateSubCourseVideoRequest) => + http.post("/course-management/sub-course-videos", data) + +export const updateSubCourseVideo = (videoId: number, data: UpdateSubCourseVideoRequest) => + http.put(`/course-management/sub-course-videos/${videoId}`, data) + +export const deleteSubCourseVideo = (videoId: number) => + http.delete(`/course-management/sub-course-videos/${videoId}`) + +// Practice APIs - for SubCourse practices (New Hierarchy) +export const getPracticesBySubCourse = (subCourseId: number) => + http.get(`/course-management/sub-courses/${subCourseId}/practices`) + +export const createPractice = (data: CreatePracticeRequest) => + http.post("/course-management/practices", data) + +export const updatePractice = (practiceId: number, data: UpdatePracticeRequest) => + http.put(`/course-management/practices/${practiceId}`, data) + +export const updatePracticeStatus = (practiceId: number, data: UpdatePracticeStatusRequest) => + http.put(`/course-management/practices/${practiceId}`, data) + +export const deletePractice = (practiceId: number) => + http.delete(`/course-management/practices/${practiceId}`) + +// Practice Questions APIs +export const getPracticeQuestions = (practiceId: number) => + http.get(`/course-management/practices/${practiceId}/questions`) + +export const createPracticeQuestion = (data: CreatePracticeQuestionRequest) => + http.post("/course-management/practice-questions", data) + +export const updatePracticeQuestion = (questionId: number, data: UpdatePracticeQuestionRequest) => + http.put(`/course-management/practice-questions/${questionId}`, data) + +export const deletePracticeQuestion = (questionId: number) => + http.delete(`/course-management/practice-questions/${questionId}`) + +// ============================================ +// Legacy APIs (deprecated - using SubCourse hierarchy now) +// Keeping for backward compatibility +// ============================================ + +export const getProgramsByCourse = (courseId: number) => + http.get(`/course-management/courses/${courseId}/programs`) + +export const updateProgramStatus = (programId: number, data: UpdateProgramStatusRequest) => + http.patch(`/course-management/programs/${programId}`, data) + +export const deleteProgram = (programId: number) => + http.delete(`/course-management/programs/${programId}`) + +export const createProgram = (data: CreateProgramRequest) => + http.post("/course-management/programs", data) + +export const updateProgram = (programId: number, data: UpdateProgramRequest) => + http.patch(`/course-management/programs/${programId}`, data) + +export const getLevelsByProgram = (programId: number) => + http.get(`/course-management/programs/${programId}/levels`) + +export const createLevel = (data: CreateLevelRequest) => + http.post("/course-management/levels", data) + +export const updateLevel = (levelId: number, data: UpdateLevelRequest) => + http.put(`/course-management/levels/${levelId}`, data) + +export const updateLevelStatus = (levelId: number, data: UpdateLevelStatusRequest) => + http.put(`/course-management/levels/${levelId}`, data) + +export const deleteLevel = (levelId: number) => + http.delete(`/course-management/levels/${levelId}`) + +export const getModulesByLevel = (levelId: number) => + http.get(`/course-management/levels/${levelId}/modules`) + +export const createModule = (data: CreateModuleRequest) => + http.post("/course-management/modules", data) + +export const updateModule = (moduleId: number, data: UpdateModuleRequest) => + http.put(`/course-management/modules/${moduleId}`, data) + +export const updateModuleStatus = (moduleId: number, data: UpdateModuleStatusRequest) => + http.put(`/course-management/modules/${moduleId}`, data) + +export const deleteModule = (moduleId: number) => + http.delete(`/course-management/modules/${moduleId}`) + +export const getPracticesByLevel = (levelId: number) => + http.get(`/course-management/levels/${levelId}/practices`) + +export const getPracticesByModule = (moduleId: number) => + http.get(`/course-management/modules/${moduleId}/practices`) + +// Question Sets API +export const getQuestionSetsByOwner = (ownerType: string, ownerId: number) => + http.get("/question-sets/by-owner", { + params: { owner_type: ownerType, owner_id: ownerId }, + }) + +export const createQuestionSet = (data: CreateQuestionSetRequest) => + http.post("/question-sets", data) + +export const addQuestionToSet = (questionSetId: number, data: AddQuestionToSetRequest) => + http.post(`/question-sets/${questionSetId}/questions`, data) + +export const createQuestion = (data: CreateQuestionRequest) => + http.post("/questions", data) + +export const deleteQuestionSet = (questionSetId: number) => + http.delete(`/question-sets/${questionSetId}`) + +export const createVimeoVideo = (data: CreateVimeoVideoRequest) => + http.post("/course-management/videos/vimeo", data) diff --git a/src/api/http.ts b/src/api/http.ts index 7760d25..3fccbc0 100644 --- a/src/api/http.ts +++ b/src/api/http.ts @@ -1,4 +1,4 @@ -import axios, { type AxiosInstance } from "axios"; +import axios, { type AxiosInstance, type AxiosError, type InternalAxiosRequestConfig } from "axios"; const http: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, @@ -7,6 +7,64 @@ const http: AxiosInstance = axios.create({ }, }); +let isRefreshing = false; +let failedQueue: Array<{ + resolve: (token: string) => void; + reject: (error: Error) => void; +}> = []; + +const processQueue = (error: Error | null, token: string | null = null) => { + failedQueue.forEach((prom) => { + if (error) { + prom.reject(error); + } else if (token) { + prom.resolve(token); + } + }); + failedQueue = []; +}; + +const clearAuthAndRedirect = () => { + localStorage.removeItem("access_token"); + localStorage.removeItem("refresh_token"); + localStorage.removeItem("member_id"); + localStorage.removeItem("role"); + window.location.href = "/login"; +}; + +const refreshAccessToken = async (): Promise => { + const accessToken = localStorage.getItem("access_token"); + const refreshToken = localStorage.getItem("refresh_token"); + const role = localStorage.getItem("role"); + const memberId = localStorage.getItem("member_id"); + + if (!refreshToken || !memberId) { + throw new Error("No refresh token available"); + } + + const response = await axios.post( + `${import.meta.env.VITE_API_BASE_URL}/auth/refresh`, + { + access_token: accessToken, + refresh_token: refreshToken, + role: role || "admin", + member_id: Number(memberId), + } + ); + + const newAccessToken = response.data?.data?.access_token; + const newRefreshToken = response.data?.data?.refresh_token; + + if (newAccessToken) { + localStorage.setItem("access_token", newAccessToken); + } + if (newRefreshToken) { + localStorage.setItem("refresh_token", newRefreshToken); + } + + return newAccessToken; +}; + // Attach access token to every request http.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); @@ -16,20 +74,41 @@ http.interceptors.request.use((config) => { return config; }); -// Handle 401 globally +// Handle 401 globally with token refresh http.interceptors.response.use( (response) => response, - (error) => { - if (error.response?.status === 401) { - // Clear stored tokens and user info - localStorage.removeItem("access_token"); - localStorage.removeItem("refresh_token"); - localStorage.removeItem("user_id"); - localStorage.removeItem("role"); + async (error: AxiosError) => { + const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }; - // Redirect to login page - window.location.href = "/login"; + if (error.response?.status === 401 && !originalRequest._retry) { + if (isRefreshing) { + return new Promise((resolve, reject) => { + failedQueue.push({ resolve, reject }); + }) + .then((token) => { + originalRequest.headers.Authorization = `Bearer ${token}`; + return http(originalRequest); + }) + .catch((err) => Promise.reject(err)); + } + + originalRequest._retry = true; + isRefreshing = true; + + try { + const newToken = await refreshAccessToken(); + processQueue(null, newToken); + originalRequest.headers.Authorization = `Bearer ${newToken}`; + return http(originalRequest); + } catch (refreshError) { + processQueue(refreshError as Error, null); + clearAuthAndRedirect(); + return Promise.reject(refreshError); + } finally { + isRefreshing = false; + } } + return Promise.reject(error); } ); diff --git a/src/api/team.api.ts b/src/api/team.api.ts new file mode 100644 index 0000000..c9b3270 --- /dev/null +++ b/src/api/team.api.ts @@ -0,0 +1,13 @@ +import http from "./http" +import type { GetTeamMembersResponse, GetTeamMemberResponse } from "../types/team.types" + +export const getTeamMembers = (page?: number, pageSize?: number) => + http.get("/team/members", { + params: { + page, + page_size: pageSize, + }, + }) + +export const getTeamMemberById = (id: number) => + http.get(`/team/members/${id}`) diff --git a/src/api/users.api.ts b/src/api/users.api.ts index 8dce250..89e3ca4 100644 --- a/src/api/users.api.ts +++ b/src/api/users.api.ts @@ -11,3 +11,6 @@ export const getUsers = (page?: number, pageSize?: number) => export const getUserById = (id: number) => http.get(`/user/single/${id}`); + +export const getMyProfile = () => + http.get("/team/me"); diff --git a/src/app/AppRoutes.tsx b/src/app/AppRoutes.tsx index 179f73c..9d8f677 100644 --- a/src/app/AppRoutes.tsx +++ b/src/app/AppRoutes.tsx @@ -3,14 +3,18 @@ import { AppLayout } from "../layouts/AppLayout" import { DashboardPage } from "../pages/DashboardPage" import { AnalyticsPage } from "../pages/analytics/AnalyticsPage" import { ContentManagementLayout } from "../pages/content-management/ContentManagementLayout" +import { CourseCategoryPage } from "../pages/content-management/CourseCategoryPage" import { ContentOverviewPage } from "../pages/content-management/ContentOverviewPage" import { CoursesPage } from "../pages/content-management/CoursesPage" +import { PracticeQuestionsPage } from "../pages/content-management/PracticeQuestionsPage" +import { AddNewPracticePage } from "../pages/content-management/AddNewPracticePage" +import { SubCoursesPage } from "../pages/content-management/SubCoursesPage" +import { SubCourseContentPage } from "../pages/content-management/SubCourseContentPage" import { SpeakingPage } from "../pages/content-management/SpeakingPage" import { AddVideoPage } from "../pages/content-management/AddVideoPage" import { AddPracticePage } from "../pages/content-management/AddPracticePage" import { NotFoundPage } from "../pages/NotFoundPage" import { NotificationsPage } from "../pages/notifications/NotificationsPage" -import { PlaceholderPage } from "../pages/PlaceholderPage" import { UserDetailPage } from "../pages/user-management/UserDetailPage" import { UserManagementLayout } from "../pages/user-management/UserManagementLayout" import { UsersListPage } from "../pages/user-management/UsersListPage" @@ -25,6 +29,9 @@ import { PracticeMembersPage } from "../pages/content-management/PracticeMembers import { QuestionsPage } from "../pages/content-management/QuestionsPage" import { AddQuestionPage } from "../pages/content-management/AddQuestionPage" import { UserLogPage } from "../pages/user-log/UserLogPage" +import { ProfilePage } from "../pages/ProfilePage" +import { TeamManagementPage } from "../pages/team/TeamManagementPage" +import { TeamMemberDetailPage } from "../pages/team/TeamMemberDetailPage" import { LoginPage } from "../pages/auth/LoginPage" import { ForgotPasswordPage } from "../pages/auth/ForgotPasswordPage" import { VerificationPage } from "../pages/auth/VerificationPage" @@ -52,9 +59,15 @@ export function AppRoutes() { }> - } /> - } /> - } /> + } /> + } /> + } /> + {/* Course → Sub-course → Video/Practice */} + } /> + } /> + } /> + } /> + } /> } /> } /> } /> @@ -68,8 +81,9 @@ export function AppRoutes() { } /> } /> - } /> - } /> + } /> + } /> + } /> } /> diff --git a/src/components/topbar/Topbar.tsx b/src/components/topbar/Topbar.tsx index 6b17688..35b5ea6 100644 --- a/src/components/topbar/Topbar.tsx +++ b/src/components/topbar/Topbar.tsx @@ -1,7 +1,7 @@ "use client" // make sure this is a client component import { useEffect, useState } from "react" -import { Bell } from "lucide-react" +import { Bell, LogOut, Settings, UserCircle2 } from "lucide-react" import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar" import * as DropdownMenu from "@radix-ui/react-dropdown-menu" import { cn } from "../../lib/utils" @@ -11,9 +11,16 @@ export function Topbar() { const [shortName, setShortName] = useState("AA") useEffect(() => { - const first = localStorage.getItem("user_first_name") ?? "A" - const last = localStorage.getItem("user_last_name") ?? "A" - setShortName(first.charAt(0).toUpperCase() + last.charAt(0).toUpperCase()) + const updateShortName = () => { + const first = localStorage.getItem("user_first_name") ?? "A" + const last = localStorage.getItem("user_last_name") ?? "A" + setShortName(first.charAt(0).toUpperCase() + last.charAt(0).toUpperCase()) + } + + updateShortName() + + window.addEventListener("user-profile-updated", updateShortName) + return () => window.removeEventListener("user-profile-updated", updateShortName) }, []) const handleOptionClick = (option: string) => { @@ -66,30 +73,39 @@ export function Topbar() { handleOptionClick("profile")} > + + + Profile handleOptionClick("settings")} > + + + Settings handleOptionClick("logout")} > + + + Logout diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index 65df0d9..7a7ffe2 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -25,8 +25,7 @@ import { StatCard } from "../components/dashboard/StatCard" import { Button } from "../components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card" import { cn } from "../lib/utils" -import { getUserById } from "../api/users.api" -import type { UserProfileResponse } from "../types/user.types" +import { getTeamMemberById } from "../api/team.api" import { useEffect, useState } from "react" const userGrowth = [ @@ -69,13 +68,14 @@ export function DashboardPage() { useEffect(() => { const fetchUser = async () => { try { - const userId = Number(localStorage.getItem("user_id")) - const res = await getUserById(userId) - const userProfile: UserProfileResponse = res.data + const memberId = Number(localStorage.getItem("member_id")) + const res = await getTeamMemberById(memberId) + const member = res.data.data - setUserFirstName(userProfile.data.first_name) - localStorage.setItem("user_first_name", userProfile.data.first_name) - localStorage.setItem("user_last_name", userProfile.data.last_name) + setUserFirstName(member.first_name) + localStorage.setItem("user_first_name", member.first_name) + localStorage.setItem("user_last_name", member.last_name) + window.dispatchEvent(new Event("user-profile-updated")) } catch (err) { console.error(err) } diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx new file mode 100644 index 0000000..be3f399 --- /dev/null +++ b/src/pages/ProfilePage.tsx @@ -0,0 +1,308 @@ +import { useEffect, useState } from "react"; +import { + Calendar, + CheckCircle2, + Clock, + Globe, + GraduationCap, + Languages, + Mail, + MapPin, + Phone, + Shield, + User, + XCircle, + Briefcase, +} from "lucide-react"; +import { Badge } from "../components/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card"; +import { Separator } from "../components/ui/separator"; +import { Avatar, AvatarFallback, AvatarImage } from "../components/ui/avatar"; +import { cn } from "../lib/utils"; +import { getMyProfile } from "../api/users.api"; +import type { UserProfileData } from "../types/user.types"; + +function formatDate(dateStr: string | null | undefined): string { + if (!dateStr) return "—"; + return new Date(dateStr).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); +} + +function formatDateTime(dateStr: string | null | undefined): string { + if (!dateStr) return "—"; + return new Date(dateStr).toLocaleString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); +} + +function LoadingSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+
+ ); +} + +function InfoRow({ + icon: Icon, + label, + value, + extra, +}: { + icon: typeof User; + label: string; + value: string; + extra?: React.ReactNode; +}) { + return ( +
+
+ + {label} +
+
+ {value || "—"} + {extra} +
+
+ ); +} + +function VerifiedIcon({ verified }: { verified: boolean }) { + return verified ? ( + + ) : ( + + ); +} + +export function ProfilePage() { + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchProfile = async () => { + try { + const res = await getMyProfile(); + setProfile(res.data.data); + } catch (err) { + console.error("Failed to fetch profile", err); + setError("Failed to load profile. Please try again later."); + } finally { + setLoading(false); + } + }; + fetchProfile(); + }, []); + + if (loading) return ; + + if (error || !profile) { + return ( +
+ + +
+ +
+
+ {error || "Profile not available"} +
+
+
+
+ ); + } + + const fullName = `${profile.first_name} ${profile.last_name}`; + const initials = `${profile.first_name?.[0] ?? ""}${profile.last_name?.[0] ?? ""}`.toUpperCase(); + const completionPct = profile.profile_completion_percentage ?? 0; + + return ( +
+ +
+ +
+ + + + {initials} + + + +

{fullName}

+ + + + {profile.role} + + +
+
+ + {profile.status} +
+ +
+ {profile.email_verified ? ( + + ) : ( + + )} + Email {profile.email_verified ? "Verified" : "Unverified"} +
+ +
+ {profile.phone_verified ? ( + + ) : ( + + )} + Phone {profile.phone_verified ? "Verified" : "Unverified"} +
+ +
+ + Profile {completionPct}% Complete +
+
+
+
+ + +
+ + + Personal Information + + + + + + + + + + + + + + + + + Contact & Location + + + } + /> + + } + /> + + + + + + + + + + Account Details + + + + + + + + + + + + } + /> + + +
+
+ ); +} diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx index 547eaee..9100023 100644 --- a/src/pages/auth/LoginPage.tsx +++ b/src/pages/auth/LoginPage.tsx @@ -36,7 +36,7 @@ export function LoginPage() { localStorage.setItem("access_token", res.accessToken); localStorage.setItem("refresh_token", res.refreshToken); localStorage.setItem("role", res.role); - localStorage.setItem("user_id", res.user_id.toString()); + localStorage.setItem("member_id", res.memberId.toString()); navigate("/dashboard"); } catch (err: any) { @@ -69,7 +69,7 @@ export function LoginPage() {
)} -
+ {/* Email */}