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.
 
 
 
 
 

168 lines
5.5 KiB

"use client";
import { useMemo } from "react";
import { IoAlert, IoCheckmark } from "react-icons/io5";
import { getLocalSectionProgress, getStoredAge } from "./progress-helper";
import {
getQuestionListItems,
isQuestionListItemVisibleForProfile,
type QuestionListItem,
} from "@/data/question-data";
import { toFrontendSlug } from "@/data/section-slug-map";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { useMarriageSectionsQuery } from "@/hooks/marriage/use-sections";
import { useI18n } from "@/translations/provider";
type RequiredStepsCardProps = {
items?: QuestionListItem[];
progressBySlug?: Map<string, number>;
};
type RequiredStep = {
slug: string;
required: boolean;
progress: number;
};
function getRequiredStepStats(steps: RequiredStep[]) {
const requiredSteps = steps.filter((step) => step.required);
const completedSteps = requiredSteps.filter((step) => step.progress >= 100);
return {
completed: completedSteps.length,
total: requiredSteps.length,
};
}
export default function RequiredStepsCard({
items,
progressBySlug,
}: RequiredStepsCardProps = {}) {
const { dictionary: t, locale } = useI18n();
const { data: profile } = useMarriageProfileQuery();
const { data: sections } = useMarriageSectionsQuery();
const questionListItems = useMemo(
() =>
items ??
getQuestionListItems(locale).filter((item) =>
isQuestionListItemVisibleForProfile(item, {
gender: profile?.gender,
}),
),
[items, locale, profile?.gender],
);
const steps: RequiredStep[] = useMemo(() => {
const age = getStoredAge();
type SectionType = NonNullable<typeof sections>[number];
const sectionMap = new Map<string, SectionType>();
sections?.forEach((s) => {
sectionMap.set(toFrontendSlug(s.slug), s);
sectionMap.set(s.slug, s);
});
return questionListItems.map((item) => {
const localProgress = getLocalSectionProgress(item, profile, age);
const section = sectionMap.get(item.slug);
let progress = 0;
if (localProgress !== null) {
progress = localProgress;
} else if (
progressBySlug &&
typeof progressBySlug.get(item.slug) === "number"
) {
progress = progressBySlug.get(item.slug) ?? 0;
} else if (item.slug === "family_marital_history" && sections) {
const fbSec = sections.find((s) => s.slug === "family_background");
const mhSec = sections.find((s) => s.slug === "marital_history");
if (fbSec || mhSec) {
const fbTotal = fbSec?.total_steps ?? 6;
const fbCurrent = fbSec
? Math.round((fbSec.completion_percent / 100) * fbTotal)
: 0;
const mhTotal = mhSec?.total_steps ?? 6;
const mhCurrent = mhSec
? Math.round((mhSec.completion_percent / 100) * mhTotal)
: 0;
progress =
fbTotal + mhTotal > 0
? Math.max(
0,
Math.min(
100,
Math.round(
((fbCurrent + mhCurrent) / (fbTotal + mhTotal)) * 100,
),
),
)
: 0;
}
} else if (section) {
progress = Math.max(
0,
Math.min(100, Math.round(section.completion_percent)),
);
} else {
progress = item.progress;
}
return {
slug: item.slug,
required: Boolean(item.required),
progress,
};
});
}, [questionListItems, progressBySlug, sections, profile]);
const { completed, total } = getRequiredStepStats(steps);
const completion = total > 0 ? Math.round((completed / total) * 100) : 0;
const isCompleted = total > 0 && completed === total;
return (
<section
aria-label="Required steps progress"
className="rounded-[15px] bg-[#40506A] p-4 text-white shadow-[0_18px_34px_rgba(38,52,73,0.16)]"
>
<div className="flex items-center justify-between gap-5">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-3">
<span className="flex h-[24px] w-[24px] shrink-0 items-center justify-center rounded-full bg-white text-[#40506A]">
{isCompleted ? (
<IoCheckmark aria-hidden="true" className="text-[16px]" />
) : (
<IoAlert aria-hidden="true" className="text-[16px]" />
)}
</span>
<h2 className="group-16 leading-none font-bold tracking-[-0.02em]">
{t.questions.requiredSteps}
</h2>
</div>
<p className="mt-2.5 max-w-[220px] group-12 leading-[1.25] font-medium text-white/80">
{isCompleted
? t.questions.requiredStepsDescriptionCompleted
: t.questions.requiredStepsDescription}
</p>
</div>
<div
role="img"
aria-label={t.questions.requiredStepsProgress
.replace("{completed}", String(completed))
.replace("{total}", String(total))}
className="relative flex h-[60px] w-[60px] shrink-0 items-center justify-center rounded-full"
style={{
background: `conic-gradient(#FFFFFF ${completion}%,rgba(255,255,255,0.24) 0)`,
}}
>
<div className="absolute inset-[6px] rounded-full bg-[#40506A]" />
<span className="relative group-14 leading-none font-bold tracking-[-0.02em]">
{completed}/{total}
</span>
</div>
</div>
</section>
);
}