From 04bcf27f624826013bc55fdad7a41f1c525463fa Mon Sep 17 00:00:00 2001 From: dat369 <116793130+dat369@users.noreply.github.com> Date: Sun, 22 Jun 2025 00:13:42 +0700 Subject: [PATCH 01/19] add create new subject page, add material detail page --- package-lock.json | 31 ++ package.json | 1 + src/App.tsx | 4 + src/app/Staff/add-new-subject.tsx | 173 ++++++++++++ src/components/sections/chapter-form.tsx | 118 ++++++++ src/components/sections/cta.tsx | 10 +- src/components/sections/data-overview.tsx | 224 +++++++++++++++ src/components/sections/features.tsx | 28 +- src/components/sections/hero.tsx | 25 +- .../sections/japanese-learning-page.tsx | 265 ++++++++++++++++++ src/components/sections/slot-form.tsx | 116 ++++++++ src/components/sections/subject-form.tsx | 151 ++++++++++ src/components/ui/collapsible.tsx | 24 ++ src/components/ui/textarea.tsx | 21 ++ src/img/hinhnennhatban.jpg | Bin 0 -> 52501 bytes 15 files changed, 1158 insertions(+), 33 deletions(-) create mode 100644 src/app/Staff/add-new-subject.tsx create mode 100644 src/components/sections/chapter-form.tsx create mode 100644 src/components/sections/data-overview.tsx create mode 100644 src/components/sections/japanese-learning-page.tsx create mode 100644 src/components/sections/slot-form.tsx create mode 100644 src/components/sections/subject-form.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/img/hinhnennhatban.jpg diff --git a/package-lock.json b/package-lock.json index a1a1eca..1ff68b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "japanlearn", "version": "0.1.0", "dependencies": { + "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.9", @@ -1170,6 +1171,36 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", + "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", diff --git a/package.json b/package.json index d41f6c1..d543704 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "preview": "vite preview" }, "dependencies": { + "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.9", diff --git a/src/App.tsx b/src/App.tsx index 9da61d3..ee73b5c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,8 @@ import HomePage from './app/page'; import ChatbotPage from './app/Common/ChatBot'; import SlotSkills from './components/sections/slot-skills'; import Vocabulary from './components/sections/vocabulary'; +import CMSStaffInterface from './app/Staff/add-new-subject'; +import JapaneseLearningPage from './components/sections/japanese-learning-page'; function App() { const isAuthenticated = false; @@ -22,6 +24,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> ) diff --git a/src/app/Staff/add-new-subject.tsx b/src/app/Staff/add-new-subject.tsx new file mode 100644 index 0000000..d580907 --- /dev/null +++ b/src/app/Staff/add-new-subject.tsx @@ -0,0 +1,173 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { SubjectForm } from "@/components/sections/subject-form" +import { ChapterForm } from "@/components/sections/chapter-form" +import { SlotForm } from "@/components/sections/slot-form" +import { DataOverview } from "@/components/sections/data-overview" +import { BookOpen, FileText, Clock, Plus } from "lucide-react" + +export interface Subject { + id?: number + title: string + topic: string + description: string + level: string + estimatedDuration: string + creatorId: string + image: string + orderNumber: number + createdAt?: string + updatedAt?: string + chapters?: Chapter[] +} + +export interface Chapter { + id?: number + title: string + description: string + orderNumber: number + subjectId: number + createdAt?: string + updatedAt?: string + slots?: Slot[] +} + +export interface Slot { + id?: number + title: string + description: string + orderNumber: number + chapterId: number + createdAt?: string + updatedAt?: string +} + +export default function CMSStaffInterface() { + const [subjects, setSubjects] = useState([]) + const [chapters, setChapters] = useState([]) + const [slots, setSlots] = useState([]) + const [activeTab, setActiveTab] = useState("overview") + + const addSubject = (subject: Subject) => { + const newSubject = { + ...subject, + id: Date.now(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + chapters: [], + } + setSubjects((prev) => [...prev, newSubject]) + } + + const addChapter = (chapter: Chapter) => { + const newChapter = { + ...chapter, + id: Date.now(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + slots: [], + } + setChapters((prev) => [...prev, newChapter]) + + // Update the subject to include this chapter + setSubjects((prev) => + prev.map((subject) => + subject.id === chapter.subjectId + ? { ...subject, chapters: [...(subject.chapters || []), newChapter] } + : subject, + ), + ) + } + + const addSlot = (slot: Slot) => { + const newSlot = { + ...slot, + id: Date.now(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + setSlots((prev) => [...prev, newSlot]) + + // Update the chapter to include this slot + setChapters((prev) => + prev.map((chapter) => + chapter.id === slot.chapterId ? { ...chapter, slots: [...(chapter.slots || []), newSlot] } : chapter, + ), + ) + } + + return ( +
+
+
+

CMS Staff Interface

+

Manage subjects, chapters, and slots for the learning management system

+
+ + + + + + Overview + + + + Add Subject + + + + Add Chapter + + + + Add Slot + + + + + + + + + + + Add New Subject + Create a new subject with all required information + + + + + + + + + + + Add New Chapter + Add a chapter to an existing subject + + + + + + + + + + + Add New Slot + Add a slot to an existing chapter + + + + + + + +
+
+ ) +} diff --git a/src/components/sections/chapter-form.tsx b/src/components/sections/chapter-form.tsx new file mode 100644 index 0000000..03c9310 --- /dev/null +++ b/src/components/sections/chapter-form.tsx @@ -0,0 +1,118 @@ +"use client" + +import type React from "react" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Textarea } from "../ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Alert, AlertDescription } from "@/components/ui/alert" +import { AlertCircle } from "lucide-react" +import type { Subject, Chapter, Slot } from "@/app/Staff/add-new-subject" + +interface ChapterFormProps { + onSubmit: (chapter: Chapter) => void + subjects: Subject[] +} + +export function ChapterForm({ onSubmit, subjects }: ChapterFormProps) { + const [formData, setFormData] = useState>({ + title: "", + description: "", + orderNumber: 1, + subjectId: 0, + }) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (formData.subjectId === 0) return + + onSubmit(formData) + setFormData({ + title: "", + description: "", + orderNumber: 1, + subjectId: 0, + }) + } + + const handleChange = (field: keyof typeof formData, value: string | number) => { + setFormData((prev) => ({ ...prev, [field]: value })) + } + + if (subjects.length === 0) { + return ( + + + + No subjects available. Please create a subject first before adding chapters. + + + ) + } + + return ( +
+
+ + +
+ +
+
+ + handleChange("title", e.target.value)} + placeholder="Enter chapter title" + required + /> +
+ +
+ + handleChange("orderNumber", Number.parseInt(e.target.value) || 1)} + min="1" + required + /> +
+
+ +
+ +