feat: complete Phase 3 — advanced features for Casera web app

Adds sharing (residence share codes, join, user management, .casera file
export/import), subscription status with feature comparison, notification
preferences with bell icon, profile settings (edit info, change password,
theme picker, delete account), onboarding wizard with create/join paths,
enhanced dashboard with stats cards, Recharts completion chart, recent
activity feed, and task report PDF download.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-03 09:31:29 -06:00
commit 5a50d77515
183 changed files with 34450 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
// ---------------------------------------------------------------------------
// Notifications API client (client-side)
// ---------------------------------------------------------------------------
import { apiFetch } from './client';
// ---------------------------------------------------------------------------
// Request / response shapes
// TODO: import from @/lib/types once the shared types package is finalised
// ---------------------------------------------------------------------------
export interface NotificationResponse {
id: number;
title: string;
body: string;
notification_type: string;
is_read: boolean;
data?: Record<string, unknown>;
created_at: string;
}
export interface NotificationListResponse {
count: number;
results: NotificationResponse[];
}
export interface UnreadCountResponse {
unread_count: number;
}
export interface NotificationPreferencesResponse {
task_reminders: boolean;
task_completions: boolean;
residence_updates: boolean;
share_notifications: boolean;
marketing: boolean;
}
export interface UpdatePreferencesRequest {
task_reminders?: boolean;
task_completions?: boolean;
residence_updates?: boolean;
share_notifications?: boolean;
marketing?: boolean;
}
export interface RegisterDeviceRequest {
registration_id: string;
platform: 'ios' | 'android' | 'web';
device_name?: string;
}
export interface DeviceResponse {
id: number;
registration_id: string;
platform: string;
device_name: string;
is_active: boolean;
created_at: string;
}
export interface UnregisterDeviceRequest {
registration_id: string;
platform?: string;
}
export interface MessageResponse {
message: string;
}
// ---------------------------------------------------------------------------
// API functions
// ---------------------------------------------------------------------------
/** List notifications with optional pagination. */
export function listNotifications(
limit?: number,
offset?: number,
): Promise<NotificationListResponse> {
const params = new URLSearchParams();
if (limit != null) params.set('limit', String(limit));
if (offset != null) params.set('offset', String(offset));
const qs = params.toString();
return apiFetch<NotificationListResponse>(
`/notifications/${qs ? `?${qs}` : ''}`,
);
}
/** Get unread notification count. */
export function getUnreadCount(): Promise<UnreadCountResponse> {
return apiFetch<UnreadCountResponse>('/notifications/unread-count/');
}
/** Mark a single notification as read. */
export function markAsRead(id: number): Promise<MessageResponse> {
return apiFetch<MessageResponse>(`/notifications/${id}/read/`, {
method: 'POST',
});
}
/** Mark all notifications as read. */
export function markAllAsRead(): Promise<MessageResponse> {
return apiFetch<MessageResponse>('/notifications/mark-all-read/', {
method: 'POST',
});
}
/** Get notification preferences. */
export function getPreferences(): Promise<NotificationPreferencesResponse> {
return apiFetch<NotificationPreferencesResponse>(
'/notifications/preferences/',
);
}
/** Update notification preferences. */
export function updatePreferences(
data: UpdatePreferencesRequest,
): Promise<NotificationPreferencesResponse> {
return apiFetch<NotificationPreferencesResponse>(
'/notifications/preferences/',
{
method: 'PUT',
body: JSON.stringify(data),
},
);
}
/** Register a push notification device. */
export function registerDevice(
data: RegisterDeviceRequest,
): Promise<DeviceResponse> {
return apiFetch<DeviceResponse>('/notifications/devices/', {
method: 'POST',
body: JSON.stringify(data),
});
}
/** List registered devices. */
export function listDevices(): Promise<DeviceResponse[]> {
return apiFetch<DeviceResponse[]>('/notifications/devices/');
}
/** Unregister a push notification device by registration ID. */
export function unregisterDevice(
data: UnregisterDeviceRequest,
): Promise<MessageResponse> {
return apiFetch<MessageResponse>('/notifications/devices/unregister/', {
method: 'POST',
body: JSON.stringify(data),
});
}
/** Delete a device by ID. */
export function deleteDevice(
id: number,
platform?: string,
): Promise<MessageResponse> {
const qs = platform ? `?platform=${platform}` : '';
return apiFetch<MessageResponse>(`/notifications/devices/${id}/${qs}`, {
method: 'DELETE',
});
}