From 8790c45edc1374f9649b8438563fc8844a645367 Mon Sep 17 00:00:00 2001 From: Dries Augustyns Date: Fri, 2 Jan 2026 10:40:39 +0100 Subject: [PATCH] fix: Migrate over to new pagination format in dashboard --- apps/api/src/controllers/Campaigns.ts | 8 +------- apps/api/src/services/CampaignService.ts | 6 +++--- .../components/CampaignSelectionDialog.tsx | 15 ++++---------- .../components/TemplateSelectionDialog.tsx | 15 ++++---------- apps/web/src/pages/campaigns/index.tsx | 15 ++++---------- apps/web/src/pages/templates/index.tsx | 15 ++++---------- apps/web/src/pages/workflows/index.tsx | 20 ++++++------------- 7 files changed, 26 insertions(+), 68 deletions(-) diff --git a/apps/api/src/controllers/Campaigns.ts b/apps/api/src/controllers/Campaigns.ts index 8628d25..3f165f9 100644 --- a/apps/api/src/controllers/Campaigns.ts +++ b/apps/api/src/controllers/Campaigns.ts @@ -77,13 +77,7 @@ export class Campaigns { pageSize, }); - return res.json({ - campaigns: result.campaigns, - page: result.page, - pageSize: result.pageSize, - total: result.total, - totalPages: result.totalPages, - }); + return res.json(result); } /** diff --git a/apps/api/src/services/CampaignService.ts b/apps/api/src/services/CampaignService.ts index ce9c033..077e0c9 100644 --- a/apps/api/src/services/CampaignService.ts +++ b/apps/api/src/services/CampaignService.ts @@ -1,6 +1,6 @@ import type {Campaign, Contact, Prisma} from '@plunk/db'; import {CampaignAudienceType, CampaignStatus, EmailSourceType} from '@plunk/db'; -import type {CreateCampaignData, FilterCondition, UpdateCampaignData} from '@plunk/types'; +import type {CreateCampaignData, FilterCondition, PaginatedResponse, UpdateCampaignData} from '@plunk/types'; import {fromPrismaJson, toPrismaJson} from '@plunk/types'; import signale from 'signale'; @@ -184,7 +184,7 @@ export class CampaignService { page?: number; pageSize?: number; } = {}, - ): Promise<{campaigns: Campaign[]; total: number; page: number; pageSize: number; totalPages: number}> { + ): Promise> { const {status, page = 1, pageSize = 20} = options; const skip = (page - 1) * pageSize; @@ -207,7 +207,7 @@ export class CampaignService { ]); return { - campaigns, + data: campaigns, total, page, pageSize, diff --git a/apps/web/src/components/CampaignSelectionDialog.tsx b/apps/web/src/components/CampaignSelectionDialog.tsx index f5ba650..738d920 100644 --- a/apps/web/src/components/CampaignSelectionDialog.tsx +++ b/apps/web/src/components/CampaignSelectionDialog.tsx @@ -21,18 +21,11 @@ import { } from '@plunk/ui'; import type {Campaign} from '@plunk/db'; import {CampaignStatus} from '@plunk/db'; +import type {PaginatedResponse} from '@plunk/types'; import {ArrowLeft, Calendar, Mail, Users} from 'lucide-react'; import {useState} from 'react'; import useSWR from 'swr'; -interface PaginatedCampaigns { - campaigns: Campaign[]; - total: number; - page: number; - pageSize: number; - totalPages: number; -} - interface CampaignSelectionDialogProps { open: boolean; onOpenChange: (open: boolean) => void; @@ -62,7 +55,7 @@ export function CampaignSelectionDialog({open, onOpenChange, onSelectCampaign}: audience: true, }); - const {data, isLoading} = useSWR( + const {data, isLoading} = useSWR>( open ? `/campaigns?page=${page}&pageSize=10${statusFilter !== 'ALL' ? `&status=${statusFilter}` : ''}` : null, {revalidateOnFocus: false}, ); @@ -192,7 +185,7 @@ export function CampaignSelectionDialog({open, onOpenChange, onSelectCampaign}: )} - {!isLoading && data?.campaigns.length === 0 && ( + {!isLoading && data?.data.length === 0 && (

No campaigns found

@@ -202,7 +195,7 @@ export function CampaignSelectionDialog({open, onOpenChange, onSelectCampaign}:
)} - {data?.campaigns.map(campaign => ( + {data?.data.map(campaign => ( void; @@ -57,7 +50,7 @@ export function TemplateSelectionDialog({open, onOpenChange, onSelectTemplate}: replyTo: true, }); - const {data, isLoading} = useSWR( + const {data, isLoading} = useSWR>( open ? `/templates?page=${page}&pageSize=10${search ? `&search=${search}` : ''}${typeFilter !== 'ALL' ? `&type=${typeFilter}` : ''}` : null, @@ -219,7 +212,7 @@ export function TemplateSelectionDialog({open, onOpenChange, onSelectTemplate}: )} - {!isLoading && data?.templates.length === 0 && ( + {!isLoading && data?.data.length === 0 && (

No templates found

@@ -229,7 +222,7 @@ export function TemplateSelectionDialog({open, onOpenChange, onSelectTemplate}:
)} - {data?.templates.map(template => ( + {data?.data.map(template => ( ( + const {data, mutate, isLoading} = useSWR>( `/campaigns?page=${page}&pageSize=20${statusFilter !== 'ALL' ? `&status=${statusFilter}` : ''}`, {revalidateOnFocus: false}, ); @@ -293,7 +286,7 @@ export default function CampaignsPage() { )} - {!isLoading && data?.campaigns.length === 0 && ( + {!isLoading && data?.data.length === 0 && (
@@ -367,7 +360,7 @@ export default function CampaignsPage() { )} - {data?.campaigns.map(campaign => { + {data?.data.map(campaign => { const openRate = campaign.sentCount > 0 ? (campaign.openedCount / campaign.sentCount) * 100 : 0; const clickRate = campaign.sentCount > 0 ? (campaign.clickedCount / campaign.sentCount) * 100 : 0; const deliveryProgress = diff --git a/apps/web/src/pages/templates/index.tsx b/apps/web/src/pages/templates/index.tsx index 8e4db04..e80ea1a 100644 --- a/apps/web/src/pages/templates/index.tsx +++ b/apps/web/src/pages/templates/index.tsx @@ -10,6 +10,7 @@ import { Input, } from '@plunk/ui'; import type {Template} from '@plunk/db'; +import type {PaginatedResponse} from '@plunk/types'; import {DashboardLayout} from '../../components/DashboardLayout'; import {network} from '../../lib/network'; import {formatRelativeTime} from '../../lib/dateUtils'; @@ -21,14 +22,6 @@ import {toast} from 'sonner'; import useSWR from 'swr'; import dayjs from 'dayjs'; -interface PaginatedTemplates { - templates: Template[]; - total: number; - page: number; - pageSize: number; - totalPages: number; -} - export default function TemplatesPage() { const [page, setPage] = useState(1); const [search, setSearch] = useState(''); @@ -37,7 +30,7 @@ export default function TemplatesPage() { const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [templateToDelete, setTemplateToDelete] = useState(null); - const {data, mutate, isLoading} = useSWR( + const {data, mutate, isLoading} = useSWR>( `/templates?page=${page}&pageSize=20${search ? `&search=${search}` : ''}${typeFilter !== 'ALL' ? `&type=${typeFilter}` : ''}`, {revalidateOnFocus: false}, ); @@ -182,7 +175,7 @@ export default function TemplatesPage() {
- ) : data?.templates.length === 0 ? ( + ) : data?.data.length === 0 ? (
@@ -204,7 +197,7 @@ export default function TemplatesPage() { ) : ( <> - {data?.templates.map(template => ( + {data?.data.map(template => (
diff --git a/apps/web/src/pages/workflows/index.tsx b/apps/web/src/pages/workflows/index.tsx index d9cb1f8..8e825d9 100644 --- a/apps/web/src/pages/workflows/index.tsx +++ b/apps/web/src/pages/workflows/index.tsx @@ -21,6 +21,7 @@ import { SelectValue, } from '@plunk/ui'; import type {Workflow} from '@plunk/db'; +import type {PaginatedResponse} from '@plunk/types'; import {DashboardLayout} from '../../components/DashboardLayout'; import {network} from '../../lib/network'; import {formatRelativeTime} from '../../lib/dateUtils'; @@ -33,14 +34,6 @@ import useSWR from 'swr'; import {WorkflowSchemas} from '@plunk/shared'; import dayjs from 'dayjs'; -interface PaginatedWorkflows { - workflows: (Workflow & {_count?: {steps: number; executions: number}})[]; - total: number; - page: number; - pageSize: number; - totalPages: number; -} - export default function WorkflowsPage() { const [page, setPage] = useState(1); const [search, setSearch] = useState(''); @@ -49,10 +42,9 @@ export default function WorkflowsPage() { const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [workflowToDelete, setWorkflowToDelete] = useState(null); - const {data, mutate, isLoading} = useSWR( - `/workflows?page=${page}&pageSize=20${search ? `&search=${search}` : ''}`, - {revalidateOnFocus: false}, - ); + const {data, mutate, isLoading} = useSWR< + PaginatedResponse + >(`/workflows?page=${page}&pageSize=20${search ? `&search=${search}` : ''}`, {revalidateOnFocus: false}); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); @@ -164,7 +156,7 @@ export default function WorkflowsPage() {
- ) : data?.workflows.length === 0 ? ( + ) : data?.data.length === 0 ? (
@@ -184,7 +176,7 @@ export default function WorkflowsPage() { ) : ( <> - {data?.workflows.map(workflow => ( + {data?.data.map(workflow => (