mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 21:44:06 +01:00
feat: add update log tab
This commit is contained in:
parent
121c609077
commit
c7766f78fc
72
src/app/(investment)/deals/[id]/UpdateTab.tsx
Normal file
72
src/app/(investment)/deals/[id]/UpdateTab.tsx
Normal file
@ -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 (
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="text-center text-gray-500">
|
||||
<CardTitle className="mb-3 mt-4">No updates available</CardTitle>
|
||||
<CardDescription>There are no updates to display at this time. Please check back later.</CardDescription>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const parsedLogs = parseProjectLog(data as unknown as LogEntry[]);
|
||||
|
||||
if (parsedLogs.length === 0) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="text-center text-gray-500">
|
||||
<CardTitle className="mb-3 mt-4">No updates available</CardTitle>
|
||||
<CardDescription>There are no updates to display at this time. Please check back later.</CardDescription>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{parsedLogs.map((log, index) => (
|
||||
<div key={index} className="grid grid-cols-3 gap-4 overflow-y-auto">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{log.table}</CardTitle>
|
||||
<CardDescription>{log.changes.length} Changes</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid grid-cols-2 overflow-y-auto h-1/2 mx-4">
|
||||
{log.changes.map((change, changeIndex) => (
|
||||
<div key={changeIndex} className="mb-3">
|
||||
<div className="text-sm font-semibold">{change.field}</div>
|
||||
<div className="text-gray-500">
|
||||
<span className="text-red-500">From:</span> {JSON.stringify(change.from)}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
<span className="text-green-500">To:</span> {JSON.stringify(change.to)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="text-xs text-gray-500">Updated at: {new Date(log.changed_at).toLocaleString()}</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
57
src/app/(investment)/deals/[id]/logParser.ts
Normal file
57
src/app/(investment)/deals/[id]/logParser.ts
Normal file
@ -0,0 +1,57 @@
|
||||
export interface LogEntry {
|
||||
id: number;
|
||||
operation_type: string;
|
||||
record_id: number;
|
||||
old_data: Record<string, any>;
|
||||
new_data: Record<string, any>;
|
||||
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 };
|
||||
@ -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
|
||||
</div>
|
||||
{/* menu */}
|
||||
<div id="deck">
|
||||
<div className="flex w-fit">
|
||||
<Tabs.Root defaultValue="pitch">
|
||||
<div className="flex w-full">
|
||||
<Tabs.Root defaultValue="pitch" className="w-full">
|
||||
<Tabs.List className="list-none flex gap-10 text-lg md:text-xl">
|
||||
<Tabs.Trigger value="pitch">Pitch</Tabs.Trigger>
|
||||
<Tabs.Trigger value="general">General Data</Tabs.Trigger>
|
||||
{/* <Tabs.Trigger value="general">General Data</Tabs.Trigger> */}
|
||||
<Tabs.Trigger value="update">Updates</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Separator className="mb-4 mt-2 w-full border-1" />
|
||||
@ -253,7 +254,8 @@ export default async function ProjectDealPage({ params }: { params: { id: number
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{projectData.project_name}</CardTitle>
|
||||
<CardDescription />
|
||||
<CardDescription>Project Pitch</CardDescription>
|
||||
<Separator className="my-4" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="prose dark:prose-invert prose-sm max-w-none ">
|
||||
@ -264,7 +266,7 @@ export default async function ProjectDealPage({ params }: { params: { id: number
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="general">
|
||||
{/* <Tabs.Content value="general">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>general</CardTitle>
|
||||
@ -274,15 +276,16 @@ export default async function ProjectDealPage({ params }: { params: { id: number
|
||||
<p>general Content</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Tabs.Content>
|
||||
</Tabs.Content> */}
|
||||
<Tabs.Content value="update">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>update</CardTitle>
|
||||
<CardDescription>update Description</CardDescription>
|
||||
<CardTitle>Update</CardTitle>
|
||||
<CardDescription>Project log and updates</CardDescription>
|
||||
<Separator className="my-4" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>update Content</p>
|
||||
<UpdateTab projectId={params.id} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Tabs.Content>
|
||||
|
||||
19
src/lib/data/projectLogQuery.ts
Normal file
19
src/lib/data/projectLogQuery.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { Database } from "@/types/database.types";
|
||||
|
||||
export function getProjectLogByProjectId(client: SupabaseClient<Database>, 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);
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user