From 0403813b8954be68283f4b48b3f6b43e799c17d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckirukib=E2=80=9D?= <“kirubeljkl679@gmail.com”> Date: Fri, 27 Feb 2026 16:58:05 +0300 Subject: [PATCH 04/10] - --- .env | 2 +- package-lock.json | 19 +------------------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.env b/.env index ff1a1d8..9eb5bee 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -VITE_API_BASE_URL=http://localhost:8080/api/v1 +VITE_API_BASE_URL= https://api.yimaru.yaltopia.com/ VITE_GOOGLE_CLIENT_ID=google_client_id diff --git a/package-lock.json b/package-lock.json index 6fc436d..c725334 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2754,7 +2753,6 @@ "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2765,7 +2763,6 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2776,7 +2773,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2832,7 +2828,6 @@ "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", @@ -3084,7 +3079,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3323,7 +3317,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3900,7 +3893,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5015,7 +5007,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5063,7 +5054,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5252,7 +5242,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5262,7 +5251,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5282,7 +5270,6 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -5488,8 +5475,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -5893,7 +5879,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6061,7 +6046,6 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -6199,7 +6183,6 @@ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From d0e694bc079fb6a183a8c64e516e5b558fb6bc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckirukib=E2=80=9D?= <“kirubeljkl679@gmail.com”> Date: Fri, 27 Feb 2026 17:08:11 +0300 Subject: [PATCH 05/10] t --- .env | 4 ++-- src/pages/auth/LoginPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 86dd9a4..e5a2141 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -VITE_API_BASE_URL=http://localhost:8080/api/v1 -VITE_GOOGLE_CLIENT_ID=google_client_id +VITE_API_BASE_URL= http://api.yimaru.yaltopia.com/ +VITE_GOOGLE_CLIENT_ID= google_client_id VERSION=1.0.0 diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx index 3b1d522..0de60fd 100644 --- a/src/pages/auth/LoginPage.tsx +++ b/src/pages/auth/LoginPage.tsx @@ -216,7 +216,7 @@ export function LoginPage() {

- Yimaru Academy + Yimaru Academy Test Mode

Manage your academy, track student progress, and streamline From e35defe48a7b43690d81205d08da6a8fe0fd5734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckirukib=E2=80=9D?= <“kirubeljkl679@gmail.com”> Date: Fri, 27 Feb 2026 19:03:47 +0300 Subject: [PATCH 06/10] ui-fixes --- .env | 2 +- src/pages/DashboardPage.tsx | 163 +++--- src/pages/ProfilePage.tsx | 515 ++++++++++-------- src/pages/analytics/AnalyticsPage.tsx | 254 +++++---- src/pages/issues/IssuesPage.tsx | 2 +- src/pages/user-log/UserLogPage.tsx | 2 +- .../UserManagementDashboard.tsx | 20 +- 7 files changed, 571 insertions(+), 387 deletions(-) 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

