diff --git a/.env b/.env index 9eb5bee..a4657ce 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -VITE_API_BASE_URL= https://api.yimaru.yaltopia.com/ +VITE_API_BASE_URL= https://api.yimaru.yaltopia.com/api/v1 VITE_GOOGLE_CLIENT_ID=google_client_id diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index aae2257..dc0b049 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -27,7 +27,7 @@ import { } from "recharts" import { StatCard } from "../components/dashboard/StatCard" import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card" -// import { cn } from "../lib/utils" +import { cn } from "../lib/utils" import { getTeamMemberById } from "../api/team.api" import { getDashboard } from "../api/analytics.api" import { useEffect, useState } from "react" @@ -44,6 +44,7 @@ export function DashboardPage() { const [userFirstName, setUserFirstName] = useState("") const [dashboard, setDashboard] = useState(null) const [loading, setLoading] = useState(true) + const [activeStatTab, setActiveStatTab] = useState<"primary" | "secondary">("primary") useEffect(() => { const fetchUser = async () => { @@ -115,69 +116,109 @@ export function DashboardPage() {
Failed to load dashboard data.
) : ( <> - {/* Stat Cards */} -
- 0} - /> - 0} - /> - 0} - /> - 0.5} - /> + {/* Stat tabs */} +
+
+ + +
+ {/* Stat Cards */} + {activeStatTab === "primary" && ( +
+ 0} + /> + 0} + /> + 0} + /> + 0.5} + /> +
+ )} + {/* Secondary Stats */} -
- - - - -
+ {activeStatTab === "secondary" && ( +
+ + + + +
+ )} {/* User Registrations Chart */}
diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index c632bce..b86c816 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -44,7 +44,7 @@ function formatDateTime(dateStr: string | null | undefined): string { function LoadingSkeleton() { return ( -
+
{/* Hero skeleton */}
@@ -93,15 +93,15 @@ function InfoRow({ extra?: React.ReactNode; }) { return ( -
+
{label}
-
- {value || "—"} +
+ {value || "—"} {extra}
@@ -121,13 +121,13 @@ function VerifiedIcon({ verified }: { verified: boolean }) { } function ProgressRing({ percent }: { percent: number }) { - const radius = 18; + const radius = 14; const circumference = 2 * Math.PI * radius; const offset = circumference - (percent / 100) * circumference; return (
- + - {percent}% + {percent}%
); } @@ -179,7 +179,7 @@ export function ProfilePage() { if (error || !profile) { return ( -
+
@@ -203,227 +203,310 @@ export function ProfilePage() { const initials = `${profile.first_name?.[0] ?? ""}${profile.last_name?.[0] ?? ""}`.toUpperCase(); const completionPct = profile.profile_completion_percentage ?? 0; - const sectionCardIcons: Record = { - personal: { icon: User, color: "from-brand-500 to-brand-600" }, - contact: { icon: Mail, color: "from-brand-400 to-brand-500" }, - account: { icon: Shield, color: "from-brand-600 to-brand-500" }, - }; - return ( -
- {/* Hero Card */} - - {/* Banner gradient */} -
- {/* Decorative pattern overlay */} -
-
+
+ {/* Page header (no tabs) */} +
+

My Info

+

Profile

+
+ + {/* Main profile layout card */} +
+ {/* Header strip */} +
+
+
+

Overview

+

+ Personal, job and account details for this team member. +

+
- {/* Bottom fade */} -
- -
- {/* Avatar */} - - - - {initials} - - - - {/* Name */} -

- {fullName} -

- - {/* Role badge */} - - - {profile.role} - - - {/* Status pills */} -
- {/* Active status */} -
- - {profile.status} +
+
+ {/* Left column: About & details */} +
+ {/* Identity */} +
+ + + + {initials} + + +
+
+

{fullName}

+ + #{profile.id} + +
+
+ + + {profile.role} + + + + Joined {formatDate(profile.created_at)} + +
+
+ + + {profile.status} + +
+ + Profile complete +
+
+
- {/* Email verification */} -
- {profile.email_verified ? ( - - ) : ( - - )} - Email {profile.email_verified ? "Verified" : "Unverified"} + {/* About / Contact */} +
+

+ About +

+
+ } /> + } /> + +
- {/* Phone verification */} -
- {profile.phone_verified ? ( - - ) : ( - - )} - Phone {profile.phone_verified ? "Verified" : "Unverified"} + {/* Employee details */} +
+

+ Employee details +

+
+
+
Date of birth
+
+ {formatDate(profile.birth_day)} +
+
+
+
Age
+
+ {profile.age ? `${profile.age} years` : "—"} +
+
+
+
Gender
+
+ {profile.gender || "Not specified"} +
+
+
+
Age group
+
+ {profile.age_group || "—"} +
+
+
+
Occupation
+
+ {profile.occupation || "—"} +
+
+
+
Preferred language
+
+ {profile.preferred_language || "—"} +
+
+
+
+
+ + {/* Middle column: Job information */} +
+
+

+ Job information +

+
+ + + + + + + + + + + + + + + + + + + + + +
TitleTeamDivisionManagerHire dateLocation
{profile.occupation || profile.role}{profile.role}{profile.preferred_language || "—"}{formatDate(profile.created_at)} + {[profile.region, profile.country].filter(Boolean).join(", ") || "—"} +
+
- {/* Profile completion ring */} -
- - Profile Complete + {/* Learning & goals */} +
+ + + + Learning goal + + + +

+ {profile.learning_goal || "No learning goal specified."} +

+
+
+ + + + + Language goal + + + +

+ {profile.language_goal || "No language goal specified."} +

+
+
+
+
+ + {/* Right column: Activity & account summary */} +
+ {/* Activity */} +
+

+ Activity +

+ + +
+
+
+ +
+
+

+ Last login +

+

+ {formatDateTime(profile.last_login)} +

+
+
+
+
+
+ +
+
+

+ Account created +

+

+ {formatDateTime(profile.created_at)} +

+
+
+
+
+
+ + {/* Account summary */} +
+

+ Account +

+ + +
+ Role + {profile.role} +
+
+ Status + + + {profile.status} + +
+
+ Email + + + {profile.email} + + + +
+
+ Phone + + + {profile.phone_number || "—"} + + + +
+
+
- - - - {/* Info Cards */} -
- {/* Personal Information */} - -
- -
-
- -
- - Personal Information - -
-
- - - - - - - - - - {/* Contact & Location */} - -
- -
-
- -
- - Contact & Location - -
-
- - } - /> - } - /> - - - - - - {/* Account Details */} - -
- -
-
- -
- - Account Details - -
-
- - - - - - - } - /> - - +
); diff --git a/src/pages/analytics/AnalyticsPage.tsx b/src/pages/analytics/AnalyticsPage.tsx index 882fd0c..d4d37d2 100644 --- a/src/pages/analytics/AnalyticsPage.tsx +++ b/src/pages/analytics/AnalyticsPage.tsx @@ -282,6 +282,7 @@ export function AnalyticsPage() { const [dashboard, setDashboard] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) + const [activeSummaryTab, setActiveSummaryTab] = useState<"key" | "content" | "operations">("key") const fetchData = async () => { setLoading(true) @@ -384,107 +385,166 @@ export function AnalyticsPage() {
+ {/* Summary Tabs */} +
+
+ + + +
+
+
- {/* ─── Key Metrics ─── */} -
-
- 0 ? "up" : "neutral"} - /> - 0 ? "up" : "neutral"} - /> - 0 ? "up" : "neutral"} - /> - = 0.5 ? "up" : "down"} - /> -
-
+ {activeSummaryTab === "key" && ( + <> + {/* ─── Key Metrics ─── */} +
+
+ 0 ? "up" : "neutral"} + /> + 0 ? "up" : "neutral"} + /> + 0 ? "up" : "neutral"} + /> + = 0.5 ? "up" : "down"} + /> +
+
+ + )} - {/* ─── Content & Platform ─── */} -
-
- - + {/* ─── Content & Platform ─── */} +
- - -
-
+ count={courses.total_courses + content.total_questions} + defaultOpen + > +
+ + + + +
+ + + )} - {/* ─── Operations ─── */} -
-
- - - 0 ? "up" : "neutral"} - /> - `${q.count} ${q.label.toLowerCase()}`).join(" · ")} - trend="neutral" - /> -
-
+ {activeSummaryTab === "operations" && ( + <> + {/* ─── Operations ─── */} +
+
+ + + 0 ? "up" : "neutral"} + /> + `${q.count} ${q.label.toLowerCase()}`).join(" · ")} + trend="neutral" + /> +
+
+ + )} {/* ─── User Analytics ─── */}
diff --git a/src/pages/issues/IssuesPage.tsx b/src/pages/issues/IssuesPage.tsx index d16a2e7..7241c29 100644 --- a/src/pages/issues/IssuesPage.tsx +++ b/src/pages/issues/IssuesPage.tsx @@ -512,7 +512,7 @@ export function IssuesPage() {
-
+
diff --git a/src/pages/user-log/UserLogPage.tsx b/src/pages/user-log/UserLogPage.tsx index c3ebe60..fc4da43 100644 --- a/src/pages/user-log/UserLogPage.tsx +++ b/src/pages/user-log/UserLogPage.tsx @@ -404,7 +404,7 @@ export function UserLogPage() {
-
+
-

Total Users

-

1,248

+

Total Users

+

1,248

- + -
+
-

Active Users

-

1,180

+

Active Users

+

1,180

- + -
+
-

New This Month

-

64

+

New This Month

+

64