feat: Fix invite-codes page with AdminGuard and UI improvements
Some checks failed
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
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 / 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
Some checks failed
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
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 / 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
Backend changes: - Added AdminGuard to InviteCodesController for authentication - Added PATCH endpoint alongside PUT for invite code updates - Secured all admin invite code endpoints Frontend changes: - Removed deprecated MUI Grid v1, replaced with CSS Grid - Added thousand separators to all numbers (total codes, uses, etc.) - Removed Grid import (no longer used) - Applied .toLocaleString() to stats cards and DataGrid uses column - Fixed responsive layout with CSS Grid breakpoints
This commit is contained in:
@@ -9,11 +9,14 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
Request,
|
Request,
|
||||||
|
Patch,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InviteCodesService } from './invite-codes.service';
|
import { InviteCodesService } from './invite-codes.service';
|
||||||
import { CreateInviteCodeDto, UpdateInviteCodeDto } from './invite-codes.dto';
|
import { CreateInviteCodeDto, UpdateInviteCodeDto } from './invite-codes.dto';
|
||||||
|
import { AdminGuard } from '../../common/guards/admin.guard';
|
||||||
|
|
||||||
@Controller('api/v1/admin/invite-codes')
|
@Controller('api/v1/admin/invite-codes')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
export class InviteCodesController {
|
export class InviteCodesController {
|
||||||
constructor(private readonly inviteCodesService: InviteCodesService) {}
|
constructor(private readonly inviteCodesService: InviteCodesService) {}
|
||||||
|
|
||||||
@@ -57,6 +60,11 @@ export class InviteCodesController {
|
|||||||
return this.inviteCodesService.update(id, dto);
|
return this.inviteCodesService.update(id, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
async patch(@Param('id') id: string, @Body() dto: UpdateInviteCodeDto) {
|
||||||
|
return this.inviteCodesService.update(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
async remove(@Param('id') id: string) {
|
async remove(@Param('id') id: string) {
|
||||||
await this.inviteCodesService.delete(id);
|
await this.inviteCodesService.delete(id);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Alert,
|
Alert,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Grid,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { DataGrid, GridColDef } from '@mui/x-data-grid';
|
import { DataGrid, GridColDef } from '@mui/x-data-grid';
|
||||||
import {
|
import {
|
||||||
@@ -178,7 +177,7 @@ export default function InviteCodesPage() {
|
|||||||
width: 100,
|
width: 100,
|
||||||
renderCell: (params) => (
|
renderCell: (params) => (
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{params.value} / {params.row.maxUses || '∞'}
|
{params.value.toLocaleString()} / {params.row.maxUses?.toLocaleString() || '∞'}
|
||||||
</Typography>
|
</Typography>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -255,50 +254,42 @@ export default function InviteCodesPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: 3, mb: 3 }}>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
<Paper sx={{ p: 2 }}>
|
||||||
<Paper sx={{ p: 2 }}>
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
Total Codes
|
||||||
Total Codes
|
</Typography>
|
||||||
</Typography>
|
<Typography variant="h4">{inviteCodes.length.toLocaleString()}</Typography>
|
||||||
<Typography variant="h4">{inviteCodes.length}</Typography>
|
</Paper>
|
||||||
</Paper>
|
<Paper sx={{ p: 2 }}>
|
||||||
</Grid>
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
Active Codes
|
||||||
<Paper sx={{ p: 2 }}>
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
<Typography variant="h4">
|
||||||
Active Codes
|
{inviteCodes.filter(c => c.isActive).length.toLocaleString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4">
|
</Paper>
|
||||||
{inviteCodes.filter(c => c.isActive).length}
|
<Paper sx={{ p: 2 }}>
|
||||||
</Typography>
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||||
</Paper>
|
Total Uses
|
||||||
</Grid>
|
</Typography>
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
<Typography variant="h4">
|
||||||
<Paper sx={{ p: 2 }}>
|
{inviteCodes.reduce((sum, c) => sum + c.uses, 0).toLocaleString()}
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
</Typography>
|
||||||
Total Uses
|
</Paper>
|
||||||
</Typography>
|
<Paper sx={{ p: 2 }}>
|
||||||
<Typography variant="h4">
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||||
{inviteCodes.reduce((sum, c) => sum + c.uses, 0)}
|
Available
|
||||||
</Typography>
|
</Typography>
|
||||||
</Paper>
|
<Typography variant="h4">
|
||||||
</Grid>
|
{inviteCodes.filter(c => {
|
||||||
<Grid item xs={12} sm={6} md={3}>
|
const isExpired = c.expiresAt && new Date(c.expiresAt) < new Date();
|
||||||
<Paper sx={{ p: 2 }}>
|
const isMaxedOut = c.maxUses && c.uses >= c.maxUses;
|
||||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
return c.isActive && !isExpired && !isMaxedOut;
|
||||||
Available
|
}).length.toLocaleString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4">
|
</Paper>
|
||||||
{inviteCodes.filter(c => {
|
</Box>
|
||||||
const isExpired = c.expiresAt && new Date(c.expiresAt) < new Date();
|
|
||||||
const isMaxedOut = c.maxUses && c.uses >= c.maxUses;
|
|
||||||
return c.isActive && !isExpired && !isMaxedOut;
|
|
||||||
}).length}
|
|
||||||
</Typography>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Paper sx={{ p: 2 }}>
|
<Paper sx={{ p: 2 }}>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
Reference in New Issue
Block a user