Archived
feat: implement per-member reading progress tracking for shared books (#30)
Backend (apps/groups):
- ReadingGroup model: named groups linked to EBooks with creator tracking
- GroupMembership model: user-group association with member/admin roles
- MemberProgress model: per-member progress with section tracking,
percentage, time_spent, privacy toggles, and device-position preservation
- ReadingGroupViewSet: CRUD, join/leave, members list, progress endpoints
- GET /api/groups/{id}/members/progress/ — all members' progress
(public: section label only; own: full detail; private: stubbed)
- PATCH /api/groups/{id}/progress/ — update own progress with auto-
percentage calculation from section boundaries
- GET /api/groups/{id}/progress/summary/ — admin dashboard with
averages, started/finished counts, and per-member details
- Permissions: IsGroupMember, IsGroupAdmin
Frontend:
- Groups list page (/groups) with create modal and book selector
- Group detail page (/groups/:id) with progress and admin summary tabs
- Progress bar visualization per member with privacy-aware display
- Admin stat cards (total members, started, finished, avg progress, avg time)
- Navigation link from Library header
Shared:
- ReadingGroupSummary and MemberProgressPublic types
- API endpoint constants for groups routes
Closes #30
This commit is contained in:
@@ -215,4 +215,36 @@ export interface PaginatedResponse<T> {
|
||||
export interface ApiError {
|
||||
detail?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// ---- Reading Groups ----
|
||||
|
||||
export interface ReadingGroupSummary {
|
||||
id: number;
|
||||
name: string;
|
||||
ebook: number;
|
||||
ebook_title: string;
|
||||
ebook_author: string;
|
||||
created_by_email: string;
|
||||
description: string;
|
||||
member_count: number;
|
||||
my_progress: {
|
||||
current_section: number;
|
||||
percentage: number;
|
||||
time_spent_seconds: number;
|
||||
section_label: string;
|
||||
} | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface MemberProgressPublic {
|
||||
user_id: number;
|
||||
user_email: string;
|
||||
current_section: number;
|
||||
total_sections: number;
|
||||
section_label: string;
|
||||
percentage: number;
|
||||
time_spent_seconds: number;
|
||||
updated_at: string;
|
||||
}
|
||||
@@ -114,4 +114,13 @@ export const API_ENDPOINTS = {
|
||||
bookmarkDetail: (id: string) => `/api/annotations/bookmarks/${id}/`,
|
||||
noteDetail: (id: string) => `/api/annotations/notes/${id}/`,
|
||||
},
|
||||
groups: {
|
||||
list: "/api/groups/",
|
||||
detail: (id: number) => `/api/groups/${id}/`,
|
||||
membersProgress: (id: number) => `/api/groups/${id}/members/progress/`,
|
||||
myProgress: (id: number) => `/api/groups/${id}/progress/`,
|
||||
progressSummary: (id: number) => `/api/groups/${id}/progress/summary/`,
|
||||
join: (id: number) => `/api/groups/${id}/join/`,
|
||||
leave: (id: number) => `/api/groups/${id}/leave/`,
|
||||
},
|
||||
} as const;
|
||||
Reference in New Issue
Block a user