Skip to content

Commit efb43ef

Browse files
committed
feat: PDF for ready to refund v0
1 parent 4262c7a commit efb43ef

File tree

9 files changed

+169
-25
lines changed

9 files changed

+169
-25
lines changed

client/src/locales/base/ar-SA.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"transaction-id": "معرف المعاملة",
163163
"utilities": "المرافق",
164164
"oldest-ready-to-refund-pdf": "أقدم استعداد لاسترداد الأموال (PDF)",
165-
"oldest-ready-to-refund": "أقدم عمليات الشراء جاهزة لاسترداد الأموال"
165+
"oldest-ready-to-refund": "أقدم عمليات الشراء جاهزة لاسترداد الأموال",
166+
"oldest-ready-to-refund-description": "قم بإنشاء ملف PDF لأقدم 10 عمليات شراء جاهزة لاستردادها"
166167
}

client/src/locales/base/en-US.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"optional-transaction-id": "Optional transaction Id",
163163
"utilities": "Utilities",
164164
"oldest-ready-to-refund-pdf": "Oldest ready to refund (PDF)",
165-
"oldest-ready-to-refund": "Oldest purchases ready for refund"
165+
"oldest-ready-to-refund": "Oldest purchases ready for refund",
166+
"oldest-ready-to-refund-description": "Generate a PDF for the 10 oldest purchases ready to be refunded"
166167
}

client/src/locales/base/es-ES.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"transaction-id": "ID de transacción",
163163
"utilities": "Utilidades",
164164
"oldest-ready-to-refund-pdf": "Más antiguo listo para reembolsar (PDF)",
165-
"oldest-ready-to-refund": "Compras más antiguas listas para el reembolso"
165+
"oldest-ready-to-refund": "Compras más antiguas listas para el reembolso",
166+
"oldest-ready-to-refund-description": "Genere un PDF para las 10 compras más antiguas listas para ser reembolsadas"
166167
}

client/src/locales/base/fr-FR.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"transaction-id": "ID de transaction",
163163
"utilities": "Utilitaires",
164164
"oldest-ready-to-refund-pdf": "Les plus anciens prêts à être remboursés (PDF)",
165-
"oldest-ready-to-refund": "Les plus anciens achats prêts à être remboursés"
165+
"oldest-ready-to-refund": "Les plus anciens achats prêts à être remboursés",
166+
"oldest-ready-to-refund-description": "Générez un PDF pour les 10 plus anciens achats prêts à être remboursés"
166167
}

client/src/locales/base/he-IL.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"transaction-id": "מזהה עסקה",
163163
"utilities": "כלי עזר",
164164
"oldest-ready-to-refund-pdf": "הוותיק ביותר מוכן להחזר (PDF)",
165-
"oldest-ready-to-refund": "רכישות ישנות ביותר מוכנות להחזר"
165+
"oldest-ready-to-refund": "רכישות ישנות ביותר מוכנות להחזר",
166+
"oldest-ready-to-refund-description": "צור PDF עבור 10 הרכישות העתיקות ביותר שמוכנות להחזיר"
166167
}

client/src/locales/base/zh-CN.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@
162162
"transaction-id": "事务ID",
163163
"utilities": "公用事业",
164164
"oldest-ready-to-refund-pdf": "最古老的准备退款(PDF)",
165-
"oldest-ready-to-refund": "最古老的购买准备退款"
165+
"oldest-ready-to-refund": "最古老的购买准备退款",
166+
"oldest-ready-to-refund-description": "为准备退款的10个最古老的购买生成PDF"
166167
}

client/src/pages/oldest-ready-to-refund.tsx

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,84 @@
11
import { useTranslation } from "react-i18next";
2-
import { Page, Text, View, Document, PDFViewer } from "@react-pdf/renderer";
2+
import { useEffect, useState } from "react";
3+
import {
4+
Image as PDFImage,
5+
Page,
6+
Text,
7+
View,
8+
Document,
9+
PDFViewer,
10+
} from "@react-pdf/renderer";
311

