forked from Tadrisino/MathApp_frontend
parent report page with two charts, Jalali birth date picker, user ID and invoices in profile
- the parents section is gone: no parent login, invite codes or "I'm a parent" button; "گزارشِ والدین" in the profile shows the week against the previous one (praise line, six tiles, minutes-per-day and right/wrong-per-day charts, a table of the numbers) - birth date as day / month / year selects (Jalali, leap Esfand handled) instead of age; adults (19+) are asked which grade's math they want to learn - user ID under the name, copyable - "پرداختها و فاکتورها": every order with invoice number, discount, amount and reference code - tutor: when the free daily quota runs out, a button to the subscription page instead of "retry" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
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';
|
||||
@@ -20,7 +19,7 @@ type Flow =
|
||||
| { kind: 'onboarding' }
|
||||
| { kind: 'login' }
|
||||
| { kind: 'setup'; profile: UserProfile; grades: Grade[] }
|
||||
| { kind: 'main'; profile: UserProfile; grades: Grade[]; start?: Route };
|
||||
| { kind: 'main'; profile: UserProfile; grades: Grade[] };
|
||||
|
||||
/** کمترین زمانِ نمایشِ اسپلش، تا صفحه چشمک نزند. */
|
||||
const SPLASH_MIN_MS = 700;
|
||||
@@ -37,8 +36,6 @@ export function App() {
|
||||
const [profile, grades] = await Promise.all([api.profile.get(), api.grades()]);
|
||||
await trackReferral(profile);
|
||||
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 });
|
||||
}, []);
|
||||
|
||||
@@ -117,7 +114,6 @@ 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' && (
|
||||
@@ -125,7 +121,6 @@ export function App() {
|
||||
profile={flow.profile}
|
||||
grades={flow.grades}
|
||||
payment={launchPayment}
|
||||
start={flow.start}
|
||||
onSignedOut={() => setFlow({ kind: 'login' })}
|
||||
onRegistered={trackReferral}
|
||||
/>
|
||||
@@ -137,21 +132,6 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* اگر کاربر با لینکِ دعوت آمده بود و حالا با شماره وارد شده، معرفی را ثبت کن.
|
||||
* مهمان را سرور نمیپذیرد، پس کد تا ثبتِ شماره نگه داشته میشود.
|
||||
|
||||
@@ -26,13 +26,14 @@ function screen<M extends Record<K, ComponentType<any>>, K extends keyof M>(load
|
||||
}
|
||||
const AskScreen = screen(() => import('@/screens/AskScreen'), 'AskScreen');
|
||||
const CharactersScreen = screen(() => import('@/screens/CharactersScreen'), 'CharactersScreen');
|
||||
const FamilyScreen = screen(() => import('@/screens/FamilyScreen'), 'FamilyScreen');
|
||||
const InviteScreen = screen(() => import('@/screens/InviteScreen'), 'InviteScreen');
|
||||
const InvoicesScreen = screen(() => import('@/screens/InvoicesScreen'), 'InvoicesScreen');
|
||||
const LeaderboardScreen = screen(() => import('@/screens/LeaderboardScreen'), 'LeaderboardScreen');
|
||||
const LessonScreen = screen(() => import('@/screens/LessonScreen'), 'LessonScreen');
|
||||
const MapScreen = screen(() => import('@/screens/MapScreen'), 'MapScreen');
|
||||
const NotificationsScreen = screen(() => import('@/screens/NotificationsScreen'), 'NotificationsScreen');
|
||||
const ProfileScreen = screen(() => import('@/screens/ProfileScreen'), 'ProfileScreen');
|
||||
const ReportScreen = screen(() => import('@/screens/ReportScreen'), 'ReportScreen');
|
||||
const QuizScreen = screen(() => import('@/screens/QuizScreen'), 'QuizScreen');
|
||||
const StreakScreen = screen(() => import('@/screens/StreakScreen'), 'StreakScreen');
|
||||
const SubscriptionScreen = screen(() => import('@/screens/SubscriptionScreen'), 'SubscriptionScreen');
|
||||
@@ -52,18 +53,16 @@ interface Props {
|
||||
profile: UserProfile;
|
||||
grades: Grade[];
|
||||
payment: PaymentReturn | null;
|
||||
/** صفحهی شروع؛ برای والد بخشِ والدین است. */
|
||||
start?: Route;
|
||||
onSignedOut: () => void;
|
||||
onRegistered: (profile: UserProfile) => Promise<void>;
|
||||
}
|
||||
|
||||
/** اپِ اصلی بعد از ورود: تبها، صفحهها و دادهی مشترک. */
|
||||
export function MainApp({ profile, grades, payment, start, onSignedOut, onRegistered }: Props) {
|
||||
export function MainApp({ profile, grades, payment, onSignedOut, onRegistered }: Props) {
|
||||
useEffect(preloadScreens, []);
|
||||
return (
|
||||
<AppDataProvider initialProfile={profile} grades={grades}>
|
||||
<NavigatorProvider initial={start ?? { name: payment ? 'subscription' : 'home' }}>
|
||||
<NavigatorProvider initial={{ name: payment ? 'subscription' : 'home' }}>
|
||||
<Screens onSignedOut={onSignedOut} onRegistered={onRegistered} />
|
||||
{payment && <PaymentResult payment={payment} />}
|
||||
</NavigatorProvider>
|
||||
@@ -114,7 +113,8 @@ function RouteView({ route, onSignedOut, onRegistered }: { route: Route } & Pick
|
||||
case 'characters': return <CharactersScreen />;
|
||||
case 'streak': return <StreakScreen />;
|
||||
case 'support': return <SupportScreen />;
|
||||
case 'family': return <FamilyScreen onSignedOut={onSignedOut} />;
|
||||
case 'report': return <ReportScreen />;
|
||||
case 'invoices': return <InvoicesScreen />;
|
||||
case 'invite': return <InviteScreen />;
|
||||
case 'register': return <RegisterView onRegistered={onRegistered} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user