Archived
feat: implement reading pace notifications (US #33)
Backend: - New hermes app with ReadingGroup, GroupMeeting, GroupMembership models - NotificationPreference (per-user: enable/disable, frequency daily/weekly) - PaceNotification model for tracking sent/dismissed notifications - Pace calculation service comparing current progress vs meeting targets - API endpoints: pace status, notification preferences CRUD, reminders, dismiss - Ahead/behind/on_track/completed status determination Frontend: - PaceNotification component for in-app alerts (behind/ahead/on_track statuses) - ReadingPaceBanner — persistent banner in reader view - Notification settings in Settings page (enable/disable, frequency, reminders) - API client module (groupsApi) for all notification endpoints
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import api from "./client";
|
||||
import type {
|
||||
GroupMeeting,
|
||||
MeetingReminder,
|
||||
NotificationPreferences,
|
||||
PaceStatus,
|
||||
ReadingGroup,
|
||||
} from "../types/notifications";
|
||||
|
||||
export const groupsApi = {
|
||||
async getPaceStatus(ebookId?: number): Promise<PaceStatus[]> {
|
||||
const params: Record<string, string> = {};
|
||||
if (ebookId) params.ebook_id = String(ebookId);
|
||||
const { data } = await api.get<PaceStatus[]>("/groups/notifications/pace/", { params });
|
||||
return data;
|
||||
},
|
||||
|
||||
async getPreferences(): Promise<NotificationPreferences> {
|
||||
const { data } = await api.get<NotificationPreferences>("/groups/notifications/preferences/");
|
||||
return data;
|
||||
},
|
||||
|
||||
async updatePreferences(
|
||||
prefs: Partial<NotificationPreferences>,
|
||||
): Promise<NotificationPreferences> {
|
||||
const { data } = await api.patch<NotificationPreferences>(
|
||||
"/groups/notifications/preferences/",
|
||||
prefs,
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
async dismissPaceNotification(meetingId: number, ebookId: number): Promise<void> {
|
||||
await api.post("/groups/notifications/dismiss/", {
|
||||
meeting_id: meetingId,
|
||||
ebook_id: ebookId,
|
||||
});
|
||||
},
|
||||
|
||||
async getReminders(): Promise<MeetingReminder[]> {
|
||||
const { data } = await api.get<MeetingReminder[]>("/groups/notifications/reminders/");
|
||||
return data;
|
||||
},
|
||||
|
||||
async getGroups(): Promise<ReadingGroup[]> {
|
||||
const { data } = await api.get<{ count: number; results: ReadingGroup[] } | ReadingGroup[]>(
|
||||
"/groups/groups/",
|
||||
);
|
||||
if (Array.isArray(data)) return data;
|
||||
return data.results ?? [];
|
||||
},
|
||||
|
||||
async getUpcomingMeetings(): Promise<GroupMeeting[]> {
|
||||
const { data } = await api.get<GroupMeeting[]>("/groups/meetings/upcoming/");
|
||||
return data;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user