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

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;
}
}