Summary
Extract duplicated code between admin and staff dashboards into shared components and utilities. Follow the successful InvoicesPage pattern.
Current Duplication
- ~1,200 lines of duplicated code
- Same table patterns in 5+ pages
- Same API handler patterns in 10+ routes
- Same filter/search UI in 4+ pages
Phase 1: Shared UI Components
1.1 DataTablePage Component
File: components/dashboard/shared/DataTablePage.tsx
Reusable table page with:
- Search input
- Filter dropdowns
- Pagination
- Loading states
- Error handling
- Export to CSV
Props:
interface DataTablePageProps<T> {
apiEndpoint: string;
columns: ColumnDef<T>[];
title: string;
description?: string;
filters?: FilterConfig[];
searchPlaceholder?: string;
showExport?: boolean;
queryKeyPrefix: string;
}
Can Replace:
- Admin: payments, disputes, refunds, subscriptions pages
- Staff: tickets, users, payments pages
1.2 StatusBadge Component
File: components/dashboard/shared/StatusBadge.tsx
Props:
interface StatusBadgeProps {
status: string;
variant: 'payment' | 'payout' | 'dispute' | 'refund' | 'ticket';
}
1.3 StatsCard Component
File: components/dashboard/shared/StatsCard.tsx
Props:
interface StatsCardProps {
title: string;
value: string | number;
subtitle?: string;
icon?: React.ReactNode;
trend?: { value: number; direction: 'up' | 'down' };
}
1.4 FilterCard Component
File: components/dashboard/shared/FilterCard.tsx
Reusable filter UI for:
- Search input
- Status dropdown
- Gateway dropdown
- Date range
- Clear filters button
Phase 2: Shared API Utilities
2.1 API Handler Factory
File: lib/api/create-handler.ts
export function createPaginatedHandler<T>({
allowedRoles,
queryFn,
transformFn,
}: HandlerConfig<T>) {
return async function GET(req: NextRequest) {
await requireRole(allowedRoles);
const params = parseQueryParams(req);
const data = await queryFn(params);
return paginatedResponse(transformFn(data));
};
}
2.2 Query Parameter Parser
File: lib/api/query-parser.ts
export function parseQueryParams(req: NextRequest) {
const { searchParams } = new URL(req.url);
return {
page: parseInt(searchParams.get('page') || '1'),
limit: parseInt(searchParams.get('limit') || '50'),
search: searchParams.get('search') || '',
status: searchParams.get('status') || undefined,
// ... other common params
};
}
2.3 Response Formatter
File: lib/api/response.ts
export function paginatedResponse<T>(data: {
items: T[];
total: number;
page: number;
limit: number;
}) {
return NextResponse.json({
data: data.items,
pagination: {
total: data.total,
page: data.page,
limit: data.limit,
hasMore: data.page * data.limit < data.total,
},
});
}
2.4 Role Authorization Helper
File: lib/api/auth.ts
export async function requireRole(
allowedRoles: UserRole[],
): Promise<{ userId: string; role: UserRole }> {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
throw new UnauthorizedError();
}
const user = await prisma.user.findUnique({
where: { id: session.user.id },
select: { role: true },
});
if (!user || !allowedRoles.includes(user.role)) {
throw new ForbiddenError();
}
return { userId: session.user.id, role: user.role };
}
Phase 3: Refactor Existing Pages
Admin Pages to Refactor
Staff Pages to Refactor
API Routes to Refactor
Expected Results
- ~800 lines of code reduction (67%)
- Consistent UI/UX across dashboards
- Easier maintenance
- Faster development of new features
Testing
Priority: Medium
Labels: refactor, DRY, components
Summary
Extract duplicated code between admin and staff dashboards into shared components and utilities. Follow the successful
InvoicesPagepattern.Current Duplication
Phase 1: Shared UI Components
1.1 DataTablePage Component
File:
components/dashboard/shared/DataTablePage.tsxReusable table page with:
Props:
Can Replace:
1.2 StatusBadge Component
File:
components/dashboard/shared/StatusBadge.tsxProps:
1.3 StatsCard Component
File:
components/dashboard/shared/StatsCard.tsxProps:
1.4 FilterCard Component
File:
components/dashboard/shared/FilterCard.tsxReusable filter UI for:
Phase 2: Shared API Utilities
2.1 API Handler Factory
File:
lib/api/create-handler.ts2.2 Query Parameter Parser
File:
lib/api/query-parser.ts2.3 Response Formatter
File:
lib/api/response.ts2.4 Role Authorization Helper
File:
lib/api/auth.tsPhase 3: Refactor Existing Pages
Admin Pages to Refactor
app/dashboard/admin/payments/page.tsx→ Use DataTablePageapp/dashboard/admin/disputes/page.tsx→ Use DataTablePageapp/dashboard/admin/refunds/page.tsx→ Use DataTablePageapp/dashboard/admin/subscriptions/page.tsx→ Use DataTablePageStaff Pages to Refactor
app/dashboard/staff/[staffId]/(features)/tickets/page.tsx→ Use DataTablePageapp/dashboard/staff/[staffId]/(features)/users/page.tsx→ Use DataTablePageAPI Routes to Refactor
Expected Results
Testing
Priority: Medium
Labels: refactor, DRY, components