draw every kind of textbook figure, lock math text left-to-right, student-first family screen
- src/diagram: plane (constructed points, graphs with computed roots, vertex, extrema, tangent, asymptotes, piecewise limits, regions, rotations and reflections, line from ax + by = c), number line with intervals and stacked fractions, solids with hidden edges, bar/line/pie charts, sign tables, Venn, arrow mappings, probability trees, fraction bars and mixed numbers, grids, groups, clock. Pure-math text is bidi-overridden: Persian digits used to turn 3 × 4 = 12 into 12 = 4 × 3 even with dir=ltr. - MathText locks math runs inside Persian sentences in chat bubbles and figure captions, so -1 no longer shows as 1-. - Family screen: a student sees only the invite-code form; the parent form is behind a link. - Dev-only diagram gallery at ?gallery (not in the production build). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ import { askStore, useAskState, type ChatMessage } from '@/state/askStore';
|
||||
import { SeyarMascot } from '@/mascots/SeyarMascot';
|
||||
import { useToast } from '@/components/Toast';
|
||||
import { Screen, ScreenHeader } from '@/components/ui';
|
||||
import { Diagram } from '@/components/Diagram';
|
||||
import { Diagram } from '@/diagram';
|
||||
import { MathText } from '@/lib/MathText';
|
||||
import { friendlyError } from '@/lib/errors';
|
||||
import { toFa } from '@/lib/format';
|
||||
|
||||
@@ -222,7 +223,7 @@ function Message({
|
||||
}) {
|
||||
const toast = useToast();
|
||||
|
||||
if (message.role === 'user') return <Bubble role="user">{message.text}</Bubble>;
|
||||
if (message.role === 'user') return <Bubble role="user"><MathText text={message.text} /></Bubble>;
|
||||
|
||||
if (!message.text) {
|
||||
return (
|
||||
@@ -255,7 +256,7 @@ function Message({
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Bubble role="assistant" extra={message.diagram ? <div className="whitespace-normal"><Diagram spec={message.diagram} /></div> : undefined}>
|
||||
{message.text}
|
||||
<MathText text={message.text} />
|
||||
</Bubble>
|
||||
<div className="flex flex-wrap items-center gap-2 pr-10">
|
||||
{message.failedQuestion ? (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { BookOpen, Flame, Link2, LogOut, Star, Trophy, UserPlus } from 'lucide-react';
|
||||
import { useNav } from '@/nav/Navigator';
|
||||
import { useAppData } from '@/state/AppData';
|
||||
import { api, ApiError, type FamilyLink, type FamilyWeeklyReport } from '@/api';
|
||||
import { useAsync } from '@/lib/useAsync';
|
||||
import { useToast } from '@/components/Toast';
|
||||
@@ -15,6 +16,11 @@ import { digitsOnly, formatJalaliDate, toFa } from '@/lib/format';
|
||||
*/
|
||||
export function FamilyScreen({ onSignedOut }: { onSignedOut: () => void }) {
|
||||
const nav = useNav();
|
||||
const { profile } = useAppData();
|
||||
// دانشآموز (پایه دارد) پیش از اتصال فقط «کدِ دعوت» را میبیند؛ فرمِ والد برای او پشتِ یک
|
||||
// پیوند است. قبلاً هر دو فرم با هم بود و بچه نمیدانست کدام مالِ اوست. والد پایه ندارد.
|
||||
const isStudent = profile.gradeId !== null;
|
||||
const [showParentForm, setShowParentForm] = useState(false);
|
||||
// GET /family/link نمیگوید کاربر در این اتصال والد است یا فرزند؛ گزارشِ
|
||||
// هفتگی فقط برای والد جواب میدهد و برای فرزند ۴۰۴ است، پس نقش از همان درمیآید.
|
||||
const state = useAsync(async () => {
|
||||
@@ -34,7 +40,7 @@ export function FamilyScreen({ onSignedOut }: { onSignedOut: () => void }) {
|
||||
|
||||
return (
|
||||
<Screen>
|
||||
<ScreenHeader title="بخشِ والدین" subtitle="پیشرفتِ فرزندت رو هر هفته ببین" color="#FF7A3D" back mascot={{ char: 'sigma', mood: 'happy', size: 56 }} />
|
||||
<ScreenHeader title="بخشِ والدین" subtitle={isStudent ? 'به پدر یا مادرت وصل شو تا پیشرفتت رو ببینن' : 'پیشرفتِ فرزندت رو هر هفته ببین'} color="#FF7A3D" back mascot={{ char: 'sigma', mood: 'happy', size: 56 }} />
|
||||
<ScreenBody className="flex flex-col gap-4">
|
||||
{state.loading ? (
|
||||
<LoadingState />
|
||||
@@ -44,8 +50,14 @@ export function FamilyScreen({ onSignedOut }: { onSignedOut: () => void }) {
|
||||
<>
|
||||
{data?.link && <LinkStatus link={data.link} role={data.role} />}
|
||||
{data?.report && <WeeklyReport report={data.report} />}
|
||||
{data?.role !== 'child' && <InviteChild onDone={state.reload} />}
|
||||
{!data?.role && <AcceptInvite onDone={state.reload} />}
|
||||
{!data?.role && isStudent && <AcceptInvite onDone={state.reload} />}
|
||||
{data?.role !== 'child' && (!isStudent || showParentForm || data?.role === 'parent' || data?.link?.status === 'pending') && <InviteChild onDone={state.reload} />}
|
||||
{!data?.role && !isStudent && <AcceptInvite onDone={state.reload} />}
|
||||
{!data?.role && isStudent && !showParentForm && data?.link?.status !== 'pending' && (
|
||||
<button type="button" onClick={() => setShowParentForm(true)} className="py-1 text-center text-xs font-bold text-muted">
|
||||
پدر یا مادرِ یک دانشآموز هستی؟ از اینجا دعوت بساز
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* والد بدونِ پایه نوارِ پایین را ندارد؛ خروج باید همینجا باشد. */}
|
||||
|
||||
Reference in New Issue
Block a user