feat(card): 'Finish your card' checklist (photo, name, styles/level, crew)
A card is only as good as its content, so the studio now shows what's missing with one-tap links to fix it, instead of a subtle amber hint.
This commit is contained in:
parent
00753b2769
commit
80e9552d11
3 changed files with 43 additions and 0 deletions
|
|
@ -6387,6 +6387,12 @@ walletUi: {
|
||||||
editSkaterProfile: 'add your styles & level',
|
editSkaterProfile: 'add your styles & level',
|
||||||
noPhoto: 'No profile photo yet —',
|
noPhoto: 'No profile photo yet —',
|
||||||
addPhoto: 'add one in your profile',
|
addPhoto: 'add one in your profile',
|
||||||
|
finishTitle: 'Finish your card',
|
||||||
|
finishDesc: 'A photo and a Skater profile make it look real.',
|
||||||
|
checkPhoto: 'Profile photo',
|
||||||
|
checkName: 'Display name',
|
||||||
|
checkProfile: 'Styles & level',
|
||||||
|
checkCrew: 'Join a crew',
|
||||||
accent: 'Accent colour',
|
accent: 'Accent colour',
|
||||||
accentHint: 'Overrides the theme accent',
|
accentHint: 'Overrides the theme accent',
|
||||||
accentClear: 'Use theme colour',
|
accentClear: 'Use theme colour',
|
||||||
|
|
|
||||||
|
|
@ -6387,6 +6387,12 @@ walletUi: {
|
||||||
editSkaterProfile: 'añade tus estilos y nivel',
|
editSkaterProfile: 'añade tus estilos y nivel',
|
||||||
noPhoto: 'Aún no tienes foto de perfil —',
|
noPhoto: 'Aún no tienes foto de perfil —',
|
||||||
addPhoto: 'añádela en tu perfil',
|
addPhoto: 'añádela en tu perfil',
|
||||||
|
finishTitle: 'Completa tu carné',
|
||||||
|
finishDesc: 'Una foto y un perfil de skater lo hacen parecer real.',
|
||||||
|
checkPhoto: 'Foto de perfil',
|
||||||
|
checkName: 'Nombre visible',
|
||||||
|
checkProfile: 'Estilos y nivel',
|
||||||
|
checkCrew: 'Únete a una cuadrilla',
|
||||||
accent: 'Color de acento',
|
accent: 'Color de acento',
|
||||||
accentHint: 'Sustituye el acento del tema',
|
accentHint: 'Sustituye el acento del tema',
|
||||||
accentClear: 'Usar color del tema',
|
accentClear: 'Usar color del tema',
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,15 @@ export default function RiderCardPage() {
|
||||||
const derivedLevel = skaterProfile?.level
|
const derivedLevel = skaterProfile?.level
|
||||||
? (SKATER_LEVELS[skaterProfile.level]?.label ?? '')
|
? (SKATER_LEVELS[skaterProfile.level]?.label ?? '')
|
||||||
: (skaterProfile?.customLevel ?? '');
|
: (skaterProfile?.customLevel ?? '');
|
||||||
|
|
||||||
|
// "Finish your card" checklist — a card is only as good as what's on it.
|
||||||
|
const cardChecks = [
|
||||||
|
{ done: !!metadata?.picture, label: t('cardUi.studio.checkPhoto'), to: '/settings?section=profile' },
|
||||||
|
{ done: !!metadata?.name, label: t('cardUi.studio.checkName'), to: '/settings?section=profile' },
|
||||||
|
{ done: (skaterProfile?.styles?.length ?? 0) > 0 || !!skaterProfile?.level, label: t('cardUi.studio.checkProfile'), to: '/skaters' },
|
||||||
|
{ done: memberships.length > 0, label: t('cardUi.studio.checkCrew'), to: '/crews' },
|
||||||
|
];
|
||||||
|
const incomplete = cardChecks.some((c) => !c.done);
|
||||||
const { mutate: publishEvent } = useNostrPublish();
|
const { mutate: publishEvent } = useNostrPublish();
|
||||||
const { mutateAsync: uploadFile, isPending: uploadingLogo } = useUploadFile();
|
const { mutateAsync: uploadFile, isPending: uploadingLogo } = useUploadFile();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
@ -659,6 +668,27 @@ export default function RiderCardPage() {
|
||||||
{/* ══ RIGHT: inspector ══ */}
|
{/* ══ RIGHT: inspector ══ */}
|
||||||
<div className="mt-8 space-y-6 lg:mt-0">
|
<div className="mt-8 space-y-6 lg:mt-0">
|
||||||
|
|
||||||
|
{/* Finish-your-card checklist (personal card) */}
|
||||||
|
{cardType === 'rider' && incomplete && (
|
||||||
|
<div className="rounded-xl border border-[#ccff00]/30 bg-[#ccff00]/5 p-4">
|
||||||
|
<div className="text-xs font-black uppercase tracking-wide text-[#ccff00]">{t('cardUi.studio.finishTitle')}</div>
|
||||||
|
<p className="mt-1 text-[11px] text-gray-400">{t('cardUi.studio.finishDesc')}</p>
|
||||||
|
<div className="mt-3 flex flex-wrap gap-1.5">
|
||||||
|
{cardChecks.map((c) => (
|
||||||
|
c.done ? (
|
||||||
|
<span key={c.label} className="inline-flex items-center gap-1 rounded-lg border border-white/10 bg-white/5 px-2.5 py-1 text-[11px] font-bold text-green-400">
|
||||||
|
✓ {c.label}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<Link key={c.label} to={c.to} className="inline-flex items-center gap-1 rounded-lg border border-[#ccff00]/40 bg-[#ccff00]/10 px-2.5 py-1 text-[11px] font-bold text-[#ccff00] hover:bg-[#ccff00]/20">
|
||||||
|
+ {c.label}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Card type */}
|
{/* Card type */}
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-2 text-[10px] font-black uppercase tracking-widest text-gray-600">{t('labUi.rider.cardType')}</p>
|
<p className="mb-2 text-[10px] font-black uppercase tracking-widest text-gray-600">{t('labUi.rider.cardType')}</p>
|
||||||
|
|
@ -814,6 +844,7 @@ export default function RiderCardPage() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<Label className="mb-1.5 block text-[10px] uppercase tracking-widest text-gray-500">{t('labUi.rider.ridesFor')}</Label>
|
<Label className="mb-1.5 block text-[10px] uppercase tracking-widest text-gray-500">{t('labUi.rider.ridesFor')}</Label>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue