feat: Add real data to families page and fix MUI Grid warnings
Some checks failed
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
Some checks failed
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
Backend changes: - Created FamiliesModule with controller and service - Added /admin/families endpoint to list all families with members and children - Added /admin/families/:id endpoint to get family details - Added DELETE /admin/families/:id endpoint for family deletion - Query families, family_members, children, and activities tables - Calculate activity counts and last activity timestamps Frontend changes: - Removed all mock data from families page - Connected to real /admin/families API endpoint - Replaced deprecated MUI Grid v1 with CSS Grid layout - Removed Grid import (no longer used) - Fixed all Grid deprecation warnings (item, xs, sm, md props) - Display real family data: members, children, activity counts - Maintain responsive layout with CSS Grid breakpoints
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { UserManagementModule } from './user-management/user-management.module';
|
import { UserManagementModule } from './user-management/user-management.module';
|
||||||
import { DashboardModule } from './dashboard/dashboard.module';
|
import { DashboardModule } from './dashboard/dashboard.module';
|
||||||
|
import { FamiliesModule } from './families/families.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UserManagementModule, DashboardModule],
|
imports: [UserManagementModule, DashboardModule, FamiliesModule],
|
||||||
exports: [UserManagementModule, DashboardModule],
|
exports: [UserManagementModule, DashboardModule, FamiliesModule],
|
||||||
})
|
})
|
||||||
export class AdminModule {}
|
export class AdminModule {}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Controller, Get, Delete, Param, UseGuards } from '@nestjs/common';
|
||||||
|
import { FamiliesService } from './families.service';
|
||||||
|
import { AdminGuard } from '../../../common/guards/admin.guard';
|
||||||
|
|
||||||
|
@Controller('api/v1/admin/families')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
|
export class FamiliesController {
|
||||||
|
constructor(private readonly familiesService: FamiliesService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async listFamilies() {
|
||||||
|
return this.familiesService.listFamilies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
async getFamilyById(@Param('id') id: string) {
|
||||||
|
return this.familiesService.getFamilyById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
async deleteFamily(@Param('id') id: string) {
|
||||||
|
return this.familiesService.deleteFamily(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { FamiliesController } from './families.controller';
|
||||||
|
import { FamiliesService } from './families.service';
|
||||||
|
import { User } from '../../../database/entities/user.entity';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([User])],
|
||||||
|
controllers: [FamiliesController],
|
||||||
|
providers: [FamiliesService],
|
||||||
|
exports: [FamiliesService],
|
||||||
|
})
|
||||||
|
export class FamiliesModule {}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { User } from '../../../database/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FamiliesService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async listFamilies() {
|
||||||
|
// Get all families with their members and children
|
||||||
|
const familiesRaw = await this.userRepository.query(`
|
||||||
|
SELECT
|
||||||
|
f.id,
|
||||||
|
f.name,
|
||||||
|
f.created_at as "createdAt",
|
||||||
|
COUNT(DISTINCT fm.user_id) as "memberCount",
|
||||||
|
COUNT(DISTINCT c.id) as "childrenCount",
|
||||||
|
COALESCE(act.activity_count, 0) as "activityCount",
|
||||||
|
COALESCE(act.last_activity_at, f.created_at) as "lastActivityAt"
|
||||||
|
FROM families f
|
||||||
|
LEFT JOIN family_members fm ON f.id = fm.family_id
|
||||||
|
LEFT JOIN children c ON f.id = c.family_id
|
||||||
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as activity_count,
|
||||||
|
MAX(created_at) as last_activity_at
|
||||||
|
FROM activities a
|
||||||
|
WHERE a.child_id IN (SELECT id FROM children WHERE family_id = f.id)
|
||||||
|
) act ON true
|
||||||
|
GROUP BY f.id, f.name, f.created_at, act.activity_count, act.last_activity_at
|
||||||
|
ORDER BY f.created_at DESC
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Get members for each family
|
||||||
|
const families = await Promise.all(
|
||||||
|
familiesRaw.map(async (family: any) => {
|
||||||
|
const members = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
u.id,
|
||||||
|
u.name,
|
||||||
|
u.email,
|
||||||
|
fm.role,
|
||||||
|
fm.joined_at as "joinedAt"
|
||||||
|
FROM family_members fm
|
||||||
|
JOIN users u ON fm.user_id = u.id
|
||||||
|
WHERE fm.family_id = $1
|
||||||
|
ORDER BY fm.joined_at ASC
|
||||||
|
`,
|
||||||
|
[family.id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const children = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
birth_date as "birthDate",
|
||||||
|
gender,
|
||||||
|
display_color as "displayColor"
|
||||||
|
FROM children
|
||||||
|
WHERE family_id = $1
|
||||||
|
ORDER BY birth_date DESC
|
||||||
|
`,
|
||||||
|
[family.id],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...family,
|
||||||
|
memberCount: parseInt(family.memberCount, 10),
|
||||||
|
childrenCount: parseInt(family.childrenCount, 10),
|
||||||
|
activityCount: parseInt(family.activityCount, 10),
|
||||||
|
members,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return { data: families };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFamilyById(id: string) {
|
||||||
|
const familyRaw = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
f.id,
|
||||||
|
f.name,
|
||||||
|
f.created_at as "createdAt",
|
||||||
|
COUNT(DISTINCT fm.user_id) as "memberCount",
|
||||||
|
COUNT(DISTINCT c.id) as "childrenCount"
|
||||||
|
FROM families f
|
||||||
|
LEFT JOIN family_members fm ON f.id = fm.family_id
|
||||||
|
LEFT JOIN children c ON f.id = c.family_id
|
||||||
|
WHERE f.id = $1
|
||||||
|
GROUP BY f.id, f.name, f.created_at
|
||||||
|
`,
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!familyRaw || familyRaw.length === 0) {
|
||||||
|
throw new NotFoundException(`Family with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const family = familyRaw[0];
|
||||||
|
|
||||||
|
const members = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
u.id,
|
||||||
|
u.name,
|
||||||
|
u.email,
|
||||||
|
fm.role,
|
||||||
|
fm.joined_at as "joinedAt"
|
||||||
|
FROM family_members fm
|
||||||
|
JOIN users u ON fm.user_id = u.id
|
||||||
|
WHERE fm.family_id = $1
|
||||||
|
ORDER BY fm.joined_at ASC
|
||||||
|
`,
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const children = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
birth_date as "birthDate",
|
||||||
|
gender,
|
||||||
|
display_color as "displayColor"
|
||||||
|
FROM children
|
||||||
|
WHERE family_id = $1
|
||||||
|
ORDER BY birth_date DESC
|
||||||
|
`,
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const activityStats = await this.userRepository.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as activity_count,
|
||||||
|
MAX(created_at) as last_activity_at
|
||||||
|
FROM activities
|
||||||
|
WHERE child_id IN (SELECT id FROM children WHERE family_id = $1)
|
||||||
|
`,
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...family,
|
||||||
|
memberCount: parseInt(family.memberCount, 10),
|
||||||
|
childrenCount: parseInt(family.childrenCount, 10),
|
||||||
|
activityCount: parseInt(activityStats[0]?.activity_count || '0', 10),
|
||||||
|
lastActivityAt: activityStats[0]?.last_activity_at || family.createdAt,
|
||||||
|
members,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteFamily(id: string) {
|
||||||
|
// Check if family exists
|
||||||
|
const family = await this.userRepository.query(
|
||||||
|
'SELECT id FROM families WHERE id = $1',
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!family || family.length === 0) {
|
||||||
|
throw new NotFoundException(`Family with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete family (cascading will handle members and children)
|
||||||
|
await this.userRepository.query('DELETE FROM families WHERE id = $1', [
|
||||||
|
id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { message: 'Family deleted successfully' };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
DialogActions,
|
DialogActions,
|
||||||
Avatar,
|
Avatar,
|
||||||
AvatarGroup,
|
AvatarGroup,
|
||||||
Grid,
|
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
@@ -90,142 +89,10 @@ export default function FamiliesPage() {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await apiClient.get('/admin/families');
|
const response = await apiClient.get('/admin/families');
|
||||||
setFamilies(response.data);
|
setFamilies(response.data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch families:', error);
|
console.error('Failed to fetch families:', error);
|
||||||
// Using mock data for development
|
setFamilies([]);
|
||||||
setFamilies([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'The Doe Family',
|
|
||||||
createdAt: '2024-01-15T10:00:00Z',
|
|
||||||
memberCount: 2,
|
|
||||||
childrenCount: 2,
|
|
||||||
activityCount: 542,
|
|
||||||
lastActivityAt: '2024-10-06T08:30:00Z',
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'John Doe',
|
|
||||||
email: 'john.doe@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-01-15T10:00:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: 'Jane Doe',
|
|
||||||
email: 'jane.doe@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-01-15T10:30:00Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'Emma Doe',
|
|
||||||
birthDate: '2022-03-15',
|
|
||||||
gender: 'female',
|
|
||||||
displayColor: '#FFB5A0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: 'Liam Doe',
|
|
||||||
birthDate: '2020-08-22',
|
|
||||||
gender: 'male',
|
|
||||||
displayColor: '#81C784',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: 'The Smith Family',
|
|
||||||
createdAt: '2024-02-20T14:30:00Z',
|
|
||||||
memberCount: 2,
|
|
||||||
childrenCount: 1,
|
|
||||||
activityCount: 287,
|
|
||||||
lastActivityAt: '2024-10-05T18:45:00Z',
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: 'Jane Smith',
|
|
||||||
email: 'jane.smith@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-02-20T14:30:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
name: 'Bob Smith',
|
|
||||||
email: 'bob.smith@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-02-20T15:00:00Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: 'Olivia Smith',
|
|
||||||
birthDate: '2023-01-10',
|
|
||||||
gender: 'female',
|
|
||||||
displayColor: '#FFD4CC',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: 'The Johnson Family',
|
|
||||||
createdAt: '2024-03-10T09:15:00Z',
|
|
||||||
memberCount: 3,
|
|
||||||
childrenCount: 3,
|
|
||||||
activityCount: 892,
|
|
||||||
lastActivityAt: '2024-09-30T12:00:00Z',
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
name: 'Bob Johnson',
|
|
||||||
email: 'bob.johnson@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-03-10T09:15:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
name: 'Alice Johnson',
|
|
||||||
email: 'alice.johnson@example.com',
|
|
||||||
role: 'parent',
|
|
||||||
joinedAt: '2024-03-10T09:30:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '7',
|
|
||||||
name: 'Mary (Grandma)',
|
|
||||||
email: 'mary.johnson@example.com',
|
|
||||||
role: 'caregiver',
|
|
||||||
joinedAt: '2024-03-15T10:00:00Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
name: 'Noah Johnson',
|
|
||||||
birthDate: '2021-05-20',
|
|
||||||
gender: 'male',
|
|
||||||
displayColor: '#64B5F6',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
name: 'Sophia Johnson',
|
|
||||||
birthDate: '2019-11-08',
|
|
||||||
gender: 'female',
|
|
||||||
displayColor: '#BA68C8',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
name: 'Ethan Johnson',
|
|
||||||
birthDate: '2023-07-15',
|
|
||||||
gender: 'male',
|
|
||||||
displayColor: '#FFB74D',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -288,56 +155,48 @@ export default function FamiliesPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<Grid container spacing={3} sx={{ mb: 4 }}>
|
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: 3, mb: 4 }}>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
<Card>
|
||||||
<Card>
|
<CardContent>
|
||||||
<CardContent>
|
<Typography color="text.secondary" gutterBottom>
|
||||||
<Typography color="text.secondary" gutterBottom>
|
Total Families
|
||||||
Total Families
|
</Typography>
|
||||||
</Typography>
|
<Typography variant="h3" sx={{ color: 'primary.main' }}>
|
||||||
<Typography variant="h3" sx={{ color: 'primary.main' }}>
|
{families.length}
|
||||||
{families.length}
|
</Typography>
|
||||||
</Typography>
|
</CardContent>
|
||||||
</CardContent>
|
</Card>
|
||||||
</Card>
|
<Card>
|
||||||
</Grid>
|
<CardContent>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
<Typography color="text.secondary" gutterBottom>
|
||||||
<Card>
|
Total Members
|
||||||
<CardContent>
|
</Typography>
|
||||||
<Typography color="text.secondary" gutterBottom>
|
<Typography variant="h3" sx={{ color: 'success.main' }}>
|
||||||
Total Members
|
{families.reduce((sum, f) => sum + f.memberCount, 0)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h3" sx={{ color: 'success.main' }}>
|
</CardContent>
|
||||||
{families.reduce((sum, f) => sum + f.memberCount, 0)}
|
</Card>
|
||||||
</Typography>
|
<Card>
|
||||||
</CardContent>
|
<CardContent>
|
||||||
</Card>
|
<Typography color="text.secondary" gutterBottom>
|
||||||
</Grid>
|
Total Children
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
</Typography>
|
||||||
<Card>
|
<Typography variant="h3" sx={{ color: 'info.main' }}>
|
||||||
<CardContent>
|
{families.reduce((sum, f) => sum + f.childrenCount, 0)}
|
||||||
<Typography color="text.secondary" gutterBottom>
|
</Typography>
|
||||||
Total Children
|
</CardContent>
|
||||||
</Typography>
|
</Card>
|
||||||
<Typography variant="h3" sx={{ color: 'info.main' }}>
|
<Card>
|
||||||
{families.reduce((sum, f) => sum + f.childrenCount, 0)}
|
<CardContent>
|
||||||
</Typography>
|
<Typography color="text.secondary" gutterBottom>
|
||||||
</CardContent>
|
Total Activities
|
||||||
</Card>
|
</Typography>
|
||||||
</Grid>
|
<Typography variant="h3" sx={{ color: 'secondary.main' }}>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
{families.reduce((sum, f) => sum + f.activityCount, 0)}
|
||||||
<Card>
|
</Typography>
|
||||||
<CardContent>
|
</CardContent>
|
||||||
<Typography color="text.secondary" gutterBottom>
|
</Card>
|
||||||
Total Activities
|
</Box>
|
||||||
</Typography>
|
|
||||||
<Typography variant="h3" sx={{ color: 'secondary.main' }}>
|
|
||||||
{families.reduce((sum, f) => sum + f.activityCount, 0)}
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
@@ -464,52 +323,49 @@ export default function FamiliesPage() {
|
|||||||
<DialogContent>
|
<DialogContent>
|
||||||
{selectedFamily && (
|
{selectedFamily && (
|
||||||
<Box sx={{ pt: 2 }}>
|
<Box sx={{ pt: 2 }}>
|
||||||
<Grid container spacing={3}>
|
<Box sx={{ mb: 3 }}>
|
||||||
{/* Family Info */}
|
<Typography variant="h6" gutterBottom>
|
||||||
<Grid item xs={12}>
|
Family Information
|
||||||
<Typography variant="h6" gutterBottom>
|
</Typography>
|
||||||
Family Information
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
|
||||||
</Typography>
|
<Box>
|
||||||
<Grid container spacing={2}>
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
<Grid item xs={6}>
|
Family ID
|
||||||
<Typography variant="subtitle2" color="text.secondary">
|
</Typography>
|
||||||
Family ID
|
<Typography variant="body1">{selectedFamily.id}</Typography>
|
||||||
</Typography>
|
</Box>
|
||||||
<Typography variant="body1">{selectedFamily.id}</Typography>
|
<Box>
|
||||||
</Grid>
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
<Grid item xs={6}>
|
Created
|
||||||
<Typography variant="subtitle2" color="text.secondary">
|
</Typography>
|
||||||
Created
|
<Typography variant="body1">
|
||||||
</Typography>
|
{formatDate(selectedFamily.createdAt)}
|
||||||
<Typography variant="body1">
|
</Typography>
|
||||||
{formatDate(selectedFamily.createdAt)}
|
</Box>
|
||||||
</Typography>
|
<Box>
|
||||||
</Grid>
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
<Grid item xs={6}>
|
Total Activities
|
||||||
<Typography variant="subtitle2" color="text.secondary">
|
</Typography>
|
||||||
Total Activities
|
<Typography variant="body1">
|
||||||
</Typography>
|
{selectedFamily.activityCount}
|
||||||
<Typography variant="body1">
|
</Typography>
|
||||||
{selectedFamily.activityCount}
|
</Box>
|
||||||
</Typography>
|
<Box>
|
||||||
</Grid>
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
<Grid item xs={6}>
|
Last Activity
|
||||||
<Typography variant="subtitle2" color="text.secondary">
|
</Typography>
|
||||||
Last Activity
|
<Typography variant="body1">
|
||||||
</Typography>
|
{formatDate(selectedFamily.lastActivityAt)}
|
||||||
<Typography variant="body1">
|
</Typography>
|
||||||
{formatDate(selectedFamily.lastActivityAt)}
|
</Box>
|
||||||
</Typography>
|
</Box>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Divider sx={{ mb: 3 }} />
|
||||||
<Divider />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
|
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 3 }}>
|
||||||
{/* Members */}
|
{/* Members */}
|
||||||
<Grid item xs={12} md={6}>
|
<Box>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
Members ({selectedFamily.memberCount})
|
Members ({selectedFamily.memberCount})
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -539,10 +395,10 @@ export default function FamiliesPage() {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Children */}
|
{/* Children */}
|
||||||
<Grid item xs={12} md={6}>
|
<Box>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
Children ({selectedFamily.childrenCount})
|
Children ({selectedFamily.childrenCount})
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -569,8 +425,8 @@ export default function FamiliesPage() {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user