Add backend with analytics, notifications, and enhanced features
Some checks failed
Backend CI/CD Pipeline / Lint and Test Backend (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
Backend CI/CD Pipeline / E2E Tests Backend (push) Has been cancelled
Backend CI/CD Pipeline / Build Backend Application (push) Has been cancelled
Backend CI/CD Pipeline / Performance Testing (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
Some checks failed
Backend CI/CD Pipeline / Lint and Test Backend (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
Backend CI/CD Pipeline / E2E Tests Backend (push) Has been cancelled
Backend CI/CD Pipeline / Build Backend Application (push) Has been cancelled
Backend CI/CD Pipeline / Performance Testing (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
Backend: - Complete NestJS backend implementation with comprehensive features - Analytics: Weekly/monthly reports with PDF/CSV export - Smart notifications: Persistent notifications with milestones and anomaly detection - AI safety: Medical disclaimer triggers and prompt injection protection - COPPA/GDPR compliance: Full audit logging system Frontend: - Updated settings page and analytics components - API integration improvements Docs: - Added implementation gaps tracking - Azure OpenAI integration documentation - Testing and post-launch summaries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import { Box, Typography, Card, CardContent, TextField, Button, Divider, Switch, FormControlLabel, Alert, CircularProgress, Snackbar } from '@mui/material';
|
||||
import { Save, Logout } from '@mui/icons-material';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { AppShell } from '@/components/layouts/AppShell/AppShell';
|
||||
import { ProtectedRoute } from '@/components/common/ProtectedRoute';
|
||||
import { usersApi } from '@/lib/api/users';
|
||||
@@ -22,6 +22,24 @@ export default function SettingsPage() {
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
const [nameError, setNameError] = useState<string | null>(null);
|
||||
|
||||
// Load preferences from user object when it changes
|
||||
useEffect(() => {
|
||||
if (user?.preferences) {
|
||||
setSettings({
|
||||
notifications: user.preferences.notifications ?? true,
|
||||
emailUpdates: user.preferences.emailUpdates ?? false,
|
||||
darkMode: user.preferences.darkMode ?? false,
|
||||
});
|
||||
}
|
||||
}, [user?.preferences]);
|
||||
|
||||
// Sync name state when user data changes
|
||||
useEffect(() => {
|
||||
if (user?.name) {
|
||||
setName(user.name);
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const handleSave = async () => {
|
||||
// Validate name
|
||||
if (!name || name.trim() === '') {
|
||||
@@ -34,12 +52,20 @@ export default function SettingsPage() {
|
||||
setNameError(null);
|
||||
|
||||
try {
|
||||
await usersApi.updateProfile({ name: name.trim() });
|
||||
const response = await usersApi.updateProfile({
|
||||
name: name.trim(),
|
||||
preferences: settings
|
||||
});
|
||||
console.log('✅ Profile updated successfully:', response);
|
||||
|
||||
// Refresh user to get latest data from server
|
||||
await refreshUser();
|
||||
|
||||
setSuccessMessage('Profile updated successfully!');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to update profile:', err);
|
||||
setError(err.response?.data?.message || 'Failed to update profile. Please try again.');
|
||||
console.error('❌ Failed to update profile:', err);
|
||||
console.error('Error response:', err.response);
|
||||
setError(err.response?.data?.message || err.message || 'Failed to update profile. Please try again.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -129,15 +155,13 @@ export default function SettingsPage() {
|
||||
<Typography variant="h6" fontWeight="600" gutterBottom>
|
||||
Notifications
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Settings are stored locally (backend integration coming soon)
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 2 }}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={settings.notifications}
|
||||
onChange={(e) => setSettings({ ...settings, notifications: e.target.checked })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
}
|
||||
label="Push Notifications"
|
||||
@@ -147,11 +171,21 @@ export default function SettingsPage() {
|
||||
<Switch
|
||||
checked={settings.emailUpdates}
|
||||
onChange={(e) => setSettings({ ...settings, emailUpdates: e.target.checked })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
}
|
||||
label="Email Updates"
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSave}
|
||||
disabled={isLoading}
|
||||
sx={{ mt: 2, alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Preferences'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
@@ -32,6 +32,7 @@ interface FeedingTypeData {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
[key: string]: string | number;
|
||||
}
|
||||
|
||||
const COLORS = {
|
||||
@@ -248,7 +249,7 @@ export default function FeedingFrequencyGraph() {
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
labelLine={false}
|
||||
label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`}
|
||||
label={({ name, percent }: any) => `${name}: ${(percent * 100).toFixed(0)}%`}
|
||||
outerRadius={100}
|
||||
fill="#8884d8"
|
||||
dataKey="value"
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import apiClient from './client';
|
||||
|
||||
export interface UserPreferences {
|
||||
notifications?: boolean;
|
||||
emailUpdates?: boolean;
|
||||
darkMode?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateProfileData {
|
||||
name?: string;
|
||||
preferences?: UserPreferences;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
@@ -11,6 +18,7 @@ export interface UserProfile {
|
||||
role: string;
|
||||
locale: string;
|
||||
emailVerified: boolean;
|
||||
preferences?: UserPreferences;
|
||||
families?: string[];
|
||||
}
|
||||
|
||||
|
||||
0
maternal-web/public/icons/icon-128x128.png
Normal file
0
maternal-web/public/icons/icon-128x128.png
Normal file
0
maternal-web/public/icons/icon-144x144.png
Normal file
0
maternal-web/public/icons/icon-144x144.png
Normal file
0
maternal-web/public/icons/icon-152x152.png
Normal file
0
maternal-web/public/icons/icon-152x152.png
Normal file
0
maternal-web/public/icons/icon-192x192.png
Normal file
0
maternal-web/public/icons/icon-192x192.png
Normal file
0
maternal-web/public/icons/icon-384x384.png
Normal file
0
maternal-web/public/icons/icon-384x384.png
Normal file
0
maternal-web/public/icons/icon-512x512.png
Normal file
0
maternal-web/public/icons/icon-512x512.png
Normal file
0
maternal-web/public/icons/icon-72x72.png
Normal file
0
maternal-web/public/icons/icon-72x72.png
Normal file
0
maternal-web/public/icons/icon-96x96.png
Normal file
0
maternal-web/public/icons/icon-96x96.png
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user