From c7766f78fc3cc1e21ad3b09d2c74ac9156e52e2b Mon Sep 17 00:00:00 2001 From: Sosokker Date: Sun, 10 Nov 2024 23:01:50 +0700 Subject: [PATCH] feat: add update log tab --- src/app/(investment)/deals/[id]/UpdateTab.tsx | 72 ++++++++++++++++++ src/app/(investment)/deals/[id]/logParser.ts | 57 ++++++++++++++ src/app/(investment)/deals/[id]/page.tsx | 21 ++--- src/lib/data/projectLogQuery.ts | 19 +++++ src/types/database.types.ts | Bin 65178 -> 66276 bytes 5 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 src/app/(investment)/deals/[id]/UpdateTab.tsx create mode 100644 src/app/(investment)/deals/[id]/logParser.ts create mode 100644 src/lib/data/projectLogQuery.ts diff --git a/src/app/(investment)/deals/[id]/UpdateTab.tsx b/src/app/(investment)/deals/[id]/UpdateTab.tsx new file mode 100644 index 0000000..bbe73c3 --- /dev/null +++ b/src/app/(investment)/deals/[id]/UpdateTab.tsx @@ -0,0 +1,72 @@ +import { createSupabaseClient } from "@/lib/supabase/serverComponentClient"; +import { getProjectLogByProjectId } from "@/lib/data/projectLogQuery"; +import { LogEntry, parseProjectLog } from "./logParser"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card"; + +export const UpdateTab = async ({ projectId }: { projectId: number }) => { + const supabase = createSupabaseClient(); + const { data, error } = await getProjectLogByProjectId(supabase, projectId); + + if (error) { + return ( +
+ + +
+ No updates available + There are no updates to display at this time. Please check back later. +
+
+
+
+ ); + } + + const parsedLogs = parseProjectLog(data as unknown as LogEntry[]); + + if (parsedLogs.length === 0) { + return ( +
+ + +
+ No updates available + There are no updates to display at this time. Please check back later. +
+
+
+
+ ); + } + + return ( +
+ {parsedLogs.map((log, index) => ( +
+ + + {log.table} + {log.changes.length} Changes + + + {log.changes.map((change, changeIndex) => ( +
+
{change.field}
+
+ From: {JSON.stringify(change.from)} +
+
+ To: {JSON.stringify(change.to)} +
+
+ ))} +
+ +
Updated at: {new Date(log.changed_at).toLocaleString()}
+
+
+
+ ))} +
+ ); +}; diff --git a/src/app/(investment)/deals/[id]/logParser.ts b/src/app/(investment)/deals/[id]/logParser.ts new file mode 100644 index 0000000..52b8c31 --- /dev/null +++ b/src/app/(investment)/deals/[id]/logParser.ts @@ -0,0 +1,57 @@ +export interface LogEntry { + id: number; + operation_type: string; + record_id: number; + old_data: Record; + new_data: Record; + changed_at: string; + table_name: string; +} + +type ChangeSummary = { + field: string; + from: any; + to: any; +}; + +function parseProjectLog(logs: LogEntry[]): { changes: ChangeSummary[]; table: string; changed_at: string }[] { + return logs.map((log) => { + const changes: ChangeSummary[] = []; + + if (log.table_name === "project_investment_detail") { + if (log.operation_type === "UPDATE") { + for (const key in log.old_data) { + if (log.old_data[key] !== log.new_data[key]) { + changes.push({ + field: key, + from: log.old_data[key], + to: log.new_data[key], + }); + } + } + } + } + + if (log.table_name === "project") { + if (log.operation_type === "UPDATE") { + for (const key in log.old_data) { + if (log.old_data[key] !== log.new_data[key]) { + changes.push({ + field: key, + from: log.old_data[key], + to: log.new_data[key], + }); + } + } + } + } + + return { + table: log.table_name, + changed_at: log.changed_at, + changes, + }; + }); +} + +export { parseProjectLog }; diff --git a/src/app/(investment)/deals/[id]/page.tsx b/src/app/(investment)/deals/[id]/page.tsx index 2a422d4..08a1430 100644 --- a/src/app/(investment)/deals/[id]/page.tsx +++ b/src/app/(investment)/deals/[id]/page.tsx @@ -17,6 +17,7 @@ import { getDealList } from "@/app/api/dealApi"; import { sumByKey, toPercentage } from "@/lib/utils"; import { redirect } from "next/navigation"; import { isOwnerOfProject } from "./query"; +import { UpdateTab } from "./UpdateTab"; import remarkGfm from "remark-gfm"; const PHOTO_MATERIAL_ID = 2; @@ -241,11 +242,11 @@ export default async function ProjectDealPage({ params }: { params: { id: number {/* menu */}
-
- +
+ Pitch - General Data + {/* General Data */} Updates @@ -253,7 +254,8 @@ export default async function ProjectDealPage({ params }: { params: { id: number {projectData.project_name} - + Project Pitch +
@@ -264,7 +266,7 @@ export default async function ProjectDealPage({ params }: { params: { id: number - + {/* general @@ -274,15 +276,16 @@ export default async function ProjectDealPage({ params }: { params: { id: number

general Content

-
+
*/} - update - update Description + Update + Project log and updates + -

update Content

+
diff --git a/src/lib/data/projectLogQuery.ts b/src/lib/data/projectLogQuery.ts new file mode 100644 index 0000000..6981995 --- /dev/null +++ b/src/lib/data/projectLogQuery.ts @@ -0,0 +1,19 @@ +import { SupabaseClient } from "@supabase/supabase-js"; +import { Database } from "@/types/database.types"; + +export function getProjectLogByProjectId(client: SupabaseClient, projectId: number) { + return client + .from("project_log") + .select( + ` + id, + operation_type, + record_id, + old_data, + new_data, + changed_at, + table_name + ` + ) + .eq("record_id", projectId); +} diff --git a/src/types/database.types.ts b/src/types/database.types.ts index 9169905fac1846b659658521c1e1b25d8d38b94e..146cd0524e30cacf6cc3018dfd3686e18e0dd81f 100644 GIT binary patch delta 1012 zcmbRBm-$I6%Z3eHlM`eGHt!Ku;n=)jdKBa2dwW$jC&>Qc1@h)GPCh3ou$f2qnat#b z5P``xmvtszkkgrb;GWH7hXlFF9||lcFA&uN%e_;I0gG>#q5u&Ox0(zQ6PY~0M{V*4 zRiVlIrU(J$A|@YTi`XpS&%wCa$1ja(vOv8HcN#-JLkG8xJyFI1F9 z^I8%@uZAv)-a zlqs657&jFdDqX-h)Y{CKRprVG3eoh*iXo2d89;;bfNXmv>B$pv^(H%{sz~PnHI)O6 zO#xey$Y8~wz~BW86rg334Mo+ZL2;6TTV67sp#WqpiW5s1Dj5nUUo=vme8H9j7^L|? z`IO0ua{7}4jjlr5IVU-vmIk|vU0x7)eC&^B}V4OFZFH&H0$NDPAfgAve!Z+&x delta 775 zcmaFT$};OO^M(yvyon4cKv=|(%23QuJlQb8ku{T{grRcsM1J$h0h}zHr3`sMei2aQ zp`Z37CYH$s2^qo#4EaFiB@BrSISla($v|Gpk*vl9)fm)J5d`^%WkkP4= z8xw>lf8bVQEoLZX$eeuemigojTO8PvfoegP9ptmbZkdB~o*)*ha;#CU%CSbW>R^^N z)S8XFmXn;FpdP}s!?4VFvVwjVcHaS zT`qadlON1eSuEhiIr&wX+-8x`4%W$j0Roea671Na>1uL+p6uic3pFM$;MSVFK%xjP z?%(S=`GAfdP|O2lGlsZ;z#*WaJi__F1QZYS8FDHG1t+QqC^(TsCM(uAgPb-wZ@bFo h15r_|lh0MKZN8IM<+^#*rYi=M=e_3Jyyc038~~xc_NM><