forked from Tadrisino/MathApp_frontend
question packs page, free/purchased counters in the tutor, packs from the no-hearts page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,11 @@ export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number;
|
||||
const chat = useAskState();
|
||||
const [input, setInput] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [quota, setQuota] = useState<number | null>(null);
|
||||
// «X سوالِ رایگانِ امروز + Y سوالِ خریدهشده»؛ پیش از اولین سوال از سرور، بعد از هر جواب از خودِ جواب.
|
||||
const [quota, setQuota] = useState<{ free: number; credits: number } | null>(null);
|
||||
useEffect(() => {
|
||||
api.ai.quota().then((q) => setQuota({ free: q.remainingQuota, credits: q.credits })).catch(() => {});
|
||||
}, []);
|
||||
const listEnd = useRef<HTMLDivElement>(null);
|
||||
const autoSent = useRef(false);
|
||||
|
||||
@@ -91,7 +95,7 @@ export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number;
|
||||
diagram: done.diagram ?? null,
|
||||
});
|
||||
askStore.setConversation(done.conversationId);
|
||||
setQuota(done.remainingQuota);
|
||||
setQuota((q) => ({ free: done.remainingQuota, credits: done.credits ?? q?.credits ?? 0 }));
|
||||
} catch (err) {
|
||||
// سقفِ روزانه (نه throttleِ چندثانیهای): «دوباره» تا فردا بیفایده است.
|
||||
const quotaReached = err instanceof ApiError && err.isRateLimited && err.detail.includes('سوال');
|
||||
@@ -99,7 +103,7 @@ export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number;
|
||||
text: friendlyError(err, 'جوابی نرسید'),
|
||||
...(quotaReached ? { quotaReached: true } : { failedQuestion: question }),
|
||||
});
|
||||
if (quotaReached) setQuota(0);
|
||||
if (quotaReached) setQuota({ free: 0, credits: 0 });
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -194,7 +198,7 @@ export function AskScreen({ lessonId, questionId, prompt }: { lessonId?: number;
|
||||
<Send size={16} className="-scale-x-100" />
|
||||
</button>
|
||||
</div>
|
||||
{quota !== null && <p className="mt-1.5 text-center text-[10px] text-muted">امروز {toFa(quota)} سوالِ دیگه میتونی بپرسی</p>}
|
||||
{quota !== null && <QuotaLine free={quota.free} credits={quota.credits} />}
|
||||
</form>
|
||||
</Screen>
|
||||
);
|
||||
@@ -293,7 +297,7 @@ function Message({
|
||||
<div className="flex flex-wrap items-center gap-2 pr-10">
|
||||
{message.quotaReached ? (
|
||||
<button type="button" onClick={() => nav.switchTab('subscription')} className="rounded-full bg-violet px-3 py-1 text-xs font-bold text-white">
|
||||
دیدنِ اشتراکها
|
||||
خریدِ بستهی سوال
|
||||
</button>
|
||||
) : message.failedQuestion ? (
|
||||
<button type="button" disabled={busy} onClick={() => onRetry(message)} className="flex items-center gap-1 rounded-full bg-white px-3 py-1 text-xs font-bold text-coral ring-1 ring-line disabled:opacity-50">
|
||||
@@ -331,3 +335,22 @@ function Message({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** شمارندهی زیرِ جعبهی پرسش: رایگانِ امروز و اعتبارِ خریدهشده، هر دو جدا و روشن. */
|
||||
function QuotaLine({ free, credits }: { free: number; credits: number }) {
|
||||
const nav = useNav();
|
||||
return (
|
||||
<div className="mt-1.5 flex items-center justify-center gap-2 text-[11px]">
|
||||
<span className={`rounded-full px-2.5 py-0.5 font-bold ${free > 0 ? 'bg-[#EAF8F0] text-[#2E7D5B]' : 'bg-soft text-muted'}`}>
|
||||
رایگانِ امروز: {toFa(free)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => nav.switchTab('subscription')}
|
||||
className={`rounded-full px-2.5 py-0.5 font-bold ${credits > 0 ? 'bg-[#F5F1FF] text-violet' : 'bg-soft text-muted'}`}
|
||||
>
|
||||
سوالِ خریدهشده: {toFa(credits)}{credits === 0 && ' · خرید'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user