mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
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:
@@ -1,10 +1,13 @@
|
||||
import apiClient from './client';
|
||||
|
||||
export type InfoPageType = 'page' | 'faq';
|
||||
|
||||
export interface InfoPage {
|
||||
id: number;
|
||||
slug: string;
|
||||
title: Record<string, string>;
|
||||
content: Record<string, string>;
|
||||
page_type: InfoPageType;
|
||||
is_active: boolean;
|
||||
sort_order: number;
|
||||
icon: string | null;
|
||||
@@ -16,6 +19,7 @@ export interface InfoPageListItem {
|
||||
id: number;
|
||||
slug: string;
|
||||
title: Record<string, string>;
|
||||
page_type: InfoPageType;
|
||||
is_active: boolean;
|
||||
sort_order: number;
|
||||
icon: string | null;
|
||||
@@ -26,6 +30,7 @@ export interface InfoPageCreateRequest {
|
||||
slug: string;
|
||||
title: Record<string, string>;
|
||||
content: Record<string, string>;
|
||||
page_type: InfoPageType;
|
||||
is_active: boolean;
|
||||
sort_order: number;
|
||||
icon: string | null;
|
||||
@@ -35,19 +40,27 @@ export interface InfoPageUpdateRequest {
|
||||
slug?: string;
|
||||
title?: Record<string, string>;
|
||||
content?: Record<string, string>;
|
||||
page_type?: InfoPageType;
|
||||
is_active?: boolean;
|
||||
sort_order?: number;
|
||||
icon?: string | null;
|
||||
}
|
||||
|
||||
/** Single FAQ Q&A item stored in content JSONB. */
|
||||
export interface FaqItem {
|
||||
q: string;
|
||||
a: string;
|
||||
}
|
||||
|
||||
export interface InfoPageReorderRequest {
|
||||
items: Array<{ id: number; sort_order: number }>;
|
||||
}
|
||||
|
||||
export const infoPagesApi = {
|
||||
// Public endpoints
|
||||
getPages: async (): Promise<InfoPageListItem[]> => {
|
||||
const response = await apiClient.get<InfoPageListItem[]>('/cabinet/info-pages');
|
||||
getPages: async (pageType?: InfoPageType): Promise<InfoPageListItem[]> => {
|
||||
const params = pageType ? { page_type: pageType } : undefined;
|
||||
const response = await apiClient.get<InfoPageListItem[]>('/cabinet/info-pages', { params });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -59,8 +72,11 @@ export const infoPagesApi = {
|
||||
},
|
||||
|
||||
// Admin endpoints
|
||||
getAdminPages: async (): Promise<InfoPageListItem[]> => {
|
||||
const response = await apiClient.get<InfoPageListItem[]>('/cabinet/admin/info-pages');
|
||||
getAdminPages: async (pageType?: InfoPageType): Promise<InfoPageListItem[]> => {
|
||||
const params = pageType ? { page_type: pageType } : undefined;
|
||||
const response = await apiClient.get<InfoPageListItem[]>('/cabinet/admin/info-pages', {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user