feat: FAQ pages — Q&A builder in admin, accordion view for users

Admin editor:
- page_type selector: "Страница" / "FAQ" radio buttons
- When FAQ: FaqBuilder component replaces TipTap editor
  - Question input + answer textarea per Q&A item
  - Add/remove items, up/down reorder buttons
  - Per-locale Q&A (stored as JSON array in content JSONB)
- "Создать FAQ" button in list alongside "Создать страницу"
- Filter tabs: Все / Страницы / FAQ
- Page type badge on each row (accent for pages, amber for FAQ)

Public view (InfoPageView):
- Auto-detects page_type='faq' and renders FaqView component
- Accordion with smooth height animation (300ms ease-in-out)
- Search filter appears when >3 questions
- DOMPurify sanitization on FAQ answers
- Single-item expand (click to open/close)

i18n: full FAQ keys for all 4 locales (questions, answers,
search, counts, empty states, builder labels)
This commit is contained in:
Fringg
2026-04-24 14:02:03 +03:00
parent 596f638cdd
commit 1ee0f18343
8 changed files with 781 additions and 192 deletions

View File

@@ -3804,6 +3804,7 @@
"title": "Information Pages",
"subtitle": "Manage static information pages",
"create": "Create Page",
"createFaq": "Create FAQ",
"edit": "Edit",
"save": "Save",
"saving": "Saving...",
@@ -3815,6 +3816,7 @@
"notFound": "Page not found",
"active": "Active",
"inactive": "Inactive",
"typePage": "Page",
"localeLabel": "Content Language",
"fields": {
"slug": "URL Slug",
@@ -3822,7 +3824,34 @@
"content": "Content",
"isActive": "Active",
"sortOrder": "Sort Order",
"icon": "Icon (emoji)"
"icon": "Icon (emoji)",
"pageType": "Page Type"
},
"filter": {
"all": "All",
"page": "Pages",
"faq": "FAQ"
},
"pageTypes": {
"page": "Page",
"faq": "FAQ"
},
"faq": {
"questions": "Questions & Answers",
"questionsCount": "questions",
"noQuestions": "No questions yet. Click the button below to add one.",
"questionNumber": "Question #{{n}}",
"question": "Question",
"answer": "Answer",
"questionPlaceholder": "Enter question...",
"answerPlaceholder": "Enter answer (HTML supported)...",
"htmlHint": "HTML markup is supported",
"addQuestion": "Add Question",
"removeQuestion": "Remove question",
"moveUp": "Move up",
"moveDown": "Move down",
"searchPlaceholder": "Search questions...",
"noResults": "No results found"
},
"locales": {
"ru": "Russian",

View File

@@ -3372,6 +3372,7 @@
"title": "صفحات اطلاعات",
"subtitle": "مدیریت صفحات اطلاعات ثابت",
"create": "ایجاد صفحه",
"createFaq": "ایجاد FAQ",
"edit": "ویرایش",
"save": "ذخیره",
"saving": "در حال ذخیره...",
@@ -3383,6 +3384,7 @@
"notFound": "صفحه پیدا نشد",
"active": "فعال",
"inactive": "غیرفعال",
"typePage": "صفحه",
"localeLabel": "زبان محتوا",
"fields": {
"slug": "شناسه URL",
@@ -3390,7 +3392,34 @@
"content": "محتوا",
"isActive": "فعال",
"sortOrder": "ترتیب",
"icon": "آیکون (ایموجی)"
"icon": "آیکون (ایموجی)",
"pageType": "نوع صفحه"
},
"filter": {
"all": "همه",
"page": "صفحات",
"faq": "FAQ"
},
"pageTypes": {
"page": "صفحه",
"faq": "FAQ"
},
"faq": {
"questions": "سوالات و پاسخ‌ها",
"questionsCount": "سوال",
"noQuestions": "هنوز سوالی وجود ندارد. روی دکمه زیر کلیک کنید.",
"questionNumber": "سوال #{{n}}",
"question": "سوال",
"answer": "پاسخ",
"questionPlaceholder": "سوال را وارد کنید...",
"answerPlaceholder": "پاسخ را وارد کنید (HTML پشتیبانی می‌شود)...",
"htmlHint": "نشانه‌گذاری HTML پشتیبانی می‌شود",
"addQuestion": "افزودن سوال",
"removeQuestion": "حذف سوال",
"moveUp": "انتقال به بالا",
"moveDown": "انتقال به پایین",
"searchPlaceholder": "جستجو در سوالات...",
"noResults": "نتیجه‌ای یافت نشد"
},
"locales": {
"ru": "روسی",

View File

@@ -4348,6 +4348,7 @@
"title": "Информационные страницы",
"subtitle": "Управление статическими информационными страницами",
"create": "Создать страницу",
"createFaq": "Создать FAQ",
"edit": "Редактировать",
"save": "Сохранить",
"saving": "Сохранение...",
@@ -4359,6 +4360,7 @@
"notFound": "Страница не найдена",
"active": "Активна",
"inactive": "Неактивна",
"typePage": "Страница",
"localeLabel": "Язык контента",
"fields": {
"slug": "URL-адрес",
@@ -4366,7 +4368,34 @@
"content": "Содержание",
"isActive": "Активна",
"sortOrder": "Порядок сортировки",
"icon": "Иконка (эмодзи)"
"icon": "Иконка (эмодзи)",
"pageType": "Тип страницы"
},
"filter": {
"all": "Все",
"page": "Страницы",
"faq": "FAQ"
},
"pageTypes": {
"page": "Страница",
"faq": "FAQ"
},
"faq": {
"questions": "Вопросы и ответы",
"questionsCount": "вопросов",
"noQuestions": "Вопросов пока нет. Нажмите кнопку ниже, чтобы добавить.",
"questionNumber": "Вопрос #{{n}}",
"question": "Вопрос",
"answer": "Ответ",
"questionPlaceholder": "Введите вопрос...",
"answerPlaceholder": "Введите ответ (поддерживается HTML)...",
"htmlHint": "Поддерживается HTML-разметка",
"addQuestion": "Добавить вопрос",
"removeQuestion": "Удалить вопрос",
"moveUp": "Переместить вверх",
"moveDown": "Переместить вниз",
"searchPlaceholder": "Поиск по вопросам...",
"noResults": "Ничего не найдено"
},
"locales": {
"ru": "Русский",

View File

@@ -3371,6 +3371,7 @@
"title": "信息页面",
"subtitle": "管理静态信息页面",
"create": "创建页面",
"createFaq": "创建FAQ",
"edit": "编辑",
"save": "保存",
"saving": "保存中...",
@@ -3382,6 +3383,7 @@
"notFound": "页面未找到",
"active": "已激活",
"inactive": "未激活",
"typePage": "页面",
"localeLabel": "内容语言",
"fields": {
"slug": "URL标识",
@@ -3389,7 +3391,34 @@
"content": "内容",
"isActive": "已激活",
"sortOrder": "排序",
"icon": "图标(表情)"
"icon": "图标(表情)",
"pageType": "页面类型"
},
"filter": {
"all": "全部",
"page": "页面",
"faq": "FAQ"
},
"pageTypes": {
"page": "页面",
"faq": "FAQ"
},
"faq": {
"questions": "问答",
"questionsCount": "个问题",
"noQuestions": "暂无问题。点击下方按钮添加。",
"questionNumber": "问题 #{{n}}",
"question": "问题",
"answer": "答案",
"questionPlaceholder": "输入问题...",
"answerPlaceholder": "输入答案支持HTML...",
"htmlHint": "支持HTML标记",
"addQuestion": "添加问题",
"removeQuestion": "删除问题",
"moveUp": "上移",
"moveDown": "下移",
"searchPlaceholder": "搜索问题...",
"noResults": "未找到结果"
},
"locales": {
"ru": "俄语",