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.
228 lines
6.6 KiB
228 lines
6.6 KiB
import {
|
|
isQuestionVisibleForProfile,
|
|
type QuestionListItem,
|
|
} from "@/data/question-data";
|
|
import type {
|
|
MarriageFieldValue,
|
|
MarriageGender,
|
|
} from "@/hooks/marriage/types";
|
|
import { hasQuestionAnswerValue } from "./question-answer-storage";
|
|
|
|
export function getStoredAge(): number | null {
|
|
try {
|
|
if (typeof window === "undefined") return null;
|
|
const rawValue = window.localStorage.getItem(
|
|
"marriage:sections:personal_info:answers",
|
|
);
|
|
if (!rawValue) return null;
|
|
const storedAnswers = JSON.parse(rawValue);
|
|
const ageField = storedAnswers.fields?.find(
|
|
(f: Record<string, unknown>) =>
|
|
f.type === "number" ||
|
|
f.label === "Age" ||
|
|
f.label === "سن" ||
|
|
(typeof f.key === "string" &&
|
|
(f.key.endsWith("_age") || f.key.endsWith("_sn"))),
|
|
);
|
|
if (ageField && ageField.value !== undefined && ageField.value !== null) {
|
|
const num = Number(ageField.value);
|
|
if (Number.isFinite(num)) return num;
|
|
}
|
|
const dobField = storedAnswers.fields?.find(
|
|
(f: Record<string, unknown>) =>
|
|
f.type === "date" ||
|
|
f.label === "Date of Birth" ||
|
|
f.label === "تاریخ تولد" ||
|
|
(typeof f.key === "string" &&
|
|
(f.key.endsWith("_date_of_birth") || f.key.endsWith("_tarykh_twld"))),
|
|
);
|
|
if (dobField?.value) {
|
|
const dob = new Date(String(dobField.value));
|
|
if (!Number.isNaN(dob.getTime())) {
|
|
const today = new Date();
|
|
let age = today.getFullYear() - dob.getFullYear();
|
|
const m = today.getMonth() - dob.getMonth();
|
|
if (m < 0 || (m === 0 && today.getDate() < dob.getDate())) {
|
|
age--;
|
|
}
|
|
return age >= 0 ? age : null;
|
|
}
|
|
}
|
|
} catch (_e) {}
|
|
return null;
|
|
}
|
|
|
|
function isFieldAnswered(
|
|
q: Record<string, unknown>,
|
|
field: Record<string, unknown> | undefined,
|
|
): boolean {
|
|
if (!field || !hasQuestionAnswerValue(field.value as MarriageFieldValue)) {
|
|
return false;
|
|
}
|
|
if (q.type === "birthplace") {
|
|
const strVal = String(field.value);
|
|
const parts = strVal.split(",").map((p) => p.trim());
|
|
return parts.length >= 2 && parts[0].length > 0 && parts[1].length > 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function hashString(value: string) {
|
|
let hash = 0;
|
|
for (let index = 0; index < value.length; index += 1) {
|
|
hash = (hash * 31 + value.charCodeAt(index)) >>> 0;
|
|
}
|
|
return hash.toString(36);
|
|
}
|
|
|
|
function slugifyTitle(title: string) {
|
|
const slug = title
|
|
.normalize("NFKD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "_")
|
|
.replace(/^_+|_+$/g, "");
|
|
return slug || `field_${hashString(title)}`;
|
|
}
|
|
|
|
export function getLocalSectionProgress(
|
|
item: QuestionListItem,
|
|
profile: { gender?: MarriageGender | null } | null | undefined,
|
|
age: number | null,
|
|
): number | null {
|
|
try {
|
|
if (typeof window === "undefined") return null;
|
|
|
|
const fields: Record<string, unknown>[] = [];
|
|
let hasFoundStorage = false;
|
|
|
|
const mainKey = `marriage:sections:${item.slug}:answers`;
|
|
const mainRaw = window.localStorage.getItem(mainKey);
|
|
if (mainRaw) {
|
|
try {
|
|
const parsed = JSON.parse(mainRaw);
|
|
if (
|
|
parsed &&
|
|
Array.isArray(parsed.fields) &&
|
|
parsed.fields.length > 0
|
|
) {
|
|
fields.push(...parsed.fields);
|
|
hasFoundStorage = true;
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
if (item.slug === "family_marital_history") {
|
|
const fbRaw = window.localStorage.getItem(
|
|
"marriage:sections:family_background:answers",
|
|
);
|
|
if (fbRaw) {
|
|
try {
|
|
const parsed = JSON.parse(fbRaw);
|
|
if (parsed && Array.isArray(parsed.fields)) {
|
|
fields.push(...parsed.fields);
|
|
hasFoundStorage = true;
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
const mhRaw = window.localStorage.getItem(
|
|
"marriage:sections:marital_history_children:answers",
|
|
);
|
|
if (mhRaw) {
|
|
try {
|
|
const parsed = JSON.parse(mhRaw);
|
|
if (parsed && Array.isArray(parsed.fields)) {
|
|
fields.push(...parsed.fields);
|
|
hasFoundStorage = true;
|
|
}
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
if (!hasFoundStorage || fields.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const profileContext = {
|
|
age,
|
|
gender: profile?.gender,
|
|
};
|
|
|
|
const hasDobQuestion = item.questions.some(
|
|
(q) => q.title === "Date of Birth" || q.title === "تاریخ تولد",
|
|
);
|
|
|
|
const profileVisible = item.questions.filter((question) => {
|
|
if (
|
|
hasDobQuestion &&
|
|
(question.title === "Age" || question.title === "سن")
|
|
) {
|
|
return false;
|
|
}
|
|
return isQuestionVisibleForProfile(question, profileContext);
|
|
});
|
|
|
|
const findAnswer = (
|
|
questionTitle: string,
|
|
questionIndex: number,
|
|
originalIndex?: number,
|
|
) => {
|
|
const indexToUse =
|
|
originalIndex !== undefined ? originalIndex : questionIndex;
|
|
const key = `q${indexToUse + 1}_${slugifyTitle(questionTitle)}`;
|
|
const field = fields.find(
|
|
(f) => f && (f.key === key || f.label === questionTitle),
|
|
);
|
|
return field?.value;
|
|
};
|
|
|
|
const activeQuestions = profileVisible.filter((question, _idx) => {
|
|
if (question.logic?.dependsOn) {
|
|
const { title, values } = question.logic.dependsOn;
|
|
const depIndex = profileVisible.findIndex((q) => q.title === title);
|
|
const depQuestion = depIndex !== -1 ? profileVisible[depIndex] : null;
|
|
const depAnswer = depQuestion
|
|
? findAnswer(depQuestion.title, depIndex, depQuestion.originalIndex)
|
|
: undefined;
|
|
|
|
if (depAnswer === undefined || depAnswer === null) {
|
|
return false;
|
|
}
|
|
|
|
const ansList = Array.isArray(depAnswer)
|
|
? depAnswer.map(String)
|
|
: [String(depAnswer)];
|
|
|
|
const isMatch = values.some((val) =>
|
|
ansList.some((a) => a === val || a.includes(val) || val.includes(a)),
|
|
);
|
|
|
|
if (!isMatch) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (activeQuestions.length === 0) {
|
|
return 100;
|
|
}
|
|
|
|
const answeredCount = activeQuestions.filter((q, idx) => {
|
|
const indexToUse = q.originalIndex !== undefined ? q.originalIndex : idx;
|
|
const key = `q${indexToUse + 1}_${slugifyTitle(q.title)}`;
|
|
const field = fields.find(
|
|
(f) => f && (f.key === key || f.label === q.title),
|
|
);
|
|
return isFieldAnswered(q, field);
|
|
}).length;
|
|
|
|
return Math.max(
|
|
0,
|
|
Math.min(100, Math.round((answeredCount / activeQuestions.length) * 100)),
|
|
);
|
|
} catch (_e) {
|
|
return null;
|
|
}
|
|
}
|