Expose by_country breakdown on GET /api/v1/analytics/dashboard from users.country. Co-authored-by: Cursor <cursoragent@cursor.com>
169 lines
6.5 KiB
Go
169 lines
6.5 KiB
Go
package domain
|
||
|
||
import "time"
|
||
|
||
type AnalyticsLabelCount struct {
|
||
Label string `json:"label"`
|
||
Count int64 `json:"count"`
|
||
}
|
||
|
||
type AnalyticsLabelAmount struct {
|
||
Label string `json:"label"`
|
||
Count int64 `json:"count"`
|
||
Amount float64 `json:"amount"`
|
||
}
|
||
|
||
type AnalyticsRevenueByPlan struct {
|
||
PlanName string `json:"plan_name"`
|
||
Currency string `json:"currency"`
|
||
Revenue float64 `json:"revenue"`
|
||
}
|
||
|
||
type AnalyticsTimePoint struct {
|
||
Date time.Time `json:"date"`
|
||
Count int64 `json:"count"`
|
||
}
|
||
|
||
type AnalyticsRevenueTimePoint struct {
|
||
Date time.Time `json:"date"`
|
||
Revenue float64 `json:"revenue"`
|
||
}
|
||
|
||
// AnalyticsMonthlyRevenuePoint is one calendar month bucket (UTC month start) within a dashboard year query.
|
||
type AnalyticsMonthlyRevenuePoint struct {
|
||
Month int `json:"month"` // 1–12
|
||
MonthStart time.Time `json:"month_start"` // UTC date of month start (for sorting / tooltips)
|
||
Label string `json:"label"` // Short English month label, e.g. Jan
|
||
Currency string `json:"currency"`
|
||
Revenue float64 `json:"revenue"` // SUCCESS payments aggregate for that bucket
|
||
}
|
||
|
||
type AnalyticsUsersSection struct {
|
||
TotalUsers int64 `json:"total_users"`
|
||
NewToday int64 `json:"new_today"`
|
||
NewWeek int64 `json:"new_week"`
|
||
NewMonth int64 `json:"new_month"`
|
||
|
||
ByRole []AnalyticsLabelCount `json:"by_role"`
|
||
ByStatus []AnalyticsLabelCount `json:"by_status"`
|
||
ByAgeGroup []AnalyticsLabelCount `json:"by_age_group"`
|
||
ByEducationLevel []AnalyticsLabelCount `json:"by_education_level"`
|
||
ByOccupation []AnalyticsLabelCount `json:"by_occupation"`
|
||
ByLearningGoal []AnalyticsLabelCount `json:"by_learning_goal"`
|
||
ByLanguageChallange []AnalyticsLabelCount `json:"by_language_challange"`
|
||
ByKnowledgeLevel []AnalyticsLabelCount `json:"by_knowledge_level"`
|
||
ByCountry []AnalyticsLabelCount `json:"by_country"`
|
||
ByRegion []AnalyticsLabelCount `json:"by_region"`
|
||
|
||
RegistrationsLast30Days []AnalyticsTimePoint `json:"registrations_last_30_days"`
|
||
}
|
||
|
||
type AnalyticsSubscriptionsSection struct {
|
||
TotalSubscriptions int64 `json:"total_subscriptions"`
|
||
ActiveSubscriptions int64 `json:"active_subscriptions"`
|
||
NewToday int64 `json:"new_today"`
|
||
NewWeek int64 `json:"new_week"`
|
||
NewMonth int64 `json:"new_month"`
|
||
|
||
ByStatus []AnalyticsLabelCount `json:"by_status"`
|
||
RevenueByPlan []AnalyticsRevenueByPlan `json:"revenue_by_plan"`
|
||
|
||
NewSubscriptionsLast30Days []AnalyticsTimePoint `json:"new_subscriptions_last_30_days"`
|
||
}
|
||
|
||
type AnalyticsPaymentsSection struct {
|
||
TotalRevenue float64 `json:"total_revenue"`
|
||
AvgTransactionValue float64 `json:"avg_transaction_value"`
|
||
TotalPayments int64 `json:"total_payments"`
|
||
SuccessfulPayments int64 `json:"successful_payments"`
|
||
|
||
ByStatus []AnalyticsLabelAmount `json:"by_status"`
|
||
ByMethod []AnalyticsLabelAmount `json:"by_method"`
|
||
|
||
RevenueLast30Days []AnalyticsRevenueTimePoint `json:"revenue_last_30_days"`
|
||
|
||
// RevenueMonthly is populated only when the request includes year=..., with 12 months (possibly multiple currencies per month).
|
||
RevenueMonthly []AnalyticsMonthlyRevenuePoint `json:"revenue_monthly,omitempty"`
|
||
// MonthlyRevenueYear is set when RevenueMonthly is non-empty (the calendar year of those buckets).
|
||
MonthlyRevenueYear *int `json:"monthly_revenue_year,omitempty"`
|
||
}
|
||
|
||
// AnalyticsLMSContentCounts reflects the LMS hierarchy (Learn English): programs → courses → modules → lessons.
|
||
type AnalyticsLMSContentCounts struct {
|
||
Programs int64 `json:"programs"`
|
||
Courses int64 `json:"courses"`
|
||
Modules int64 `json:"modules"`
|
||
Lessons int64 `json:"lessons"`
|
||
LessonsWithVideo int64 `json:"lessons_with_video"`
|
||
Practices int64 `json:"practices"`
|
||
PracticesAtCourse int64 `json:"practices_at_course"`
|
||
PracticesAtModule int64 `json:"practices_at_module"`
|
||
PracticesAtLesson int64 `json:"practices_at_lesson"`
|
||
}
|
||
|
||
// AnalyticsExamPrepContentCounts reflects the exam_prep schema: catalog_courses → units → unit_modules → lessons → lesson_practices.
|
||
type AnalyticsExamPrepContentCounts struct {
|
||
CatalogCourses int64 `json:"catalog_courses"`
|
||
Units int64 `json:"units"`
|
||
UnitModules int64 `json:"unit_modules"`
|
||
Lessons int64 `json:"lessons"`
|
||
LessonsWithVideo int64 `json:"lessons_with_video"`
|
||
LessonPractices int64 `json:"lesson_practices"`
|
||
}
|
||
|
||
type AnalyticsCoursesSection struct {
|
||
// Top-level keys preserved for existing clients: map to LMS programs, courses, modules, and all video lessons (LMS + exam prep).
|
||
TotalCategories int64 `json:"total_categories"`
|
||
TotalCourses int64 `json:"total_courses"`
|
||
TotalSubCourses int64 `json:"total_sub_courses"`
|
||
TotalVideos int64 `json:"total_videos"`
|
||
|
||
LMS AnalyticsLMSContentCounts `json:"lms"`
|
||
ExamPrep AnalyticsExamPrepContentCounts `json:"exam_prep"`
|
||
}
|
||
|
||
type AnalyticsContentSection struct {
|
||
TotalQuestions int64 `json:"total_questions"`
|
||
TotalQuestionSets int64 `json:"total_question_sets"`
|
||
|
||
QuestionsByType []AnalyticsLabelCount `json:"questions_by_type"`
|
||
QuestionSetsByType []AnalyticsLabelCount `json:"question_sets_by_type"`
|
||
}
|
||
|
||
type AnalyticsNotificationsSection struct {
|
||
TotalSent int64 `json:"total_sent"`
|
||
ReadCount int64 `json:"read_count"`
|
||
UnreadCount int64 `json:"unread_count"`
|
||
|
||
ByChannel []AnalyticsLabelCount `json:"by_channel"`
|
||
ByType []AnalyticsLabelCount `json:"by_type"`
|
||
}
|
||
|
||
type AnalyticsIssuesSection struct {
|
||
TotalIssues int64 `json:"total_issues"`
|
||
ResolvedIssues int64 `json:"resolved_issues"`
|
||
ResolutionRate float64 `json:"resolution_rate"`
|
||
|
||
ByStatus []AnalyticsLabelCount `json:"by_status"`
|
||
ByType []AnalyticsLabelCount `json:"by_type"`
|
||
}
|
||
|
||
type AnalyticsTeamSection struct {
|
||
TotalMembers int64 `json:"total_members"`
|
||
ByRole []AnalyticsLabelCount `json:"by_role"`
|
||
ByStatus []AnalyticsLabelCount `json:"by_status"`
|
||
}
|
||
|
||
type AnalyticsDashboard struct {
|
||
GeneratedAt time.Time `json:"generated_at"`
|
||
DateFilter AnalyticsDateFilter `json:"date_filter"`
|
||
Users AnalyticsUsersSection `json:"users"`
|
||
Subscriptions AnalyticsSubscriptionsSection `json:"subscriptions"`
|
||
Payments AnalyticsPaymentsSection `json:"payments"`
|
||
Courses AnalyticsCoursesSection `json:"courses"`
|
||
Content AnalyticsContentSection `json:"content"`
|
||
Notifications AnalyticsNotificationsSection `json:"notifications"`
|
||
Issues AnalyticsIssuesSection `json:"issues"`
|
||
Team AnalyticsTeamSection `json:"team"`
|
||
}
|