refactor(FE): Refactor PdfTable components to support generic data types

This commit is contained in:
rstubryan
2026-02-11 10:38:51 +07:00
parent 02d13efc25
commit 70b63f7773
6 changed files with 167 additions and 123 deletions
+44 -54
View File
@@ -1,22 +1,8 @@
'use client';
import { Text, View, StyleSheet } from '@react-pdf/renderer';
export interface PdfColumn {
key: string;
header: string;
flex: number;
align?: 'left' | 'center' | 'right';
}
export interface PdfTbodyCell {
key: string;
value: string | number | React.ReactNode;
align?: 'left' | 'center' | 'right';
color?: string;
formatAs?: 'text' | 'date' | 'currency' | 'number';
formatDate?: string;
}
import { ReactNode } from 'react';
import type { PdfColumn } from './types';
const styles = StyleSheet.create({
tableRow: {
@@ -71,21 +57,22 @@ const styles = StyleSheet.create({
},
});
interface PdfTbodyProps {
columns: PdfColumn[];
rows: PdfTbodyCell[][];
interface PdfTbodyProps<TData = Record<string, unknown>> {
columns: PdfColumn<TData>[];
data: TData[];
firstRow?: {
valueKey: string;
value: number;
align?: 'right';
color?: string;
};
formatDate?: (date: string, format: string) => string;
formatNumber?: (num: number) => string;
formatCurrency?: (num: number) => string;
}
export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
export const PdfTbody = <TData = Record<string, unknown>,>({
columns,
data,
firstRow,
}: PdfTbodyProps<TData>) => {
return (
<>
{/* First Row */}
@@ -93,17 +80,17 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
<View style={[styles.tableRow, styles.tableBorderBottom]}>
{columns.map((column, index) => {
const isLastColumn = index === columns.length - 1;
const isfirstRowColumn = column.key === firstRow.valueKey;
const align = column.align || 'center';
const isFirstRowColumn = column.key === firstRow.valueKey;
const align = column.align || 'left';
const cellStyle =
column.key === 'no'
? [styles.tableCellNo, { flex: column.flex }]
: isfirstRowColumn
? [styles.tableCellNo, { flex: column.flex || 1 }]
: isFirstRowColumn
? [
styles.tableCellRight,
{
flex: column.flex,
flex: column.flex || 1,
color: firstRow.color || 'black',
borderRightWidth: isLastColumn ? 0 : 1,
},
@@ -112,7 +99,7 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
? [
styles.tableCellRight,
{
flex: column.flex,
flex: column.flex || 1,
borderRightWidth: isLastColumn ? 0 : 1,
},
]
@@ -120,7 +107,7 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
? [
styles.tableCellCenter,
{
flex: column.flex,
flex: column.flex || 1,
borderRightWidth: isLastColumn ? 0 : 1,
},
]
@@ -128,15 +115,15 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
? [
styles.tableCellLast,
{
flex: column.flex,
flex: column.flex || 1,
borderRightWidth: 0,
},
]
: [styles.tableCell, { flex: column.flex }];
: [styles.tableCell, { flex: column.flex || 1 }];
return (
<View key={column.key} style={cellStyle}>
<Text>{isfirstRowColumn ? firstRow.value : ''}</Text>
<Text>{isFirstRowColumn ? firstRow.value : ''}</Text>
</View>
);
})}
@@ -144,8 +131,8 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
)}
{/* Data Rows */}
{rows.map((row, rowIndex) => {
const isLastRow = rowIndex === rows.length - 1;
{data.map((row, rowIndex) => {
const isLastRow = rowIndex === data.length - 1;
return (
<View
@@ -156,19 +143,27 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
]}
>
{columns.map((column, colIndex) => {
const cell = row.find((c) => c.key === column.key);
const isLastColumn = colIndex === columns.length - 1;
const align = cell?.align || column.align || 'center';
const align = column.align || 'left';
// Get cell content from column.cell function or fallback to row value
let cellContent: ReactNode;
if (column.cell) {
cellContent = column.cell({ row, index: rowIndex });
} else {
cellContent =
((row as Record<string, unknown>)[column.key] as ReactNode) ??
'-';
}
const cellStyle =
column.key === 'no'
? [styles.tableCellNo, { flex: column.flex }]
? [styles.tableCellNo, { flex: column.flex || 1 }]
: align === 'right'
? [
styles.tableCellRight,
{
flex: column.flex,
color: cell?.color || 'black',
flex: column.flex || 1,
borderRightWidth: isLastColumn ? 0 : 1,
},
]
@@ -176,37 +171,30 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
? [
styles.tableCellCenter,
{
flex: column.flex,
color: cell?.color || 'black',
flex: column.flex || 1,
borderRightWidth: isLastColumn ? 0 : 1,
},
]
: isLastColumn
? [
styles.tableCellLast,
{ flex: column.flex, borderRightWidth: 0 },
{ flex: column.flex || 1, borderRightWidth: 0 },
]
: [
styles.tableCell,
{
flex: column.flex,
color: cell?.color || 'black',
flex: column.flex || 1,
borderRightWidth: isLastColumn ? 0 : 1,
},
];
return (
<View key={column.key} style={cellStyle}>
{cell?.value !== undefined &&
cell?.value !== null &&
cell?.value !== '' ? (
typeof cell.value === 'object' ? (
cell.value
) : (
<Text>{String(cell.value)}</Text>
)
{typeof cellContent === 'string' ||
typeof cellContent === 'number' ? (
<Text>{String(cellContent)}</Text>
) : (
<Text>-</Text>
cellContent
)}
</View>
);
@@ -217,3 +205,5 @@ export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
</>
);
};
export type { PdfColumn };