From cd2ed669607d632d236bdc8744a55af0fd987368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckirukib=E2=80=9D?= <“kirubeljkl679@gmail.com”> Date: Fri, 27 Feb 2026 19:31:41 +0300 Subject: [PATCH 07/10] ui+plus --- src/api/courses.api.ts | 4 + src/api/users.api.ts | 14 + src/pages/ProfilePage.tsx | 66 +- .../content-management/AddQuestionPage.tsx | 23 +- .../content-management/CourseCategoryPage.tsx | 106 ++- src/pages/issues/IssuesPage.tsx | 122 +++- src/pages/notifications/NotificationsPage.tsx | 675 ++++++++++++++++-- .../user-management/RegisterUserPage.tsx | 113 ++- src/types/course.types.ts | 4 + 9 files changed, 979 insertions(+), 148 deletions(-) diff --git a/src/api/courses.api.ts b/src/api/courses.api.ts index 7bf5ce7..c831e5a 100644 --- a/src/api/courses.api.ts +++ b/src/api/courses.api.ts @@ -37,11 +37,15 @@ import type { CreateQuestionRequest, CreateQuestionResponse, CreateVimeoVideoRequest, + CreateCourseCategoryRequest, } from "../types/course.types" export const getCourseCategories = () => http.get("/course-management/categories") +export const createCourseCategory = (data: CreateCourseCategoryRequest) => + http.post("/course-management/categories", data) + export const getCoursesByCategory = (categoryId: number) => http.get(`/course-management/categories/${categoryId}/courses`) diff --git a/src/api/users.api.ts b/src/api/users.api.ts index 89e3ca4..30936b6 100644 --- a/src/api/users.api.ts +++ b/src/api/users.api.ts @@ -14,3 +14,17 @@ export const getUserById = (id: number) => export const getMyProfile = () => http.get("/team/me"); + +// Best-guess API for creating a new user (admin-side). +// Adjust payload shape or endpoint if backend differs. +export interface CreateUserRequest { + first_name: string; + last_name: string; + email: string; + phone_number: string; + role: string; + notes?: string; +} + +export const createUser = (payload: CreateUserRequest) => + http.post("/users", payload); diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index b86c816..933fa24 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -226,7 +226,7 @@ export function ProfilePage() {
-
+
{/* Left column: About & details */}
{/* Identity */} @@ -348,70 +348,6 @@ export function ProfilePage() {
- {/* 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(", ") || "—"} -
-
-
- - {/* 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 */} diff --git a/src/pages/content-management/AddQuestionPage.tsx b/src/pages/content-management/AddQuestionPage.tsx index 13ec07c..85f7251 100644 --- a/src/pages/content-management/AddQuestionPage.tsx +++ b/src/pages/content-management/AddQuestionPage.tsx @@ -1,6 +1,7 @@ import { useState } from "react" import { useNavigate, useParams } from "react-router-dom" import { ArrowLeft, Plus, X } from "lucide-react" +import { toast } from "sonner" import { Button } from "../../components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" import { Input } from "../../components/ui/input" @@ -105,32 +106,44 @@ export function AddQuestionPage() { // Validation if (!formData.question.trim()) { - alert("Please enter a question") + toast.error("Missing question", { + description: "Please enter a question before saving.", + }) return } if (formData.type === "multiple-choice" || formData.type === "true-false") { if (!formData.correctAnswer) { - alert("Please select a correct answer") + toast.error("Missing correct answer", { + description: "Select the correct answer for this question.", + }) return } if (formData.type === "multiple-choice") { const hasEmptyOptions = formData.options.some((opt) => !opt.trim()) if (hasEmptyOptions) { - alert("Please fill in all options") + toast.error("Incomplete options", { + description: "Fill in all answer options for this multiple choice question.", + }) return } } } else if (formData.type === "short-answer") { if (!formData.correctAnswer.trim()) { - alert("Please enter a correct answer") + toast.error("Missing correct answer", { + description: "Enter the expected correct answer.", + }) return } } // In a real app, save the question here console.log("Saving question:", formData) - alert(isEditing ? "Question updated successfully!" : "Question created successfully!") + toast.success(isEditing ? "Question updated" : "Question created", { + description: isEditing + ? "The question has been updated successfully." + : "Your new question has been created.", + }) navigate("/content/questions") } diff --git a/src/pages/content-management/CourseCategoryPage.tsx b/src/pages/content-management/CourseCategoryPage.tsx index fd44f88..6804ef6 100644 --- a/src/pages/content-management/CourseCategoryPage.tsx +++ b/src/pages/content-management/CourseCategoryPage.tsx @@ -1,14 +1,27 @@ import { useEffect, useState } from "react" import { Link } from "react-router-dom" -import { FolderOpen, RefreshCw, AlertCircle, BookOpen } from "lucide-react" +import { FolderOpen, RefreshCw, AlertCircle, BookOpen, Plus } from "lucide-react" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" -import { getCourseCategories } from "../../api/courses.api" +import { Button } from "../../components/ui/button" +import { Input } from "../../components/ui/input" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "../../components/ui/dialog" +import { getCourseCategories, createCourseCategory } from "../../api/courses.api" import type { CourseCategory } from "../../types/course.types" +import { toast } from "sonner" export function CourseCategoryPage() { const [categories, setCategories] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) + const [createOpen, setCreateOpen] = useState(false) + const [newCategoryName, setNewCategoryName] = useState("") + const [creating, setCreating] = useState(false) const fetchCategories = async () => { setLoading(true) @@ -68,11 +81,21 @@ export function CourseCategoryPage() { return (
{/* Page header */} -
-

Course Categories

-

- Browse and manage your course categories below -

+
+
+

Course Categories

+

+ Browse and manage your course categories below +

+
+
{categories.length === 0 ? ( @@ -126,6 +149,75 @@ export function CourseCategoryPage() { ))}
)} + + {/* Create category dialog */} + + + + + + Create course category + + + Add a new high-level bucket to organize your courses. + + + +
+
+ + setNewCategoryName(e.target.value)} + /> +
+
+ +
+ + +
+
+
) } diff --git a/src/pages/issues/IssuesPage.tsx b/src/pages/issues/IssuesPage.tsx index 7241c29..891f63c 100644 --- a/src/pages/issues/IssuesPage.tsx +++ b/src/pages/issues/IssuesPage.tsx @@ -20,6 +20,7 @@ import { CheckCircle2, XCircle, ArrowUpCircle, + MessageCircle, } from "lucide-react"; import { Button } from "../../components/ui/button"; import { Input } from "../../components/ui/input"; @@ -201,6 +202,12 @@ export function IssuesPage() { // Status update const [statusUpdating, setStatusUpdating] = useState(null); + // Create issue dialog (admin-created) + const [createOpen, setCreateOpen] = useState(false); + const [createSubject, setCreateSubject] = useState(""); + const [createType, setCreateType] = useState("bug"); + const [createDescription, setCreateDescription] = useState(""); + const fetchIssues = useCallback(async () => { setLoading(true); try { @@ -345,17 +352,26 @@ export function IssuesPage() { Review and manage user-reported issues across the platform.

- +
+ + +
{/* Stats cards */} @@ -840,6 +856,90 @@ export function IssuesPage() { + {/* Create Issue Dialog */} + + + + + + Create admin issue + + + Log an issue directly from the admin panel so it can be tracked and resolved. + + + +
+
+ + setCreateSubject(e.target.value)} + /> +
+
+ + +
+
+ +