diff --git a/messages/en/admin/en_admin.json b/messages/en/admin/en_admin.json
index 427d14c7d..a6c54547b 100644
--- a/messages/en/admin/en_admin.json
+++ b/messages/en/admin/en_admin.json
@@ -27,8 +27,9 @@
"plan": "Plan",
"resource": "Resource",
"operationCenter": "Operation Center",
- "organizationSubscription": "Organization Subscription",
- "contact": "Contact"
+ "organizationSubscription": "Organization",
+ "contact": "Contact",
+ "group": "Student Group"
},
"course_management": {
"title": "Resource Management"
diff --git a/messages/en/agent/en_agent.json b/messages/en/agent/en_agent.json
index 4cdc50f6d..32ec52931 100644
--- a/messages/en/agent/en_agent.json
+++ b/messages/en/agent/en_agent.json
@@ -6,6 +6,32 @@
"minutes": "minutes",
"accept": "Accept",
"generating": "Generating..."
+ },
+ "modelMaker": {
+ "title": "Model Maker",
+ "description": "A fast, easy way to create machine learning models for your sites, apps, and more – no expertise or coding required.",
+ "classCreate": "Create Classes",
+ "uploaded": "Uploaded: {images} images",
+ "newClass": "Enter new class name",
+ "model": {
+ "title": "Train Model",
+ "description": "Click the button below to train the model right on the web",
+ "testModel": "Test Model",
+ "tesDes": "The camera will automatically analyze the image.",
+ "export": "Export Model",
+ "identify": "Identifying..."
+ },
+ "microbit": {
+ "instruction": "Press \"Start\" to boot the system.",
+ "title": "Load your STEMify AI Model",
+ "suggest": "Upload your exported",
+ "drag": "Drag & drop your model.zip file here",
+ "click": "or click to browse",
+ "result": "RESULT",
+ "uploading": "Đang tải model…",
+ "reset": "Reset the AI recognition",
+ "video": "Video is zoomed in for better viewing."
+ }
}
}
}
diff --git a/messages/en/classroom/en_classroom.json b/messages/en/classroom/en_classroom.json
index c2f5c6c77..a9eb40e7e 100644
--- a/messages/en/classroom/en_classroom.json
+++ b/messages/en/classroom/en_classroom.json
@@ -3,7 +3,7 @@
"list": {
"header": "Classroom List",
"searchPlaceholder": "Search...",
- "selectCurriculumPlaceholder": "Select curriculum",
+ "selectCoursePlaceholder": "Select course",
"courses": "courses"
},
"update": {
@@ -71,35 +71,33 @@
"create": {
"header": "Create New Classroom",
"subheader": "Follow the steps below to set up your classroom",
- "step1": {
- "title": "Classroom Information",
- "subtitle": "Basic classroom details",
- "basicInfo": "Basic Information",
- "className": "Class Name",
- "classCode": "Class Code",
- "description": "Description",
- "descriptionPlaceholder": "Brief description of this classroom...",
- "gradeLevel": "Grade Level",
- "selectGrade": "Select Grade",
- "duration": "Duration",
- "selectDuration": "Select Duration",
- "startDate": "Start Date",
- "endDate": "End Date",
- "weeks": "Weeks",
- "custom": "Custom"
+ "basicInfo": "Basic Information",
+ "description": "Description",
+ "descriptionPlaceholder": "Brief description of this classroom...",
+ "duration": "Duration",
+ "selectDuration": "Select Duration",
+ "startDate": "Start Date",
+ "endDate": "End Date",
+ "weeks": "Weeks",
+ "custom": "Custom",
+ "groupList": "Group List",
+ "grade": "Grade"
+ },
+ "studentClassroom": {
+ "list": {
+ "title": "Student List",
+ "description": "Manage the students currently enrolled in this classroom.",
+ "number": "Number of students:",
+ "placeholder": "There is no students in the class."
},
- "step2": {
- "title": "Assignments",
- "subtitle": "Assign curriculum, teacher & students",
- "curriculumAndTeacher": "Curriculum & Teacher",
- "curriculum": "Curriculum",
- "chooseCurriculum": "Choose Curriculum",
- "teacher": "Teacher",
- "chooseTeacher": "Choose Teacher",
- "students": "Students",
- "selected": "Selected",
- "selectStudents": "Select Students for the Classroom",
- "searchStudent": "Search students by name or email"
+ "detail": {
+ "title": "Learning Profile",
+ "description": "Detailed progress and academic performance in the class.",
+ "quizScore": "Avg Quiz Score",
+ "asmScore": "Avg Assignment Score",
+ "quizTotal": "Total Quizzes",
+ "asmTotal": "Total Assignments",
+ "submitted": "submitted"
}
}
}
diff --git a/messages/en/common/en_common.json b/messages/en/common/en_common.json
index 80a05b487..b299db6ce 100644
--- a/messages/en/common/en_common.json
+++ b/messages/en/common/en_common.json
@@ -11,9 +11,15 @@
},
"button": {
"create": "Create New",
+ "createClass": "Add New Class",
+ "camera": "Open Camera",
+ "ready": "Ready",
+ "connect": "Connect to",
+ "disconnect": "Disconnect to",
"update": "Update",
"browse": "Browse",
"delete": "Delete",
+ "archive": "Archive",
"remove": "Remove",
"approve": "Approve",
"reject": "Reject",
@@ -62,7 +68,7 @@
"readBlogs": "Read Blogs",
"addKit": "Add Kit",
"print": "Print",
- "start": "Start Right Now",
+ "start": "Start",
"look": "Find Out More",
"shop": "SHOP NOW",
"publish": "Publish",
@@ -101,7 +107,9 @@
"close": "Close",
"createPlan": "Create Plan",
"addQuestion": "Add Question",
- "addCriterion": "Add Criterion"
+ "addCriterion": "Add Criterion",
+ "createGroup": "Create Group",
+ "cancelSubscription": "Cancel Subscription"
},
"message": {
"courseCreateSuccess": "Course created successfully!",
@@ -178,9 +186,10 @@
"grade": "Grade",
"teacher": "Teacher",
"numberOfStudents": "No. Students",
- "numberOfCourses": "No. Courses",
+ "numberOfLessons": "No. Lessons",
"accountType": "Account Type",
- "assignedDate": "Assigned Date"
+ "assignedDate": "Assigned Date",
+ "course": "Course"
},
"paging": {
"previous": "Previous",
@@ -221,7 +230,8 @@
"suspended": "Suspended",
"upcoming": "Upcoming",
"endsoon": "End Soon",
- "inprogress": "In Progress"
+ "inprogress": "In Progress",
+ "locked": "Locked"
},
"level": {
"all": "All Levels",
@@ -237,7 +247,9 @@
"student": "Student",
"teacher": "Teacher",
"member": "Member",
+ "organizationadmin": "Organization Admin",
"organization_admin": "Organization Admin"
- }
+ },
+ "grade": "Grade"
}
}
diff --git a/messages/en/common/en_toast.json b/messages/en/common/en_toast.json
index fee3d6b0a..a0d29a87d 100644
--- a/messages/en/common/en_toast.json
+++ b/messages/en/common/en_toast.json
@@ -2,7 +2,9 @@
"toast": {
"successMessage": {
"create": "Successfully created \"{title}\"!",
+ "createNoTitle": "Successfully created!",
"update": "Successfully updated \"{title}\"!",
+ "updateNoTitle": "Successfully updated!",
"delete": "Successfully deleted!",
"action": "Successfully {action} \"{title}\"",
"review": "Successfully submitted to review!",
@@ -22,7 +24,8 @@
"clearCart": "Cart cleared!",
"addToCart": "Item added to cart!",
"uploadCSV": "Upload CSV Successfully",
- "reorder": "Reordered Successfully"
+ "reorder": "Reordered Successfully",
+ "copiedToClipboard": "Copied to clipboard!"
},
"errorMessage": "An error occurred. Please try again. ",
"errorSpecific": {
@@ -33,6 +36,7 @@
"confirmMessage": {
"confirmLabel": "Are you sure?",
"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?",
"removeKit": "Are you sure you want to remove \"{title}\" from this course?",
"removeComponent": "Are you sure you want to remove component from this kit?",
diff --git a/messages/en/common/en_validation.json b/messages/en/common/en_validation.json
index bfe13db1c..1db32c208 100644
--- a/messages/en/common/en_validation.json
+++ b/messages/en/common/en_validation.json
@@ -114,6 +114,15 @@
"maxPoints": "Max points must be a positive number"
}
}
+ },
+ "quiz": {
+ "title": "Title is required",
+ "description": "Description is required",
+ "passingMarks": "Passing marks must be between 0 and 100",
+ "durationDays": "Duration (in days) must be a positive number",
+ "totalMarks": "Total marks must be a positive number",
+ "timeLimitMinutes": "Time limit (in minutes) must be a positive number",
+ "cooldownHours": "Cooldown period (in hours) must be a positive number"
}
}
}
diff --git a/messages/en/dashboard/en_dashboard.json b/messages/en/dashboard/en_dashboard.json
index 2c30f0f83..873f22080 100644
--- a/messages/en/dashboard/en_dashboard.json
+++ b/messages/en/dashboard/en_dashboard.json
@@ -4,14 +4,27 @@
"submission": "Submissions",
"passRate": "Pass Rate",
"passed": "Passed",
- "failed": "Not Passed",
+ "failed": "Not Pass",
"score": "Average Score",
"overview": {
"title": "Dashboard Overview",
"subTitle": "Track learning progress and quiz performance",
"quizStat": "Quiz Statistic",
+ "performance": "Performance Overview",
"tooltip": "Identify students with the slowest learning progress by AI.",
"asmStat": "Assignment Statistic",
+ "legendSummary": {
+ "quizPass": "Quiz Pass",
+ "asmPass": "Asm Pass",
+ "notPass": "Not Pass"
+ },
+ "histogram": {
+ "title": "Course Histogram",
+ "studentCount": "Student Count",
+ "scoreDistribution": "Score Distribution",
+ "number": "Number of Students",
+ "scoreRange": "Score Range"
+ },
"ungraded": {
"title": "Ungraded Assignment",
"pending": "pending",
diff --git a/messages/en/header/en_header.json b/messages/en/header/en_header.json
index 4f6c77511..bff1f9f2e 100644
--- a/messages/en/header/en_header.json
+++ b/messages/en/header/en_header.json
@@ -10,6 +10,7 @@
"signIn": "Sign In",
"classroom": "Classroom",
"overview": "Overview",
+ "student": "Student",
"quiz": "Quiz",
"assignment": "Assignment",
"course": "Course",
diff --git a/messages/en/header/en_tableHeader.json b/messages/en/header/en_tableHeader.json
index 39aec1ebf..6a7bcb35b 100644
--- a/messages/en/header/en_tableHeader.json
+++ b/messages/en/header/en_tableHeader.json
@@ -10,6 +10,8 @@
"delete": "Delete",
"disable": "Disable",
"name": "Name",
+ "studentName": "Student Name",
+ "action": "Action",
"code": "Code",
"image": "Image",
"noImage": "No Image",
diff --git a/messages/en/home/en_home.json b/messages/en/home/en_home.json
index 7cd1094de..5b068f130 100644
--- a/messages/en/home/en_home.json
+++ b/messages/en/home/en_home.json
@@ -50,5 +50,35 @@
"title": "Do you still have any questions?",
"description": "Book a meeting to learn about our offline materials. We will contact you for customer care.",
"emailPlaceholder": "Enter your email"
+ },
+ "footer": {
+ "infoForParents": {
+ "title": "Information for Parents",
+ "description": "STEMIFY Education is an online STEM learning platform for students in grades 3-8, offering courses and resources that help develop critical thinking, problem-solving, and creativity through hands-on learning."
+ },
+ "info": {
+ "title": "Information",
+ "aboutUs": "About Us",
+ "blog": "Blog",
+ "careers": "Careers",
+ "press": "Press",
+ "contact": "Contact",
+ "home": "Home"
+ },
+ "education": {
+ "title": "Education",
+ "programs": "Programs",
+ "team": "Team",
+ "partners": "Partners",
+ "resources": "Resources"
+ },
+ "help": {
+ "title": "Help",
+ "support": "Support Center",
+ "faqs": "Frequently Asked Questions",
+ "termsOfService": "Terms of Service",
+ "privacyPolicy": "Privacy Policy",
+ "contactUs": "Contact Us"
+ }
}
}
diff --git a/messages/en/organization/en_organization.json b/messages/en/organization/en_organization.json
index 14967151d..d54dd03b9 100644
--- a/messages/en/organization/en_organization.json
+++ b/messages/en/organization/en_organization.json
@@ -9,6 +9,10 @@
"noData": "No organization data available.",
"noSubscription": "No subscriptions found for this organization.",
"header": "Organization Details",
+ "status": "Status",
+ "description": "Description",
+ "createdDate": "Created Date",
+ "lastModifiedDate": "Last Modified Date",
"organizationType": "Organization Type",
"createdAt": "Created At",
"updatedAt": "Updated At",
@@ -37,7 +41,31 @@
"annual": "Annual",
"semiAnnual": "Semi-Annual",
"includeCurriculum": "Included Curriculum(s)",
- "course": "Course(s)"
+ "course": "Course(s)",
+ "currentPlan": {
+ "title": "Current Plan",
+ "packageDetails": "Package Details",
+ "pendingActivation": "This subscription will be activated on",
+ "expiredOn": "Expired On",
+ "annual": "Annual",
+ "semiAnnual": "Semi-Annual",
+ "monthRemaing": "month(s) remaining",
+ "of": "of",
+ "subscriptionPeriod": "Subscription Period",
+ "assignedLicenses": "Assigned Licenses",
+ "licenseRemaing": "license(s) remaining",
+ "totalStudent": "Total Student",
+ "seatRemaining": "seat(s) remaining",
+ "totalTeacher": "Total Teacher",
+ "totalCurricula": "Total Curricula",
+ "includingCurricula": "Including {curriculum} Curricula"
+ },
+ "curricula": {
+ "includedCurricula": "Included Curricula",
+ "description": "Courses and learning materials available in your subscription",
+ "age": "Age",
+ "courses": "Courses"
+ }
},
"license": {
"label": "License(s)",
@@ -53,7 +81,19 @@
"role": "Role",
"status": "Status",
"assignedAt": "Assigned At",
- "notSet": "Not set"
+ "notSet": "Not set",
+ "userManagement": {
+ "header": "User Management",
+ "description": "Manage users and pending invitations",
+ "searchUser": "Search by name or email",
+ "accountType": "Account Type",
+ "active": "Active",
+ "pending": "Pending"
+ }
+ },
+ "user": {
+ "title": "Organization Members",
+ "noData": "No members found for this organization."
}
},
"form": {
@@ -70,6 +110,7 @@
"subscription": {
"header": "Create Organization Subscription",
"subHeader": "Follow the steps to set up your organization",
+ "months": " months",
"step1": {
"title": "Configure Subscription",
"description": "Select plan and options"
@@ -148,7 +189,7 @@
},
"license": {
"header": "Create Organization Account(s)",
- "subHeader": "New accounts will be created for this organization. The account will automatically be assigned a valid license from this subscription.",
+ "description": "New accounts will be created for this organization. The account will automatically be assigned a valid license from this subscription.",
"downloadCSVTemplate": "Download CSV Template",
"uploadCSV": "Select a CSV file to upload",
"dragAndDrop": "Drag and drop your CSV file here",
@@ -158,6 +199,50 @@
"pleaseUploadCSV": "Please upload a CSV file",
"uploadSuccess": "Users have been successfully invited and licenses assigned!",
"userType": "User Type"
+ },
+ "group": {
+ "title": "Organization Student Groups",
+ "subTitle": "Manage your organization groups",
+ "noData": "No groups found for this organization.",
+ "groupCode": "Group Code:",
+ "groupName": "Group Name:",
+ "numberOfStudents": "Number of Students: {quantity}",
+ "groupList": "Group List",
+ "createdDate": "Created Date",
+ "totalStudents": "Total Students",
+ "attendance": "Attendance",
+ "step1": {
+ "title": "Step 1: Select Students",
+ "description": "Choose students to include in the groups",
+ "numberOfStudents": "Number of Students",
+ "gradeLevel": "Grade Level",
+ "gradeLevelPlaceholder": "Select Grade Level",
+ "studentList": "Student List",
+ "selected": "Selected"
+ },
+ "step2": {
+ "title": "Step 2: Create Groups",
+ "numberOfStudents": "Number of Students",
+ "studentsPerGroup": "Students per Group",
+ "gradeLevel": "Grade Level",
+ "groupList": "Group List",
+ "name": "Name",
+ "studentCount": "Student Count"
+ }
+ },
+ "curriculum": {
+ "curriculum": "Curriculum",
+ "title": "Curriculum List",
+ "noData": "No curriculum found for this organization.",
+ "noResultsForFilter": "No results found for the selected filter.",
+ "courseCount": "{count} courses",
+ "startDate": "Start Date",
+ "endDate": "End Date",
+ "courses": "Courses",
+ "filterByStatus": "Filter by Status",
+ "showing": "Showing",
+ "results": "results",
+ "all": "All"
}
}
}
diff --git a/messages/en/quiz/en_quiz.json b/messages/en/quiz/en_quiz.json
index ee45a5a63..25f2c4b80 100644
--- a/messages/en/quiz/en_quiz.json
+++ b/messages/en/quiz/en_quiz.json
@@ -14,13 +14,15 @@
"mins": "min(s)"
},
"answerTable": {
+ "submitted": "Submitted at:",
"head": "Learner",
- "point": "point:",
+ "point": "Point:",
"accuracy": "Accuracy",
"answered": "Answered",
"question": "Questions",
"correct": "Correct",
"incorrect": "Incorrect",
+ "skip": "Skip",
"correctAnswer": "Correct Answer"
},
"questionTab": {
@@ -36,11 +38,14 @@
"settings": "Quiz Settings",
"form": {
"title": "Title",
+ "titlePlaceholder": "Enter Quiz Title",
"description": "Description",
- "timeLimit": "Time Limit (minutes)",
+ "descriptionPlaceholder": "Enter Quiz Description",
+ "timeLimitMinutes": "Time Limit (minutes)",
+ "durationDays": "Duration (days)",
+ "cooldownHours": "Cooldown (hours)",
"totalMarks": "Total Marks",
"passingMarks": "Passing Marks",
- "duration": "Duration (days)",
"updateQuizInfo": "Update Quiz Info",
"question": {
"question": "Question",
diff --git a/messages/en/subscription/en_subscription.json b/messages/en/subscription/en_subscription.json
index 3d16e0549..8393d66d1 100644
--- a/messages/en/subscription/en_subscription.json
+++ b/messages/en/subscription/en_subscription.json
@@ -9,11 +9,26 @@
"organizationSubscriptionDescription": "Browse and manage all organization subscriptions registered on the platform.",
"placeholder": {
"search": "Search organizations by name...",
- "status": "Filter by status"
- }
+ "status": "Filter by status",
+ "billingCycle": "Billing Cycle"
+ },
+ "total": "Total",
+ "totalDescription": "All subscriptions in system",
+ "active": "Active",
+ "activeDescription": "Currently active subscriptions",
+ "expired": "Expired",
+ "expiredDescription": "Ended or canceled subscriptions",
+ "pending": "Pending",
+ "pendingDescription": "Awaiting approval",
+ "searchSubscription": "Search subscriptions...",
+ "status": "Status",
+ "annual": "Annual",
+ "semiAnnual": "Semi-Annual"
},
"detail": {
"title": "Organization Details",
+ "overview": "Subscription Overview",
+ "overViewDescription": "Manage your organization's subscription and users",
"currentPlan": "Current Plan",
"StartDate": "Start Date",
"ExpiredDate": "Expired Date",
diff --git a/messages/vi/admin/vi_admin.json b/messages/vi/admin/vi_admin.json
index 76e752783..ecc3f365b 100644
--- a/messages/vi/admin/vi_admin.json
+++ b/messages/vi/admin/vi_admin.json
@@ -27,8 +27,9 @@
"plan": "Gói",
"resource": "Tài nguyên",
"operationCenter": "Hệ thống",
- "organizationSubscription": "Các Gói đăng ký tổ chức",
- "contact": "Liên hệ"
+ "organizationSubscription": "Tổ chức",
+ "contact": "Liên hệ",
+ "group": "Nhóm học sinh"
},
"course_management": {
"title": "Quản lý tài nguyên",
diff --git a/messages/vi/agent/vi_agent.json b/messages/vi/agent/vi_agent.json
index 61a3212c3..e1b771e37 100644
--- a/messages/vi/agent/vi_agent.json
+++ b/messages/vi/agent/vi_agent.json
@@ -1,11 +1,37 @@
{
"agent": {
"section": {
- "title": "Nội dung bài học (AI)",
+ "title": "Tạo Mục bằng AI",
"duration": "Thời lượng",
"minutes": "phút",
"accept": "Chấp nhận",
"generating": "Đang tạo..."
+ },
+ "modelMaker": {
+ "title": "Trình Tạo Mô Hình",
+ "description": "Một cách nhanh chóng và dễ dàng để tạo mô hình máy học cho website, ứng dụng và nhiều hơn nữa – không cần chuyên môn hay viết mã.",
+ "classCreate": "Tạo Nhóm (Class)",
+ "uploaded": "Đã tải lên: {images} ảnh",
+ "newClass": "Nhập tên nhóm mới",
+ "model": {
+ "title": "Huấn Luyện Mô Hình",
+ "description": "Nhấn nút bên dưới để huấn luyện mô hình trực tiếp trên web",
+ "testModel": "Kiểm Tra Mô Hình",
+ "tesDes": "Camera sẽ tự động phân tích hình ảnh.",
+ "export": "Xuất Mô Hình",
+ "identify": "Đang nhận diện..."
+ },
+ "microbit": {
+ "instruction": "Nhấn \"Bắt đầu\" để khởi động hệ thống.",
+ "title": "Tải Mô Hình STEMify AI của bạn",
+ "suggest": "Tải lên file",
+ "drag": "Kéo & thả file model.zip vào đây",
+ "click": "hoặc nhấn để chọn file",
+ "result": "KẾT QUẢ",
+ "uploading": "Đang tải mô hình…",
+ "reset": "Đặt lại quá trình nhận diện AI",
+ "video": "Video được phóng to để dễ quan sát hơn."
+ }
}
}
}
diff --git a/messages/vi/classroom/vi_classroom.json b/messages/vi/classroom/vi_classroom.json
index a255c54d3..2d5e14f33 100644
--- a/messages/vi/classroom/vi_classroom.json
+++ b/messages/vi/classroom/vi_classroom.json
@@ -3,7 +3,7 @@
"list": {
"header": "Danh sách lớp học",
"searchPlaceholder": "Tìm kiếm...",
- "selectCurriculumPlaceholder": "Chọn chương trình học",
+ "selectCoursePlaceholder": "Chọn chương trình học",
"courses": "khóa học"
},
"update": {
@@ -70,36 +70,33 @@
"create": {
"header": "Tạo lớp học mới",
"subheader": "Làm theo các bước dưới đây để thiết lập lớp học của bạn",
- "step1": {
- "title": "Thông tin lớp học",
- "subtitle": "Chi tiết cơ bản về lớp học",
- "basicInfo": "Thông tin cơ bản",
- "className": "Tên lớp học",
- "classCode": "Mã lớp học",
- "description": "Mô tả",
- "descriptionPlaceholder": "Mô tả ngắn gọn về lớp học này...",
- "gradeLevel": "Cấp lớp",
- "grade": "Lớp",
- "selectGrade": "Chọn cấp lớp",
- "duration": "Thời lượng",
- "selectDuration": "Chọn thời lượng",
- "startDate": "Ngày bắt đầu",
- "endDate": "Ngày kết thúc",
- "weeks": "Tuần",
- "custom": "Tùy chỉnh"
+ "basicInfo": "Thông tin cơ bản",
+ "duration": "Thời lượng",
+ "selectDuration": "Chọn thời lượng",
+ "startDate": "Ngày bắt đầu",
+ "endDate": "Ngày kết thúc",
+ "weeks": "Tuần",
+ "custom": "Tùy chỉnh",
+ "description": "Mô tả",
+ "descriptionPlaceholder": "Mô tả ngắn gọn về lớp học này...",
+ "groupList": "Danh sách nhóm",
+ "grade": "Khối"
+ },
+ "studentClassroom": {
+ "list": {
+ "title": "Danh sách học sinh",
+ "description": "Quản lý các học sinh hiện có trong lớp học này.",
+ "number": "Sĩ số:",
+ "placeholder": "Chưa có học sinh nào trong lớp này."
},
- "step2": {
- "title": "Bài tập",
- "subtitle": "Chỉ định chương trình học, giáo viên & học sinh",
- "curriculumAndTeacher": "Chương trình học & Giáo viên",
- "curriculum": "Chương trình học",
- "chooseCurriculum": "Chọn chương trình học",
- "teacher": "Giáo viên",
- "chooseTeacher": "Chọn giáo viên",
- "students": "Học sinh",
- "selected": "Đã chọn",
- "selectStudents": "Chọn học sinh cho lớp học",
- "searchStudent": "Tìm kiếm học sinh theo tên hoặc email"
+ "detail": {
+ "title": "Hồ sơ học tập",
+ "description": "Chi tiết tiến độ và kết quả học tập trong lớp.",
+ "quizScore": "Điểm Quiz trung bình",
+ "asmScore": "Điểm Bài Tập trung bình",
+ "quizTotal": "Tổng số bài kiểm tra",
+ "asmTotal": "Tổng số Bài Tập",
+ "submitted": "đã nộp"
}
}
}
diff --git a/messages/vi/common/vi_common.json b/messages/vi/common/vi_common.json
index 435904dda..5f8c00763 100644
--- a/messages/vi/common/vi_common.json
+++ b/messages/vi/common/vi_common.json
@@ -11,9 +11,14 @@
},
"button": {
"create": "Tạo Mới",
+ "createClass": "Thêm Class mới",
+ "camera": "Mở Camera",
+ "ready": "Sẵn sàng",
+ "connect": "Kết nối",
"update": "Cập Nhật",
"browse": "Tải Lên",
"delete": "Xóa",
+ "archive": "Lưu Trữ",
"remove": "Loại bỏ",
"approve": "Phê Duyệt",
"reject": "Từ Chối",
@@ -64,7 +69,7 @@
"publish": "Xuất Bản",
"print": "In",
"share": "Chia sẻ",
- "start": "Bắt Đầu Ngay",
+ "start": "Bắt Đầu",
"look": "Tìm hiểu thêm",
"addToCart": "Thêm Vào Giỏ Hàng",
"quickView": "Xem Nhanh",
@@ -101,7 +106,9 @@
"seeFeedback": "Xem Phản Hồi",
"close": "Đóng",
"addQuestion": "Thêm Câu Hỏi",
- "addCriterion": "Thêm Tiêu Chí"
+ "addCriterion": "Thêm Tiêu Chí",
+ "createGroup": "Tạo Nhóm",
+ "cancelSubscription": "Hủy Đăng Ký"
},
"message": {
"courseCreateSuccess": "Khóa học được tạo thành công!",
@@ -167,8 +174,8 @@
"netAmount": "Số Tiền Sau Giảm",
"lastModified": "Lần Chỉnh Sửa Cuối",
"grossAmount": "Tổng Số Tiền",
- "studentSeats": "Ghế Học Sinh",
- "teacherSeats": "Ghế Giáo Viên",
+ "studentSeats": "Học Sinh",
+ "teacherSeats": "Giáo Viên",
"avatar": "Ảnh Đại Diện",
"action": "Hành Động",
"createPlan": "Tạo Gói",
@@ -177,9 +184,10 @@
"grade": "Lớp",
"teacher": "Giáo Viên",
"numberOfStudents": "Số Học Sinh",
- "numberOfCourses": "Số Khóa Học",
+ "numberOfLessons": "Số Bài Học",
"accountType": "Loại Tài Khoản",
- "assignedDate": "Ngày Gán"
+ "assignedDate": "Ngày Gán",
+ "course": "Khóa Học"
},
"paging": {
"previous": "Trước",
@@ -211,14 +219,14 @@
"approved": "Phê Duyệt",
"rejected": "Từ Chối",
"draft": "Bản Nháp",
- "published": "Xuất Bản",
- "unpublished": "Chưa Xuất Bản",
+ "published": "Hoạt Động",
+ "unpublished": "Không Hoạt Động",
"archived": "Lưu Trữ",
"inProgress": "Đang Tiến Hành",
"submitted": "Gửi",
"deleted": "Đã Xóa",
"expired": "Hết Hạn",
- "cancelled": "Hủy",
+ "cancelled": "Đã hủy",
"failed": "Thất Bại",
"resolved": "Giải Quyết",
"completed": "Hoàn Thành",
@@ -226,7 +234,8 @@
"suspended": "Tạm Ngưng",
"upcoming": "Sắp Diễn Ra",
"endsoon": "Kết Thúc Sớm",
- "inprogress": "Đang Diễn Ra"
+ "inprogress": "Đang Diễn Ra",
+ "locked": "Khóa"
},
"accountType": {
"accountTypeLabel": "Loại Tài Khoản",
@@ -238,6 +247,7 @@
"student": "Học Sinh",
"organization_admin": "Quản Trị Viên Tổ Chức",
"organizationadmin": "Quản Trị Viên Tổ Chức"
- }
+ },
+ "grade": "Khối"
}
}
diff --git a/messages/vi/common/vi_toast.json b/messages/vi/common/vi_toast.json
index 8f35fe687..e36ebc6c7 100644
--- a/messages/vi/common/vi_toast.json
+++ b/messages/vi/common/vi_toast.json
@@ -2,7 +2,9 @@
"toast": {
"successMessage": {
"create": "Đã tạo \"{title}\" thành công!",
+ "createNoTitle": "Đã tạo thành công!",
"update": "Đã cập nhật \"{title}\" thành công!",
+ "updateNoTitle": "Đã cập nhật thành công!",
"delete": "Đã xóa thành công!",
"action": "Đã {action} \"{title}\" thành công",
"review": "Đã gửi để xét duyệt thành công!",
@@ -22,7 +24,8 @@
"clearCart": "Đã xóa giỏ hàng!",
"addToCart": "Đã thêm sản phẩm vào giỏ hàng!",
"uploadCSV": "Đã tải CSV thành công",
- "reorder": "Đã sắp xếp lại thành công"
+ "reorder": "Đã sắp xếp lại thành công",
+ "copiedToClipboard": "Đã sao chép vào clipboard!"
},
"errorMessage": "Đã xảy ra lỗi. Vui lòng thử lại.",
"errorSpecific": {
@@ -33,6 +36,7 @@
"confirmMessage": {
"confirmLabel": "Bạn có chắc chắn?",
"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?",
"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?",
diff --git a/messages/vi/common/vi_validation.json b/messages/vi/common/vi_validation.json
index ab8433c82..3410d7846 100644
--- a/messages/vi/common/vi_validation.json
+++ b/messages/vi/common/vi_validation.json
@@ -108,6 +108,15 @@
"durationDays": "Thời gian làm bài phải là một số dương",
"cooldownHours": "Thời gian chờ giữa các lần làm bài phải là một số không âm",
"questionsMin": "Phải có ít nhất {min} câu hỏi trong bài tập"
+ },
+ "quiz": {
+ "title": "Hãy nhập tiêu đề",
+ "description": "Hãy nhập mô tả",
+ "passingMarks": "Điểm đạt phải nằm trong khoảng từ 0 đến 100",
+ "durationDays": "Thời gian làm bài phải là một số dương",
+ "totalMarks": "Tổng điểm phải là một số dương",
+ "timeLimitMinutes": "Giới hạn thời gian (tính bằng phút) phải là một số dương",
+ "cooldownHours": "Thời gian chờ giữa các lần làm bài (tính bằng giờ) phải là một số không âm"
}
}
}
diff --git a/messages/vi/dashboard/vi_dashboard.json b/messages/vi/dashboard/vi_dashboard.json
index a498c8a1d..36cc33b7d 100644
--- a/messages/vi/dashboard/vi_dashboard.json
+++ b/messages/vi/dashboard/vi_dashboard.json
@@ -10,8 +10,21 @@
"title": "Tổng quan lớp học",
"subTitle": "Theo dõi tiến trình học tập và hiệu suất làm bài",
"quizStat": "Thống kê Quiz",
+ "performance": "Tổng quan về hiệu suất",
"tooltip": "Xác định học viên có tiến độ học tập chậm nhất bằng AI.",
"asmStat": "Thống kê Bài tập",
+ "legendSummary": {
+ "quizPass": "Quiz đạt",
+ "asmPass": "Bài tập đạt",
+ "notPass": "Chưa đạt"
+ },
+ "histogram": {
+ "title": "Histogram Khóa học",
+ "studentCount": "Số lượng học sinh đạt",
+ "scoreDistribution": "Phân phối điểm số học sinh",
+ "number": "Số lượng học sinh",
+ "scoreRange": "Khoảng điểm"
+ },
"ungraded": {
"title": "Bài tập chưa chấm",
"pending": "đang chờ",
diff --git a/messages/vi/header/vi_header.json b/messages/vi/header/vi_header.json
index 4576c3912..44394ab22 100644
--- a/messages/vi/header/vi_header.json
+++ b/messages/vi/header/vi_header.json
@@ -9,6 +9,7 @@
"gift": "Quà Tặng",
"classroom": "Lớp Học",
"overview": "Tổng Quan",
+ "student": "Học Sinh",
"quiz": "Bài Kiểm Tra",
"assignment": "Bài Tập",
"course": "Khóa Học",
diff --git a/messages/vi/header/vi_tableHeader.json b/messages/vi/header/vi_tableHeader.json
index 93841b9e6..17ba3bff7 100644
--- a/messages/vi/header/vi_tableHeader.json
+++ b/messages/vi/header/vi_tableHeader.json
@@ -10,6 +10,8 @@
"delete": "Xóa",
"disable": "Vô hiệu hóa",
"name": "Tên",
+ "studentName": "Tên học sinh",
+ "action": "Thao tác",
"code": "Mã",
"image": "Hình ảnh",
"noImage": "Không có hình",
diff --git a/messages/vi/home/vi_home.json b/messages/vi/home/vi_home.json
index 8c147c052..0e99351cb 100644
--- a/messages/vi/home/vi_home.json
+++ b/messages/vi/home/vi_home.json
@@ -49,5 +49,35 @@
"title": "Bạn vẫn còn câu hỏi nào không?",
"description": "Đặt lịch hẹn để tìm hiểu về tài liệu ngoại tuyến của chúng tôi. Chúng tôi sẽ liên hệ với bạn để chăm sóc khách hàng.",
"emailPlaceholder": "Nhập email của bạn"
+ },
+ "footer": {
+ "infoForParents": {
+ "title": "Thông tin cho phụ huynh",
+ "description": "STEMIFY Education là nền tảng học tập STEM trực tuyến dành cho học sinh từ lớp 3–8, cung cấp các khóa học và tài nguyên giúp phát triển kỹ năng tư duy phản biện, giải quyết vấn đề và sáng tạo thông qua học tập thực hành."
+ },
+ "info": {
+ "title": "Thông tin",
+ "aboutUs": "Về chúng tôi",
+ "blog": "Blog",
+ "careers": "Cơ hội nghề nghiệp",
+ "press": "Truyền thông",
+ "contact": "Liên hệ",
+ "home": "Trang chủ"
+ },
+ "education": {
+ "title": "Giáo dục",
+ "programs": "Chương trình",
+ "team": "Đội ngũ",
+ "partners": "Đối tác",
+ "resources": "Tài nguyên"
+ },
+ "help": {
+ "title": "Trợ giúp",
+ "support": "Hỗ trợ",
+ "faqs": "Câu hỏi thường gặp",
+ "termsOfService": "Điều khoản dịch vụ",
+ "privacyPolicy": "Chính sách bảo mật",
+ "contactUs": "Liên hệ"
+ }
}
}
diff --git a/messages/vi/organization/vi_organization.json b/messages/vi/organization/vi_organization.json
index 9fb46bc0d..97665832f 100644
--- a/messages/vi/organization/vi_organization.json
+++ b/messages/vi/organization/vi_organization.json
@@ -9,7 +9,11 @@
"noData": "Không có dữ liệu tổ chức nào.",
"noSubscription": "Không có gói đăng ký nào cho tổ chức này.",
"header": "Chi tiết tổ chức",
+ "status": "Trạng thái",
+ "description": "Mô tả",
"organizationType": "Loại tổ chức",
+ "createdDate": "Ngày tạo",
+ "lastModifiedDate": "Ngày cập nhật",
"createdAt": "Ngày tạo",
"updatedAt": "Ngày cập nhật",
"package": "Gói",
@@ -37,7 +41,31 @@
"includeCurriculum": "Chương trình giảng dạy",
"annual": "Hàng năm",
"semiAnnual": "Nửa năm",
- "course": "Khóa học"
+ "course": "Khóa học",
+ "currentPlan": {
+ "title": "Gói hiện tại",
+ "packageDetails": "Chi tiết gói",
+ "pendingActivation": "Gói đăng ký này sẽ được kích hoạt vào",
+ "expiredOn": "Hết hạn vào",
+ "annual": "Hàng năm",
+ "semiAnnual": "Nửa năm",
+ "monthRemaing": "tháng còn lại",
+ "of": "trên",
+ "subscriptionPeriod": "Thời gian gói đăng ký",
+ "assignedLicenses": "Suất sử dụng đã cấp",
+ "licenseRemaing": "suất sử dụng còn lại",
+ "totalStudent": "Tổng số học sinh",
+ "seatRemaining": "suất còn lại",
+ "totalTeacher": "Tổng số giáo viên",
+ "totalCurricula": "Tổng số khung chương trình",
+ "includingCurricula": "Bao gồm {curriculum} chương trình giảng dạy"
+ },
+ "curricula": {
+ "includedCurricula": "Chương trình giảng dạy bao gồm",
+ "description": "Khóa học và tài liệu học tập có trong gói đăng ký của bạn",
+ "age": "Độ tuổi",
+ "courses": "Khóa học"
+ }
},
"license": {
"label": "suất sử dụng",
@@ -52,7 +80,19 @@
"role": "Vai trò",
"status": "Trạng thái",
"assignedAt": "Ngày được cấp",
- "notSet": "Chưa đặt"
+ "notSet": "Chưa đặt",
+ "userManagement": {
+ "header": "Quản lý người dùng",
+ "description": "Quản lý người dùng và lời mời đang chờ",
+ "searchUser": "Tìm kiếm theo tên hoặc email",
+ "accountType": "Loại tài khoản",
+ "active": "Đang hoạt động",
+ "pending": "Chưa xác nhận"
+ }
+ },
+ "user": {
+ "title": "Thành viên tổ chức",
+ "noData": "Không tìm thấy thành viên nào cho tổ chức này."
}
},
"form": {
@@ -69,6 +109,7 @@
"subscription": {
"header": "Tạo gói đăng ký tổ chức",
"subHeader": "Làm theo các bước để thiết lập tổ chức của bạn",
+ "months": " tháng",
"step1": {
"title": "Cấu hình gói đăng ký",
"description": "Chọn gói và các tùy chọn"
@@ -157,6 +198,50 @@
"pleaseUploadCSV": "Vui lòng tải lên tệp CSV",
"uploadSuccess": "Đã gửi lời mời thành công!",
"userType": "Loại người dùng"
+ },
+ "group": {
+ "title": "Nhóm học sinh của tổ chức",
+ "subTitle": "Quản lý các nhóm của tổ chức bạn",
+ "noData": "Không tìm thấy nhóm nào cho tổ chức này.",
+ "groupCode": "Mã nhóm:",
+ "groupName": "Tên nhóm:",
+ "numberOfStudents": "Số lượng: {quantity} học sinh",
+ "groupList": "Danh sách học sinh",
+ "createdDate": "Ngày tạo",
+ "totalStudents": "Tổng số học sinh",
+ "attendance": "Tham gia",
+ "step1": {
+ "title": "Bước 1: Chọn học sinh cho nhóm",
+ "description": "Chọn học sinh để thêm vào nhóm",
+ "numberOfStudents": "Số lượng học sinh",
+ "gradeLevel": "Cấp lớp",
+ "gradeLevelPlaceholder": "Chọn cấp lớp",
+ "studentList": "Danh sách học sinh",
+ "selected": "Đã chọn"
+ },
+ "step2": {
+ "title": "Bước 2: Tạo nhóm",
+ "numberOfStudents": "Số học sinh",
+ "studentsPerGroup": "Số học sinh mỗi nhóm",
+ "gradeLevel": "Cấp lớp",
+ "groupList": "Danh sách nhóm",
+ "name": "Tên",
+ "studentCount": "Số học sinh"
+ }
+ },
+ "curriculum": {
+ "curriculum": "Khung chương trình",
+ "title": "Danh sách khung chương trình",
+ "noData": "Không tìm thấy chương trình giảng dạy nào cho tổ chức này.",
+ "noResultsForFilter": "Không tìm thấy kết quả nào cho bộ lọc đã chọn.",
+ "courseCount": "{count} khóa học",
+ "startDate": "Ngày kích hoạt",
+ "endDate": "Ngày hết hạn",
+ "courses": "Khóa học",
+ "filterByStatus": "Lọc theo trạng thái",
+ "showing": "Hiển thị",
+ "results": "kết quả",
+ "all": "Tất cả"
}
}
}
diff --git a/messages/vi/quiz/vi_quiz.json b/messages/vi/quiz/vi_quiz.json
index 3c76d4e55..1c080ab6e 100644
--- a/messages/vi/quiz/vi_quiz.json
+++ b/messages/vi/quiz/vi_quiz.json
@@ -14,13 +14,15 @@
"mins": "phút"
},
"answerTable": {
+ "submitted": "Nộp vào: ",
"head": "Học viên",
- "point": "điểm:",
+ "point": "Điểm:",
"accuracy": "Điểm trung bình",
"answered": "Đã trả lời",
"question": "Câu hỏi",
"correct": "Đúng",
"incorrect": "Sai",
+ "skip": "Bỏ qua",
"correctAnswer": "Đáp án đúng"
},
"questionTab": {
@@ -33,14 +35,17 @@
"upsert": {
"create": "Tạo bài kiểm tra",
"update": "Cập nhật bài kiểm tra",
- "settings": "Cài đặt bài kiểm tra",
+ "settings": "Bài kiểm tra",
"form": {
"title": "Tiêu đề",
+ "titlePlaceholder": "Nhập tiêu đề bài kiểm tra",
"description": "Mô tả",
- "timeLimit": "Thời gian (phút)",
+ "descriptionPlaceholder": "Nhập mô tả bài kiểm tra",
+ "durationDays": "Thời lượng (ngày)",
+ "timeLimitMinutes": "Thời gian (phút)",
+ "cooldownHours": "Thời gian chờ (giờ)",
"totalMarks": "Tổng điểm",
"passingMarks": "Điểm đạt",
- "duration": "Thời lượng (ngày)",
"updateQuizInfo": "Cập nhật bài trắc nghiệm",
"question": {
"question": "Câu hỏi",
diff --git a/messages/vi/subscription/vi_subscription.json b/messages/vi/subscription/vi_subscription.json
index 6a1781edb..fea81fd77 100644
--- a/messages/vi/subscription/vi_subscription.json
+++ b/messages/vi/subscription/vi_subscription.json
@@ -9,12 +9,27 @@
"organizationSubscriptionDescription": "Duyệt và quản lý tất cả các đăng ký tổ chức đã đăng ký trên nền tảng.",
"placeholder": {
"search": "Tìm kiếm tổ chức theo tên...",
- "status": "Lọc theo trạng thái"
- }
+ "status": "Lọc theo trạng thái",
+ "billingCycle": "Chu kỳ"
+ },
+ "total": "Tổng cộng",
+ "totalDescription": "Tất cả gói đăng ký trong hệ thống",
+ "active": "Đang hoạt động",
+ "activeDescription": "Gói đăng ký đang hoạt động",
+ "expired": "Hết hạn",
+ "expiredDescription": "Gói đăng ký đã kết thúc hoặc bị hủy",
+ "pending": "Chờ xử lý",
+ "pendingDescription": "Đang chờ phê duyệt",
+ "searchSubscription": "Tìm kiếm gói đăng ký...",
+ "status": "Trạng thái",
+ "annual": "Hàng năm",
+ "semiAnnual": "Nửa năm"
},
"detail": {
"title": "Thông tin tổ chức",
"currentPlan": "Gói hiện tại",
+ "overview": "Tổng quan về đăng ký",
+ "overviewDescription": "Quản lý đăng ký và người dùng của tổ chức bạn",
"StartDate": "Ngày bắt đầu",
"ExpiredDate": "Ngày hết hạn",
"totalSeats": "Tổng số người dùng",
diff --git a/public/components/templates/MaterialLibrary/plastic_blue.json b/public/components/templates/MaterialLibrary/plastic_blue.json
index f349dad68..8d36148d2 100644
--- a/public/components/templates/MaterialLibrary/plastic_blue.json
+++ b/public/components/templates/MaterialLibrary/plastic_blue.json
@@ -4,18 +4,18 @@
"version": "1.0",
"type": "plastic",
"properties": {
- "color": "#62cbfb",
+ "color": "#26c6ff",
"flexibility": 15,
"opacity": 1.0,
- "roughness": 0.3,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.1
},
"physics": {
- "friction": 0.4,
- "elasticity": 0.2,
+ "friction": 0.35,
+ "elasticity": 0.25,
"density": 920
},
"lod": {
diff --git a/public/components/templates/MaterialLibrary/plastic_green.json b/public/components/templates/MaterialLibrary/plastic_green.json
index 6ed1fb6ae..db894fbdf 100644
--- a/public/components/templates/MaterialLibrary/plastic_green.json
+++ b/public/components/templates/MaterialLibrary/plastic_green.json
@@ -4,18 +4,18 @@
"version": "1.0",
"type": "plastic",
"properties": {
- "color": "#c1e500",
+ "color": "#b6ff1a",
"flexibility": 15,
"opacity": 1.0,
- "roughness": 0.3,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.1
},
"physics": {
- "friction": 0.4,
- "elasticity": 0.2,
+ "friction": 0.35,
+ "elasticity": 0.25,
"density": 920
},
"lod": {
@@ -25,7 +25,7 @@
"enableSubsurfaceScattering": true
},
"medium": {
- "shaderComplexity": "pbr_simplified",
+ "shaderComplexity": "pbr_simplified",
"textureResolution": 512,
"enableSubsurfaceScattering": false
},
diff --git a/public/components/templates/MaterialLibrary/plastic_orange.json b/public/components/templates/MaterialLibrary/plastic_orange.json
index 405fc0b84..cfbcbee38 100644
--- a/public/components/templates/MaterialLibrary/plastic_orange.json
+++ b/public/components/templates/MaterialLibrary/plastic_orange.json
@@ -4,18 +4,18 @@
"version": "1.0",
"type": "plastic",
"properties": {
- "color": "#f08100",
+ "color": "#ff9a1f",
"flexibility": 15,
"opacity": 1.0,
- "roughness": 0.3,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.1
},
"physics": {
- "friction": 0.4,
- "elasticity": 0.2,
+ "friction": 0.35,
+ "elasticity": 0.25,
"density": 920
},
"lod": {
diff --git a/public/components/templates/MaterialLibrary/plastic_pink.json b/public/components/templates/MaterialLibrary/plastic_pink.json
index 26e74245c..549d45d58 100644
--- a/public/components/templates/MaterialLibrary/plastic_pink.json
+++ b/public/components/templates/MaterialLibrary/plastic_pink.json
@@ -4,18 +4,18 @@
"version": "1.0",
"type": "plastic",
"properties": {
- "color": "#e788c9",
+ "color": "#ff7ad6",
"flexibility": 15,
"opacity": 1.0,
- "roughness": 0.3,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.1
},
"physics": {
- "friction": 0.4,
- "elasticity": 0.2,
+ "friction": 0.35,
+ "elasticity": 0.25,
"density": 920
},
"lod": {
diff --git a/public/components/templates/MaterialLibrary/plastic_red.json b/public/components/templates/MaterialLibrary/plastic_red.json
index 7e1cf31dc..5284b9cac 100644
--- a/public/components/templates/MaterialLibrary/plastic_red.json
+++ b/public/components/templates/MaterialLibrary/plastic_red.json
@@ -1,21 +1,21 @@
{
"id": "plastic_red",
"name": "Red Plastic Material",
- "version": "1.0",
+ "version": "1.0",
"type": "plastic",
"properties": {
- "color": "#f0201c",
+ "color": "#ff2b2b",
"flexibility": 5,
"opacity": 1.0,
- "roughness": 0.4,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.15
},
"physics": {
"friction": 0.6,
- "elasticity": 0.1,
+ "elasticity": 0.18,
"density": 1050
},
"lod": {
@@ -26,7 +26,7 @@
},
"medium": {
"shaderComplexity": "pbr_simplified",
- "textureResolution": 512,
+ "textureResolution": 512,
"enableSubsurfaceScattering": false
},
"low": {
diff --git a/public/components/templates/MaterialLibrary/plastic_yellow.json b/public/components/templates/MaterialLibrary/plastic_yellow.json
index 67d0c652d..e09b0a084 100644
--- a/public/components/templates/MaterialLibrary/plastic_yellow.json
+++ b/public/components/templates/MaterialLibrary/plastic_yellow.json
@@ -4,18 +4,18 @@
"version": "1.0",
"type": "plastic",
"properties": {
- "color": "#f9ed00",
+ "color": "#fff328",
"flexibility": 15,
"opacity": 1.0,
- "roughness": 0.3,
+ "roughness": 0.15,
"metalness": 0.0,
- "transmission": 0.0,
- "ior": 1.4,
+ "transmission": 0.05,
+ "ior": 1.46,
"thickness": 0.1
},
"physics": {
- "friction": 0.4,
- "elasticity": 0.2,
+ "friction": 0.35,
+ "elasticity": 0.25,
"density": 920
},
"lod": {
diff --git a/src/app/[locale]/admin/(biz)/organization/[organizationId]/page.tsx b/src/app/[locale]/admin/(biz)/organization/[organizationId]/page.tsx
new file mode 100644
index 000000000..a1df8049e
--- /dev/null
+++ b/src/app/[locale]/admin/(biz)/organization/[organizationId]/page.tsx
@@ -0,0 +1,18 @@
+'use client'
+import BackButton from '@/components/shared/button/BackButton'
+import SystemOrganizationDetail from '@/features/organization/components/detail/SystemOrganizationDetail'
+import { useTranslations } from 'next-intl'
+import React from 'react'
+
+export default function SystemOrganizationDetailPage() {
+ const t = useTranslations('organization')
+ return (
+
+
+
+
{t('detail.header')}
+
+
+
+ )
+}
diff --git a/src/app/[locale]/admin/(biz)/organization/page.tsx b/src/app/[locale]/admin/(biz)/organization/page.tsx
index cb91ee5ab..06cf0dee6 100644
--- a/src/app/[locale]/admin/(biz)/organization/page.tsx
+++ b/src/app/[locale]/admin/(biz)/organization/page.tsx
@@ -1,4 +1,4 @@
-import SystemOrganizationList from '@/features/organization/components/SystemOrganizationList'
+import SystemOrganizationList from '@/features/organization/components/list/SystemOrganizationList'
import React from 'react'
export default function OrganizationSubscriptionListPage() {
diff --git a/src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/[quizId]/question/page.tsx b/src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/[quizId]/page.tsx
similarity index 100%
rename from src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/[quizId]/question/page.tsx
rename to src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/[quizId]/page.tsx
diff --git a/src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/question/page.tsx b/src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/question/page.tsx
deleted file mode 100644
index d2cf4f436..000000000
--- a/src/app/[locale]/admin/(main)/lesson/[lessonId]/section/[sectionId]/quiz/question/page.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import QuizEditor from '@/features/resource/question/components/QuizEditor'
-import React from 'react'
-
-export default function QuizPage() {
- return (
-
- )
-}
diff --git a/src/app/[locale]/classroom/[classroomId]/page.tsx b/src/app/[locale]/classroom/[classroomId]/page.tsx
index 3771a0056..6f7dd763f 100644
--- a/src/app/[locale]/classroom/[classroomId]/page.tsx
+++ b/src/app/[locale]/classroom/[classroomId]/page.tsx
@@ -3,19 +3,19 @@ import SEmpty from '@/components/shared/empty/SEmpty'
import LoadingComponent from '@/components/shared/loading/LoadingComponent'
import { AssignmentList } from '@/features/assignment/components/table/AssignmentList'
import { useGetClassroomByIdQuery } from '@/features/classroom/api/classroomApi'
-import ClassroomCourseList from '@/features/classroom/components/detail/ClassroomCourseList'
import StudentClassroomDetail from '@/features/classroom/components/detail/StudentClassroomDetails'
+import StudentClassList from '@/features/classroom/components/list/StudentClassList'
import ClassroomOverview from '@/features/classroom/components/overview/ClassroomOverview'
+import { ClassroomSchedule } from '@/features/classroom/components/schedule/ClassroomSchedule'
import ClassroomSubHeader from '@/features/classroom/components/ui/ClassroomSubHeader'
-import { useSearchCurriculumEnrollmentQuery } from '@/features/enrollment/api/curriculumEnrollmentApi'
+import { useSearchCourseEnrollmentQuery } from '@/features/enrollment/api/courseEnrollmentApi'
import TeacherQuiz from '@/features/quiz/components/TeacherQuiz'
import { useAppSelector } from '@/hooks/redux-hooks'
import { LicenseType } from '@/types/userRole'
-import { useLocale } from 'next-intl'
import { useParams } from 'next/navigation'
import React from 'react'
-export type ClassroomNavItems = 'overview' | 'course' | 'quiz' | 'assignment'
+export type ClassroomNavItems = 'overview' | 'course' | 'quiz' | 'assignment' | 'student'
export default function ClassroomDetailPage() {
const { classroomId } = useParams()
@@ -24,15 +24,15 @@ export default function ClassroomDetailPage() {
const [currentTab, setCurrentTab] = React.useState('overview')
const { data: classroomData, isLoading } = useGetClassroomByIdQuery(Number(classroomId))
- const { data: curriculumEnrollment } = useSearchCurriculumEnrollmentQuery(
+ const { data: courseEnrollment } = useSearchCourseEnrollmentQuery(
{
- curriculumId: classroomData?.data.curriculum.id,
+ courseId: classroomData?.data.course.id,
studentId: auth?.user?.userId || '',
classroomId: Number(classroomId),
pageNumber: 1,
pageSize: 20
},
- { skip: !auth.user?.userId || !classroomData?.data.curriculum.id || currentRole !== LicenseType.STUDENT }
+ { skip: !auth.user?.userId || !classroomData?.data.course.id || currentRole !== LicenseType.STUDENT }
)
if (isLoading) {
return (
@@ -50,18 +50,11 @@ export default function ClassroomDetailPage() {
{currentTab === 'overview' && currentRole === LicenseType.TEACHER ? : null}
{currentTab === 'overview' && currentRole === LicenseType.STUDENT ? (
-
+
) : null}
{currentTab === 'course' ? (
-
+
) : null}
{currentTab === 'quiz' ? (
@@ -74,6 +67,11 @@ export default function ClassroomDetailPage() {
) : null}
+ {currentTab === 'student' ? (
+
+
+
+ ) : null}
)
}
diff --git a/src/app/[locale]/lab/microbit-ai/page.tsx b/src/app/[locale]/lab/microbit-ai/page.tsx
index 3ce0064bc..96439ab50 100644
--- a/src/app/[locale]/lab/microbit-ai/page.tsx
+++ b/src/app/[locale]/lab/microbit-ai/page.tsx
@@ -10,19 +10,23 @@ export default function MicroAiPage() {
const [ready, setReady] = useState(false)
return !ready ? (
- {
- setZipFile(f)
- setModelUrl(undefined)
- setReady(true)
- }}
- onLoadUrl={(u) => {
- setModelUrl(u)
- setZipFile(undefined)
- setReady(true)
- }}
- />
+
+ {
+ setZipFile(f)
+ setModelUrl(undefined)
+ setReady(true)
+ }}
+ onLoadUrl={(u) => {
+ setModelUrl(u)
+ setZipFile(undefined)
+ setReady(true)
+ }}
+ />
+
) : (
-
+
+
+
)
}
diff --git a/src/app/[locale]/organization/classroom/create/page.tsx b/src/app/[locale]/organization/classroom/create/page.tsx
index 40d8e8408..f4093ee84 100644
--- a/src/app/[locale]/organization/classroom/create/page.tsx
+++ b/src/app/[locale]/organization/classroom/create/page.tsx
@@ -1,10 +1,10 @@
-import UpsertClassroom from '@/features/classroom/components/upsert/UpsertClassroom'
+import CreateClassroom from '@/features/classroom/components/upsert/CreateClassroom'
import React from 'react'
export default function createClassroomPage() {
return (
-
+
)
}
diff --git a/src/app/[locale]/organization/curriculum/[curriculumId]/course/[courseId]/page.tsx b/src/app/[locale]/organization/curriculum/[curriculumId]/course/[courseId]/page.tsx
new file mode 100644
index 000000000..32bb2054e
--- /dev/null
+++ b/src/app/[locale]/organization/curriculum/[curriculumId]/course/[courseId]/page.tsx
@@ -0,0 +1,6 @@
+import OrganizationCourseDetail from '@/features/resource/course/components/detail/organization/OrganizationCourseDetail'
+import React from 'react'
+
+export default function OrganizationCourseDetailPage() {
+ return
+}
diff --git a/src/app/[locale]/organization/curriculum/[curriculumId]/page.tsx b/src/app/[locale]/organization/curriculum/[curriculumId]/page.tsx
new file mode 100644
index 000000000..e4fa7c2e4
--- /dev/null
+++ b/src/app/[locale]/organization/curriculum/[curriculumId]/page.tsx
@@ -0,0 +1,10 @@
+import OrganizationCurriculumDetail from '@/features/resource/curriculum/components/detail/OrganizationCurriculumDetail'
+import React from 'react'
+
+export default function OrganizationCurriculumDetailPage() {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/[locale]/organization/curriculum/page.tsx b/src/app/[locale]/organization/curriculum/page.tsx
new file mode 100644
index 000000000..ac2edbfbf
--- /dev/null
+++ b/src/app/[locale]/organization/curriculum/page.tsx
@@ -0,0 +1,10 @@
+import OrganizationCurriculumList from '@/features/resource/curriculum/components/list/OrganizationCurriculumList'
+import React from 'react'
+
+export default function OrganizationCurriculumPage() {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/[locale]/organization/group/[groupId]/page.tsx b/src/app/[locale]/organization/group/[groupId]/page.tsx
new file mode 100644
index 000000000..c5bfc1bbb
--- /dev/null
+++ b/src/app/[locale]/organization/group/[groupId]/page.tsx
@@ -0,0 +1,10 @@
+import OrganizationGroupDetail from '@/features/group/components/detail/OrganizationGroupDetail'
+import React from 'react'
+
+export default function GroupDetailPage() {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/[locale]/organization/group/create/page.tsx b/src/app/[locale]/organization/group/create/page.tsx
new file mode 100644
index 000000000..5e53719a7
--- /dev/null
+++ b/src/app/[locale]/organization/group/create/page.tsx
@@ -0,0 +1,6 @@
+import CreateStudentGroupPage from '@/features/group/components/upsert/CreateStudentGroupPage'
+import React from 'react'
+
+export default function Page() {
+ return
+}
diff --git a/src/app/[locale]/organization/group/page.tsx b/src/app/[locale]/organization/group/page.tsx
new file mode 100644
index 000000000..a5e39304d
--- /dev/null
+++ b/src/app/[locale]/organization/group/page.tsx
@@ -0,0 +1,10 @@
+import OrganizationGroupList from '@/features/group/components/list/OrganizationGroupList'
+import React from 'react'
+
+export default function OrganizationGroupPage() {
+ return (
+
+
+
+ )
+}
diff --git a/src/app/[locale]/organization/user/page.tsx b/src/app/[locale]/organization/user/page.tsx
new file mode 100644
index 000000000..eb043c8c3
--- /dev/null
+++ b/src/app/[locale]/organization/user/page.tsx
@@ -0,0 +1,8 @@
+import OrganizationUserTable from '@/features/organization/components/user/OrganizationUserTable'
+import React from 'react'
+
+export default function OrganizationUserPage() {
+ return (
+
+ )
+}
diff --git a/src/app/[locale]/resource/activities/page.tsx b/src/app/[locale]/resource/activities/page.tsx
index 1f6870dc0..dfeca6417 100644
--- a/src/app/[locale]/resource/activities/page.tsx
+++ b/src/app/[locale]/resource/activities/page.tsx
@@ -2,5 +2,5 @@ import CardRandomGame from '@/features/card-random/CardGame'
import React from 'react'
export default function ActivityPage() {
- // return
+ return
}
diff --git a/src/app/[locale]/test/page.tsx b/src/app/[locale]/test/page.tsx
index 06143e15f..2eca4985b 100644
--- a/src/app/[locale]/test/page.tsx
+++ b/src/app/[locale]/test/page.tsx
@@ -2,572 +2,1218 @@ import React from 'react'
export default function Page() {
const jsonObject = {
- scene: {
- workspace: {
- grid: { size: 1, visible: true, divisions: 100 },
- bounds: { max: { x: 100, y: 100, z: 100 }, min: { x: -100, y: -100, z: -100 } }
- },
- environment: {
- camera: { fov: 60, target: { x: 0, y: 0, z: 0 }, controls: 'orbit', position: { x: 30, y: 20, z: 30 } },
- lighting: {
- ambient: '#404040',
- directional: { color: '#FFFFFF', position: { x: 10, y: 15, z: 8 }, intensity: 1.2 }
- },
- background: '#f5f5f5'
- }
- },
- actions: [
- {
- id: 'action_1',
- name: 'Default Action',
- type: 'highlight',
- targets: [
- 'straw_2',
- 'straw_5',
- 'connector_5',
- 'connector_7',
- 'connector_9',
- 'connector_11',
- 'straw_6',
- 'connector_14',
- 'connector_15',
- 'straw_8',
- 'connector_16',
- 'straw_9',
- 'connector_18',
- 'connector_19',
- 'connector_20'
- ],
- duration: 2
- },
- {
- id: 'action_2',
- name: 'Highlight Action 2',
- type: 'highlight',
- targets: [
- 'straw_10',
- 'connector_21',
- 'straw_11',
- 'connector_23',
- 'straw_12',
- 'connector_25',
- 'connector_27',
- 'straw_13',
- 'connector_29',
- 'connector_30'
- ],
- duration: 2
- },
- {
- id: 'action_3',
- name: 'Highlight Action 3',
- type: 'highlight',
- targets: ['straw_14', 'straw_15', 'straw_16', 'straw_17', 'connector_31'],
- duration: 2
- },
- {
- id: 'action_4',
- name: 'Highlight Action 4',
- type: 'highlight',
- targets: ['straw_18', 'straw_19', 'straw_20', 'straw_21', 'connector_32'],
- duration: 2
- },
- {
- id: 'action_5',
- name: 'Highlight Action 5',
- type: 'highlight',
- targets: ['straw_22', 'connector_33', 'connector_34'],
- duration: 2
- },
- {
- id: 'action_6',
- name: 'Highlight Action 6',
- type: 'highlight',
- targets: ['straw_23', 'straw_24', 'straw_25', 'straw_26', 'straw_27', 'straw_28'],
- duration: 2
- },
- {
- id: 'action_7',
- name: 'Highlight Action 7',
- type: 'highlight',
- targets: ['straw_29', 'connector_35'],
- duration: 2
- },
- {
- id: 'action_8',
- name: 'Highlight Action 8',
- type: 'highlight',
- targets: ['straw_30', 'connector_36'],
- duration: 2
- },
- { id: 'action_9', name: 'Highlight Action 9', type: 'highlight', targets: ['straw_31'], duration: 2 },
- {
- id: 'action_10',
- name: 'Highlight Action 10',
- type: 'highlight',
- targets: ['straw_32', 'straw_34', 'connector_37'],
- duration: 2
- },
- { id: 'action_11', name: 'Highlight Action 10', type: 'highlight', targets: [], duration: 2 }
- ],
metadata: {
- title: 'Cây Cầu Thông Minh',
- author: 'My',
- created: '2025-10-11T08:13:16.830Z',
+ title: 'Assembly_emu_018899090919',
+ description: 'Exported assembly JSON',
+ author: 'STEMify User',
version: '2.0',
- description: 'Cây Cầu Thông Minh',
- lastModified: '2025-10-11T08:13:16.830Z'
+ created: '2025-12-04T07:48:09.476Z',
+ lastModified: '2025-12-04T07:48:09.476Z'
+ },
+ templates: {
+ materials: [
+ {
+ id: 'plastic_green',
+ source: '/components/templates/MaterialLibrary/plastic_green.json'
+ },
+ {
+ id: 'plastic_red',
+ source: '/components/templates/MaterialLibrary/plastic_red.json'
+ },
+ {
+ id: 'plastic_blue',
+ source: '/components/templates/MaterialLibrary/plastic_blue.json'
+ },
+ {
+ id: 'plastic_yellow',
+ source: '/components/templates/MaterialLibrary/plastic_yellow.json'
+ },
+ {
+ id: 'plastic_orange',
+ source: '/components/templates/MaterialLibrary/plastic_orange.json'
+ },
+ {
+ id: 'plastic_pink',
+ source: '/components/templates/MaterialLibrary/plastic_pink.json'
+ }
+ ],
+ components: [
+ {
+ id: 'blue_19_0',
+ source: '/components/templates/StrawTypes/blue_19_0.json'
+ },
+ {
+ id: 'green_16_2',
+ source: '/components/templates/StrawTypes/green_16_2.json'
+ },
+ {
+ id: 'pink_8_9',
+ source: '/components/templates/StrawTypes/pink_8_9.json'
+ },
+ {
+ id: 'orange_6_3',
+ source: '/components/templates/StrawTypes/orange_6_3.json'
+ },
+ {
+ id: 'yellow_3_8',
+ source: '/components/templates/StrawTypes/yellow_3_8.json'
+ },
+ {
+ id: '1leg',
+ source: '/components/templates/ConnectorTypes/1leg.json'
+ },
+ {
+ id: '2leg',
+ source: '/components/templates/ConnectorTypes/2legs.json'
+ },
+ {
+ id: '3leg',
+ source: '/components/templates/ConnectorTypes/3legs.json'
+ },
+ {
+ id: '5leg',
+ source: '/components/templates/ConnectorTypes/5legs.json'
+ }
+ ]
},
instances: {
straws: [
{
+ templateId: 'orange_6_3',
instances: [
{
id: 'straw_2',
- transform: { position: { x: -16.81, y: 0, z: 0.5 }, rotation: { x: 0, y: 1.5707963267948966, z: 0 } }
+ transform: {
+ position: {
+ x: -16.81,
+ y: 0,
+ z: 0.5
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
+ }
},
{
id: 'straw_6',
- transform: { position: { x: 1.2, y: 0, z: 0.1 }, rotation: { x: 0, y: 1.5707963267948966, z: 0 } }
+ transform: {
+ position: {
+ x: 1.2,
+ y: 0,
+ z: 0.1
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
+ }
},
{
id: 'straw_9',
- transform: { position: { x: 19.58, y: 0, z: 0.1 }, rotation: { x: 0, y: 1.5707963267948966, z: 0 } }
+ transform: {
+ position: {
+ x: 19.58,
+ y: 0,
+ z: 0.1
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
+ }
}
- ],
- templateId: 'orange_6_3'
+ ]
},
{
+ templateId: 'blue_19_0',
instances: [
{
id: 'straw_5',
- transform: { position: { x: -7.9, y: 0, z: 0.7 }, rotation: { x: 0, y: 0.40142572795869574, z: 0 } }
+ transform: {
+ position: {
+ x: -7.9,
+ y: 0,
+ z: 0.7
+ },
+ rotation: {
+ x: 0,
+ y: 0.40142572795869574,
+ z: 0
+ }
+ }
},
{
id: 'straw_8',
transform: {
- position: { x: 10.3, y: 0, z: 0.2 },
- rotation: { x: 0, y: 0.40142572795869574, z: 0.011693705988362009 }
+ position: {
+ x: 10.3,
+ y: 0,
+ z: 0.2
+ },
+ rotation: {
+ x: 0,
+ y: 0.40142572795869574,
+ z: 0.011693705988362009
+ }
}
},
{
id: 'straw_23',
transform: {
- position: { x: 25.37, y: 8.05, z: -7.242924715885387 },
- rotation: { x: 0, y: 0, z: 1.0471975511965976 }
+ position: {
+ x: 25.37,
+ y: 8.05,
+ z: -7.242924715885387
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 1.0471975511965976
+ }
}
},
{
id: 'straw_24',
transform: {
- position: { x: 34.77249725174951, y: 8.1, z: -7.2 },
- rotation: { x: 0, y: 0, z: -1.0471975511965976 }
+ position: {
+ x: 34.77249725174951,
+ y: 8.1,
+ z: -7.2
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ }
}
},
{
id: 'straw_25',
transform: {
- position: { x: 30.31080251771205, y: 0, z: -7.170156821198586 },
- rotation: { x: 0, y: 0, z: 0 }
+ position: {
+ x: 30.31080251771205,
+ y: 0,
+ z: -7.170156821198586
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ },
+ {
+ id: 'straw_26',
+ transform: {
+ position: {
+ x: 30.3,
+ y: 0,
+ z: 9
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
}
},
- { id: 'straw_26', transform: { position: { x: 30.3, y: 0, z: 9 }, rotation: { x: 0, y: 0, z: 0 } } },
{
id: 'straw_27',
transform: {
- position: { x: 25.3, y: 8.1, z: 8.999041192800192 },
- rotation: { x: 0, y: 0, z: 1.0471975511965976 }
+ position: {
+ x: 25.3,
+ y: 8.1,
+ z: 8.999041192800192
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 1.0471975511965976
+ }
}
},
{
id: 'straw_28',
transform: {
- position: { x: 34.86959286657108, y: 8.1, z: 8.951652990328856 },
- rotation: { x: 0, y: 0, z: -1.0471975511965976 }
+ position: {
+ x: 34.86959286657108,
+ y: 8.1,
+ z: 8.951652990328856
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ }
}
},
{
id: 'straw_31',
transform: {
- position: { x: 34.81607758931416, y: 8.45, z: 1.7943938040032492 },
- rotation: { x: 0, y: 0, z: -1.0471975511965976 }
+ position: {
+ x: 34.81607758931416,
+ y: 8.45,
+ z: 1.7943938040032492
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ }
}
},
{
id: 'straw_32',
transform: {
- position: { x: 19.093986291090562, y: 8.90132410410023, z: 0.2544576705499644 },
- rotation: { x: 0, y: 0, z: -0.5235987755982988 }
+ position: {
+ x: 19.093986291090562,
+ y: 8.90132410410023,
+ z: 0.2544576705499644
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -0.5235987755982988
+ }
+ }
+ },
+ {
+ id: 'straw_35',
+ transform: {
+ position: {
+ x: 1.7507498195927065,
+ y: 13.354867557265095,
+ z: 0.29941936665361224
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
}
}
- ],
- templateId: 'blue_19_0'
+ ]
},
{
+ templateId: 'green_16_2',
instances: [
- { id: 'straw_10', transform: { position: { x: -7.7, y: 0, z: -3.8 }, rotation: { x: 0, y: 0, z: 0 } } },
- { id: 'straw_11', transform: { position: { x: 10.4, y: 0, z: -3.8 }, rotation: { x: 0, y: 0, z: 0 } } },
- { id: 'straw_12', transform: { position: { x: -7.7, y: 0, z: 4.7 }, rotation: { x: 0, y: 0, z: 0 } } },
- { id: 'straw_13', transform: { position: { x: 10.4, y: 0, z: 4.7 }, rotation: { x: 0, y: 0, z: 0 } } },
+ {
+ id: 'straw_10',
+ transform: {
+ position: {
+ x: -7.7,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ },
+ {
+ id: 'straw_11',
+ transform: {
+ position: {
+ x: 10.4,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ },
+ {
+ id: 'straw_12',
+ transform: {
+ position: {
+ x: -7.7,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ },
+ {
+ id: 'straw_13',
+ transform: {
+ position: {
+ x: 10.4,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ },
{
id: 'straw_14',
transform: {
- position: { x: -12.1, y: 6.5, z: -1.8 },
- rotation: { x: 0, y: -0.4537856055185257, z: 0.8726646259971648 }
+ position: {
+ x: -12.1,
+ y: 6.5,
+ z: -1.8
+ },
+ rotation: {
+ x: 0,
+ y: -0.4537856055185257,
+ z: 0.8726646259971648
+ }
}
},
{
id: 'straw_15',
transform: {
- position: { x: -12.1, y: 6.5, z: 2.3 },
- rotation: { x: 0, y: 0.41887902047863906, z: 0.8726646259971648 }
+ position: {
+ x: -12.1,
+ y: 6.5,
+ z: 2.3
+ },
+ rotation: {
+ x: 0,
+ y: 0.41887902047863906,
+ z: 0.8726646259971648
+ }
}
},
{
id: 'straw_16',
transform: {
- position: { x: -3.330841863339714, y: 6.248445372099425, z: -1.9038064789070885 },
- rotation: { x: 0, y: 0.47123889803846897, z: -0.9075712110370514 }
+ position: {
+ x: -3.330841863339714,
+ y: 6.248445372099425,
+ z: -1.9038064789070885
+ },
+ rotation: {
+ x: 0,
+ y: 0.47123889803846897,
+ z: -0.9075712110370514
+ }
}
},
{
id: 'straw_17',
transform: {
- position: { x: -3.2, y: 6.47, z: 2.26 },
- rotation: { x: 0, y: -0.4363323129985824, z: -0.890117918517108 }
+ position: {
+ x: -3.2,
+ y: 6.47,
+ z: 2.26
+ },
+ rotation: {
+ x: 0,
+ y: -0.4363323129985824,
+ z: -0.890117918517108
+ }
}
},
{
id: 'straw_18',
transform: {
- position: { x: 6.3, y: 6.5, z: -1.8 },
- rotation: { x: 0, y: -0.4537856055185257, z: 0.8726646259971648 }
+ position: {
+ x: 6.3,
+ y: 6.5,
+ z: -1.8
+ },
+ rotation: {
+ x: 0,
+ y: -0.4537856055185257,
+ z: 0.8726646259971648
+ }
}
},
{
id: 'straw_19',
transform: {
- position: { x: 6.3, y: 6.5, z: 2.3 },
- rotation: { x: 0, y: 0.41887902047863906, z: 0.8726646259971648 }
+ position: {
+ x: 6.3,
+ y: 6.5,
+ z: 2.3
+ },
+ rotation: {
+ x: 0,
+ y: 0.41887902047863906,
+ z: 0.8726646259971648
+ }
}
},
{
id: 'straw_20',
transform: {
- position: { x: 15.33, y: 6.5, z: -1.9 },
- rotation: { x: 0, y: 0.47123889803846897, z: -0.9075712110370514 }
+ position: {
+ x: 15.33,
+ y: 6.5,
+ z: -1.9
+ },
+ rotation: {
+ x: 0,
+ y: 0.47123889803846897,
+ z: -0.9075712110370514
+ }
}
},
{
id: 'straw_21',
transform: {
- position: { x: 15.3, y: 6.5, z: 2.5 },
- rotation: { x: 0, y: -0.4363323129985824, z: -0.890117918517108 }
+ position: {
+ x: 15.3,
+ y: 6.5,
+ z: 2.5
+ },
+ rotation: {
+ x: 0,
+ y: -0.4363323129985824,
+ z: -0.890117918517108
+ }
}
},
{
id: 'straw_22',
transform: {
- position: { x: 20.722631586967886, y: 0, z: 1.0057328801081784 },
- rotation: { x: 0, y: 1.5707963267948966, z: 0 }
+ position: {
+ x: 20.722631586967886,
+ y: 0,
+ z: 1.0057328801081784
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
}
},
{
id: 'straw_29',
transform: {
- position: { x: 30.0045944833505, y: 16.5, z: 0.8561350731218518 },
- rotation: { x: 0, y: 1.5707963267948966, z: 0 }
+ position: {
+ x: 30.0045944833505,
+ y: 16.5,
+ z: 0.8561350731218518
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
}
},
{
id: 'straw_30',
transform: {
- position: { x: 40.057301867781874, y: 0, z: 1.1100846063914593 },
- rotation: { x: 0, y: 1.5707963267948966, z: 0 }
+ position: {
+ x: 40.057301867781874,
+ y: 0,
+ z: 1.1100846063914593
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ }
}
}
- ],
- templateId: 'green_16_2'
+ ]
},
{
+ templateId: 'yellow_3_8',
instances: [
{
id: 'straw_34',
transform: {
- position: { x: 26.17041960671652, y: 2.1148457882507943, z: 0.5329097101846596 },
- rotation: { x: 0, y: 0, z: -2.2689280275926285 }
+ position: {
+ x: 26.17041960671652,
+ y: 2.1148457882507943,
+ z: 0.5329097101846596
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -2.2689280275926285
+ }
}
}
- ],
- templateId: 'yellow_3_8'
+ ]
}
],
connectors: [
{
+ templateId: '2leg',
instances: [
{
id: 'connector_5',
transform: {
- position: { x: -16.8, y: 0, z: -3.1 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: -16.8,
+ y: 0,
+ z: -3.1
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_7',
transform: {
- position: { x: -16.9, y: 0, z: 4.1 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: -16.9,
+ y: 0,
+ z: 4.1
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_14',
transform: {
- position: { x: 1.3, y: 0, z: -3.7 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: 1.3,
+ y: 0,
+ z: -3.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_15',
transform: {
- position: { x: 1.3, y: 0, z: 3.8 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: 1.3,
+ y: 0,
+ z: 3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_18',
transform: {
- position: { x: 19.66, y: 0, z: -3.15 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: 19.66,
+ y: 0,
+ z: -3.15
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_20',
transform: {
- position: { x: 19.5, y: 0, z: 3.8 },
- rotation: { x: 1.5707963267948966, y: 0, z: 1.5707963267948966 }
+ position: {
+ x: 19.5,
+ y: 0,
+ z: 3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ }
}
},
{
id: 'connector_23',
transform: {
- position: { x: 1.2, y: 0, z: -3.7 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0 }
+ position: {
+ x: 1.2,
+ y: 0,
+ z: -3.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ }
}
},
{
id: 'connector_27',
- transform: { position: { x: 1.3, y: 0, z: 4.7 }, rotation: { x: 1.5707963267948966, y: 0, z: 0 } }
+ transform: {
+ position: {
+ x: 1.3,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ }
+ }
}
- ],
- templateId: '2leg_red'
+ ]
},
{
+ templateId: '1leg',
instances: [
{
id: 'connector_9',
transform: {
- position: { x: -16.97, y: 0, z: 4.5 },
- rotation: { x: 1.5707963267948966, y: 0, z: -0.3490658503988659 }
+ position: {
+ x: -16.97,
+ y: 0,
+ z: 4.5
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: -0.3490658503988659
+ }
}
},
{
id: 'connector_11',
transform: {
- position: { x: 1.3, y: 0, z: -3.34 },
- rotation: { x: 1.5707963267948966, y: 0, z: 2.8099800957108703 }
+ position: {
+ x: 1.3,
+ y: 0,
+ z: -3.34
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 2.8099800957108703
+ }
}
},
{
id: 'connector_16',
transform: {
- position: { x: 1.2, y: 0, z: 4.3 },
- rotation: { x: 1.5707963267948966, y: -0.03490658503988659, z: -0.6108652381980153 }
+ position: {
+ x: 1.2,
+ y: 0,
+ z: 4.3
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -0.03490658503988659,
+ z: -0.6108652381980153
+ }
}
},
{
id: 'connector_19',
transform: {
- position: { x: 19.74, y: 0, z: -3.97 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0.4363323129985824 }
+ position: {
+ x: 19.74,
+ y: 0,
+ z: -3.97
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0.4363323129985824
+ }
}
},
{
id: 'connector_21',
- transform: { position: { x: -16.8, y: 0, z: -3.8 }, rotation: { x: 1.5707963267948966, y: 0, z: 0 } }
+ transform: {
+ position: {
+ x: -16.8,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ }
+ }
},
{
id: 'connector_25',
- transform: { position: { x: -17, y: 0, z: 4.6 }, rotation: { x: 1.5707963267948966, y: 0, z: 0 } }
+ transform: {
+ position: {
+ x: -17,
+ y: 0,
+ z: 4.6
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ }
+ }
},
{
id: 'connector_29',
transform: {
- position: { x: 19.41, y: -0.013953688090604155, z: 4.76 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0 }
+ position: {
+ x: 19.41,
+ y: -0.013953688090604155,
+ z: 4.76
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ }
}
},
{
id: 'connector_30',
transform: {
- position: { x: 19.5, y: 0, z: -3.9 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0 }
+ position: {
+ x: 19.5,
+ y: 0,
+ z: -3.9
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ }
}
},
{
id: 'connector_31',
- transform: { position: { x: -7.6, y: 13.3, z: 0.3 }, rotation: { x: 0, y: 0, z: -1.5707963267948966 } }
+ transform: {
+ position: {
+ x: -7.6,
+ y: 13.3,
+ z: 0.3
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.5707963267948966
+ }
+ }
},
{
id: 'connector_32',
- transform: { position: { x: 10.94, y: 13.6, z: 0.4 }, rotation: { x: 0, y: 0, z: -1.5707963267948966 } }
+ transform: {
+ position: {
+ x: 10.94,
+ y: 13.6,
+ z: 0.4
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.5707963267948966
+ }
+ }
},
{
id: 'connector_33',
transform: {
- position: { x: 20.74784905648725, y: 0, z: -3.496217776302761 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0 }
+ position: {
+ x: 20.74784905648725,
+ y: 0,
+ z: -3.496217776302761
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ }
}
},
{
id: 'connector_34',
transform: {
- position: { x: 20.79, y: 0, z: 3.687578897905908 },
- rotation: { x: 1.5707963267948966, y: 3.141592653589793, z: 0 }
+ position: {
+ x: 20.79,
+ y: 0,
+ z: 3.687578897905908
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ }
}
},
{
id: 'connector_35',
transform: {
- position: { x: 30.073391112008878, y: 16.68, z: 1.7167804549312669 },
- rotation: { x: 1.5707963267948966, y: -1.0471975511965976, z: 0 }
+ position: {
+ x: 30.073391112008878,
+ y: 16.68,
+ z: 1.7167804549312669
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -1.0471975511965976,
+ z: 0
+ }
}
},
{
id: 'connector_36',
transform: {
- position: { x: 39.83283639272023, y: 0, z: 1.7928342987420356 },
- rotation: { x: 1.5707963267948966, y: 2.0943951023931953, z: 0 }
+ position: {
+ x: 39.83283639272023,
+ y: 0,
+ z: 1.7928342987420356
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 2.0943951023931953,
+ z: 0
+ }
}
},
{
id: 'connector_37',
transform: {
- position: { x: 27.8646836486829, y: 4.1066451023783985, z: 0.5060142278475936 },
- rotation: { x: 1.5707963267948966, y: -2.2689280275926285, z: 0 }
+ position: {
+ x: 27.8646836486829,
+ y: 4.1066451023783985,
+ z: 0.5060142278475936
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -2.2689280275926285,
+ z: 0
+ }
+ }
+ },
+ {
+ id: 'connector_38',
+ transform: {
+ position: {
+ x: 2.874957028770967,
+ y: 0,
+ z: 21.993926994193817
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
}
}
- ],
- templateId: '1leg_red'
+ ]
}
]
},
- templates: {
- materials: [
- { id: 'plastic_green', source: '/components/templates/MaterialLibrary/plastic_green.json' },
- { id: 'plastic_red', source: '/components/templates/MaterialLibrary/plastic_red.json' },
- { id: 'plastic_blue', source: '/components/templates/MaterialLibrary/plastic_blue.json' },
- { id: 'plastic_yellow', source: '/components/templates/MaterialLibrary/plastic_yellow.json' },
- { id: 'plastic_orange', source: '/components/templates/MaterialLibrary/plastic_orange.json' },
- { id: 'plastic_pink', source: '/components/templates/MaterialLibrary/plastic_pink.json' }
- ],
- components: [
- { id: 'blue_19_0', source: '/components/templates/StrawTypes/blue_19_0.json' },
- { id: 'green_16_2', source: '/components/templates/StrawTypes/green_16_2.json' },
- { id: 'pink_8_9', source: '/components/templates/StrawTypes/pink_8_9.json' },
- { id: 'orange_6_3', source: '/components/templates/StrawTypes/orange_6_3.json' },
- { id: 'yellow_3_8', source: '/components/templates/StrawTypes/yellow_3_8.json' },
- { id: '1leg_red', source: '/components/templates/ConnectorTypes/1leg.json' },
- { id: '2leg_red', source: '/components/templates/ConnectorTypes/2legs.json' },
- { id: '3leg_red', source: '/components/templates/ConnectorTypes/3legs.json' },
- { id: '5leg_red', source: '/components/templates/ConnectorTypes/5legs.json' }
- ]
- },
+ actions: [
+ {
+ id: 'action_1',
+ name: 'Default Action',
+ type: 'highlight',
+ targets: [
+ 'straw_2',
+ 'straw_5',
+ 'connector_5',
+ 'connector_7',
+ 'connector_9',
+ 'connector_11',
+ 'straw_6',
+ 'connector_14',
+ 'connector_15',
+ 'straw_8',
+ 'connector_16',
+ 'straw_9',
+ 'connector_18',
+ 'connector_19',
+ 'connector_20'
+ ],
+ duration: 2
+ },
+ {
+ id: 'action_2',
+ name: 'Highlight Action 2',
+ type: 'highlight',
+ targets: [
+ 'straw_10',
+ 'connector_21',
+ 'straw_11',
+ 'connector_23',
+ 'straw_12',
+ 'connector_25',
+ 'connector_27',
+ 'straw_13',
+ 'connector_29',
+ 'connector_30'
+ ],
+ duration: 2
+ },
+ {
+ id: 'action_3',
+ name: 'Highlight Action 3',
+ type: 'highlight',
+ targets: ['straw_14', 'straw_15', 'straw_16', 'straw_17', 'connector_31'],
+ duration: 2
+ },
+ {
+ id: 'action_4',
+ name: 'Highlight Action 4',
+ type: 'highlight',
+ targets: ['straw_18', 'straw_19', 'straw_20', 'straw_21', 'connector_32'],
+ duration: 2
+ },
+ {
+ id: 'action_5',
+ name: 'Highlight Action 5',
+ type: 'highlight',
+ targets: ['straw_22', 'connector_33', 'connector_34'],
+ duration: 2
+ },
+ {
+ id: 'action_6',
+ name: 'Highlight Action 6',
+ type: 'highlight',
+ targets: ['straw_23', 'straw_24', 'straw_25', 'straw_26', 'straw_27', 'straw_28'],
+ duration: 2
+ },
+ {
+ id: 'action_7',
+ name: 'Highlight Action 7',
+ type: 'highlight',
+ targets: ['straw_29', 'connector_35'],
+ duration: 2
+ },
+ {
+ id: 'action_8',
+ name: 'Highlight Action 8',
+ type: 'highlight',
+ targets: ['straw_30', 'connector_36'],
+ duration: 2
+ },
+ {
+ id: 'action_9',
+ name: 'Highlight Action 9',
+ type: 'highlight',
+ targets: ['straw_31'],
+ duration: 2
+ },
+ {
+ id: 'action_10',
+ name: 'Highlight Action 10',
+ type: 'highlight',
+ targets: ['straw_32', 'straw_34', 'connector_37', 'straw_35'],
+ duration: 2
+ },
+ {
+ id: 'action_11',
+ name: 'Highlight Action 10',
+ type: 'highlight',
+ targets: ['connector_38'],
+ duration: 2
+ }
+ ],
activities: [
{
id: 'custom_assembly',
name: 'Cây Cầu Thông Minh',
+ description: 'Cây Cầu Thông Minh',
+ difficulty: 'beginner',
+ estimatedTime: 600,
steps: [
{
- title: 'Khởi Động Cầu Nâng Thông Minh',
actionId: 'action_11',
+ title: 'Khởi Động Cầu Nâng Thông Minh',
description: 'Hãy nhấn nút tiếp tục bắt đầu hành trình sáng tạo của bạn!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Tạo Dầm Cầu Ziczac',
actionId: 'action_1',
+ title: 'Tạo Dầm Cầu Ziczac',
description:
'Lắp các ống hút nối nhau như hình ziczac nhé! Cây cầu sẽ trông thật ngầu và chắc chắn, sẵn sàng cho phần nâng hạ ở bước tiếp theo!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Hoàn Thiện Mặt Cầu',
actionId: 'action_2',
+ title: 'Hoàn Thiện Mặt Cầu',
description:
'Tạo các thanh dài và gắn chúng quanh khung ziczac nào! Giờ thì cây cầu của bạn đã có phần nền cực kỳ vững chắc rồi đấy!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Lắp Các Thanh Nối Vào Khung Cầu',
actionId: 'action_3',
+ title: 'Lắp Các Thanh Nối Vào Khung Cầu',
description:
'Tạo các thanh nối chắc chắn rồi gắn chúng vào phần khung cầu nhé! Những thanh nối này sẽ giúp cây cầu thêm bền vững và sẵn sàng cho cơ chế nâng hạ hoạt động trơn tru.',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Làm Lại Một Lần Nữa!',
actionId: 'action_4',
+ title: 'Làm Lại Một Lần Nữa!',
description: 'Lặp lại bước trước ở bên còn lại nhé! Cây cầu của bạn sắp hoàn thiện rồi — thật tuyệt vời!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Tạo Thanh Trượt Cho Cầu Nâng',
actionId: 'action_5',
+ title: 'Tạo Thanh Trượt Cho Cầu Nâng',
description:
'Hãy lắp một thanh trượt và đặt nó dọc theo khung cầu. Thanh này giúp mô phỏng cơ chế trượt – nơi cây cầu có thể di chuyển mượt mà khi được nâng lên hoặc hạ xuống.',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Dựng Hai Khung Tam Giác Siêu Chắc!',
actionId: 'action_6',
+ title: 'Dựng Hai Khung Tam Giác Siêu Chắc!',
description:
'Hãy lắp các thanh nối thành hình tam giác ở hai bên cây cầu. Hình tam giác là cấu trúc vững chắc nhất trong kỹ thuật xây dựng, giúp cây cầu của bạn đứng vững và chịu lực tốt hơn!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Gắn Ống Hút Nối 2 Khung Tam Giác',
actionId: 'action_7',
+ title: 'Gắn Ống Hút Nối 2 Khung Tam Giác',
description:
'Lắp các ống hút vào hình tam giác nhé, rồi trượt connector vào đầu ống như đang “mặc áo giáp” cho cầu vậy! Giờ cây cầu của bạn trông thật vững chãi và cực kỳ ngầu!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Làm Lại Ở Bên Còn Lại Nào!',
actionId: 'action_8',
+ title: 'Làm Lại Ở Bên Còn Lại Nào!',
description:
'Lặp lại bước trước ở phía bên kia nhé! Khi hoàn thành, cây cầu của bạn sẽ trông thật cân đối và sẵn sàng nâng lên – hạ xuống như một cây cầu thật!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Làm Thanh Chống Siêu Vững Cho Cầu!',
actionId: 'action_9',
+ title: 'Làm Thanh Chống Siêu Vững Cho Cầu!',
description:
'ắn các khớp nối vào ống hút để tạo thanh chống nhé! Vậy là cây cầu của bạn đã có “cánh tay đỡ” cực kỳ mạnh mẽ!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
},
{
- title: 'Lắp Đòn Bẩy Thần Kỳ!',
actionId: 'action_10',
+ title: 'Lắp Đòn Bẩy Thần Kỳ!',
description:
'Hãy gắn một ống hút làm đòn bẩy ở phần chân cầu nhé! Khi bạn ấn xuống, cây cầu sẽ nâng lên – giống như phép thuật vậy! Đã đến lúc thử nghiệm mô hình của bạn!',
- expectedResult: 'Targets are highlighted'
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ title: 'Transform Step 12',
+ actionId: 'action_12',
+ description: '',
+ expectedResult: '',
+ hints: []
+ },
+ {
+ title: 'Highlight Step 13',
+ actionId: 'action_13',
+ description: '',
+ expectedResult: '',
+ hints: []
+ },
+ {
+ title: 'Highlight Step 14',
+ actionId: 'action_14',
+ description: '',
+ expectedResult: '',
+ hints: []
+ },
+ {
+ title: 'Highlight Step 15',
+ actionId: 'action_15',
+ description: '',
+ expectedResult: '',
+ hints: []
}
- ],
- difficulty: 'beginner',
- description: 'Cây Cầu Thông Minh',
- estimatedTime: 600
+ ]
+ }
+ ],
+ scene: {
+ environment: {
+ background: '#f5f5f5',
+ lighting: {
+ ambient: '#404040',
+ directional: {
+ color: '#FFFFFF',
+ intensity: 1.2,
+ position: {
+ x: 10,
+ y: 15,
+ z: 8
+ }
+ }
+ },
+ camera: {
+ position: {
+ x: 30,
+ y: 20,
+ z: 30
+ },
+ target: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ fov: 60,
+ controls: 'orbit'
+ }
+ },
+ workspace: {
+ bounds: {
+ min: {
+ x: -100,
+ y: -100,
+ z: -100
+ },
+ max: {
+ x: 100,
+ y: 100,
+ z: 100
+ }
+ },
+ grid: {
+ visible: true,
+ size: 1,
+ divisions: 100
+ }
}
- ]
+ }
}
const jsonString = JSON.stringify(jsonObject)
diff --git a/src/app/[locale]/test2/page.tsx b/src/app/[locale]/test2/page.tsx
new file mode 100644
index 000000000..7c34db2e5
--- /dev/null
+++ b/src/app/[locale]/test2/page.tsx
@@ -0,0 +1,1537 @@
+import React from 'react'
+
+export default function Page() {
+ const jsonObject = {
+ metadata: {
+ title: 'Assembly_emu_e3676a7ca85a',
+ description: 'Exported assembly JSON',
+ author: 'STEMify User',
+ version: '2.0',
+ created: '2025-12-04T08:00:41.963Z',
+ lastModified: '2025-12-04T08:00:41.963Z'
+ },
+ templates: {
+ materials: [
+ {
+ id: 'plastic_green',
+ source: '/components/templates/MaterialLibrary/plastic_green.json'
+ },
+ {
+ id: 'plastic_red',
+ source: '/components/templates/MaterialLibrary/plastic_red.json'
+ },
+ {
+ id: 'plastic_blue',
+ source: '/components/templates/MaterialLibrary/plastic_blue.json'
+ },
+ {
+ id: 'plastic_yellow',
+ source: '/components/templates/MaterialLibrary/plastic_yellow.json'
+ },
+ {
+ id: 'plastic_orange',
+ source: '/components/templates/MaterialLibrary/plastic_orange.json'
+ },
+ {
+ id: 'plastic_pink',
+ source: '/components/templates/MaterialLibrary/plastic_pink.json'
+ }
+ ],
+ components: [
+ {
+ id: 'blue_19_0',
+ source: '/components/templates/StrawTypes/blue_19_0.json'
+ },
+ {
+ id: 'green_16_2',
+ source: '/components/templates/StrawTypes/green_16_2.json'
+ },
+ {
+ id: 'pink_8_9',
+ source: '/components/templates/StrawTypes/pink_8_9.json'
+ },
+ {
+ id: 'orange_6_3',
+ source: '/components/templates/StrawTypes/orange_6_3.json'
+ },
+ {
+ id: 'yellow_3_8',
+ source: '/components/templates/StrawTypes/yellow_3_8.json'
+ },
+ {
+ id: '1leg',
+ source: '/components/templates/ConnectorTypes/1leg.json'
+ },
+ {
+ id: '2leg',
+ source: '/components/templates/ConnectorTypes/2legs.json'
+ },
+ {
+ id: '3leg',
+ source: '/components/templates/ConnectorTypes/3legs.json'
+ },
+ {
+ id: '5leg',
+ source: '/components/templates/ConnectorTypes/5legs.json'
+ }
+ ]
+ },
+ instances: {
+ straws: [
+ {
+ templateId: 'orange_6_3',
+ instances: [
+ {
+ id: 'straw_2',
+ transform: {
+ position: {
+ x: -16.81,
+ y: 0,
+ z: 0.5
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_6',
+ transform: {
+ position: {
+ x: 1.2,
+ y: 0,
+ z: 0.1
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_9',
+ transform: {
+ position: {
+ x: 19.58,
+ y: 0,
+ z: 0.1
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ }
+ ]
+ },
+ {
+ templateId: 'blue_19_0',
+ instances: [
+ {
+ id: 'straw_5',
+ transform: {
+ position: {
+ x: -7.9,
+ y: 0,
+ z: 0.7
+ },
+ rotation: {
+ x: 0,
+ y: 0.40142572795869574,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_8',
+ transform: {
+ position: {
+ x: 10.3,
+ y: 0,
+ z: 0.2
+ },
+ rotation: {
+ x: 0,
+ y: 0.40142572795869574,
+ z: 0.011693705988362009
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_23',
+ transform: {
+ position: {
+ x: 25.37,
+ y: 8.05,
+ z: -7.242924715885387
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 1.0471975511965976
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_24',
+ transform: {
+ position: {
+ x: 34.77249725174951,
+ y: 8.1,
+ z: -7.2
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_25',
+ transform: {
+ position: {
+ x: 30.31080251771205,
+ y: 0,
+ z: -7.170156821198586
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_26',
+ transform: {
+ position: {
+ x: 30.3,
+ y: 0,
+ z: 9
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_27',
+ transform: {
+ position: {
+ x: 25.3,
+ y: 8.1,
+ z: 8.999041192800192
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 1.0471975511965976
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_28',
+ transform: {
+ position: {
+ x: 34.86959286657108,
+ y: 8.1,
+ z: 8.951652990328856
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_31',
+ transform: {
+ position: {
+ x: 34.81607758931416,
+ y: 8.45,
+ z: 1.7943938040032492
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.0471975511965976
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_32',
+ transform: {
+ position: {
+ x: 19.093986291090562,
+ y: 8.90132410410023,
+ z: 0.2544576705499644
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -0.5235987755982988
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_35',
+ transform: {
+ position: {
+ x: 1.7507498195927065,
+ y: 13.354867557265095,
+ z: 0.29941936665361224
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ }
+ ]
+ },
+ {
+ templateId: 'green_16_2',
+ instances: [
+ {
+ id: 'straw_10',
+ transform: {
+ position: {
+ x: -7.7,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_11',
+ transform: {
+ position: {
+ x: 10.4,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_12',
+ transform: {
+ position: {
+ x: -7.7,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_13',
+ transform: {
+ position: {
+ x: 10.4,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_14',
+ transform: {
+ position: {
+ x: -12.1,
+ y: 6.5,
+ z: -1.8
+ },
+ rotation: {
+ x: 0,
+ y: -0.4537856055185257,
+ z: 0.8726646259971648
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_15',
+ transform: {
+ position: {
+ x: -12.1,
+ y: 6.5,
+ z: 2.3
+ },
+ rotation: {
+ x: 0,
+ y: 0.41887902047863906,
+ z: 0.8726646259971648
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_16',
+ transform: {
+ position: {
+ x: -3.330841863339714,
+ y: 6.248445372099425,
+ z: -1.9038064789070885
+ },
+ rotation: {
+ x: 0,
+ y: 0.47123889803846897,
+ z: -0.9075712110370514
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_17',
+ transform: {
+ position: {
+ x: -3.2,
+ y: 6.47,
+ z: 2.26
+ },
+ rotation: {
+ x: 0,
+ y: -0.4363323129985824,
+ z: -0.890117918517108
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_18',
+ transform: {
+ position: {
+ x: 6.3,
+ y: 6.5,
+ z: -1.8
+ },
+ rotation: {
+ x: 0,
+ y: -0.4537856055185257,
+ z: 0.8726646259971648
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_19',
+ transform: {
+ position: {
+ x: 6.3,
+ y: 6.5,
+ z: 2.3
+ },
+ rotation: {
+ x: 0,
+ y: 0.41887902047863906,
+ z: 0.8726646259971648
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_20',
+ transform: {
+ position: {
+ x: 15.33,
+ y: 6.5,
+ z: -1.9
+ },
+ rotation: {
+ x: 0,
+ y: 0.47123889803846897,
+ z: -0.9075712110370514
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_21',
+ transform: {
+ position: {
+ x: 15.3,
+ y: 6.5,
+ z: 2.5
+ },
+ rotation: {
+ x: 0,
+ y: -0.4363323129985824,
+ z: -0.890117918517108
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_22',
+ transform: {
+ position: {
+ x: 20.722631586967886,
+ y: 0,
+ z: 1.0057328801081784
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_29',
+ transform: {
+ position: {
+ x: 30.0045944833505,
+ y: 16.5,
+ z: 0.8561350731218518
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ },
+ {
+ id: 'straw_30',
+ transform: {
+ position: {
+ x: 40.057301867781874,
+ y: 0,
+ z: 1.1100846063914593
+ },
+ rotation: {
+ x: 0,
+ y: 1.5707963267948966,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ }
+ ]
+ },
+ {
+ templateId: 'yellow_3_8',
+ instances: [
+ {
+ id: 'straw_34',
+ transform: {
+ position: {
+ x: 26.17041960671652,
+ y: 2.1148457882507943,
+ z: 0.5329097101846596
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -2.2689280275926285
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ }
+ }
+ ]
+ }
+ ],
+ connectors: [
+ {
+ templateId: '2leg',
+ instances: [
+ {
+ id: 'connector_5',
+ transform: {
+ position: {
+ x: -16.8,
+ y: 0,
+ z: -3.1
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_7',
+ transform: {
+ position: {
+ x: -16.9,
+ y: 0,
+ z: 4.1
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_14',
+ transform: {
+ position: {
+ x: 1.3,
+ y: 0,
+ z: -3.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_15',
+ transform: {
+ position: {
+ x: 1.3,
+ y: 0,
+ z: 3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_18',
+ transform: {
+ position: {
+ x: 19.66,
+ y: 0,
+ z: -3.15
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_20',
+ transform: {
+ position: {
+ x: 19.5,
+ y: 0,
+ z: 3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_23',
+ transform: {
+ position: {
+ x: 1.2,
+ y: 0,
+ z: -3.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_27',
+ transform: {
+ position: {
+ x: 1.3,
+ y: 0,
+ z: 4.7
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ }
+ ]
+ },
+ {
+ templateId: '1leg',
+ instances: [
+ {
+ id: 'connector_9',
+ transform: {
+ position: {
+ x: -16.97,
+ y: 0,
+ z: 4.5
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: -0.3490658503988659
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_11',
+ transform: {
+ position: {
+ x: 1.3,
+ y: 0,
+ z: -3.34
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 2.8099800957108703
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_16',
+ transform: {
+ position: {
+ x: 1.2,
+ y: 0,
+ z: 4.3
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -0.03490658503988659,
+ z: -0.6108652381980153
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_19',
+ transform: {
+ position: {
+ x: 19.74,
+ y: 0,
+ z: -3.97
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0.4363323129985824
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_21',
+ transform: {
+ position: {
+ x: -16.8,
+ y: 0,
+ z: -3.8
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_25',
+ transform: {
+ position: {
+ x: -17,
+ y: 0,
+ z: 4.6
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_29',
+ transform: {
+ position: {
+ x: 19.41,
+ y: -0.013953688090604155,
+ z: 4.76
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_30',
+ transform: {
+ position: {
+ x: 19.5,
+ y: 0,
+ z: -3.9
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_31',
+ transform: {
+ position: {
+ x: -7.6,
+ y: 13.3,
+ z: 0.3
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_32',
+ transform: {
+ position: {
+ x: 10.94,
+ y: 13.6,
+ z: 0.4
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: -1.5707963267948966
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_33',
+ transform: {
+ position: {
+ x: 20.74784905648725,
+ y: 0,
+ z: -3.496217776302761
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_34',
+ transform: {
+ position: {
+ x: 20.79,
+ y: 0,
+ z: 3.687578897905908
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 3.141592653589793,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_35',
+ transform: {
+ position: {
+ x: 30.073391112008878,
+ y: 16.68,
+ z: 1.7167804549312669
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -1.0471975511965976,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_36',
+ transform: {
+ position: {
+ x: 39.83283639272023,
+ y: 0,
+ z: 1.7928342987420356
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: 2.0943951023931953,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_37',
+ transform: {
+ position: {
+ x: 27.8646836486829,
+ y: 4.1066451023783985,
+ z: 0.5060142278475936
+ },
+ rotation: {
+ x: 1.5707963267948966,
+ y: -2.2689280275926285,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {}
+ },
+ {
+ id: 'connector_38',
+ transform: {
+ position: {
+ x: 10.558742342045136,
+ y: 0,
+ z: 17.134284182871355
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ scale: {
+ x: 1,
+ y: 1,
+ z: 1
+ }
+ },
+ arms: {
+ arm_1: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ arm_2: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ arm_3: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ actions: [
+ {
+ id: 'action_1',
+ name: 'Default Action',
+ type: 'highlight',
+ targets: [
+ 'straw_2',
+ 'straw_5',
+ 'connector_5',
+ 'connector_7',
+ 'connector_9',
+ 'connector_11',
+ 'straw_6',
+ 'connector_14',
+ 'connector_15',
+ 'straw_8',
+ 'connector_16',
+ 'straw_9',
+ 'connector_18',
+ 'connector_19',
+ 'connector_20'
+ ],
+ duration: 2
+ },
+ {
+ id: 'action_2',
+ name: 'Highlight Action 2',
+ type: 'highlight',
+ targets: [
+ 'straw_10',
+ 'connector_21',
+ 'straw_11',
+ 'connector_23',
+ 'straw_12',
+ 'connector_25',
+ 'connector_27',
+ 'straw_13',
+ 'connector_29',
+ 'connector_30'
+ ],
+ duration: 2
+ },
+ {
+ id: 'action_3',
+ name: 'Highlight Action 3',
+ type: 'highlight',
+ targets: ['straw_14', 'straw_15', 'straw_16', 'straw_17', 'connector_31'],
+ duration: 2
+ },
+ {
+ id: 'action_4',
+ name: 'Highlight Action 4',
+ type: 'highlight',
+ targets: ['straw_18', 'straw_19', 'straw_20', 'straw_21', 'connector_32'],
+ duration: 2
+ },
+ {
+ id: 'action_5',
+ name: 'Highlight Action 5',
+ type: 'highlight',
+ targets: ['straw_22', 'connector_33', 'connector_34'],
+ duration: 2
+ },
+ {
+ id: 'action_6',
+ name: 'Highlight Action 6',
+ type: 'highlight',
+ targets: ['straw_23', 'straw_24', 'straw_25', 'straw_26', 'straw_27', 'straw_28'],
+ duration: 2
+ },
+ {
+ id: 'action_7',
+ name: 'Highlight Action 7',
+ type: 'highlight',
+ targets: ['straw_29', 'connector_35'],
+ duration: 2
+ },
+ {
+ id: 'action_8',
+ name: 'Highlight Action 8',
+ type: 'highlight',
+ targets: ['straw_30', 'connector_36'],
+ duration: 2
+ },
+ {
+ id: 'action_9',
+ name: 'Highlight Action 9',
+ type: 'highlight',
+ targets: ['straw_31'],
+ duration: 2
+ },
+ {
+ id: 'action_10',
+ name: 'Highlight Action 10',
+ type: 'highlight',
+ targets: ['straw_32', 'straw_34', 'connector_37', 'straw_35'],
+ duration: 2
+ },
+ {
+ id: 'action_11',
+ name: 'Highlight Action 10',
+ type: 'highlight',
+ targets: ['connector_38'],
+ duration: 2
+ }
+ ],
+ activities: [
+ {
+ id: 'custom_assembly',
+ name: 'Cây Cầu Thông Minh',
+ description: 'Cây Cầu Thông Minh',
+ difficulty: 'beginner',
+ estimatedTime: 600,
+ steps: [
+ {
+ actionId: 'action_11',
+ title: 'Khởi Động Cầu Nâng Thông Minh',
+ description: 'Hãy nhấn nút tiếp tục bắt đầu hành trình sáng tạo của bạn!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_1',
+ title: 'Tạo Dầm Cầu Ziczac',
+ description:
+ 'Lắp các ống hút nối nhau như hình ziczac nhé! Cây cầu sẽ trông thật ngầu và chắc chắn, sẵn sàng cho phần nâng hạ ở bước tiếp theo!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_2',
+ title: 'Hoàn Thiện Mặt Cầu',
+ description:
+ 'Tạo các thanh dài và gắn chúng quanh khung ziczac nào! Giờ thì cây cầu của bạn đã có phần nền cực kỳ vững chắc rồi đấy!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_3',
+ title: 'Lắp Các Thanh Nối Vào Khung Cầu',
+ description:
+ 'Tạo các thanh nối chắc chắn rồi gắn chúng vào phần khung cầu nhé! Những thanh nối này sẽ giúp cây cầu thêm bền vững và sẵn sàng cho cơ chế nâng hạ hoạt động trơn tru.',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_4',
+ title: 'Làm Lại Một Lần Nữa!',
+ description: 'Lặp lại bước trước ở bên còn lại nhé! Cây cầu của bạn sắp hoàn thiện rồi — thật tuyệt vời!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_5',
+ title: 'Tạo Thanh Trượt Cho Cầu Nâng',
+ description:
+ 'Hãy lắp một thanh trượt và đặt nó dọc theo khung cầu. Thanh này giúp mô phỏng cơ chế trượt – nơi cây cầu có thể di chuyển mượt mà khi được nâng lên hoặc hạ xuống.',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_6',
+ title: 'Dựng Hai Khung Tam Giác Siêu Chắc!',
+ description:
+ 'Hãy lắp các thanh nối thành hình tam giác ở hai bên cây cầu. Hình tam giác là cấu trúc vững chắc nhất trong kỹ thuật xây dựng, giúp cây cầu của bạn đứng vững và chịu lực tốt hơn!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_7',
+ title: 'Gắn Ống Hút Nối 2 Khung Tam Giác',
+ description:
+ 'Lắp các ống hút vào hình tam giác nhé, rồi trượt connector vào đầu ống như đang “mặc áo giáp” cho cầu vậy! Giờ cây cầu của bạn trông thật vững chãi và cực kỳ ngầu!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_8',
+ title: 'Làm Lại Ở Bên Còn Lại Nào!',
+ description:
+ 'Lặp lại bước trước ở phía bên kia nhé! Khi hoàn thành, cây cầu của bạn sẽ trông thật cân đối và sẵn sàng nâng lên – hạ xuống như một cây cầu thật!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_9',
+ title: 'Làm Thanh Chống Siêu Vững Cho Cầu!',
+ description:
+ 'ắn các khớp nối vào ống hút để tạo thanh chống nhé! Vậy là cây cầu của bạn đã có “cánh tay đỡ” cực kỳ mạnh mẽ!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_10',
+ title: 'Lắp Đòn Bẩy Thần Kỳ!',
+ description:
+ 'Hãy gắn một ống hút làm đòn bẩy ở phần chân cầu nhé! Khi bạn ấn xuống, cây cầu sẽ nâng lên – giống như phép thuật vậy! Đã đến lúc thử nghiệm mô hình của bạn!',
+ expectedResult: 'Targets are highlighted',
+ hints: null,
+ validation: null
+ },
+ {
+ actionId: 'action_12',
+ title: 'Transform Step 12',
+ description: '',
+ expectedResult: '',
+ hints: [],
+ validation: null
+ },
+ {
+ actionId: 'action_13',
+ title: 'Highlight Step 13',
+ description: '',
+ expectedResult: '',
+ hints: [],
+ validation: null
+ },
+ {
+ actionId: 'action_14',
+ title: 'Highlight Step 14',
+ description: '',
+ expectedResult: '',
+ hints: [],
+ validation: null
+ },
+ {
+ actionId: 'action_15',
+ title: 'Highlight Step 15',
+ description: '',
+ expectedResult: '',
+ hints: [],
+ validation: null
+ }
+ ]
+ }
+ ],
+ scene: {
+ environment: {
+ background: '#f5f5f5',
+ lighting: {
+ ambient: '#404040',
+ directional: {
+ color: '#FFFFFF',
+ intensity: 1.2,
+ position: {
+ x: 10,
+ y: 15,
+ z: 8
+ }
+ }
+ },
+ camera: {
+ position: {
+ x: 30,
+ y: 20,
+ z: 30
+ },
+ target: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ fov: 60,
+ controls: 'orbit'
+ }
+ },
+ workspace: {
+ bounds: {
+ min: {
+ x: -100,
+ y: -100,
+ z: -100
+ },
+ max: {
+ x: 100,
+ y: 100,
+ z: 100
+ }
+ },
+ grid: {
+ visible: true,
+ size: 1,
+ divisions: 100
+ }
+ }
+ }
+ }
+
+ const jsonString = JSON.stringify(jsonObject)
+ console.log(jsonString)
+
+ return {jsonString.replace(/"/g, '\\"')}
+}
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 35158f49d..d359bb687 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -1,9 +1,12 @@
+'use client'
import React from 'react'
import { Facebook, Instagram } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
+import { useTranslations } from 'next-intl'
const Footer = () => {
+ const tf = useTranslations('footer')
return (