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:
@@ -61,7 +61,7 @@ export const api = {
|
||||
|
||||
profile: {
|
||||
get: () => http.get<T.UserProfile>('/user/profile'),
|
||||
update: (patch: Partial<Pick<T.UserProfile, 'name' | 'avatar' | 'birthYear' | 'parentPhone' | 'bio'>>) =>
|
||||
update: (patch: Partial<Pick<T.UserProfile, 'name' | 'avatar' | 'birthYear' | 'birthDate' | 'bio'>>) =>
|
||||
http.patch<T.UserProfile>('/user/profile', patch),
|
||||
setGrade: (gradeId: number) =>
|
||||
http.post<{ success: boolean; gradeId: number }>('/user/grade', { gradeId }),
|
||||
@@ -70,6 +70,8 @@ export const api = {
|
||||
summary: () => http.get<T.UserSummary>('/user/summary'),
|
||||
hearts: () => http.get<T.Hearts>('/user/hearts'),
|
||||
streakCalendar: () => http.get<T.StreakCalendar>('/user/streak/calendar'),
|
||||
/** «گزارشِ والدین»: ۷ روزِ اخیرِ همین حساب در کنارِ ۷ روزِ قبلش. */
|
||||
weeklyReport: () => http.get<T.WeeklyReport>('/user/weekly-report'),
|
||||
|
||||
/** بدونِ grade، پایهی ذخیرهشدهی خودِ کاربر ملاک است. */
|
||||
home: () => http.get<T.HomeResponse>('/home'),
|
||||
@@ -167,6 +169,8 @@ export const api = {
|
||||
redeem: (code: string, orderId: string) =>
|
||||
http.post<T.RedeemResponse>('/discount/redeem', { code, orderId }),
|
||||
order: (orderId: string) => http.get<T.OrderStatus>(`/payment/orders/${orderId}`),
|
||||
/** پرداختها و فاکتورهای همین کاربر، تازهترین اول. */
|
||||
invoices: () => http.get<{ invoices: T.Invoice[] }>('/payment/invoices').then((r) => r.invoices),
|
||||
},
|
||||
|
||||
support: {
|
||||
@@ -181,17 +185,6 @@ export const api = {
|
||||
http.create<{ bugReportId: string; status: string }>('/support/bug-reports', { title, description }),
|
||||
},
|
||||
|
||||
family: {
|
||||
link: () => http.get<T.FamilyLink>('/family/link'),
|
||||
invite: (childPhone: string) =>
|
||||
http.post<{ familyLinkId: string; inviteCode: string; status: string }>('/family/link/invite', { childPhone }),
|
||||
accept: (inviteCode: string) =>
|
||||
http.post<{ familyLinkId: string; status: string }>('/family/link/accept', { inviteCode }),
|
||||
weeklyReport: () => http.get<T.FamilyWeeklyReport>('/family/weekly-report'),
|
||||
settings: () => http.get<T.FamilySettings>('/family/settings'),
|
||||
updateSettings: (patch: Partial<T.FamilySettings>) => http.patch('/family/settings', patch),
|
||||
},
|
||||
|
||||
affiliate: {
|
||||
me: () => http.get<T.AffiliateMe>('/affiliate/me'),
|
||||
/** کاربرِ تازه (با شماره، نه مهمان) را به صاحبِ کدِ معرف وصل میکند. */
|
||||
|
||||
@@ -34,7 +34,8 @@ export interface UserProfile {
|
||||
isGuest: boolean;
|
||||
gradeId: number | null;
|
||||
birthYear: number | null;
|
||||
parentPhone: string;
|
||||
/** تاریخِ تولدِ شمسی «1385-04-12»؛ null وقتی فقط سال داده شده یا هیچ. */
|
||||
birthDate: string | null;
|
||||
bio: string;
|
||||
}
|
||||
|
||||
@@ -288,24 +289,57 @@ export interface Ticket {
|
||||
messages: { id: string; sender: string; text: string }[];
|
||||
}
|
||||
|
||||
export interface FamilyLink {
|
||||
familyLinkId: string;
|
||||
status: string;
|
||||
childName: string;
|
||||
/** یک سفارش به شکلِ فاکتور (GET /payment/invoices). مبلغها توماناند. */
|
||||
export interface Invoice {
|
||||
orderId: string;
|
||||
invoiceNumber: string;
|
||||
status: 'pending' | 'paid' | 'failed';
|
||||
provider: 'gateway' | 'bazaar' | string;
|
||||
planTitle: string;
|
||||
/** قیمتِ پایه پیش از تخفیف؛ null برای سفارشهای قدیمیتر از ثبتِ فاکتور. */
|
||||
listPrice: number | null;
|
||||
discountAmount: number | null;
|
||||
discountCode: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
createdAt: string;
|
||||
paidAt: string | null;
|
||||
/** کدِ پیگیریِ بانکی/بازار؛ خالی برای پرداختنشده یا کدِ تخفیفِ ۱۰۰٪. */
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export interface FamilyWeeklyReport {
|
||||
weekStart: string;
|
||||
xpEarned: number;
|
||||
lessonsCompleted: number;
|
||||
islandsCompleted: number;
|
||||
currentStreak: number;
|
||||
/** یک روز از گزارشِ هفتگی. `minutes` تخمینِ سرور است (از فاصلهی جوابها)، نه زمانِ باز بودنِ اپ. */
|
||||
export interface ReportDay {
|
||||
date: string;
|
||||
minutes: number;
|
||||
questions: number;
|
||||
correct: number;
|
||||
xp: number;
|
||||
lessons: number;
|
||||
islands: number;
|
||||
aiQuestions: number;
|
||||
}
|
||||
|
||||
export interface FamilySettings {
|
||||
dailyLimitMinutes: number;
|
||||
quietHoursStart: string;
|
||||
quietHoursEnd: string;
|
||||
export interface ReportTotals {
|
||||
minutes: number;
|
||||
activeDays: number;
|
||||
questions: number;
|
||||
correct: number;
|
||||
xp: number;
|
||||
/** یکتا: درس یا جزیرهی تکراری یک بار شمرده میشود. */
|
||||
lessons: number;
|
||||
islands: number;
|
||||
aiQuestions: number;
|
||||
}
|
||||
|
||||
/** GET /user/weekly-report: هفت روزِ اخیر و هفت روزِ قبلش، هر دو قدیمی به جدید. */
|
||||
export interface WeeklyReport {
|
||||
today: string;
|
||||
days: ReportDay[];
|
||||
previousDays: ReportDay[];
|
||||
thisWeek: ReportTotals;
|
||||
lastWeek: ReportTotals;
|
||||
streak: { current: number; longest: number };
|
||||
}
|
||||
|
||||
export interface AffiliateMe {
|
||||
|
||||
Reference in New Issue
Block a user