expose the fields and endpoint the screens were missing
بعد از merge شدنِ PR اول، صفحههای اپ با API مقایسه شدند و چند چیز کم بود.
همهی تغییراتِ بکاند افزایشیاند، پس کدِ فعلی نمیشکند — این فقط دسترسی به
آنها را اضافه میکند.
grades() حالا lessonCount دارد: فهرستِ پایهها تا دوازدهم میرود ولی محتوا
تا نهم است، و بدونِ این عدد کاربر پایهی خالی را انتخاب میکند و صفحهی
خالی میبیند بیآنکه بفهمد چرا.
home() در هر مبحث icon و color و در هر فصل blurb میدهد، تا رنگها لازم
نباشد در کد هاردکد بمانند.
nodes.lessons(nodeId) تازه است. home() فقط شناسهی درسها را میدهد، ولی
کارتِ نقشهی مهارت عنوان، نوع (آزمون/ویدیو)، زیرنویس («۵ سوال» / «۱۲
دقیقه») و «انجام شد» میخواهد — بدونِ این، برای هر کارت یک درخواستِ intro
جدا لازم بود.
lessons.intro() متن را سهتکه میدهد: introText (توضیح)، example، و steps
(حلِ گامبهگام)؛ بهعلاوه kind و videoUrl و durationMinutes.
profile.update({ age }) هم پذیرفته میشود چون صفحهی ثبتنام سن میپرسد؛
سرور خودش به سالِ تولدِ شمسی تبدیل میکند.
هنوز هیچ فایلِ موجودی دست نخورده و پکیجی اضافه نشده. هر ۲۰ صدا در برابرِ
یک بکاندِ در حالِ اجرا تست شد.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,12 +35,27 @@ import { ConnectionCheck } from '@/api/ConnectionCheck';
|
||||
```tsx
|
||||
import { api, ApiError } from '@/api';
|
||||
|
||||
const grades = await api.grades();
|
||||
const home = await api.home(); // پایهی خودِ کاربر
|
||||
const intro = await api.lessons.intro(lessonId);
|
||||
const grades = await api.grades(); // lessonCount=0 یعنی پایه خالی است
|
||||
const home = await api.home(); // بدونِ آرگومان: پایهی خودِ کاربر
|
||||
const steps = await api.nodes.lessons(nodeId); // کارتهای نقشهی مهارت
|
||||
const intro = await api.lessons.intro(lessonId); // توضیح + مثال + گامبهگام
|
||||
const answer = await api.ai.ask('این رو نفهمیدم', { lessonId });
|
||||
```
|
||||
|
||||
### چند نکته که وقت میگیرند اگر ندانید
|
||||
|
||||
- `grades()` حالا `lessonCount` میدهد. صفر یعنی آن پایه هنوز محتوایی ندارد —
|
||||
گزینهاش را غیرفعال کنید، وگرنه کاربر انتخابش میکند و صفحهی خالی میبیند.
|
||||
- `home()` در هر مبحث `icon` و `color` و در هر فصل `blurb` میدهد؛ لازم نیست
|
||||
رنگها را هاردکد کنید.
|
||||
- `home()` فقط شناسهی درسها را میدهد. برای کارتهای نقشهی مهارت (عنوان،
|
||||
آزمون یا ویدیو، «۵ سوال»/«۱۲ دقیقه»، انجامشده) از `nodes.lessons(nodeId)`
|
||||
استفاده کنید — یک درخواست برای کلِ مبحث، نه یکی بهازای هر کارت.
|
||||
- `lessons.intro()` متن را سهتکه میدهد: `introText` (توضیح)، `example`، و
|
||||
`steps` (حلِ گامبهگام). `kind` میگوید آزمون است یا ویدیو.
|
||||
- `profile.update({ age })` هم کار میکند؛ سرور خودش به سالِ تولدِ شمسی تبدیل
|
||||
میکند. `age` و `birthYear` را با هم نفرستید.
|
||||
|
||||
### ورود
|
||||
|
||||
```tsx
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
import { ApiError, createHttpClient, localTokenStorage } from './client';
|
||||
import type { ClientOptions, HttpClient, RequestOptions, Tokens } from './client';
|
||||
import type {
|
||||
AnswerResponse, AskResponse, Grade, HomeResponse, LessonIntro, Plan, Question,
|
||||
ReportReason, ReportResponse, UserProfile,
|
||||
AnswerResponse, AskResponse, Grade, HomeResponse, LessonIntro, NodeLessons, Plan,
|
||||
Question, ReportReason, ReportResponse, UserProfile,
|
||||
} from './types';
|
||||
|
||||
export { ApiError, localTokenStorage, createHttpClient };
|
||||
@@ -66,8 +66,13 @@ export function createApi(options: ClientOptions = {}) {
|
||||
/* ── کاربر ────────────────────────────────────────────────────────── */
|
||||
profile: {
|
||||
get: (opts?: RequestOptions) => http.get<UserProfile>('/user/profile', opts),
|
||||
/**
|
||||
* فقط فیلدهایی که میفرستید عوض میشوند. صفحهی ثبتنام سن میپرسد، پس
|
||||
* `age` هم پذیرفته میشود و سرور خودش به سالِ تولدِ شمسی تبدیلش میکند —
|
||||
* `age` و `birthYear` را با هم نفرستید.
|
||||
*/
|
||||
update: (patch: Partial<Pick<UserProfile,
|
||||
'name' | 'avatar' | 'birthYear' | 'parentPhone' | 'bio'>>) =>
|
||||
'name' | 'avatar' | 'birthYear' | 'parentPhone' | 'bio'>> & { age?: number }) =>
|
||||
http.patch<UserProfile>('/user/profile', patch),
|
||||
setGrade: (gradeId: number) =>
|
||||
http.post<{ success: boolean; gradeId: number }>('/user/grade', { gradeId }),
|
||||
@@ -96,6 +101,16 @@ export function createApi(options: ClientOptions = {}) {
|
||||
http.create<Record<string, unknown>>(`/lessons/${lessonId}/complete`, {}),
|
||||
},
|
||||
|
||||
nodes: {
|
||||
/**
|
||||
* قدمهای یک مبحث برای نقشهی مهارت — عنوان، نوع، زیرنویس و «انجام شد».
|
||||
* `home()` فقط شناسهی درسها را میدهد؛ بدونِ این، برای هر کارت یک
|
||||
* درخواستِ intro جدا لازم بود.
|
||||
*/
|
||||
lessons: (nodeId: number | string, opts?: RequestOptions) =>
|
||||
http.get<NodeLessons>(`/nodes/${nodeId}/lessons`, opts),
|
||||
},
|
||||
|
||||
questions: {
|
||||
/**
|
||||
* ثبتِ پاسخ. مسیر زیرِ /questions است نه /lessons، و نامِ فیلد
|
||||
|
||||
@@ -12,6 +12,9 @@ export type NodeStatusValue = 'locked' | 'unlocked' | 'in_progress' | 'completed
|
||||
export interface HomeNode {
|
||||
id: string;
|
||||
title: string;
|
||||
/** آیکون و رنگِ کارتِ مبحث — از سرور میآید، هاردکد نکنید. */
|
||||
icon: string;
|
||||
color: string;
|
||||
status: NodeStatusValue;
|
||||
lessonIds: number[];
|
||||
}
|
||||
@@ -19,6 +22,8 @@ export interface HomeNode {
|
||||
export interface HomeUnit {
|
||||
id: string;
|
||||
title: string;
|
||||
/** یک جملهی کوتاه زیرِ عنوانِ فصل. */
|
||||
blurb: string;
|
||||
nodes: HomeNode[];
|
||||
}
|
||||
|
||||
@@ -38,6 +43,8 @@ export interface Grade {
|
||||
title: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
/** صفر یعنی این پایه هنوز محتوایی ندارد — گزینهاش را غیرفعال کنید. */
|
||||
lessonCount: number;
|
||||
}
|
||||
|
||||
/** آمارِ بازی (xp/سکه/استریک) اینجا نیست — از /user/summary میآید. */
|
||||
@@ -53,14 +60,40 @@ export interface UserProfile {
|
||||
bio: string;
|
||||
}
|
||||
|
||||
/** یک قدم در مسیر: آزمون یا ویدیو. */
|
||||
export type LessonKind = 'quiz' | 'video';
|
||||
|
||||
export interface LessonIntro {
|
||||
lessonId: string;
|
||||
title: string;
|
||||
kind: LessonKind;
|
||||
/** متنِ درس سه تکه است: توضیح، مثال، و حلِ گامبهگام. */
|
||||
introText: string;
|
||||
example: string;
|
||||
steps: string[];
|
||||
/** فقط وقتی kind === 'video' پر است. */
|
||||
videoUrl: string;
|
||||
durationMinutes: number | null;
|
||||
mascotAnimation: string;
|
||||
questionCount: number;
|
||||
}
|
||||
|
||||
/** یک کارت در نقشهی مهارت. */
|
||||
export interface NodeLesson {
|
||||
id: string;
|
||||
kind: LessonKind;
|
||||
title: string;
|
||||
/** زیرنویسِ کارت: «۵ سوال» یا «۱۲ دقیقه». */
|
||||
meta: string;
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
export interface NodeLessons {
|
||||
nodeId: string;
|
||||
title: string;
|
||||
lessons: NodeLesson[];
|
||||
}
|
||||
|
||||
export interface ChoiceOption {
|
||||
id: string;
|
||||
value: string;
|
||||
|
||||
Reference in New Issue
Block a user