diff --git a/README.md b/README.md
index e215bc4cc..aeb0a9a96 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,396 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# 🎓 STEMify Frontend
-## Getting Started
+
-First, run the development server:
+
+
+**Nền tảng giáo dục STEM tương tác với công nghệ 3D và AI**
+
+
+
+[](https://nextjs.org/)
+[](https://react.dev/)
+[](https://www.typescriptlang.org/)
+[](https://nodejs.org/)
+
+
+
+[](https://tailwindcss.com/)
+[](https://ui.shadcn.com/)
+[](https://www.radix-ui.com/)
+[](https://tiptap.dev/)
+
+
+
+[](https://threejs.org/)
+[](https://docs.pmnd.rs/react-three-fiber)
+
+
+
+[](https://www.tensorflow.org/js)
+[](https://ai.google.dev/)
+
+
+
+[](https://next-auth.js.org/)
+[](https://redux-toolkit.js.org/)
+[](https://vercel.com/)
+[](https://github.com/features/actions)
+
+[English](#) | [Tiếng Việt](#)
+
+
+
+---
+
+## 📋 Mục lục
+
+- [Giới thiệu](#-giới-thiệu)
+- [Tính năng chính](#-tính-năng-chính)
+- [Công nghệ sử dụng](#-công-nghệ-sử-dụng)
+- [Yêu cầu hệ thống](#-yêu-cầu-hệ-thống)
+- [Cài đặt](#-cài-đặt)
+- [Cấu trúc dự án](#-cấu-trúc-dự-án)
+- [Phát triển](#-phát-triển)
+- [Tài liệu](#-tài-liệu)
+- [Đóng góp](#-đóng-góp)
+
+---
+
+## 🌟 Giới thiệu
+
+**STEMify** là một nền tảng giáo dục STEM (Science, Technology, Engineering, Mathematics) hiện đại, tích hợp công nghệ 3D, AI, và lập trình trực quan. Dự án cung cấp trải nghiệm học tập tương tác cho học sinh, giáo viên và tổ chức giáo dục.
+
+### 🎯 Mục tiêu
+
+- 🎨 Tạo trải nghiệm học tập 3D tương tác
+- 🤖 Tích hợp AI để hỗ trợ học tập cá nhân hóa
+- 📱 Ứng dụng web responsive, tối ưu giao diện cho nhiều kích thước màn hình trên trình duyệt
+- 🌐 Đa ngôn ngữ (Tiếng Việt, Tiếng Anh)
+
+---
+
+## ✨ Tính năng chính
+
+### 🎓 Học tập & Quản lý
+
+- **Khóa học tương tác**: Quản lý khóa học, bài học, và tài liệu học tập
+- **Bài tập & Quiz**: Hệ thống bài tập và đánh giá tự động
+- **Tiến độ học tập**: Theo dõi tiến độ và phân tích kết quả học tập
+- **Lớp học ảo**: Quản lý lớp học, học sinh và giáo viên
+
+### 🎨 3D Creator & Assembly
+
+- **Trình soạn 3D**: Tạo và chỉnh sửa mô hình 3D cho giáo dục
+- **Assembly Builder**: Xây dựng các mô hình lắp ráp tương tác
+- **Transform Controls**: Điều khiển xoay, di chuyển, và scale đối tượng 3D
+- **Template Library**: Thư viện component và template có sẵn
+
+### 🤖 AI & Chatbot
+
+- **AI Assistant**: Trợ lý AI hỗ trợ học tập
+- **Google Gemini Integration**: Tích hợp AI Gemini
+- **Teachable Machine**: ML model training trực tiếp
+
+### 🌐 Đa ngôn ngữ & Đa vai trò
+
+- **i18n Support**: Tiếng Việt và Tiếng Anh
+- **Role-based Access**: Admin, Teacher, Student, Organization
+- **Real-time Collaboration**: SignalR cho collaboration thời gian thực
+
+### 📊 Dashboard & Analytics
+
+- **Learning Analytics**: Phân tích dữ liệu học tập
+- **Progress Tracking**: Theo dõi tiến độ chi tiết
+- **Charts & Visualizations**: Biểu đồ với Chart.js và Recharts
+
+---
+
+## 🛠 Công nghệ sử dụng
+
+### Core Framework
+
+- **Next.js 15.5.7** - React framework với App Router
+- **React 19.2.1** - UI library
+- **TypeScript 5.9.3** - Type-safe JavaScript
+- **Tailwind CSS 4.1.13** - Utility-first CSS framework
+
+### 3D Graphics & Animation
+
+- **Three.js** - 3D rendering engine
+- **@react-three/fiber** - React renderer cho Three.js
+- **@react-three/drei** - Helper components cho R3F
+- **GSAP** - Animation library
+- **Framer Motion** - React animation library
+
+### UI Components
+
+- **Radix UI** - Headless UI components
+- **Shadcn/ui** - Re-usable component collection
+- **Lucide React** - Icon library
+- **React Icons** - Additional icons
+
+### State Management
+
+- **Redux Toolkit** - State management
+- **React Hook Form** - Form handling
+- **Redux Persist** - Persist state
+
+### Backend Integration
+
+- **Next Auth** - Authentication
+
+### Programming Education & STEM Integration
+
+- **Visual Programming UI** - Giao diện lập trình trực quan cho giáo dục STEM
+- **External STEM Modules** - Tích hợp các module lập trình và thiết bị thông qua repository riêng
+
+### AI & ML
+
+- **@google/generative-ai** - Google Gemini AI
+- **@teachablemachine/image** - ML model integration
+- **TensorFlow.js** - ML in browser
+
+### Content & Rich Text
+
+- **Tiptap** - Rich text editor
+- **React Markdown** - Markdown rendering
+- **MD Editor** - Markdown editor
+
+### Charts & Data Visualization
+
+- **Chart.js** - Chart library
+- **Recharts** - React charts
+- **react-chartjs-2** - React wrapper cho Chart.js
+
+### Development Tools
+
+- **ESLint** - Code linting
+- **Prettier** - Code formatting
+- **PostCSS** - CSS processing
+- **Sass** - CSS preprocessor
+
+---
+
+## 💻 Yêu cầu hệ thống
+
+- **Node.js**: >= 20.0.0
+- **npm/yarn/pnpm**: Latest version
+- **Git**: Latest version
+- **Browser**: Chrome, Firefox, Safari, Edge (latest versions)
+
+---
+
+## 🚀 Cài đặt
+
+### 1. Clone repository
+
+```bash
+git clone https://github.com/Capstone-STEMify/STEMify-Frontend.git
+cd STEMify-Frontend
+```
+
+### 2. Cài đặt dependencies
+
+```bash
+npm install
+# hoặc
+yarn install
+# hoặc
+pnpm install
+```
+
+### 3. Cấu hình environment variables
+
+Tạo file `.env.local` từ `.env.example`:
+
+```bash
+cp .env.example .env.local
+```
+
+Cấu hình các biến môi trường cần thiết:
+
+```env
+# Supabase
+NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
+NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_key
+
+# API
+NEXT_PUBLIC_API_URL=your_api_url
+
+# Google AI
+NEXT_PUBLIC_GOOGLE_AI_KEY=your_gemini_key
+
+# Cloudinary
+NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name
+```
+
+### 4. Chạy development server
```bash
npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+Mở [https://localhost:3000](https://localhost:3000) để xem kết quả.
+
+> ⚠️ **Lưu ý**: Development server chạy với HTTPS (--experimental-https)
+
+---
+
+## 📁 Cấu trúc dự án
+
+```
+STEMify-Frontend/
+├── public/ # Static files
+│ ├── animations/ # Lottie animations
+│ ├── assemblies/ # 3D assembly JSON files
+│ ├── components/ # Component templates
+│ ├── images/ # Images & logos
+│ └── models/ # 3D models
+│
+├── src/
+│ ├── app/ # Next.js App Router
+│ │ ├── [locale]/ # Internationalized pages
+│ │ └── api/ # API routes
+│ │
+│ ├── components/ # Shared components
+│ │ ├── layout/ # Layout components
+│ │ ├── shadcn/ # Shadcn UI components
+│ │ ├── shared/ # Shared UI components
+│ │ └── tiptap/ # Tiptap editor components
+│ │
+│ ├── features/ # Feature modules
+│ │ ├── 3d-creator/ # 3D creator feature
+│ │ ├── assembly/ # Assembly builder
+│ │ ├── auth/ # Authentication
+│ │ ├── blockly-*/ # Blockly integrations
+│ │ ├── chat/ # Chat & AI assistant
+│ │ ├── classroom/ # Classroom management
+│ │ ├── course/ # Course management
+│ │ ├── dashboard/ # Dashboard & analytics
+│ │ └── ... # Other features
+│ │
+│ ├── hooks/ # Custom React hooks
+│ ├── i18n/ # Internationalization
+│ ├── libs/ # Library configurations
+│ │ ├── auth/ # Auth config
+│ │ ├── redux/ # Redux store
+│ │ └── supabase/ # Supabase client
+│ │
+│ ├── providers/ # Context providers
+│ ├── schemas/ # Zod schemas
+│ ├── types/ # TypeScript types
+│ └── utils/ # Utility functions
+│
+├── messages/ # i18n translations
+│ ├── en/ # English translations
+│ └── vi/ # Vietnamese translations
+```
+
+---
+
+## 🔧 Phát triển
+
+### Available Scripts
+
+```bash
+# Install
+npm install
+# Development
+npm run dev # Start dev server with Turbopack & HTTPS
+
+# Build
+npm run build # Build for production
+npm run start # Start production server
+
+# Code Quality
+npm run lint # Run ESLint
+npm run format # Format code with Prettier
+
+# CSS
+npm run build:css # Build and minify CSS
+```
+
+### Coding Standards
+
+- **TypeScript**: Sử dụng TypeScript cho tất cả code
+- **ESLint**: Tuân thủ ESLint rules
+- **Prettier**: Format code trước khi commit
+- **Component Structure**: Tạo component theo feature-based structure
+- **Naming Convention**:
+ - Components: PascalCase
+ - Hooks: camelCase with `use` prefix
+ - Files: kebab-case hoặc PascalCase
+
+### Git Workflow
+
+```bash
+# Create feature branch
+git checkout -b feature/your-feature-name
+
+# Commit changes
+git add .
+git commit -m "feat: add your feature"
+
+# Push to remote
+git push origin feature/your-feature-name
+
+# Create Pull Request
+```
+
+### Branch Strategy
+
+- `main`: Production branch
+- `dev`: Development branch
+- `feature/*`: Feature branches
+- `fix/*`: Bug fix branches
+- `hotfix/*`: Hotfix branches
+
+---
+
+## 📄 License
+
+Dự án này được phát triển bởi **STEMify Team**.
+
+---
+
+## 🔗 External Repositories
+
+Một số module chuyên biệt trong hệ sinh thái STEMify được phát triển và quản lý dưới dạng repository độc lập để đảm bảo tính mở rộng và dễ bảo trì.
+
+### 🧠 STEMify Micro:bit Platform
+
+- **Repository**: https://github.com/Capstone-STEMify/STEMify-Microbit
+- **Mô tả**:
+ Nền tảng lập trình Micro:bit và MakeCode dành cho giáo dục STEM, hỗ trợ lập trình kéo thả, mô phỏng và triển khai cho thiết bị thực tế.
+- **Tích hợp**:
+ STEMify Frontend kết nối với module này thông qua API và giao diện tích hợp.
+
+---
+
+## 📞 Liên hệ
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+- **Organization**: [Capstone-STEMify](https://github.com/Capstone-STEMify)
+- **Repository**: [STEMify-Frontend](https://github.com/Capstone-STEMify/STEMify-Frontend)
+- **Email**: [stemify30062025@gmail.com
+ ](stemify30062025@gmail.com)
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+---
-## Learn More
+## 🙏 Acknowledgments
-To learn more about Next.js, take a look at the following resources:
+- [Next.js](https://nextjs.org/)
+- [React Three Fiber](https://docs.pmnd.rs/react-three-fiber)
+- [Tailwind CSS](https://tailwindcss.com/)
+- [Shadcn/ui](https://ui.shadcn.com/)
+- [Supabase](https://supabase.com/)
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+---
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
-## Deploy on Vercel
+**Made with ❤️ by STEMify Team**
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+⭐ Star us on GitHub — it motivates us a lot!
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+
diff --git a/messages/en/assignment/en_assignment.json b/messages/en/assignment/en_assignment.json
index 7e973a3ee..bb958ec03 100644
--- a/messages/en/assignment/en_assignment.json
+++ b/messages/en/assignment/en_assignment.json
@@ -75,15 +75,20 @@
},
"doAsm": {
"deadline": "Deadline",
- "deadline2": "Deadline (Days from enrollment)",
- "AIGrading": "AI Grading",
- "description": "After submitting your assignment and completing your required peer reviews, you'll receive an AI-generated grade based on the assignment rubrics. You'll then have the option to have your assignment reviewed by your peers instead.",
- "subDes": "Your data will be used in accordance with",
- "subDesLink": "Our Privacy Notice",
"mySub": "My Submission",
- "projectTitle": "Project Title",
"placeholder": "Type your answer here...",
- "saveDraft": "Already save draft at {time}."
+ "saveDraft": "Draft saved at {time}",
+ "restoring": "Restoring assignment session...",
+ "fileError": "Only .pdf, .doc, or .docx files are allowed.",
+ "uploadClick": "Click to upload",
+ "uploadDrag": "or drag and drop",
+ "uploadFormat": "PDF, DOC, or DOCX",
+ "restoreSuccess": "Restored your unsaved draft.",
+ "submitSuccess": "Assignment submitted successfully!",
+ "submitFail": "Failed to submit assignment. Please try again later.",
+ "uploadFail": "Failed to upload file for Question {index}.",
+ "invalidData": "Cannot submit assignment. Invalid data.",
+ "noAsmFound": "No assignment found. Please go back and select the assignment again."
}
},
"upsert": {
diff --git a/messages/en/classroom/en_classroom.json b/messages/en/classroom/en_classroom.json
index 5f67450bc..0c2aed35a 100644
--- a/messages/en/classroom/en_classroom.json
+++ b/messages/en/classroom/en_classroom.json
@@ -92,7 +92,9 @@
"semiAnnual": "Semi-Annual",
"students": "Students",
"teachers": "Teachers",
- "curricula": "Curricula"
+ "curricula": "Curricula",
+ "guideText": "Creation Guide",
+ "guide": "Courses can belong to multiple active subscriptions. Please select a subscription before creating the classroom. The selected subscription will determine the number of students, teachers, and access to learning content. After selecting a subscription, choose student groups, assign teachers, and set the classroom duration. Click Create to finalize the classroom creation."
},
"studentClassroom": {
"list": {
@@ -116,6 +118,7 @@
"noClassroom": "You are not enrolled in any classrooms yet.",
"noClassroomSubtext": "Explore available classrooms and start learning today!",
"grade": "Grade"
- }
+ },
+ "addStudentsToClassroom": "Add Students to Classroom"
}
-}
\ No newline at end of file
+}
diff --git a/messages/en/common/en_common.json b/messages/en/common/en_common.json
index 13855ede8..9495ed2a8 100644
--- a/messages/en/common/en_common.json
+++ b/messages/en/common/en_common.json
@@ -115,7 +115,7 @@
"sendInvitations": "Send Invitations",
"clearFilters": "Clear Filters",
"createClassroom": "Create Classroom",
- "addStudents": "Add Students",
+ "addStudents": "Add Users",
"reviewed": "Reviewed",
"notReviewed": "Not Reviewed",
"submitReview": "Submit Review",
@@ -142,7 +142,10 @@
"upgrade": "Upgrade Plan",
"uploadFile": "Upload File",
"exportGLB": "Export GLB",
- "restore": "Restore"
+ "restore": "Restore",
+ "removeStudents": "Remove {student} Students",
+ "deactivate": "Deactivate",
+ "activate": "Activate"
},
"message": {
"courseCreateSuccess": "Course created successfully!",
@@ -280,7 +283,8 @@
"locked": "Locked",
"correct": "Correct",
"incorrect": "Incorrect",
- "unanswered": "Unanswered"
+ "unanswered": "Unanswered",
+ "disabled": "Disabled"
},
"status2": {
"active": "Active",
@@ -302,6 +306,7 @@
"advanced": "Advanced"
},
"accountType": {
+ "all": "All",
"accountTypeLabel": "Account Type",
"admin": "Admin",
"guest": "Guest",
diff --git a/messages/en/common/en_toast.json b/messages/en/common/en_toast.json
index 5077d1c52..62d58800d 100644
--- a/messages/en/common/en_toast.json
+++ b/messages/en/common/en_toast.json
@@ -13,6 +13,7 @@
"enrollDes": "You have enrolled in {title}. Start learning now!",
"addToCourse": "Successfully added to course!",
"removeCourseFromCurriculum": "Successfully removed course from this curriculum!",
+ "removeEmulatorFromCurriculum": "Successfully removed emulator from this curriculum!",
"lessonStart": "Lesson Started!",
"lessonStatus": "Lesson status updated to {status}!",
"sectionComplete": "Section Completed!",
@@ -44,8 +45,10 @@
"delete": "Are you sure you want to delete \"{title}\"?",
"archive": "Are you sure you want to archive \"{title}\"?",
"removeCourse": "Are you sure you want to remove \"{title}\" from this curriculum?",
+ "removeEmulator": "Are you sure you want to remove \"{title}\" from this curriculum?",
"removeKit": "Are you sure you want to remove \"{title}\" from this course?",
"removeComponent": "Are you sure you want to remove component from this kit?",
+ "removeStudentFromGroup": "Are you sure you want to remove this student from group?",
"discard": "Are you sure you want to discard your changes?",
"ask": "Are you sure to make ",
"askStatus": "Are you sure to {action} \"{title}\"?",
@@ -54,7 +57,11 @@
"removeItemFromCart": "Are you sure you want to remove this item from the cart?",
"clearCart": "Are you sure you want to clear the cart?",
"deleteUserEmail": "Are you sure you want to delete the user with email \"{title}\"?",
- "restore": "Are you sure you want to restore \"{title}\"?"
+ "restore": "Are you sure you want to restore \"{title}\"?",
+ "cancelledSubscriptions": "Are you sure you want to cancel this subscription?",
+ "deactivate": "Are you sure you want to deactivate \"{title}\"?",
+ "activate": "Are you sure you want to activate \"{title}\"?",
+ "removeStudents": "Are you sure you want to remove the {count} students from the group?"
},
"unauthorized": "Unauthorized access."
}
diff --git a/messages/en/curriculum/en_curriculum.json b/messages/en/curriculum/en_curriculum.json
index 5eb88d1db..827f30273 100644
--- a/messages/en/curriculum/en_curriculum.json
+++ b/messages/en/curriculum/en_curriculum.json
@@ -13,6 +13,7 @@
"loading": "Loading curriculums...",
"error": "An error occurred while fetching curriculums. Please try again.",
"courseListTitle": "Courses List",
+ "emulatorListTitle": "Emulator List",
"viewDetails": "View Details",
"reviews": "reviews"
},
@@ -20,9 +21,9 @@
"title": "Curriculum Details",
"addCourse": "Add Course",
"price": "Price",
- "alrealyEnrolled": "Already Enrolled"
+ "alrealyEnrolled": "Already Enrolled",
+ "addEmulator": "Add Emulator"
},
-
"form": {
"title": {
"create": "Create Curriculum",
@@ -61,6 +62,11 @@
"selectCourseTitle": "Select Courses",
"selectedCourses": "Selected courses",
"searchCoursePlaceholder": "Search courses by code or title...",
+ "emulatorListTitle": "Emulator List",
+ "emulatorListDescription": "Emulators provide a virtual environment that mimics the behavior of physical devices, allowing users to test and run software applications without the need for actual hardware. They are commonly used in software development, testing, and debugging processes, enabling developers to simulate different device configurations and operating systems.",
+ "selectEmulationTitle": "Select Emulators",
+ "selectedEmulators": "Selected emulators",
+ "searchEmulatorPlaceholder": "Search emulators here...",
"selectKitTitle": "Select Kits",
"selectedKits": "Selected kits",
"searchKitPlaceholder": "Search kits here...",
diff --git a/messages/en/organization/en_organization.json b/messages/en/organization/en_organization.json
index b0c559571..9a0d11ebf 100644
--- a/messages/en/organization/en_organization.json
+++ b/messages/en/organization/en_organization.json
@@ -231,7 +231,9 @@
"groupList": "Group List",
"name": "Name",
"studentCount": "Student Count"
- }
+ },
+ "studentGroupInfo": "Student Group Information",
+ "addStudentsToGroup": "Add Students to Group"
},
"curriculum": {
"curriculum": "Curriculum",
@@ -263,6 +265,27 @@
"email": "Search by email...",
"license": "Select License"
}
+ },
+ "userDetail": {
+ "title": "User Details",
+ "description": "View detailed information about this user.",
+ "contactInfo": "Contact Information",
+ "email": "Email Address",
+ "activity": "Activity",
+ "joinedAt": "Joined At",
+ "lastLogin": "Last Login",
+ "never": "Never",
+ "professionalDetails": "Professional Details",
+ "groupName": "Group Name",
+ "groupCode": "Group Code",
+ "specialization": "Specialization",
+ "major": "Major",
+ "bio": "Bio",
+ "noData": "No user data found.",
+ "status": {
+ "active": "Active",
+ "inactive": "Inactive"
+ }
}
}
}
diff --git a/messages/vi/assignment/vi_assignment.json b/messages/vi/assignment/vi_assignment.json
index 8663c0b72..5b905f6e9 100644
--- a/messages/vi/assignment/vi_assignment.json
+++ b/messages/vi/assignment/vi_assignment.json
@@ -75,15 +75,20 @@
},
"doAsm": {
"deadline": "Hạn nộp",
- "deadline2": "Hạn nộp (Tính từ ngày đăng ký)",
- "AIGrading": "Chấm điểm bằng AI",
- "description": "Sau khi bạn nộp bài và hoàn thành phần đánh giá chéo bắt buộc, bạn sẽ nhận được điểm do AI chấm dựa trên rubric của bài tập. Sau đó, bạn có thể chọn để bài tập được đánh giá bởi các bạn học khác.",
- "subDes": "Dữ liệu của bạn sẽ được sử dụng theo",
- "subDesLink": "Thông báo Quyền riêng tư của chúng tôi",
"mySub": "Bài nộp của tôi",
- "projectTitle": "Tiêu đề dự án",
"placeholder": "Nhập câu trả lời của bạn...",
- "saveDraft": "Đã lưu bản nháp lúc {time}."
+ "saveDraft": "Đã lưu bản nháp lúc {time}",
+ "restoring": "Đang khôi phục dữ liệu bài làm...",
+ "fileError": "Chỉ chấp nhận tệp .pdf, .doc hoặc .docx.",
+ "uploadClick": "Nhấn vào để upload",
+ "uploadDrag": "hoặc kéo thả",
+ "uploadFormat": "PDF, DOC, hoặc DOCX",
+ "restoreSuccess": "Đã khôi phục bài làm chưa nộp của bạn.",
+ "submitSuccess": "Nộp bài thành công!",
+ "submitFail": "Nộp bài thất bại. Vui lòng thử lại sau.",
+ "uploadFail": "Tải lên tệp thất bại cho Câu hỏi {index}.",
+ "invalidData": "Không thể nộp bài. Dữ liệu không hợp lệ.",
+ "noAsmFound": "Không tìm thấy bài tập. Vui lòng quay lại và chọn bài tập."
}
},
"upsert": {
diff --git a/messages/vi/classroom/vi_classroom.json b/messages/vi/classroom/vi_classroom.json
index fcfbd7853..af8ea5da0 100644
--- a/messages/vi/classroom/vi_classroom.json
+++ b/messages/vi/classroom/vi_classroom.json
@@ -91,7 +91,9 @@
"semiAnnual": "Nửa năm",
"students": "Học sinh",
"teachers": "Giáo viên",
- "curricula": "Chương trình học"
+ "curricula": "Chương trình học",
+ "guideText": "Hướng dẫn tạo lớp học",
+ "guide": "Khóa học có thể thuộc nhiều gói đăng ký đang hoạt động. Vui lòng chọn một gói đăng ký trước khi tạo lớp học. Gói đăng ký được chọn sẽ quyết định số lượng học sinh, giáo viên và quyền truy cập nội dung học tập. Sau khi chọn gói, hãy chọn nhóm học sinh, gán giáo viên và thiết lập thời gian lớp học. Nhấn Tạo để hoàn tất việc tạo lớp học."
},
"studentClassroom": {
"list": {
@@ -115,6 +117,7 @@
"noClassroom": "Bạn chưa tham gia lớp học nào.",
"noClassroomSubtext": "Khám phá các lớp học có sẵn và bắt đầu học ngay hôm nay!",
"grade": "Khối"
- }
+ },
+ "addStudentsToClassroom": "Thêm học sinh vào lớp học"
}
-}
\ No newline at end of file
+}
diff --git a/messages/vi/common/vi_common.json b/messages/vi/common/vi_common.json
index ce514170b..9e5f5d74a 100644
--- a/messages/vi/common/vi_common.json
+++ b/messages/vi/common/vi_common.json
@@ -116,7 +116,7 @@
"sendInvitations": "Gửi Lời Mời",
"clearFilters": "Xóa Bộ Lọc",
"createClassroom": "Tạo Lớp Học",
- "addStudents": "Thêm Học Sinh",
+ "addStudents": "Thêm Người Dùng",
"reviewed": "Đã đánh giá",
"notReviewed": "Chưa đánh giá",
"submitReview": "Gửi Đánh Giá",
@@ -142,7 +142,10 @@
"upgrade": "Nâng Cấp Gói",
"uploadFile": "Tải Lên Tệp",
"exportGLB": "Xuất GLB",
- "restore": "Khôi Phục"
+ "restore": "Khôi Phục",
+ "removeStudents": "Xóa {student} Học Sinh",
+ "deactivate": "Vô Hiệu Hóa",
+ "activate": "Kích Hoạt"
},
"message": {
"courseCreateSuccess": "Khóa học được tạo thành công!",
@@ -252,7 +255,7 @@
},
"level": {
"all": "Tất Cả Trình Độ",
- "beginner": "Mới Bắt Đầu",
+ "beginner": "Căn bản",
"intermidiate": "Trung Bình",
"advanced": "Nâng Cao"
},
@@ -285,7 +288,8 @@
"locked": "Khóa",
"correct": "Đúng",
"incorrect": "Sai",
- "unanswered": "Chưa Trả Lời"
+ "unanswered": "Chưa Trả Lời",
+ "disabled": "Đã Vô Hiệu Hóa"
},
"status2": {
"active": "Đang Hoạt Động",
@@ -300,8 +304,8 @@
"active": "Đã xác thực",
"inactive": "Chưa xác thực"
},
-
"accountType": {
+ "all": "Tất Cả",
"accountTypeLabel": "Loại Tài Khoản",
"admin": "Quản Trị Viên",
"guest": "Khách",
diff --git a/messages/vi/common/vi_toast.json b/messages/vi/common/vi_toast.json
index 5769916e8..03be70cc1 100644
--- a/messages/vi/common/vi_toast.json
+++ b/messages/vi/common/vi_toast.json
@@ -13,6 +13,7 @@
"enrollDes": "Bạn đã đăng ký khóa học {title}. Bắt đầu học ngay!",
"addToCourse": "Đã thêm vào khóa học thành công!",
"removeCourseFromCurriculum": "Đã xóa khóa học khỏi chương trình học thành công!",
+ "removeEmulatorFromCurriculum": "Đã xóa mô hình khỏi chương trình học thành công!",
"lessonStart": "Bài học đã bắt đầu!",
"lessonStatus": "Trạng thái bài học đã được cập nhật thành {status}!",
"sectionComplete": "Đã hoàn thành!",
@@ -44,8 +45,10 @@
"delete": "Bạn có chắc chắn muốn xóa \"{title}\" không?",
"archive": "Bạn có chắc chắn muốn lưu trữ \"{title}\" không?",
"removeCourse": "Bạn có chắc chắn muốn xóa \"{title}\" khỏi chương trình học này không?",
+ "removeEmulator": "Bạn có chắc chắn muốn xóa \"{title}\" khỏi chương trình học này không?",
"removeKit": "Bạn có chắc chắn muốn xóa \"{title}\" khỏi khóa học này không?",
"removeComponent": "Bạn có chắc chắn muốn xóa thành phần khỏi bộ dụng cụ này không?",
+ "removeStudentFromGroup": "Bạn có chắn chắn muốn xóa học sinh khỏi nhóm không?",
"discard": "Bạn có chắc chắn muốn hủy bỏ các thay đổi của mình không?",
"ask": "Bạn có chắc chắn muốn thực hiện ",
"askStatus": "Bạn có chắc chắn muốn {action} \"{title}\" không?",
@@ -53,7 +56,11 @@
"addAnotherKit": "Khóa học đã có bộ kit. Bạn có muốn thay thế bằng bộ mới?",
"removeItemFromCart": "Bạn có chắc chắn muốn xóa sản phẩm này khỏi giỏ hàng không?",
"clearCart": "Bạn có chắc chắn muốn xóa giỏ hàng không?",
- "restore": "Bạn có chắc chắn muốn khôi phục \"{title}\" không?"
+ "restore": "Bạn có chắc chắn muốn khôi phục \"{title}\" không?",
+ "cancelledSubscriptions": "Bạn có chắc chắn muốn hủy gói đăng ký này không? Sau khi hủy, người dùng sẽ không thể sử dụng các tính năng của gói.",
+ "deactivate": "Bạn có chắc chắn muốn vô hiệu hóa \"{title}\" không?",
+ "activate": "Bạn có chắc chắn muốn kích hoạt \"{title}\" không?",
+ "removeStudents": "Bạn có chắc chắn muốn xóa {count} học sinh khỏi nhóm không?"
},
"unauthorized": "Truy cập không được phép."
}
diff --git a/messages/vi/curriculum/vi_curriculum.json b/messages/vi/curriculum/vi_curriculum.json
index fd4a79b4e..1e6aba304 100644
--- a/messages/vi/curriculum/vi_curriculum.json
+++ b/messages/vi/curriculum/vi_curriculum.json
@@ -13,6 +13,7 @@
"loading": "Đang tải khung chương trình...",
"error": "Đã xảy ra lỗi trong quá trình lấy khung chương trình. Vui lòng thử lại.",
"courseListTitle": "Danh Sách Các Khóa Học",
+ "emulatorListTitle": "Danh Sách Mô Hình",
"viewDetails": "Xem Chi Tiết",
"reviews": "đánh giá"
},
@@ -20,9 +21,9 @@
"title": "Chi Tiết Khung Chương Trình",
"addCourse": "Thêm Khóa Học",
"price": "Giá",
- "alreadyEnrolled": "Đã Đăng Ký"
+ "alreadyEnrolled": "Đã Đăng Ký",
+ "addEmulator": "Thêm Mô Hình"
},
-
"form": {
"title": {
"create": "Tạo Khung Chương Trình",
@@ -61,6 +62,11 @@
"selectCourseTitle": "Chọn Khóa Học",
"selectedCourses": "Khóa học đã chọn",
"searchCoursePlaceholder": "Tìm kiếm khóa học theo mã hoặc tiêu đề...",
+ "emulatorListTitle": "Danh Sách Mô Hình",
+ "emulatorListDescription": "Mô hình cung cấp một môi trường ảo mô phỏng hành vi của các thiết bị vật lý, cho phép người dùng thử nghiệm và chạy các ứng dụng phần mềm mà không cần phần cứng thực tế. Chúng thường được sử dụng trong quá trình phát triển phần mềm, kiểm thử và gỡ lỗi, cho phép các nhà phát triển mô phỏng các cấu hình thiết bị và hệ điều hành khác nhau.",
+ "selectEmulationTitle": "Chọn Mô Hình",
+ "selectedEmulators": "Mô hình đã chọn",
+ "searchEmulatorPlaceholder": "Tìm kiếm mô hình ở đây...",
"selectKitTitle": "Chọn Bộ Dụng Cụ Học Tập",
"selectedKits": "Kit đã chọn",
"searchKitPlaceholder": "Tìm kiếm bộ dụng cụ ở đây...",
diff --git a/messages/vi/organization/vi_organization.json b/messages/vi/organization/vi_organization.json
index 5fdef5219..d9a354fe6 100644
--- a/messages/vi/organization/vi_organization.json
+++ b/messages/vi/organization/vi_organization.json
@@ -230,7 +230,9 @@
"groupList": "Danh sách nhóm",
"name": "Tên",
"studentCount": "Số học sinh"
- }
+ },
+ "studentGroupInfo": "Thông tin nhóm học sinh",
+ "addStudentsToGroup": "Thêm học sinh vào nhóm"
},
"curriculum": {
"curriculum": "Khung chương trình",
@@ -262,6 +264,27 @@
"email": "Tìm kiếm theo email...",
"license": "Chọn vai trò"
}
+ },
+ "userDetail": {
+ "title": "Chi tiết người dùng",
+ "description": "Xem thông tin chi tiết về người dùng này.",
+ "contactInfo": "Thông tin liên hệ",
+ "email": "Địa chỉ Email",
+ "activity": "Hoạt động",
+ "joinedAt": "Ngày tham gia",
+ "lastLogin": "Đăng nhập lần cuối",
+ "never": "Chưa từng",
+ "professionalDetails": "Thông tin chuyên môn",
+ "groupName": "Tên nhóm",
+ "groupCode": "Mã nhóm",
+ "specialization": "Chuyên môn",
+ "major": "Chuyên ngành",
+ "bio": "Giới thiệu",
+ "noData": "Không tìm thấy dữ liệu người dùng.",
+ "status": {
+ "active": "Hoạt động",
+ "inactive": "Không hoạt động"
+ }
}
}
}
diff --git a/src/app/[locale]/admin/(main)/straw-lab/page.tsx b/src/app/[locale]/admin/(main)/straw-lab/page.tsx
index f3efcecbc..22529b300 100644
--- a/src/app/[locale]/admin/(main)/straw-lab/page.tsx
+++ b/src/app/[locale]/admin/(main)/straw-lab/page.tsx
@@ -1,10 +1,10 @@
-import Workspace3dLibrary from '@/features/emulator/components/workspace-3d/Workspace3dLibrary'
+import SystemWorkspace3dLibrary from '@/features/emulator/components/workspace-3d/SystemWorkspace3dLibrary'
import React from 'react'
export default function StrawLabPage() {
return (
-
+
)
}
diff --git a/src/app/[locale]/certificate/(protected)/[certificateId]/page.tsx b/src/app/[locale]/certificate/(protected)/[certificateId]/page.tsx
new file mode 100644
index 000000000..6207d0ece
--- /dev/null
+++ b/src/app/[locale]/certificate/(protected)/[certificateId]/page.tsx
@@ -0,0 +1,8 @@
+import SpecificCertificate from '@/features/certificate/components/detail/SpecificCertificate'
+import React from 'react'
+
+export default function CertificateDetailPage() {
+ return (
+
+ )
+}
diff --git a/src/app/[locale]/certificate/(protected)/page.tsx b/src/app/[locale]/certificate/(protected)/page.tsx
index a7fa8f9f0..35889960d 100644
--- a/src/app/[locale]/certificate/(protected)/page.tsx
+++ b/src/app/[locale]/certificate/(protected)/page.tsx
@@ -1,6 +1,6 @@
-import Accomplishment from '@/features/certificate/components/list/Accomplishment'
+import CertificateList from '@/features/certificate/components/list/CertificateList'
import React from 'react'
export default function AccomplishmentPage() {
- return
+ return
}
diff --git a/src/app/[locale]/certificate/(protected)/[verificationCode]/page.tsx b/src/app/[locale]/certificate/(protected)/verify/[verificationCode]/page.tsx
similarity index 100%
rename from src/app/[locale]/certificate/(protected)/[verificationCode]/page.tsx
rename to src/app/[locale]/certificate/(protected)/verify/[verificationCode]/page.tsx
diff --git a/src/app/[locale]/lab/page.tsx b/src/app/[locale]/lab/page.tsx
index 52544ba85..6f0d9219a 100644
--- a/src/app/[locale]/lab/page.tsx
+++ b/src/app/[locale]/lab/page.tsx
@@ -12,7 +12,7 @@ export default function StemifiLabLibrary() {
const { token, user } = useAppSelector((state) => state.auth)
const userId = user?.userId
const redirectUrl =
- process.env.NEXT_PUBLIC_MICROBIT_URL ?? 'https://white-cliff-0cc49e300.3.azurestaticapps.net/index.html'
+ process.env.NEXT_PUBLIC_MICROBIT_URL ?? '/'
return (
diff --git a/src/app/[locale]/lab/workspace-3d/page.tsx b/src/app/[locale]/lab/workspace-3d/page.tsx
index 9de69addc..029c409f9 100644
--- a/src/app/[locale]/lab/workspace-3d/page.tsx
+++ b/src/app/[locale]/lab/workspace-3d/page.tsx
@@ -1,10 +1,10 @@
-import Workspace3dLibrary from '@/features/emulator/components/workspace-3d/Workspace3dLibrary'
+import StudentWorkspace3dLibrary from '@/features/emulator/components/workspace-3d/StudentWorkspace3dLibrary'
import React from 'react'
export default function Workspace3DLibraryPage() {
return (
-
+
)
}
diff --git a/src/app/[locale]/organization/group/create/page.tsx b/src/app/[locale]/organization/group/create/page.tsx
deleted file mode 100644
index 5e53719a7..000000000
--- a/src/app/[locale]/organization/group/create/page.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import CreateStudentGroupPage from '@/features/group/components/upsert/CreateStudentGroupPage'
-import React from 'react'
-
-export default function Page() {
- return
-}
diff --git a/src/app/[locale]/resource/course/[courseId]/learn/page.tsx b/src/app/[locale]/resource/course/[courseId]/learn/page.tsx
index f5b78c831..f496f645a 100644
--- a/src/app/[locale]/resource/course/[courseId]/learn/page.tsx
+++ b/src/app/[locale]/resource/course/[courseId]/learn/page.tsx
@@ -1,12 +1,17 @@
'use client'
import CourseDetailEnrolled from '@/features/resource/course/components/detail/enrolled/CourseDetailEnrolled'
import { useAppSelector } from '@/hooks/redux-hooks'
-import { useParams, useSearchParams } from 'next/navigation'
+import { useLocale } from 'next-intl'
+import { useParams, useRouter, useSearchParams } from 'next/navigation'
import React from 'react'
export default function CourseDetailPage() {
const { courseId } = useParams()
- const { courseEnrollmentId } = useAppSelector((state) => state.enrollment)
+ const router = useRouter()
+ const locale = useLocale()
+ const { courseEnrollmentId } = useAppSelector((state) => state.enrollment)
+ console.log('courseEnrollmentId:', courseEnrollmentId)
+ if (!courseEnrollmentId) router.push(`/${locale}/unauthorized`)
return
}
diff --git a/src/app/[locale]/review-submission/microbit/[shareId]/page.tsx b/src/app/[locale]/review-submission/microbit/[shareId]/page.tsx
new file mode 100644
index 000000000..4c4eb4369
--- /dev/null
+++ b/src/app/[locale]/review-submission/microbit/[shareId]/page.tsx
@@ -0,0 +1,8 @@
+import MicrobitReviewSubmission from '@/features/microbit/review/MicrobitReviewSubmission'
+import React from 'react'
+
+export default function ReviewMicrobitPage() {
+ return (
+
+ )
+}
diff --git a/src/components/layout/admin/sidebar/nav-user.tsx b/src/components/layout/admin/sidebar/nav-user.tsx
index bf300191b..140a52433 100644
--- a/src/components/layout/admin/sidebar/nav-user.tsx
+++ b/src/components/layout/admin/sidebar/nav-user.tsx
@@ -23,6 +23,7 @@ import { logout } from '@/features/auth/authSlice'
import { clearSelectedOrganization } from '@/features/subscription/slice/selectedOrganizationSlice'
import { persistor } from '@/libs/redux/store'
import { useRouter } from 'next/navigation'
+import { stringToHslColor } from '@/utils/index'
export function NavUser({
user
}: {
@@ -66,10 +67,14 @@ export function NavUser({
size='lg'
className='data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground'
>
-
-
- CN
-
+
+ {user?.name?.charAt(0).toUpperCase()}
+
{user?.name}{user?.email}
@@ -85,10 +90,14 @@ export function NavUser({
>
-
-
- CN
-
+
+ {user?.name?.charAt(0).toUpperCase()}
+
{user?.name}{user?.email}
@@ -96,19 +105,7 @@ export function NavUser({
- Khóa học có thể thuộc nhiều gói đăng ký đang hoạt động. Vui lòng chọn một gói đăng ký trước khi tạo lớp học.
- Gói đăng ký được chọn sẽ quyết định số lượng học sinh, giáo viên và quyền truy cập nội dung học tập. Sau khi
- chọn gói, hãy chọn nhóm học sinh, gán giáo viên và thiết lập thời gian lớp học. Nhấn Create để hoàn tất việc
- tạo lớp học.
-