Progress (interpreted as a completeness indicator, not a wizard — the form stays one scroll): - LabProfileProgress shows photo/bio/skills/rate/location/contact as done/todo with a % bar; wired into CraftProfileForm, coach, and the 4 bespoke pages. Validation (was toast-only): - CraftProfileForm splits email/website/portfolio into per-field inline errors with a red field border, gives the first skill section + contact fields ids, and scrolls to the first problem on submit; required sections show an inline message after a submit attempt. - Coach page likewise splits email/website, shows inline errors + red borders, and scrolls to the contact field or the skills grid.
55 lines
2.1 KiB
TypeScript
55 lines
2.1 KiB
TypeScript
import { useTranslation } from '@/contexts/language';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface LabProfileProgressProps {
|
|
hasPhoto: boolean;
|
|
hasBio: boolean;
|
|
hasSkills: boolean;
|
|
hasRate: boolean;
|
|
hasLocation: boolean;
|
|
hasContact: boolean;
|
|
}
|
|
|
|
/**
|
|
* Compact "profile completeness" bar for the long Lab offer forms. Not a wizard
|
|
* — the form stays in one scroll — but it shows what's still worth adding so an
|
|
* incomplete listing isn't published blind.
|
|
*/
|
|
export function LabProfileProgress(props: LabProfileProgressProps) {
|
|
const { t } = useTranslation();
|
|
const steps = [
|
|
{ label: t('labUi.progress.photo'), done: props.hasPhoto },
|
|
{ label: t('labUi.progress.bio'), done: props.hasBio },
|
|
{ label: t('labUi.progress.skills'), done: props.hasSkills },
|
|
{ label: t('labUi.progress.rate'), done: props.hasRate },
|
|
{ label: t('labUi.progress.location'), done: props.hasLocation },
|
|
{ label: t('labUi.progress.contact'), done: props.hasContact },
|
|
];
|
|
const done = steps.filter((s) => s.done).length;
|
|
const pct = Math.round((done / steps.length) * 100);
|
|
|
|
return (
|
|
<div className="mb-4 rounded-xl border border-white/10 bg-white/[0.03] p-3">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-[10px] font-black uppercase tracking-widest text-gray-500">{t('labUi.progress.title')}</span>
|
|
<span className="text-[11px] font-bold text-gray-400">{t('labUi.progress.count', { done, total: steps.length })}</span>
|
|
</div>
|
|
<div className="mt-2 h-1.5 overflow-hidden rounded-full bg-white/10">
|
|
<div className="h-full rounded-full bg-[#ccff00] transition-all" style={{ width: `${pct}%` }} />
|
|
</div>
|
|
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
{steps.map((s) => (
|
|
<span
|
|
key={s.label}
|
|
className={cn(
|
|
'rounded-full border px-2 py-0.5 text-[11px] font-semibold',
|
|
s.done ? 'border-[#ccff00]/40 bg-[#ccff00]/10 text-[#ccff00]' : 'border-white/10 text-gray-500',
|
|
)}
|
|
>
|
|
{s.done ? '✓ ' : ''}{s.label}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|