12+
import { useSecuredApi } from "@/components/auth0";
413
import { title } from "@/components/primitives";
514
import DefaultLayout from "@/layouts/default";
15+
import { ReadyForRefundPurchase } from "@/types/data";
16+
17+
const MAX_OLDEST_READY_TO_REFUND = 10;
18+
19+
/** Function for converting a webp base64 image url ( data:image/webp;base64,… )to a base64 image containing the image converted to png
20+
* @param {string} base64DataUrl - The base64 data URL ( data:image/webp;base64,… ) of the webp image
21+
* @returns {Promise<string>} - A promise that resolves to a base64 image url ( data:image/webp;base64,… ) containing the png converted of the webp image
22+
*/
23+
function convertWebpToPng(base64DataUrl: string | undefined): Promise<string> {
24+
return new Promise((resolve, reject) => {
25+
if (!base64DataUrl) {
26+
reject(new Error("No base64 data URL provided"));
27+
28+
return;
29+
}
30+
const img = new Image();
31+
32+
img.src = base64DataUrl;
33+
img.onload = () => {
34+
const canvas = document.createElement("canvas");
35+
36+
canvas.width = img.width;
37+
canvas.height = img.height;
38+
const ctx = canvas.getContext("2d");
39+
40+
if (ctx) {
41+
ctx.drawImage(img, 0, 0);
42+
const pngDataUrl = canvas.toDataURL("image/png");
43+
44+
resolve(pngDataUrl);
45+
} else {
46+
reject(new Error("Failed to get canvas context"));
47+
}
48+
};
49+
img.onerror = (error) => {
50+
reject(error);
51+
};
52+
});
53+
}
54+
655
export default function OldestReadyToRefundPage() {
756
const { t } = useTranslation();
8-
9-
const ReadyToRefundPDF = () => (
10-
<Document>
11-
<Page size="A4">
12-
<View>
13-
<Text>Section #1</Text>
14-
</View>
15-
<View>
16-
<Text>Section #2</Text>
17-
</View>
18-
</Page>
19-
</Document>
57+
const [readyToRefund, setReadyToRefund] = useState(
58+
[] as ReadyForRefundPurchase[],
2059
);
60+
const { getJson } = useSecuredApi();
61+
const fetchReadyToRefund = async () => {
62+
try {
63+
const response = await getJson(
64+
`${import.meta.env.API_BASE_URL}/purchases/ready-to-refund?limit=${MAX_OLDEST_READY_TO_REFUND}`,
65+
);
66+
67+
if (response.success) {
68+
setReadyToRefund(response.data);
69+
} else {
70+
// eslint-disable-next-line no-console
71+
console.error("Error fetching data:", JSON.stringify(response));
72+
}
73+
} catch (error) {
74+
// eslint-disable-next-line no-console
75+
console.error("Error fetching data:", error);
76+
}
77+
};
78+
79+
useEffect(() => {
80+
fetchReadyToRefund();
81+
}, []);
2182

2283
return (
2384
<DefaultLayout>
@@ -27,12 +88,33 @@ export default function OldestReadyToRefundPage() {
2788
<p>{t("oldest-ready-to-refund-description")}</p>
2889
</div>
2990
</section>
30-
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
31-
<div className="inline-block max-w-lg text-center justify-center">
32-
<PDFViewer style={{ width: "100%", height: "600px" }}>
33-
<ReadyToRefundPDF />
91+
<section className="flex flex-col items-center justify-center min-w-full lg:min-w-2xl">
92+
{readyToRefund && readyToRefund.length > 0 ? (
93+
<PDFViewer className="w-full h-screen">
94+
<Document>
95+
<Page size="A4">
96+
<View>
97+
{readyToRefund.map((purchase) => (
98+
<View key={purchase.id}>
99+
<Text>{`ID: ${purchase.id}`}</Text>
100+
<Text>{`Date: ${purchase.date}`}</Text>
101+
<Text>{`Order: ${purchase.order}`}</Text>
102+
<Text>{`Description: ${purchase.description}`}</Text>
103+
<Text>{`Refunded: ${purchase.refunded}`}</Text>
104+
<Text>{`Amount: ${purchase.amount}`}</Text>
105+
<PDFImage
106+
src={convertWebpToPng(purchase.publicationScreenShot)}
107+
style={{ width: "50%", height: "auto" }}
108+
/>
109+
</View>
110+
))}
111+
</View>
112+
</Page>
113+
</Document>
34114
</PDFViewer>
35-
</div>
115+
) : (
116+
<p>{t("no-data-available")}</p>
117+
)}
36118
</section>
37119
</DefaultLayout>
38120
);

cloudflare-worker/src/routes/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
testerAllowedSortKeys,
3535
TesterSortCriteria,
3636
PurchaseSortCriteria,
37+
ReadyForRefundPurchase,
3738
} from "../types/data";
3839
import { InMemoryDB } from "../db/in-memory-db";
3940
import { getDatabase } from "../db/db";
@@ -1051,7 +1052,7 @@ const purchaseRoutes = (router: Router, env: Env) => {
10511052
const { results: purchases, totalCount } = await db.purchases.readyForRefund(testerUuid, pagination);
10521053

10531054
// Format response with enhanced data including feedback
1054-
const formattedPurchases = purchases.map((p) => ({
1055+
const formattedPurchases:ReadyForRefundPurchase[] = purchases.map((p) => ({
10551056
id: p.id,
10561057
date: p.date,
10571058
order: p.order,

cloudflare-worker/src/types/data.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,58 @@ export type PurchaseSortCriteria = (typeof purchaseAllowedSortKeys)[number];
221221
// Define valid sort keys for testers
222222
export const testerAllowedSortKeys = ["uuid", "name"] as const;
223223
export type TesterSortCriteria = (typeof testerAllowedSortKeys)[number];
224+
225+
/**
226+
* Represents a purchase that is ready for refund with feedback and publication data
227+
*/
228+
export interface ReadyForRefundPurchase {
229+
/**
230+
* Unique identifier of the purchase
231+
*/
232+
id: string;
233+
234+
/**
235+
* Date when the purchase was made
236+
*/
237+
date: string;
238+
239+
/**
240+
* Order number or identifier
241+
*/
242+
order: string;
243+
244+
/**
245+
* Description of the purchased item
246+
*/
247+
description: string;
248+
249+
/**
250+
* Whether the purchase has been refunded
251+
*/
252+
refunded: boolean;
253+
254+
/**
255+
* Amount of the purchase
256+
*/
257+
amount: number;
258+
259+
/**
260+
* Feedback content provided by the tester
261+
*/
262+
feedback: string;
263+
264+
/**
265+
* Date when the feedback was submitted
266+
*/
267+
feedbackDate: string;
268+
269+
/**
270+
* Date when the feedback was published
271+
*/
272+
publicationDate?: string;
273+
274+
/**
275+
* Screenshot of the published feedback
276+
*/
277+
publicationScreenShot?: string;
278+
}

0 commit comments

Comments
 (0)