You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
7.4 KiB
283 lines
7.4 KiB
import type {
|
|
FormSchemaResponse,
|
|
FormSection,
|
|
FormQuestion,
|
|
FormOverviewResponse,
|
|
} from "@/hooks/marriage/use-form-schema";
|
|
import { defaultLocale, type Locale } from "@/translations/config";
|
|
|
|
export type QuestionCardIcon =
|
|
| "profile"
|
|
| "contact"
|
|
| "health"
|
|
| "education"
|
|
| "family"
|
|
| "marriage"
|
|
| "lifestyle"
|
|
| "criteria"
|
|
| "verification"
|
|
| "personality"
|
|
| "glasser"
|
|
| "details"
|
|
| "checklist"
|
|
| "family_marital";
|
|
|
|
export const sectionSlugIconMap: Record<string, QuestionCardIcon> = {
|
|
personal_info: "profile",
|
|
personal_identity: "profile",
|
|
contact_residence_family_communication: "contact",
|
|
contact_residence: "contact",
|
|
appearance_health_activity: "health",
|
|
appearance_health: "health",
|
|
education_career_economic_status: "education",
|
|
education_career: "education",
|
|
family_background: "family",
|
|
marital_history_children: "marriage",
|
|
marital_history: "marriage",
|
|
beliefs_lifestyle_boundaries: "lifestyle",
|
|
beliefs_lifestyle: "lifestyle",
|
|
future_spouse_criteria: "criteria",
|
|
spouse_criteria: "criteria",
|
|
identity_verification: "verification",
|
|
documents_verification: "verification",
|
|
personality_test: "personality",
|
|
cattell_test: "personality",
|
|
glasser_5_needs_test: "glasser",
|
|
glasser_test: "glasser",
|
|
};
|
|
|
|
export function resolveSectionIcon(
|
|
slug: string,
|
|
backendIcon?: string,
|
|
): QuestionCardIcon {
|
|
if (slug && sectionSlugIconMap[slug]) {
|
|
return sectionSlugIconMap[slug];
|
|
}
|
|
if (backendIcon && iconMap[backendIcon]) {
|
|
return iconMap[backendIcon];
|
|
}
|
|
return "details";
|
|
}
|
|
|
|
export type QuestionExtras = {
|
|
placeHolder: string;
|
|
range: [number, number];
|
|
options: string[];
|
|
noSearch?: boolean;
|
|
};
|
|
|
|
export type QuestionAudienceRule = {
|
|
genders?: string[];
|
|
maxAge?: number;
|
|
minAge?: number;
|
|
};
|
|
|
|
export type QuestionField = {
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
order: number;
|
|
required: boolean;
|
|
baseRequired: boolean;
|
|
isVisible: boolean;
|
|
private?: boolean;
|
|
description: string;
|
|
tooltip: string;
|
|
validation?: any;
|
|
ui_config?: any;
|
|
extras: QuestionExtras;
|
|
audience?: QuestionAudienceRule | any;
|
|
requiredWhen?: any;
|
|
conditionalRule?: any;
|
|
visibility?: any;
|
|
logic?: any;
|
|
showGuardianNotice?: boolean;
|
|
options: {
|
|
id: string;
|
|
value: string | number;
|
|
label: string;
|
|
order: number;
|
|
}[];
|
|
};
|
|
|
|
export type QuestionListItem = {
|
|
slug: string;
|
|
title: string;
|
|
estimate: string;
|
|
progress: number;
|
|
icon: QuestionCardIcon;
|
|
required: boolean;
|
|
showInfoBadge?: boolean;
|
|
summary: string;
|
|
checkpoints: string[];
|
|
tooltip?: string;
|
|
note?: string;
|
|
questions: QuestionField[];
|
|
};
|
|
|
|
const iconMap: Record<string, QuestionCardIcon> = {
|
|
"user-circle": "profile",
|
|
user: "profile",
|
|
person: "profile",
|
|
school: "education",
|
|
education: "education",
|
|
"heart-handshake": "marriage",
|
|
"heart-pulse": "health",
|
|
activity: "health",
|
|
"file-text": "contact",
|
|
contact: "contact",
|
|
users: "family",
|
|
family: "family",
|
|
compass: "lifestyle",
|
|
"target-heart": "criteria",
|
|
"shield-check": "verification",
|
|
"shield": "verification",
|
|
brain: "personality",
|
|
"layout-grid": "checklist",
|
|
"star-half": "glasser",
|
|
star: "glasser",
|
|
};
|
|
|
|
export function mapBackendQuestionToFrontend(
|
|
bq: FormQuestion,
|
|
index: number,
|
|
): QuestionField {
|
|
const minVal = bq.validation?.min;
|
|
const maxVal = bq.validation?.max;
|
|
const rangeVal = bq.validation?.range;
|
|
const uiRange = bq.ui_config?.range;
|
|
const uiMin = bq.ui_config?.min;
|
|
const uiMax = bq.ui_config?.max;
|
|
|
|
let range: [number, number] = [0, 0];
|
|
if (
|
|
Array.isArray(uiRange) &&
|
|
uiRange.length === 2 &&
|
|
(uiRange[0] !== 0 || uiRange[1] !== 0)
|
|
) {
|
|
range = [Number(uiRange[0]), Number(uiRange[1])];
|
|
} else if (
|
|
Array.isArray(rangeVal) &&
|
|
rangeVal.length === 2 &&
|
|
(rangeVal[0] !== 0 || rangeVal[1] !== 0)
|
|
) {
|
|
range = [Number(rangeVal[0]), Number(rangeVal[1])];
|
|
} else if (minVal !== undefined && maxVal !== undefined) {
|
|
range = [Number(minVal), Number(maxVal)];
|
|
} else if (uiMin !== undefined && uiMax !== undefined) {
|
|
range = [Number(uiMin), Number(uiMax)];
|
|
} else if (Array.isArray(uiRange) && uiRange.length === 2) {
|
|
range = [Number(uiRange[0]), Number(uiRange[1])];
|
|
}
|
|
|
|
return {
|
|
id: bq.id,
|
|
title: bq.title || "Untitled",
|
|
type: bq.type,
|
|
order: bq.order !== undefined ? bq.order : index,
|
|
required: bq.is_required !== undefined ? bq.is_required : bq.required,
|
|
baseRequired: bq.required,
|
|
isVisible: bq.is_visible,
|
|
private: bq.ui_config?.private,
|
|
validation: bq.validation,
|
|
ui_config: bq.ui_config,
|
|
description: bq.description || "",
|
|
tooltip: bq.tooltip || "",
|
|
extras: {
|
|
placeHolder: bq.placeholder || "",
|
|
options: bq.options?.map((o) => o.label) || [],
|
|
range,
|
|
noSearch: bq.ui_config?.noSearch,
|
|
},
|
|
showGuardianNotice: bq.show_guardian_notice,
|
|
audience: bq.audience || undefined,
|
|
requiredWhen: bq.required_when || undefined,
|
|
conditionalRule:
|
|
bq.conditional_rule || bq.visibility || bq.logic || undefined,
|
|
visibility: bq.visibility || bq.conditional_rule || undefined,
|
|
logic: bq.logic || undefined,
|
|
options: [...(bq.options || [])].sort(
|
|
(a, b) => (a.order ?? 0) - (b.order ?? 0),
|
|
),
|
|
};
|
|
}
|
|
|
|
export function mapBackendSectionToFrontend(
|
|
section: FormSection,
|
|
progress: number,
|
|
): QuestionListItem {
|
|
const allQuestions: QuestionField[] = [];
|
|
let index = 0;
|
|
|
|
const cards = [...(section.cards || [])].sort(
|
|
(a, b) => (a.order ?? 0) - (b.order ?? 0),
|
|
);
|
|
cards.forEach((card) => {
|
|
const questions = [...(card.questions || [])].sort(
|
|
(a, b) => (a.order ?? 0) - (b.order ?? 0),
|
|
);
|
|
questions.forEach((q) => {
|
|
const fq = mapBackendQuestionToFrontend(q, index);
|
|
allQuestions.push(fq);
|
|
index++;
|
|
});
|
|
});
|
|
|
|
return {
|
|
slug: section.id,
|
|
title: section.title,
|
|
estimate: section.estimated_minutes
|
|
? `${section.estimated_minutes} min`
|
|
: "5 min",
|
|
progress: progress,
|
|
icon: resolveSectionIcon(section.id, section.icon),
|
|
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);
|
|
});
|
|
|
|
return rawItems.sort(
|
|
(a, b) =>
|
|
(schema.sections.find((s) => s.id === a.slug)?.order ?? 0) -
|
|
(schema.sections.find((s) => s.id === b.slug)?.order ?? 0),
|
|
);
|
|
}
|
|
|
|
export function convertOverviewToFrontendItems(
|
|
overview: FormOverviewResponse | undefined,
|
|
): QuestionListItem[] {
|
|
if (!overview) return [];
|
|
return [...overview.sections]
|
|
.sort((a, b) => a.order - b.order)
|
|
.map((section) => ({
|
|
slug: section.id,
|
|
title: section.title,
|
|
estimate: section.estimated_minutes
|
|
? `${section.estimated_minutes} min`
|
|
: "5 min",
|
|
progress: section.progress?.completion_percent ?? 0,
|
|
icon: resolveSectionIcon(section.id, section.icon),
|
|
required: section.is_required,
|
|
showInfoBadge: false,
|
|
summary: "",
|
|
checkpoints: [],
|
|
tooltip: "",
|
|
questions: [],
|
|
}));
|
|
}
|