Skip to content

Commit 239f9ad

Browse files
committed
feat: implement pagination for purchase status responses
1 parent 637d344 commit 239f9ad

File tree

5 files changed

+62
-19
lines changed

5 files changed

+62
-19
lines changed

client/src/pages/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ export default function IndexPage() {
293293
if (item.hasPublication) {
294294
setScreenshot(item.publicationScreenshot);
295295
}
296-
}
296+
},
297297
},
298298
{
299299
field: "refunded",
@@ -309,6 +309,7 @@ export default function IndexPage() {
309309
},
310310
]}
311311
dataUrl={`${import.meta.env.API_BASE_URL}/purchase-status?limitToNotRefunded=${toggleAllPurchases ? "false" : "true"}`}
312+
defaultPageSize={10}
312313
defaultSortField="date"
313314
defaultSortOrder="desc"
314315
permission={import.meta.env.READ_PERMISSION}

cloudflare-worker/src/db/d1-db.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
PublicationsRepository,
4242
PurchasesRepository,
4343
PurchaseStatus,
44+
PurchaseStatusResponse,
4445
RefundsRepository,
4546
TestersRepository,
4647
} from "./db";
@@ -550,7 +551,7 @@ export class CloudflareD1DB implements FeedbackFlowDB {
550551
limit?: number,
551552
sort?: string,
552553
order?: string,
553-
): Promise<PurchaseStatus[]> => {
554+
): Promise<PurchaseStatusResponse> => {
554555
if (!limitToNotRefunded) {
555556
limitToNotRefunded = false; // Default to false
556557
}
@@ -590,15 +591,19 @@ export class CloudflareD1DB implements FeedbackFlowDB {
590591
// Pagination logic
591592
const offset = page && limit ? (page - 1) * limit : 0;
592593

594+
const countQuery = `SELECT COUNT(*) as count FROM purchase_status WHERE tester_uuid = ? ${limitToNotRefundedQuery}`;
595+
const countStatement = this.db.prepare(countQuery).bind(testerUuid);
596+
const { results: countResults } = await countStatement.all();
597+
const totalCount = countResults[0]?.count as number;
593598
const preparedStatement = this.db
594599
.prepare(
595600
`SELECT * FROM purchase_status WHERE tester_uuid = ? ${limitToNotRefundedQuery} ORDER BY ${sort} ${order.toUpperCase()} LIMIT ? OFFSET ?`,
596601
)
597602
.bind(testerUuid, limit, offset);
598603

599604
const { results } = await preparedStatement.all();
600-
601-
return results.map(
605+
606+
const mappedResults = results.map(
602607
(row) =>
603608
({
604609
purchase: row.id as string,
@@ -615,6 +620,27 @@ export class CloudflareD1DB implements FeedbackFlowDB {
615620
purchaseScreenshot: row.purchase_screenshot as string,
616621
}) as PurchaseStatus,
617622
);
623+
// Add pagination info to the result
624+
const totalPages = Math.ceil(totalCount / limit);
625+
const currentPage = page;
626+
const hasNextPage = currentPage < totalPages;
627+
const hasPreviousPage = currentPage > 1;
628+
const nextPage = hasNextPage ? currentPage + 1 : null;
629+
const previousPage = hasPreviousPage ? currentPage - 1 : null;
630+
const pageInfo = {
631+
totalCount,
632+
totalPages,
633+
currentPage,
634+
hasNextPage,
635+
hasPreviousPage,
636+
nextPage,
637+
previousPage,
638+
};
639+
// Return the results and pagination info
640+
return {
641+
results: mappedResults,
642+
pageInfo,
643+
} as PurchaseStatusResponse;
618644
},
619645
};
620646

cloudflare-worker/src/db/db.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface DATABASESCHEMA {
4848
refunds: Refund[];
4949
}
5050

51+
export interface PurchaseStatusResponse { results: PurchaseStatus[]; pageInfo: { totalCount: number; totalPages: number; currentPage: number; hasNextPage: boolean; hasPreviousPage: boolean; nextPage: number | null; previousPage: number | null; }; }
5152
export interface PurchaseStatus {
5253
purchase: string;
5354
testerUuid: string;
@@ -182,7 +183,7 @@ export interface PurchasesRepository {
182183
limit?: number,
183184
sort?: string,
184185
order?: string,
185-
): Promise<PurchaseStatus[]>;
186+
): Promise<PurchaseStatusResponse>;
186187
}
187188

188189
export interface FeedbacksRepository {

cloudflare-worker/src/db/in-memory-db.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { v4 as uuidv4 } from "uuid";
2828

2929
import { Feedback, Publication, Purchase, Refund, Tester } from "../types/data";
3030

31-
import { DATABASESCHEMA, FeedbackFlowDB, PurchaseStatus } from "./db";
31+
import { DATABASESCHEMA, FeedbackFlowDB, PurchaseStatus, PurchaseStatusResponse } from "./db";
3232

3333
/**
3434
* In-memory database class for testing purposes
@@ -380,7 +380,7 @@ export class InMemoryDB implements FeedbackFlowDB {
380380
limit?: number,
381381
sort?: string,
382382
order?: string,
383-
): Promise<PurchaseStatus[]> => {
383+
): Promise<PurchaseStatusResponse> => {
384384
if (!limitToNotRefunded) {
385385
limitToNotRefunded = false; // Default to false
386386
}
@@ -420,7 +420,7 @@ export class InMemoryDB implements FeedbackFlowDB {
420420
);
421421

422422
// Build the status for each purchase
423-
const result = testerPurchases
423+
let globalResult = testerPurchases
424424
.map((purchase) => {
425425
// Check for related feedback, publication, and refund
426426
const hasFeedback = this.data.feedbacks.find(
@@ -450,7 +450,13 @@ export class InMemoryDB implements FeedbackFlowDB {
450450
publicationScreenshot: hasPublication?.screenshot,
451451
purchaseSreenshot: purchase.screenshot,
452452
} as PurchaseStatus;
453-
})
453+
});
454+
455+
// Filter out refunded purchases if requested
456+
if (limitToNotRefunded) {
457+
globalResult = globalResult.filter((purchase) => !purchase.refunded);
458+
}
459+
const result = globalResult
454460
.slice(offset, offset + limit)
455461
.sort((a, b) => {
456462
if (sort === "date") {
@@ -468,13 +474,22 @@ export class InMemoryDB implements FeedbackFlowDB {
468474
}
469475
});
470476

471-
// Filter out refunded purchases if requested
472-
if (limitToNotRefunded) {
473-
return result.filter((purchase) => !purchase.refunded);
474-
}
475-
476-
return result;
477-
},
477+
// Construct the response object with pagination info
478+
const pageInfo = {
479+
totalCount: globalResult.length,
480+
totalPages: Math.ceil(globalResult.length / limit),
481+
currentPage: page,
482+
hasNextPage: page < Math.ceil(globalResult.length / limit),
483+
hasPreviousPage: page > 1,
484+
nextPage: page < Math.ceil(globalResult.length / limit) ? page + 1 : null,
485+
previousPage: page > 1 ? page - 1 : null,
486+
};
487+
const response: PurchaseStatusResponse = {
488+
results: result,
489+
pageInfo,
490+
};
491+
return response;
492+
}
478493
};
479494

480495
/**

cloudflare-worker/src/routes/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,9 +1115,9 @@ const purchaseRoutes = (router: Router, env: Env) => {
11151115
return new Response(
11161116
JSON.stringify({
11171117
success: true,
1118-
data: purchases,
1119-
total: purchases.length,
1120-
page,
1118+
data: purchases.results,
1119+
total: purchases.pageInfo.totalCount,
1120+
page: purchases.pageInfo.currentPage,
11211121
limit,
11221122
}),
11231123
{

0 commit comments

Comments
 (0)