Skip to content

Commit 53726b8

Browse files
committed
feat: Convert Dynamic Data Table to MRDT table and impl pagination
1 parent c18d85e commit 53726b8

File tree

7 files changed

+493
-457
lines changed

7 files changed

+493
-457
lines changed

pages/application.css

Lines changed: 1 addition & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,6 @@
1313
stroke-width: 1px;
1414
}
1515

16-
/* Audit Report Table Styles */
17-
.table-container {
18-
@apply overflow-x-auto rounded-lg border border-gray-200 shadow-sm;
19-
}
20-
21-
.data-table {
22-
@apply min-w-full divide-y divide-gray-200;
23-
}
24-
25-
.data-table thead {
26-
@apply bg-gray-50;
27-
}
28-
29-
.data-table th {
30-
@apply px-3 py-2 text-left text-xs font-medium uppercase tracking-wider text-gray-500;
31-
}
32-
33-
.data-table td {
34-
@apply border-t border-gray-200 px-3 py-2 text-sm;
35-
}
36-
37-
.data-table tr {
38-
@apply bg-white transition-colors hover:bg-gray-50;
39-
}
40-
4116
.status-badge {
4217
@apply rounded-full px-2 py-0.5 text-xs font-medium;
4318
}
@@ -47,37 +22,6 @@
4722
@apply p-0;
4823
}
4924

50-
.pdf-export .table-container {
51-
@apply overflow-visible border-0 shadow-none;
52-
}
53-
54-
.pdf-export .data-table {
55-
@apply w-full table-fixed;
56-
page-break-inside: avoid;
57-
}
58-
59-
.pdf-export .data-table thead {
60-
display: table-row-group;
61-
}
62-
63-
.pdf-export .data-table tbody {
64-
page-break-inside: avoid;
65-
break-inside: avoid;
66-
}
67-
68-
.pdf-export .data-table tr {
69-
page-break-inside: avoid;
70-
break-inside: avoid;
71-
}
72-
73-
.pdf-export .data-table th {
74-
@apply bg-gray-50 px-2 py-1 text-xs;
75-
}
76-
77-
.pdf-export .data-table td {
78-
@apply px-2 py-1 text-xs;
79-
}
80-
8125
.pdf-export .status-badge {
8226
@apply px-1.5 py-0.5 text-xs;
8327
}
@@ -111,4 +55,4 @@
11155

11256
.pdf-export svg {
11357
@apply align-middle;
114-
}
58+
}

src/api/services/views.ts

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ConfigDB, ViewAPI } from "../axios";
22
import { resolvePostGrestRequestWithPagination } from "../resolve";
3-
import { ViewResult } from "../../pages/audit-report/types";
3+
import { ViewResult, ViewColumnDef } from "../../pages/audit-report/types";
4+
import { tristateOutputToQueryFilterParam } from "../../ui/Dropdowns/TristateReactSelect";
45

56
export type View = {
67
id: string;
@@ -64,8 +65,26 @@ export const getAllViews = (
6465
})
6566
);
6667
};
68+
export const getViewDataById = async (
69+
viewId: string,
70+
headers?: Record<string, string>
71+
): Promise<ViewResult> => {
72+
const response = await fetch(`/api/view/${viewId}`, {
73+
credentials: "include",
74+
headers
75+
});
76+
77+
if (!response.ok) {
78+
const errorData = await response.json();
79+
throw new Error(
80+
errorData.error || `HTTP ${response.status}: ${response.statusText}`
81+
);
82+
}
83+
84+
return response.json();
85+
};
6786

68-
export const getViewById = (id: string) =>
87+
export const getViewSummary = (id: string) =>
6988
resolvePostGrestRequestWithPagination<ViewSummary[]>(
7089
ConfigDB.get(`/views_summary?id=eq.${id}&select=*`)
7190
);
@@ -85,30 +104,70 @@ export const deleteView = async (id: string) => {
85104
return response;
86105
};
87106

88-
export const getViewData = async (
107+
export const queryViewTable = async (
89108
namespace: string,
90109
name: string,
91-
headers?: Record<string, string>,
92-
filter?: string
93-
): Promise<ViewResult> => {
94-
let url = `/api/view/${namespace}/${name}`;
95-
if (filter && filter.trim()) {
96-
url += `?filter=${encodeURIComponent(filter)}`;
110+
columns: ViewColumnDef[],
111+
searchParams: URLSearchParams
112+
) => {
113+
const cleanNamespace = namespace.replaceAll("-", "_");
114+
const cleanName = name.replaceAll("-", "_");
115+
const tableName = `view_${cleanNamespace}_${cleanName}`;
116+
117+
// Build query string like job history does
118+
let queryString = `?select=*`;
119+
120+
// Get pagination parameters from URL like job history does
121+
const pageIndex = parseInt(searchParams.get("pageIndex") ?? "0");
122+
const pageSize = parseInt(searchParams.get("pageSize") ?? "50");
123+
queryString += `&limit=${pageSize}&offset=${pageIndex * pageSize}`;
124+
125+
// Add filters from URL parameters
126+
for (const [key, value] of searchParams.entries()) {
127+
if (
128+
key !== "filter" &&
129+
key !== "pageIndex" &&
130+
key !== "pageSize" &&
131+
value &&
132+
value.trim()
133+
) {
134+
const filterParam = tristateOutputToQueryFilterParam(value, key);
135+
if (filterParam) {
136+
queryString += filterParam;
137+
}
138+
}
97139
}
98140

99-
const response = await fetch(url, {
100-
credentials: "include",
101-
headers
102-
});
141+
const response = await resolvePostGrestRequestWithPagination(
142+
ConfigDB.get(`/${tableName}${queryString}`, {
143+
headers: {
144+
Prefer: "count=exact"
145+
}
146+
})
147+
);
103148

104-
if (!response.ok) {
105-
const errorData = await response.json();
106-
throw new Error(
107-
errorData.error || `HTTP ${response.status}: ${response.statusText}`
108-
);
149+
if (response.error) {
150+
throw response.error;
109151
}
110152

111-
return response.json();
153+
const data = response.data;
154+
155+
// Convert PostgREST object rows to array rows based on column order
156+
// Example:
157+
// data = [{"name": "John", "age": 30, "city": "New York"}]
158+
// columns = [{name: "name"}, {name: "age"}, {name: "city"}]
159+
// convertedRows = [["John", 30, "New York"]]
160+
const convertedRows =
161+
Array.isArray(data) && data.length > 0
162+
? data.map((rowObj) => {
163+
return columns.map((column) => rowObj[column.name]);
164+
})
165+
: data || [];
166+
167+
return {
168+
data: convertedRows,
169+
totalEntries: response.totalEntries
170+
};
112171
};
113172

114173
export const getViewsForSidebar = async () => {

0 commit comments

Comments
 (0)