give parents their own way in instead of the student setup form

A parent account has no grade, so the app sent the demo mother to the
"which grade are you in?" form and she never reached the parents screen.
Now an account with a name, no grade and a parent-side family link opens
straight on the parents screen (with a logout button, since it has no tab
bar), and the setup form offers "I'm a parent, not a student" for new ones.
The header's back button only shows when there is somewhere to go back to.
Found on the live site with the demo accounts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 01:33:39 +03:30
parent 33e539e562
commit 2b4db90fe4
5 changed files with 63 additions and 8 deletions

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { api, ApiError, http, type Grade, type UserProfile } from '@/api';
import type { Route } from '@/nav/routes';
import { AppShell } from '@/components/AppShell';
import { ToastProvider } from '@/components/Toast';
import { ErrorState } from '@/components/ui';
@@ -19,7 +20,7 @@ type Flow =
| { kind: 'onboarding' }
| { kind: 'login' }
| { kind: 'setup'; profile: UserProfile; grades: Grade[] }
| { kind: 'main'; profile: UserProfile; grades: Grade[] };
| { kind: 'main'; profile: UserProfile; grades: Grade[]; start?: Route };
/** کمترین زمانِ نمایشِ اسپلش، تا صفحه چشمک نزند. */
const SPLASH_MIN_MS = 700;
@@ -35,7 +36,10 @@ export function App() {
const enter = useCallback(async () => {
const [profile, grades] = await Promise.all([api.profile.get(), api.grades()]);
await trackReferral(profile);
setFlow(profile.name && profile.gradeId ? { kind: 'main', profile, grades } : { kind: 'setup', profile, grades });
if (profile.name && profile.gradeId) setFlow({ kind: 'main', profile, grades });
// والد پایه ندارد؛ به‌جای فرمِ دانش‌آموز، مستقیم به بخشِ والدین.
else if (profile.name && (await isParent())) setFlow({ kind: 'main', profile, grades, start: { name: 'family' } });
else setFlow({ kind: 'setup', profile, grades });
}, []);
const boot = useCallback(async () => {
@@ -113,6 +117,7 @@ export function App() {
profile={flow.profile}
grades={flow.grades}
onDone={(profile) => setFlow({ kind: 'main', profile, grades: flow.grades })}
onParent={(profile) => setFlow({ kind: 'main', profile, grades: flow.grades, start: { name: 'family' } })}
/>
)}
{flow.kind === 'main' && (
@@ -120,6 +125,7 @@ export function App() {
profile={flow.profile}
grades={flow.grades}
payment={launchPayment}
start={flow.start}
onSignedOut={() => setFlow({ kind: 'login' })}
onRegistered={trackReferral}
/>
@@ -131,6 +137,21 @@ export function App() {
);
}
/**
* آیا این حساب والد است؟ سرور نقش را صریح نمی‌دهد: پیوندِ «در انتظار» فقط برای
* والدی که دعوت ساخته دیده می‌شود، و گزارشِ هفتگی فقط به والد جواب می‌دهد.
*/
async function isParent(): Promise<boolean> {
try {
const link = await api.family.link();
if (link.status === 'pending') return true;
await api.family.weeklyReport();
return true;
} catch {
return false;
}
}
/**
* اگر کاربر با لینکِ دعوت آمده بود و حالا با شماره وارد شده، معرفی را ثبت کن.
* مهمان را سرور نمی‌پذیرد، پس کد تا ثبتِ شماره نگه داشته می‌شود.