From 22d30a246995decf2d15d2bf8a055db40b8c2049 Mon Sep 17 00:00:00 2001 From: taDuc Date: Sun, 7 Jun 2026 17:51:40 +0700 Subject: [PATCH] FAQ page renew and popup guide --- src/app/faq/page.tsx | 289 ++++++++++++++---- .../preview/FirstVisitGuideModal.tsx | 170 +++++++++++ .../preview/PublicPreviewClientPage.tsx | 2 + 3 files changed, 408 insertions(+), 53 deletions(-) create mode 100644 src/uhm/components/preview/FirstVisitGuideModal.tsx diff --git a/src/app/faq/page.tsx b/src/app/faq/page.tsx index 584497a..edc55a9 100644 --- a/src/app/faq/page.tsx +++ b/src/app/faq/page.tsx @@ -1,79 +1,262 @@ 'use client'; +import Link from 'next/link'; import { useState } from 'react'; -const faqData = [ +type GuideSection = { + id: string; + title: string; + summary: string; + steps: string[]; +}; + +const guideSections: GuideSection[] = [ { - id: 1, - question: "1. Tôi có thể đổi mật khẩu ở đâu?", - answer: "Bạn có thể đổi mật khẩu ở mục Thông tin tài khoản ở góc dưới bên trái màn hình. Kéo xuống mục thay đổi mật khẩu và cập nhật thông tin." + id: 'start', + title: 'Bắt đầu xem bản đồ', + summary: 'Route / là màn bản đồ lịch sử tương tác. Người dùng có thể xem dữ liệu theo năm, chọn lớp hiển thị, mở wiki và phát replay sự kiện.', + steps: [ + 'Khi vào trang chủ, chờ bản đồ tải xong hoặc nhấn vào màn hình chờ để vào nhanh hơn.', + 'Dùng chuột để kéo bản đồ, cuộn để phóng to/thu nhỏ. Trên điện thoại, dùng một ngón để kéo và hai ngón để phóng to/thu nhỏ bản đồ.', + 'Các vùng, đường, điểm hoặc biểu tượng trên bản đồ là dữ liệu lịch sử. Nhấn vào một đối tượng để xem thông tin liên quan.', + 'Nếu bản đồ đang tải dữ liệu theo năm, chờ vài giây để các lớp lịch sử cập nhật.' + ], }, { - id: 2, - question: "2. Làm sao để liên hệ với ban quản trị khi cần hỗ trợ gấp?", - answer: "Bạn có thể liên hệ với ban quản trị qua email hoặc số điện thoại được cung cấp trong mục Liên hệ hỗ trợ" + id: 'timeline', + title: 'Dùng thanh thời gian', + summary: 'Thanh thời gian ở cạnh dưới dùng để chuyển bản đồ về một mốc năm cụ thể.', + steps: [ + 'Kéo thước timeline để đổi năm nhanh.', + 'Nhập trực tiếp năm vào ô số nếu đã biết mốc cần xem.', + 'Nhấn nút - hoặc + để giảm/tăng từng năm. Giữ nút để chạy liên tục.', + 'Bật lọc timeline để chỉ ưu tiên dữ liệu phù hợp với năm đang chọn. Tắt lọc nếu muốn xem nhiều dữ liệu hơn cùng lúc.', + 'Trên màn hình desktop có ô Range. Tăng Range nếu muốn mở rộng khoảng năm gần mốc đang chọn, ví dụ xem thêm dữ liệu trong vài năm lân cận.' + ], }, { - id: 3, - question: "3. Để có thể làm kiểm duyệt viên tôi cần làm gì?", - answer: "Bạn có thể gửi yêu cầu làm Nhà sử học bằng cách truy cập vào mục Nhà Sử Học ở góc dưới bên trái màn hình. Điền đầy đủ thông tin yêu cầu và gửi cho chúng tôi. Chúng tôi sẽ xem xét và liên hệ qua email." + id: 'search', + title: 'Tìm địa danh và dữ liệu lịch sử', + summary: 'Ô tìm kiếm giúp đi nhanh tới địa danh hiện tại, wiki hoặc geometry lịch sử.', + steps: [ + 'Nhập tên địa danh, nhân vật, sự kiện hoặc thực thể lịch sử vào ô tìm kiếm phía trên bản đồ.', + 'Chọn kết quả phù hợp để bản đồ tự di chuyển tới vị trí liên quan.', + 'Nếu kết quả là địa danh hiện tại, bản đồ sẽ focus tới tọa độ hiện nay.', + 'Nếu kết quả có geometry lịch sử, hệ thống sẽ chọn đối tượng đó và có thể tự đổi timeline về năm bắt đầu của dữ liệu.' + ], }, { - id: 4, - question: "4. Thời gian phản hồi khi gửi yêu cầu là bao lâu?", - answer: "Thông thường chúng tôi sẽ phản hồi yêu cầu của bạn trong vòng 24-48 giờ làm việc." + id: 'layers', + title: 'Bật/tắt lớp bản đồ', + summary: 'Bảng lớp nằm bên trái, dùng để kiểm soát nền bản đồ và loại dữ liệu lịch sử đang hiển thị.', + steps: [ + 'Dùng nhóm lớp nền để đổi hoặc ẩn/hiện các nền như bản đồ cơ sở, vệ tinh hoặc các lớp tham chiếu.', + 'Dùng nhóm geometry để bật/tắt từng loại dữ liệu như quốc gia, vùng, thành phố, tuyến đường, trận đánh, cảng, đền, pháo đài.', + 'Nếu bản đồ quá nhiều chi tiết, hãy tắt bớt loại geometry chưa cần xem.', + 'Có thể ẩn bảng lớp để mở rộng diện tích bản đồ. Mở menu tròn bên trái rồi nhấn nút hiện bảng lớp để bật lại.' + ], }, { - id: 5, - question: "5. Làm gì nếu tài khoản của tôi bị khóa?", - answer: "Bạn có thể liên hệ với ban quản trị qua email hoặc số điện thoại được cung cấp trong mục Liên hệ hỗ trợ" + id: 'wiki', + title: 'Mở wiki từ bản đồ', + summary: 'Khi chọn một đối tượng lịch sử, panel bên phải hoặc phía dưới trên mobile sẽ hiển thị thông tin wiki liên quan.', + steps: [ + 'Nhấn vào vùng, điểm hoặc đường trên bản đồ để chọn đối tượng.', + 'Nếu đối tượng có nhiều wiki liên quan, chọn bài viết phù hợp trong danh sách.', + 'Trong nội dung wiki, nhấn các liên kết nội bộ để mở bài khác. Nếu bài đó có nhiều geometry, chọn geometry muốn focus.', + 'Kéo cạnh panel để đổi kích thước trên desktop. Trên mobile, panel nằm phía dưới và có thể điều chỉnh chiều cao.', + 'Nhấn nút đóng trong panel để quay lại chế độ xem bản đồ rộng hơn.' + ], + }, + { + id: 'replay', + title: 'Xem replay diễn biến lịch sử', + summary: 'Replay mô phỏng các bước diễn biến của một sự kiện hoặc trận đánh khi dữ liệu đó có kịch bản.', + steps: [ + 'Chọn một đối tượng trên bản đồ. Nếu đối tượng có replay, nút phát ở thanh thời gian sẽ khả dụng.', + 'Nhấn nút phát để bắt đầu xem diễn biến.', + 'Trong lúc replay chạy, bản đồ có thể tự di chuyển, đổi năm, làm nổi bật đường đi, vùng ảnh hưởng hoặc các điểm quan trọng.', + 'Dùng các nút điều khiển replay để tạm dừng, phát lại, đổi tốc độ hoặc thoát khỏi chế độ replay.', + 'Nếu không thấy nút phát hoạt động, đối tượng đang chọn có thể chưa có replay.' + ], + }, + { + id: 'menu', + title: 'Menu trợ giúp và tài khoản', + summary: 'Nút tròn bên trái mở các lối tắt tới tài khoản, chỉnh sửa, chatbot, FAQ và trang giới thiệu.', + steps: [ + 'Nhấn nút tròn avatar/người dùng ở bên trái để mở menu.', + 'Nút bút chì đưa tới khu vực quản trị và chỉnh sửa. Tính năng này chỉ hỗ trợ tốt trên desktop.', + 'Nút tia sét bật chế độ tải nhanh hơn cho lần vào sau.', + 'Nút chat mở trợ lý AI lịch sử để hỏi nhanh về dữ liệu hoặc sự kiện.', + 'Nút quyển sách mở trang hướng dẫn này. Nút thông tin mở trang giới thiệu dự án.' + ], + }, + { + id: 'mobile', + title: 'Lưu ý khi dùng trên điện thoại', + summary: 'Giao diện mobile ưu tiên bản đồ toàn màn hình, các panel và control sẽ được thu gọn.', + steps: [ + 'Thanh timeline trên mobile nằm phía dưới, gồm ô năm, nút -/+, công tắc lọc và thước kéo.', + 'Bảng wiki mở ở phía dưới màn hình để không che toàn bộ bản đồ.', + 'Một số thao tác quản trị hoặc chỉnh sửa bị khóa trên mobile để tránh lỗi thao tác.', + 'Nếu khó chọn một đối tượng nhỏ, hãy phóng to bản đồ trước rồi nhấn lại.' + ], + }, +]; + +const quickTips = [ + 'Muốn xem dữ liệu theo một năm cụ thể: nhập năm ở thanh timeline.', + 'Muốn hiểu một vùng trên bản đồ: nhấn vào vùng đó để mở wiki.', + 'Muốn bản đồ đỡ rối: tắt bớt lớp geometry bên trái.', + 'Muốn xem diễn biến: chọn đối tượng có replay rồi nhấn nút phát.', + 'Muốn hỏi nhanh: mở menu bên trái và chọn chatbot.' +]; + +const troubleshooting = [ + { + question: 'Tại sao tôi không thấy dữ liệu sau khi đổi năm?', + answer: 'Có thể năm đang chọn không có geometry phù hợp hoặc bộ lọc timeline đang bật. Hãy thử tắt lọc timeline, tăng Range trên desktop hoặc chuyển sang năm gần hơn với sự kiện bạn đang xem.', + }, + { + question: 'Tại sao nhấn vào bản đồ nhưng không mở wiki?', + answer: 'Một số geometry có thể chưa liên kết wiki hoặc bạn đang nhấn vào nền bản đồ. Hãy phóng to hơn, nhấn trực tiếp vào vùng/đường/biểu tượng, hoặc thử tìm bằng ô tìm kiếm.', + }, + { + question: 'Tại sao không phát được replay?', + answer: 'Replay chỉ có ở những đối tượng đã được biên soạn kịch bản. Nếu nút phát không phản hồi, hãy chọn một đối tượng khác hoặc tìm các sự kiện/trận đánh đã có dữ liệu replay.', + }, + { + question: 'Tại sao bản đồ tải chậm?', + answer: 'Bản đồ cần tải nền, geometry, wiki và quan hệ dữ liệu. Bạn có thể bật chế độ tải nhanh trong menu bên trái, giảm số lớp đang bật hoặc chờ bản đồ tải xong trước khi thao tác liên tục.', + }, + { + question: 'Tôi muốn chỉnh sửa hoặc đóng góp dữ liệu thì làm thế nào?', + answer: 'Mở menu bên trái và vào khu vực quản trị/chỉnh sửa trên desktop. Nếu chưa có quyền phù hợp, hãy đăng nhập và gửi yêu cầu nâng quyền theo luồng trong tài khoản người dùng.', }, ]; export default function Page() { - const [openIndex, setOpenIndex] = useState(0); - - const toggleFAQ = (index: number) => { - setOpenIndex(openIndex === index ? null : index); - }; + const [openSection, setOpenSection] = useState('start'); + const [openTrouble, setOpenTrouble] = useState(0); return ( -
-
-

FAQs

- -
- {faqData.map((faq, index) => { - const isOpen = openIndex === index; - - return ( -
+
+
+
+
+

Hướng dẫn sử dụng

+

Ultimate History Map FAQ

+

+ Trang này hướng dẫn cách sử dụng màn bản đồ tại route /: xem lịch sử theo timeline, tìm kiếm, bật/tắt lớp, đọc wiki và phát replay. +

+
+ + Quay lại bản đồ + +
+
+ +
+ + +
+
+

Dùng nhanh trong 1 phút

+
+ {quickTips.map((tip, index) => ( +
+ + {index + 1} + +

{tip}

-
- ); - })} + ))} +
+
+ +
+ {guideSections.map((section) => { + const isOpen = openSection === section.id; + return ( +
+ + +
+
    + {section.steps.map((step, index) => ( +
  1. + + {index + 1} + + {step} +
  2. + ))} +
+
+
+ ); + })} +
+ +
+

Lỗi thường gặp

+
+ {troubleshooting.map((item, index) => { + const isOpen = openTrouble === index; + return ( +
+ +
+

{item.answer}

+
+
+ ); + })} +
+
-
-
+ + ); } diff --git a/src/uhm/components/preview/FirstVisitGuideModal.tsx b/src/uhm/components/preview/FirstVisitGuideModal.tsx new file mode 100644 index 0000000..d990c06 --- /dev/null +++ b/src/uhm/components/preview/FirstVisitGuideModal.tsx @@ -0,0 +1,170 @@ +"use client"; + +import Link from "next/link"; +import { useCallback, useEffect, useState } from "react"; + +const STORAGE_KEY = "uhm-public-first-guide-seen-v1"; + +type GuideItem = { + title: string; + details?: string[]; +}; + +const guideItems: GuideItem[] = [ + { + title: "Kéo bản đồ để di chuyển, cuộn để phóng to.", + }, + { + title: "Dùng timeline phía dưới để chọn năm lịch sử.", + details: [ + "Kéo chuột sang trái hoặc phải để điều chỉnh năm hiển thị.", + "Lăn chuột trên timeline để điều chỉnh phạm vi kéo.", + "Nhập trực tiếp năm vào ô số để đi nhanh tới mốc cần xem.", + "Range là phạm vi năm muốn hiển thị. Ví dụ time 1990 và range 5 sẽ hiển thị thông tin từ 1985 đến 1995.", + ], + }, + { + title: "Nhấn vào vùng, điểm hoặc đường trên bản đồ để mở wiki.", + details: [ + "Có thể dùng Shift + lăn chuột để chọn thông tin chi tiết muốn tìm hiểu.", + ], + }, + { + title: "Bật/tắt lớp bản đồ ở bảng bên trái.", + details: [ + "Có thể bật/tắt các đối tượng bản đồ tự nhiên lẫn lịch sử.", + ], + }, + { + title: "Nếu đối tượng có replay, nhấn nút phát để xem diễn biến.", + details: [ + "Trong quá trình replay có thể dừng và tương tác như bình thường.", + ], + }, + { + title: "Sử dụng wiki để tìm kiếm thông tin liên quan.", + details: [ + "Đối với các link trong wiki, màu xanh là đã có thông tin, màu đỏ là chưa có thông tin.", + "Chọn link bằng chuột trái để hiển thị wiki đích và bản đồ tự di chuyển đến khu vực liên quan.", + "Có thể nhấn chuột phải vào các link để có thêm lựa chọn khác.", + ], + }, +]; + +export default function FirstVisitGuideModal() { + const [isOpen, setIsOpen] = useState(() => shouldShowFirstVisitGuide()); + + const closeGuide = useCallback(() => { + try { + window.localStorage.setItem(STORAGE_KEY, "1"); + } catch { + // Ignore storage failures; closing the modal should still work. + } + setIsOpen(false); + }, []); + + useEffect(() => { + if (!isOpen) return; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + closeGuide(); + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [closeGuide, isOpen]); + + if (!isOpen) return null; + + return ( +
+
+
+

+ Hướng dẫn nhanh +

+

+ Chào mừng đến Ultimate History Map +

+

+ Một vài thao tác chính để bạn bắt đầu xem bản đồ lịch sử, wiki và replay. +

+
+ +
+
    + {guideItems.map((item, index) => ( +
  1. + {item.details?.length ? ( +
    + + + {index + 1} + + + {item.title} + + + + + + + - + + +
      + {item.details.map((detail) => ( +
    • + {detail} +
    • + ))} +
    +
    + ) : ( +
    + + {index + 1} + + + {item.title} + +
    + )} +
  2. + ))} +
+
+ +
+ + Xem hướng dẫn chi tiết + + +
+
+
+ ); +} + +function shouldShowFirstVisitGuide(): boolean { + if (typeof window === "undefined") return false; + try { + return window.localStorage.getItem(STORAGE_KEY) !== "1"; + } catch { + return true; + } +} diff --git a/src/uhm/components/preview/PublicPreviewClientPage.tsx b/src/uhm/components/preview/PublicPreviewClientPage.tsx index 701460e..a6ad772 100644 --- a/src/uhm/components/preview/PublicPreviewClientPage.tsx +++ b/src/uhm/components/preview/PublicPreviewClientPage.tsx @@ -9,6 +9,7 @@ const PreviewMapShell = dynamic( import ReplayPreviewOverlay from "@/uhm/components/editor/ReplayPreviewOverlay"; import MapPlaceholder from "@/uhm/components/preview/MapPlaceholder"; +import FirstVisitGuideModal from "@/uhm/components/preview/FirstVisitGuideModal"; import GeometrySelectionPanel, { type GeometrySelectionRow, } from "@/uhm/components/preview/GeometrySelectionPanel"; @@ -878,6 +879,7 @@ export default function PublicPreviewClientPage({ /> ) : null}
+ )}