From 4ea238228b8d54cda425d78a6b4235330d94bfee Mon Sep 17 00:00:00 2001 From: ttd11204 Date: Mon, 8 Dec 2025 22:24:56 +0700 Subject: [PATCH 1/4] feat: update LoadingComponent to comment out text display and enhance Workspace3dLibrary with userId retrieval --- src/components/shared/loading/LoadingComponent.tsx | 2 +- .../emulator/components/workspace-3d/Workspace3dLibrary.tsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/shared/loading/LoadingComponent.tsx b/src/components/shared/loading/LoadingComponent.tsx index b0eadfbb7..7f156f4c0 100644 --- a/src/components/shared/loading/LoadingComponent.tsx +++ b/src/components/shared/loading/LoadingComponent.tsx @@ -14,7 +14,7 @@ export default function LoadingComponent({ size = 75, textShow = true, text }: L return (
Loading Cat - {textShow &&

{text || 'One moment please...'}

} + {/* {textShow &&

{text || 'One moment please...'}

} */}
) } diff --git a/src/features/emulator/components/workspace-3d/Workspace3dLibrary.tsx b/src/features/emulator/components/workspace-3d/Workspace3dLibrary.tsx index aebeb84d9..89ef168c7 100644 --- a/src/features/emulator/components/workspace-3d/Workspace3dLibrary.tsx +++ b/src/features/emulator/components/workspace-3d/Workspace3dLibrary.tsx @@ -37,13 +37,16 @@ export default function Workspace3dLibrary() { const [statusFilter, setStatusFilter] = useState('all') const userRole = useAppSelector((state) => state.auth.user?.userRole) + const userId = useAppSelector((state) => state.auth.user?.userId) + const allowRoles = [UserRole.STAFF, UserRole.ADMIN] const statusQuery = statusFilter === 'all' ? undefined : statusFilter const { data, isLoading } = useSearchEmulationsQuery({ page: 1, search, - status: statusQuery as EmulatorStatus | undefined + status: statusQuery as EmulatorStatus | undefined, + userId: userId }) const [updateEmulation] = useUpdateEmulatorMutation() const [deleteEmulation] = useDeleteEmulatorMutation() From f38a09b876927c760f0c5eb6ddad81c06994ab9d Mon Sep 17 00:00:00 2001 From: ttd11204 Date: Tue, 9 Dec 2025 04:07:16 +0700 Subject: [PATCH 2/4] feat: Update assignment submission and review components with localization and file type support - Translated UI text in AssignmentSubmission and SubmissionReviewDialog components to Vietnamese. - Added support for additional file types (.mp4, .png, .jpg, .jpeg) in file upload input. - Improved error and success messages for assignment submission. - Refactored SubmissionReviewDialog for better readability and maintainability. - Enhanced ClassroomOverview and ClassroomSubHeader components with role-based navigation. - Updated CreateClassroom and UpsertOrganization components to use localized success messages. - Cleaned up unused code and comments across various components. - Added logging for selected organization user ID in StudentClassroomDetails. - Improved user experience in UploadContent and AssetDetail components with localized messages. - Refactored OrganizationUserColumns to use common translations for status and account types. - Fixed issues with displaying user names and roles in OrganizationAdmins component. - Enhanced CSV upload feedback in Step2AdminAccounts with localized messages. - Updated middleware to include new public path for invitation success. - Fixed organization user ID selection in AuthSessionSync to handle array correctly. --- messages/en/common/en_common.json | 20 +++++++++- messages/en/common/en_toast.json | 1 + messages/en/lesson/en_content.json | 19 ++++++++- messages/vi/common/vi_common.json | 18 ++++++++- messages/vi/common/vi_toast.json | 1 + messages/vi/lesson/vi_content.json | 19 ++++++++- src/components/tiptap/TiptapEditor.tsx | 4 +- .../tiptap/block/step/StepBlockComponent.tsx | 7 +++- .../tiptap/sidebar/TipTapSidebar.tsx | 6 +-- .../attempt/AssignmentSubmission.tsx | 16 ++++---- .../detail/dialog/SubmissionReviewDialog.tsx | 39 +++++++++++-------- .../detail/StudentClassroomDetails.tsx | 1 + .../components/overview/ClassroomOverview.tsx | 10 ++--- .../components/ui/ClassroomSubHeader.tsx | 12 +++++- .../components/upsert/CreateClassroom.tsx | 2 +- .../components/creator3d/Creator3D.tsx | 2 - .../components/creator3d/SceneActions.tsx | 3 +- .../components/OrganizationDashboard.tsx | 4 +- .../workspace-3d/Workspace3dLibrary.tsx | 18 +++++++++ .../list/SystemOrganizationColumn.tsx | 9 ++--- .../components/upsert/UpsertOrganization.tsx | 8 ++-- .../user/OrganizationUserColumns.tsx | 31 +++++---------- .../organization/types/organization.type.ts | 1 + .../panel/template/TemplateContent.tsx | 15 ++++--- .../sidebar/panel/upload/UploadContent.tsx | 14 ++++--- .../components/detail/AdminCourseDetail.tsx | 4 +- .../components/my-learning/MyLearning.tsx | 6 +-- .../components/my-learning/MyLearningHero.tsx | 2 +- .../list/OrganizationCurriculumList.tsx | 7 ++-- .../components/document/AssetDetail.tsx | 25 +++++++----- .../components/image/ImageAssets.tsx | 15 ++++--- .../quiz/components/player/QuizSidebar.tsx | 26 +++++++------ .../quiz/components/viewer/QuizAttempt.tsx | 2 +- .../detail/system/OrganizationAdmins.tsx | 6 +-- .../components/upsert/Step2AdminAccounts.tsx | 3 +- src/middleware.ts | 10 ++++- src/providers/AuthSessionSync.tsx | 2 +- 37 files changed, 255 insertions(+), 133 deletions(-) diff --git a/messages/en/common/en_common.json b/messages/en/common/en_common.json index e739a691e..684107f3b 100644 --- a/messages/en/common/en_common.json +++ b/messages/en/common/en_common.json @@ -1,6 +1,8 @@ { "common": { + "loading": "Loading...", "noData": "Not found", + "somethingWrong": "Something went wrong, please try again later.", "breadcrumb": { "home": "Home", "resource": "Resources", @@ -26,6 +28,8 @@ }, "button": { "create": "Create New", + "detail": "Detail", + "download": "Download", "createClass": "Add New Class", "camera": "Open Camera", "ready": "Ready", @@ -135,7 +139,9 @@ "exportRSA": "Export RSA", "exporting": "Exporting...", "downloadAndPrint": "Download & Print", - "upgrade": "Upgrade Plan" + "upgrade": "Upgrade Plan", + "uploadFile": "Upload File", + "exportGLB": "Export GLB" }, "message": { "courseCreateSuccess": "Course created successfully!", @@ -227,7 +233,8 @@ "score": "Score", "correctAnswer": "Correct Answer", "submissionDate": "Submission Date", - "studentGroup": "Student Group" + "studentGroup": "Student Group", + "quizDuration": "Duration" }, "paging": { "previous": "Previous", @@ -271,6 +278,15 @@ "inprogress": "In Progress", "locked": "Locked" }, + "status2": { + "active": "Active", + "inactive": "Inactive", + "pending": "Pending", + "approved": "Approved", + "rejected": "Rejected", + "cancelled": "Cancelled", + "expired": "Expired" + }, "orgUserStatus": { "active": "Active", "inactive": "Inactive" diff --git a/messages/en/common/en_toast.json b/messages/en/common/en_toast.json index a0d29a87d..e093aba8b 100644 --- a/messages/en/common/en_toast.json +++ b/messages/en/common/en_toast.json @@ -23,6 +23,7 @@ "removeItemFromCart": "Item removed from cart!", "clearCart": "Cart cleared!", "addToCart": "Item added to cart!", + "uploadFile": "Upload file(s) successfully!", "uploadCSV": "Upload CSV Successfully", "reorder": "Reordered Successfully", "copiedToClipboard": "Copied to clipboard!" diff --git a/messages/en/lesson/en_content.json b/messages/en/lesson/en_content.json index 80aecced1..d8a5b3e18 100644 --- a/messages/en/lesson/en_content.json +++ b/messages/en/lesson/en_content.json @@ -13,6 +13,23 @@ "createQuiz": "Create Quiz", "createAssignment": "Create Assignment" } - } + }, + "guide": "Guide", + "upload": "Upload", + "templates": "Templates", + "image": "Image", + "video": "Video", + "document": "Document", + "insertBlock": "Insert Block", + "buttonLink": "Button Link", + "step": "Step", + "quiz": "Quiz", + "note": "Note", + "format": "Format", + "size": "Size", + "resolution": "Resolution", + "createAt": "Created At", + "tag": "Tag", + "noTag": "No Tag" } } diff --git a/messages/vi/common/vi_common.json b/messages/vi/common/vi_common.json index 7031b9556..a79dc6a7a 100644 --- a/messages/vi/common/vi_common.json +++ b/messages/vi/common/vi_common.json @@ -1,6 +1,8 @@ { "common": { + "loading": "Đang tải...", "noData": "Không tìm thấy", + "somethingWrong": "Đã xảy ra lỗi. Vui lòng thử lại sau.", "breadcrumb": { "home": "Trang Chủ", "course": "Khóa Học", @@ -26,6 +28,8 @@ }, "button": { "create": "Tạo Mới", + "detail": "Chi Tiết", + "download": "Tải Xuống", "createClass": "Thêm Class mới", "camera": "Mở Camera", "ready": "Sẵn sàng", @@ -135,7 +139,9 @@ "exportRSA": "Xuất RSA", "exporting": "Đang Xuất...", "downloadAndPrint": "Tải Xuống & In", - "upgrade": "Nâng Cấp Gói" + "upgrade": "Nâng Cấp Gói", + "uploadFile": "Tải Lên Tệp", + "exportGLB": "Xuất GLB" }, "message": { "courseCreateSuccess": "Khóa học được tạo thành công!", @@ -178,6 +184,7 @@ "maxAge": "Tuổi tối đa", "actions": "Hành Động", "duration": "Thời Lượng", + "quizDuration": "Thời Gian Làm Bài", "empty": "Không tìm thấy kết quả.", "content": "Nội dung", "plan": "Gói", @@ -276,6 +283,15 @@ "inprogress": "Đang Diễn Ra", "locked": "Khóa" }, + "status2": { + "active": "Đang Hoạt Động", + "inactive": "Không Hoạt Động", + "pending": "Chưa Diễn Ra", + "approved": "Phê Duyệt", + "rejected": "Từ Chối", + "cancelled": "Đã hủy", + "expired": "Hết Hạn" + }, "orgUserStatus": { "active": "Đã xác thực", "inactive": "Chưa xác thực" diff --git a/messages/vi/common/vi_toast.json b/messages/vi/common/vi_toast.json index e31d816cd..ad98d33c0 100644 --- a/messages/vi/common/vi_toast.json +++ b/messages/vi/common/vi_toast.json @@ -23,6 +23,7 @@ "removeItemFromCart": "Đã xóa sản phẩm khỏi giỏ hàng!", "clearCart": "Đã xóa giỏ hàng!", "addToCart": "Đã thêm sản phẩm vào giỏ hàng!", + "uploadFile": "Đã tải tệp tin thành công!", "uploadCSV": "Đã tải CSV thành công", "reorder": "Đã sắp xếp lại thành công", "copiedToClipboard": "Đã sao chép vào clipboard!" diff --git a/messages/vi/lesson/vi_content.json b/messages/vi/lesson/vi_content.json index 725fc0eaa..90977fc02 100644 --- a/messages/vi/lesson/vi_content.json +++ b/messages/vi/lesson/vi_content.json @@ -13,6 +13,23 @@ "createQuiz": "Tạo Quiz", "createAssignment": "Tạo Bài Thực Hành" } - } + }, + "guide": "Hướng dẫn", + "upload": "Tải lên", + "templates": "Mẫu", + "image": "Hình ảnh", + "video": "Video", + "document": "Tài liệu", + "insertBlock": "Chèn khối", + "buttonLink": "Nút liên kết", + "step": "Bước", + "quiz": "Quiz", + "note": "Ghi chú", + "format": "Định dạng", + "size": "Kích thước", + "resolution": "Độ phân giải", + "createAt": "Ngày tạo", + "tag": "Thẻ", + "noTag": "Không có thẻ" } } diff --git a/src/components/tiptap/TiptapEditor.tsx b/src/components/tiptap/TiptapEditor.tsx index e7a5ec4d3..acad3ce6d 100644 --- a/src/components/tiptap/TiptapEditor.tsx +++ b/src/components/tiptap/TiptapEditor.tsx @@ -11,6 +11,7 @@ import { usePostLessonAssetsMutation } from '@/features/resource/lesson-asset/ap import { fileToBase64 } from '@/utils/index' import { toast } from 'sonner' import { useParams } from 'next/navigation' +import { useTranslations } from 'next-intl' interface TiptapEditorProps { content?: string @@ -19,6 +20,7 @@ interface TiptapEditorProps { } export default function TiptapEditor({ content, onChange, children }: TiptapEditorProps) { + const tc = useTranslations('toast') const { lessonId } = useParams() const editor = useTiptapEditor({ content, onChange, isEditable: true }) const [uploadFiles, { isLoading }] = usePostLessonAssetsMutation() @@ -52,7 +54,7 @@ export default function TiptapEditor({ content, onChange, children }: TiptapEdit lessonId: Number(lessonId), body: { lessonAssets } }).unwrap() - toast.success('Uploaded files successfully') + toast.success(tc('successMessage.uploadFile')) res.data.assets.forEach((asset: any) => { editor.chain().focus().setImage({ src: asset.assetUrl, alt: asset.name }).run() }) diff --git a/src/components/tiptap/block/step/StepBlockComponent.tsx b/src/components/tiptap/block/step/StepBlockComponent.tsx index 825e5e13f..2e9c30193 100644 --- a/src/components/tiptap/block/step/StepBlockComponent.tsx +++ b/src/components/tiptap/block/step/StepBlockComponent.tsx @@ -7,12 +7,14 @@ import { PostLessonResponseBody } from '@/features/resource/lesson-asset/types/l import { fileToBase64 } from '@/utils/index' import { NodeViewWrapper, NodeViewProps } from '@tiptap/react' import { ChevronLeft, ChevronRight, Loader2, Plus, Trash2, Upload, X } from 'lucide-react' +import { useTranslations } from 'next-intl' import Image from 'next/image' import { useParams } from 'next/navigation' import { useRef, useState } from 'react' import { toast } from 'sonner' export default function StepBlockComponent({ node, updateAttributes, editor }: NodeViewProps) { + const tc = useTranslations('toast') const { lessonId } = useParams() const { steps, currentStep } = node.attrs const stepsArray = Array.isArray(steps) ? steps : [] @@ -70,7 +72,8 @@ export default function StepBlockComponent({ node, updateAttributes, editor }: N updateStep('images', [...(step.images || []), ...uploaded]) - toast.success('Uploaded files successfully') + toast.success(tc('successMessage.uploadFile')) + event.target.value = '' } @@ -92,7 +95,7 @@ export default function StepBlockComponent({ node, updateAttributes, editor }: N const uploaded = await uploadLessonFiles(files) updateStep('images', [...(step.images || []), ...uploaded]) - toast.success('Uploaded files successfully') + toast.success(tc('successMessage.uploadFile')) } } diff --git a/src/components/tiptap/sidebar/TipTapSidebar.tsx b/src/components/tiptap/sidebar/TipTapSidebar.tsx index d32d4351e..dacafdebd 100644 --- a/src/components/tiptap/sidebar/TipTapSidebar.tsx +++ b/src/components/tiptap/sidebar/TipTapSidebar.tsx @@ -5,10 +5,11 @@ import { PanelContent, PanelKey, sidebarItems } from '@/features/resource/conten import BackButton from '@/components/shared/button/BackButton' import { useAppDispatch, useAppSelector } from '@/hooks/redux-hooks' import { setActivePanel } from '@/components/tiptap/slice/tiptapSlice' -import { useLocale } from 'next-intl' +import { useLocale, useTranslations } from 'next-intl' import { useParams } from 'next/navigation' export default function TipTapSidebar() { + const tContent = useTranslations('content') const dispatch = useAppDispatch() const activePanel = useAppSelector((state) => state.tiptap.activePanel) const locale = useLocale() @@ -24,7 +25,6 @@ export default function TipTapSidebar() { return (