forked from Tadrisino/MathApp_frontend
- Text: kasra/fatha/damma/shadda/sukun/hamza-above and «هی» ezafe removed from every user-visible string (comments untouched); tanwin kept. Stale copy fixed (question packs are no longer sold), awkward onboarding sentences rewritten, سؤال → سوال. plainPersian() in MathText cleans the live tutor stream. - Home: the mascot + speech bubble header is gone; the selected companion hops beside the current island and sits at every third bend (CSS transform animations only). - Checkout: order summary, payment-method rows with Zarinpal/Bazaar icons and radio, amount on the pay button, lock trust line, darker overlay. paymentMethods in config.js per channel (web: zarinpal, app: zarinpal+bazaar by default). - Profile: «ورود با حساب دیگر» removed (it was logout); new «حساب کاربری» sheet shows phone/ID and holds «حذف حساب کاربری»; logout is a red row. Delete sheet tells Bazaar subscribers to cancel renewal in Bazaar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.7 KiB
TypeScript
84 lines
3.7 KiB
TypeScript
import { useState } from 'react';
|
||
import { Crown, Sparkles } from 'lucide-react';
|
||
import type { HomeNode } from '@/api';
|
||
import { useAppData } from '@/state/AppData';
|
||
import { findUnit, unitColor, unitProgress } from '@/state/curriculum';
|
||
import { useNav } from '@/nav/Navigator';
|
||
import { IslandPath } from '@/components/IslandPath';
|
||
import { IslandSheet } from '@/components/IslandSheet';
|
||
import { EmptyState, ErrorState, LoadingState, ProgressBar, Screen, ScreenHeader } from '@/components/ui';
|
||
import { toFa } from '@/lib/format';
|
||
import { MathText } from '@/lib/MathText';
|
||
|
||
/** نقشهی جزیرههای یک فصل، مثلِ مسیرِ دولینگو. */
|
||
export function UnitScreen({ unitId }: { unitId: string }) {
|
||
const { home, homeError, refreshHome, companion } = useAppData();
|
||
const nav = useNav();
|
||
const paywall = () => nav.push({ name: 'subscription' });
|
||
const [openNode, setOpenNode] = useState<HomeNode | null>(null);
|
||
const found = findUnit(home, unitId);
|
||
|
||
if (!found) {
|
||
return (
|
||
<Screen>
|
||
<ScreenHeader title="…" color="#91ADC6" back />
|
||
{homeError ? <ErrorState error={homeError} onRetry={() => void refreshHome()} /> : home ? <EmptyState text="این فصل پیدا نشد" /> : <LoadingState />}
|
||
</Screen>
|
||
);
|
||
}
|
||
|
||
const { unit, unitIndex } = found;
|
||
const color = unitColor(unitIndex);
|
||
const progress = unitProgress(unit);
|
||
const current = unit.nodes.find((n) => n.status !== 'completed' && n.status !== 'locked') ?? null;
|
||
|
||
return (
|
||
<Screen className="bg-page">
|
||
<ScreenHeader
|
||
title={unit.title}
|
||
subtitle={unit.blurb ? <MathText text={unit.blurb} /> : `${toFa(progress.done)} از ${toFa(progress.total)} جزیره`}
|
||
color={color}
|
||
back
|
||
mascot={{ char: companion, mood: progress.percent >= 50 ? 'happy' : 'idle', size: 72 }}
|
||
>
|
||
<div className="mt-4">
|
||
<div className="mb-1.5 flex justify-between text-[10px] text-white/75">
|
||
<span>پیشرفت تو</span>
|
||
<span>{toFa(progress.percent)}٪</span>
|
||
</div>
|
||
<ProgressBar percent={progress.percent} color="#fff" track="rgba(0,0,0,.1)" height={10} />
|
||
</div>
|
||
</ScreenHeader>
|
||
|
||
<div className="flex-1 overflow-y-auto pb-6 pt-10">
|
||
{unit.nodes.length === 0 ? (
|
||
<EmptyState text="این فصل هنوز جزیرهای نداره" />
|
||
) : (
|
||
<>
|
||
<IslandPath
|
||
nodes={unit.nodes}
|
||
color={color}
|
||
currentId={current?.id ?? null}
|
||
onOpen={(node) => (unit.requiresSubscription ? paywall() : setOpenNode(node))}
|
||
onStart={(node) => (unit.requiresSubscription ? paywall() : nav.push({ name: 'quiz', nodeId: node.id }))}
|
||
/>
|
||
{unit.requiresSubscription && (
|
||
<button type="button" onClick={paywall} className="mx-auto mt-6 flex items-center gap-2 rounded-2xl bg-orange px-5 py-3 text-sm font-black text-white shadow-lg shadow-orange/30">
|
||
<Crown size={16} /> این فصل با اشتراک باز میشه
|
||
</button>
|
||
)}
|
||
<div className="mt-6 flex flex-col items-center">
|
||
<span className="mb-1 text-[11px] font-bold text-muted">{progress.done === progress.total ? 'این فصل تموم شد!' : 'ادامه بده!'}</span>
|
||
<span className="flex h-11 w-11 items-center justify-center rounded-2xl" style={{ backgroundColor: `${color}18` }}>
|
||
<Sparkles size={20} style={{ color }} />
|
||
</span>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
<IslandSheet node={openNode} color={color} onClose={() => setOpenNode(null)} />
|
||
</Screen>
|
||
);
|
||
}
|