Browse Source
feat: implement question detail client logic with automated test flow and answer storage integration
front-test-2
feat: implement question detail client logic with automated test flow and answer storage integration
front-test-2
7 changed files with 186 additions and 945 deletions
-
275src/app/questions-list/[slug]/question-detail-client.tsx
-
66src/app/questions-list/page.tsx
-
476src/components/Componentes/progress-helper.ts
-
17src/components/Componentes/question-answer-storage.tsx
-
76src/components/Componentes/required-steps-card.tsx
-
91src/hooks/marriage/use-section-data.ts
-
130src/lib/schema-adapter.ts
@ -0,0 +1,130 @@ |
|||||
|
import type { |
||||
|
FormSchemaResponse, |
||||
|
FormSection, |
||||
|
FormQuestion, |
||||
|
} from "@/hooks/marriage/use-form-schema"; |
||||
|
import type { |
||||
|
QuestionListItem, |
||||
|
QuestionField, |
||||
|
QuestionCardIcon, |
||||
|
} from "@/data/question-data"; |
||||
|
import { defaultLocale, type Locale } from "@/translations/config"; |
||||
|
|
||||
|
const iconMap: Record<string, QuestionCardIcon> = { |
||||
|
"user-circle": "profile", |
||||
|
school: "education", |
||||
|
"heart-handshake": "details", |
||||
|
"file-text": "contact", |
||||
|
"layout-grid": "checklist", |
||||
|
}; |
||||
|
|
||||
|
export function mapBackendQuestionToFrontend(bq: FormQuestion, index: number, originalSlug?: string): QuestionField { |
||||
|
return { |
||||
|
title: bq.title || "Untitled", |
||||
|
englishTitle: bq.title, |
||||
|
type: bq.type, |
||||
|
required: bq.required, |
||||
|
private: bq.ui_config?.private, |
||||
|
description: bq.description || "", |
||||
|
tooltip: bq.tooltip || "", |
||||
|
extras: { |
||||
|
placeHolder: bq.placeholder || "", |
||||
|
options: bq.options?.map((o) => o.label) || [], |
||||
|
range: bq.ui_config?.range || [0, 0], |
||||
|
noSearch: bq.ui_config?.noSearch, |
||||
|
}, |
||||
|
logic: bq.logic ? { dependsOn: bq.logic.dependsOn } : undefined, |
||||
|
showGuardianNotice: bq.show_guardian_notice, |
||||
|
originalSlug: originalSlug, |
||||
|
originalIndex: index, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export function mapBackendSectionToFrontend( |
||||
|
section: FormSection, |
||||
|
progress: number |
||||
|
): QuestionListItem { |
||||
|
const allQuestions: QuestionField[] = []; |
||||
|
let index = 0; |
||||
|
|
||||
|
section.cards.forEach((card) => { |
||||
|
card.questions.forEach((q) => { |
||||
|
const fq = mapBackendQuestionToFrontend(q, index, section.id); |
||||
|
fq.required = q.required; |
||||
|
(fq as any).backendId = q.id; |
||||
|
(fq as any).isVisible = q.is_visible; |
||||
|
(fq as any).backendOptions = q.options; |
||||
|
|
||||
|
allQuestions.push(fq); |
||||
|
index++; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return { |
||||
|
slug: section.id, |
||||
|
title: section.title, |
||||
|
estimate: section.estimated_minutes ? `${section.estimated_minutes} min` : "5 min", |
||||
|
progress: progress, |
||||
|
icon: iconMap[section.icon] ?? "details", |
||||
|
required: section.is_required, |
||||
|
showInfoBadge: false, |
||||
|
summary: "", |
||||
|
checkpoints: allQuestions.map((q) => q.title), |
||||
|
tooltip: "", |
||||
|
questions: allQuestions, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export function convertSchemaToFrontendItems( |
||||
|
schema: FormSchemaResponse | undefined, |
||||
|
locale: Locale = defaultLocale |
||||
|
): QuestionListItem[] { |
||||
|
if (!schema) return []; |
||||
|
|
||||
|
const rawItems = schema.sections.map((sec) => { |
||||
|
const progInfo = schema.progress?.sections_progress?.[sec.id]; |
||||
|
const progress = progInfo ? progInfo.completion_percent : 0; |
||||
|
return mapBackendSectionToFrontend(sec, progress); |
||||
|
}); |
||||
|
|
||||
|
const fbIndex = rawItems.findIndex((item) => item.slug === "family_background"); |
||||
|
const mhIndex = rawItems.findIndex((item) => item.slug === "marital_history_children" || item.slug === "marital_history"); |
||||
|
|
||||
|
if (fbIndex !== -1 && mhIndex !== -1) { |
||||
|
const fbItem = rawItems[fbIndex]; |
||||
|
const mhItem = rawItems[mhIndex]; |
||||
|
|
||||
|
const fbProgInfo = schema.progress?.sections_progress?.[fbItem.slug]; |
||||
|
const mhProgInfo = schema.progress?.sections_progress?.[mhItem.slug]; |
||||
|
|
||||
|
const fbTotal = fbProgInfo?.total_steps ?? 0; |
||||
|
const mhTotal = mhProgInfo?.total_steps ?? 0; |
||||
|
const fbCurrent = fbProgInfo?.current_step ?? 0; |
||||
|
const mhCurrent = mhProgInfo?.current_step ?? 0; |
||||
|
|
||||
|
let combinedProgress = 0; |
||||
|
if (fbTotal + mhTotal > 0) { |
||||
|
combinedProgress = Math.round(((fbCurrent + mhCurrent) / (fbTotal + mhTotal)) * 100); |
||||
|
} |
||||
|
|
||||
|
const mergedItem: QuestionListItem = { |
||||
|
slug: "family_marital_history", |
||||
|
title: locale === "fa" ? "سوابق ازدواج و خانواده" : "Family & Marital History", |
||||
|
estimate: "5 min", |
||||
|
progress: Math.max(0, Math.min(100, combinedProgress)), |
||||
|
icon: "family_marital" as QuestionCardIcon, |
||||
|
required: fbItem.required || mhItem.required, |
||||
|
summary: "", |
||||
|
tooltip: "", |
||||
|
checkpoints: [...fbItem.checkpoints, ...mhItem.checkpoints], |
||||
|
questions: [...fbItem.questions, ...mhItem.questions], |
||||
|
}; |
||||
|
|
||||
|
const newItems = [...rawItems]; |
||||
|
newItems.splice(Math.max(fbIndex, mhIndex), 1); |
||||
|
newItems.splice(Math.min(fbIndex, mhIndex), 1, mergedItem); |
||||
|
return newItems; |
||||
|
} |
||||
|
|
||||
|
return rawItems; |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue