Skip to content

Commit

Permalink
feat/ activity table and details
Browse files Browse the repository at this point in the history
  • Loading branch information
psiddharthdesign committed Aug 12, 2024
1 parent dd85b68 commit e21aade
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -1,64 +1,75 @@
'use client';
'use client'

import { getRunsByProjectId } from "@/data/user/runs";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { getAllRunsByOrganizationId } from "@/data/user/runs";
import { supabaseUserClientComponentClient } from "@/supabase-clients/user/supabaseUserClientComponentClient";
import { useQuery } from "@tanstack/react-query";
import { motion } from "framer-motion";
import { useEffect } from "react";
import { AllActivityTable } from "./AllActivityTable";

export default function AllActivityDetails({
projectId,
projectSlug
organizationId,
allowedProjectIdsForUser
}: {
projectId: string;
projectSlug: string;
organizationId: string;
allowedProjectIdsForUser: string[];
}) {

const { data: runs, refetch, isLoading } = useQuery(
['runs', projectId],
['runs', organizationId],
async () => {
console.log('runs fetching');
return getRunsByProjectId(projectId);
return getAllRunsByOrganizationId(organizationId);
},
{
refetchOnWindowFocus: false,
}
);

useEffect(() => {
const channel = supabaseUserClientComponentClient
.channel('digger_runs_realtime')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'digger_runs',
filter: `project_id=eq.${projectId}`
},
(payload) => {
refetch();
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'digger_runs',
filter: `project_id=eq.${projectId}`
},
(payload) => {
refetch();
},
)
.subscribe();
const channels: ReturnType<typeof supabaseUserClientComponentClient.channel>[] = [];

if (runs) {
const projectIds = Array.from(new Set(runs.map(run => run.project_id)));

projectIds.forEach(projectId => {
const channel = supabaseUserClientComponentClient
.channel(`digger_runs_realtime_${projectId}`)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'digger_runs',
filter: `project_id=eq.${projectId}`
},
(payload) => {
refetch();
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'digger_runs',
filter: `project_id=eq.${projectId}`
},
(payload) => {
refetch();
},
)
.subscribe();

channels.push(channel);
});
}

return () => {
channel.unsubscribe();
channels.forEach(channel => channel.unsubscribe());
};
}, [projectId, refetch]);
}, [runs, refetch]);

/*
if (isLoading) {
return (
<div className="w-full">
Expand All @@ -76,18 +87,20 @@ export default function AllActivityDetails({
);
}

if (!runs) {
if (!runs || runs.length === 0) {
return <Card>
<CardHeader>
<CardTitle>Project Runs</CardTitle>
<CardDescription>View all runs for this project</CardDescription>
<CardTitle>Activity</CardTitle>
<CardDescription>View all activity in this organization</CardDescription>
</CardHeader>
<CardContent>
<p>No runs found</p>
<p>No activity found</p>
</CardContent>
</Card>
}

const filteredRuns = runs.filter(run => allowedProjectIdsForUser.includes(run.project_id));

return (
<motion.div
initial={{ opacity: 0, y: 10 }}
Expand All @@ -102,10 +115,8 @@ export default function AllActivityDetails({
transition={{ duration: 0.15, delay: 0.1 }}
>
<CardHeader>
<CardTitle>Project Runs</CardTitle>
<CardDescription>View all runs for this project</CardDescription>
<CardTitle>Organization Activity</CardTitle>
<CardDescription>View all activity for this organization</CardDescription>
</CardHeader>
</motion.div>
<CardContent>
Expand All @@ -114,14 +125,16 @@ export default function AllActivityDetails({
animate={{ opacity: 1 }}
transition={{ duration: 0.15, delay: 0.2 }}
>
<AllActivityTable runs={runs} projectSlug={projectSlug} />
<AllActivityTable runs={runs.map(run => ({
...run,
project_name: run.project_name || run.projects?.name || 'Unknown',
project_slug: run.projects?.slug || 'Unknown',
}))}
allowedRunsForUser={filteredRuns.map(run => run.id)}
/>
</motion.div>
</CardContent>
</Card>
</motion.div>
);
*/
return (
<div>TEST</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tables } from "@/lib/database.types";
import { ToSnakeCase } from "@/lib/utils";
import { AnimatePresence, motion } from "framer-motion";
import { Activity } from "lucide-react";
import moment from "moment";
import Link from "next/link";
import { statusColors } from "../../../../project/[projectSlug]/(specific-project-pages)/AllRunsTable";

export type StatusColor = {
[key: string]: string;
};

export const statusColors: StatusColor = {
queued: 'bg-yellow-200/50 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-200',
pending_plan: 'bg-amber-200/50 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200',
running_plan: 'bg-purple-200/50 text-purple-800 dark:bg-purple-900/50 dark:text-purple-200',
pending_approval: 'bg-blue-200/50 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200',
approved: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
pending_apply: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
running_apply: 'bg-indigo-200/50 text-indigo-800 dark:bg-indigo-900/50 dark:text-indigo-200',
succeeded: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
failed: 'bg-red-200/50 text-red-800 dark:bg-red-900/50 dark:text-red-200',
discarded: 'bg-neutral-200 text-neutral-800 dark:bg-neutral-950 dark:text-neutral-200',
};


export const AllActivityTable = ({ runs, projectSlug }: { runs: Tables<'digger_runs'>[], projectSlug: string }) => {
export const AllActivityTable = ({ runs, allowedRunsForUser }: {
runs: {
id: string;
commit_id: string;
status: string;
updated_at: string;
project_id: string;
project_slug: string;
project_name: string;
repo_id: number;
approver_user_name: string | null;
}[]
allowedRunsForUser: string[]
}) => {
const sortedRuns = [...runs].sort((a, b) => {
// Sort primarily by created_at in descending order (most recent first)
return moment(b.created_at).valueOf() - moment(a.created_at).valueOf();
return moment(b.updated_at).valueOf() - moment(a.updated_at).valueOf();
});

return (
Expand All @@ -38,6 +32,7 @@ export const AllActivityTable = ({ runs, projectSlug }: { runs: Tables<'digger_r
<TableHead className="text-left">Commit ID</TableHead>
<TableHead className="text-left">Status</TableHead>
<TableHead className="text-left">Last updated</TableHead>
<TableHead className="text-left">Project ID</TableHead>
<TableHead className="text-left">User</TableHead>
</TableRow>
</TableHeader>
Expand All @@ -53,20 +48,29 @@ export const AllActivityTable = ({ runs, projectSlug }: { runs: Tables<'digger_r
transition={{ duration: 0.3 }}
>
<TableCell>
<Link href={`/project/${projectSlug}/runs/${run.id}`}>
<span className="cursor-pointer hover:underline">
{allowedRunsForUser.includes(run.id) ? (
<Link href={`/project/${run.project_slug}/runs/${run.id}`} className="hover:underline cursor-pointer">
<span>
{run.id.length > 8 ? `${run.id.substring(0, 8)}...` : run.id}
</span>
</Link>
) : (
<span>
{run.id.length > 8 ? `${run.id.substring(0, 8)}...` : run.id}
</span>
</Link>
)}
</TableCell>
<TableCell>{run.commit_id}</TableCell>
<TableCell>
<span className={`px-2 py-1 rounded-full text-xs font-medium ${statusColors[ToSnakeCase(run.status)] || ''}`}>
{run.status.toUpperCase()}
</span>
<Link href={`/project/${run.project_slug}/runs/${run.id}`} className="hover:underline cursor-pointer">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${statusColors[ToSnakeCase(run.status)] || ''}`}>
{run.status.toUpperCase()}
</span>
</Link>
</TableCell>
<TableCell>{moment(run.updated_at).fromNow()}</TableCell>
<TableCell>{run.approval_author}</TableCell>
<TableCell>{run.project_name}</TableCell>
<TableCell>{run.approver_user_name}</TableCell>
</motion.tr>
))
) : (
Expand All @@ -86,7 +90,7 @@ export const AllActivityTable = ({ runs, projectSlug }: { runs: Tables<'digger_r
<Activity className="h-8 w-8 text-gray-400" />
</motion.div>
<p className="mt-2 text-sm text-foreground">
Runs will appear here once they are initiated. Note you need to setup your repo with digger_workflow.yml to be able to trigger runs, for more information refer to the which includes example workflow file <Link href="https://docs.digger.dev/team/getting-started/gha-aws" className="text-blue-500 underline" >Docs quickstart</Link>
No activity found for this organization. Runs will appear here once they are initiated.
</p>
</motion.div>
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,47 @@
import { PageHeading } from "@/components/PageHeading";
import { T } from "@/components/ui/Typography";
import { getLoggedInUserOrganizationRole } from "@/data/user/organizations";
import { getProjectsIdsListForUser } from "@/data/user/projects";
import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser";
import {
organizationParamSchema,
projectsfilterSchema
organizationParamSchema
} from "@/utils/zod-schemas/params";
import type { Metadata } from "next";
import { Suspense } from "react";
import type { DashboardProps } from "../page";
import AllActivityDetails from "./AllActivityDetails";

export const metadata: Metadata = {
title: "Teams",
description: "You can create teams within your organization.",
title: "Activity",
description: "Activity in all projects of this organization",
};

export default async function Page({
params,
searchParams,
}: DashboardProps) {
const { organizationId } = organizationParamSchema.parse(params);
const filters = projectsfilterSchema.parse(searchParams);
const [{ id }, userRole, allowedProjectIdsForUser] = await Promise.all([
serverGetLoggedInUser(),
getLoggedInUserOrganizationRole(organizationId),
getProjectsIdsListForUser({ userId: (await serverGetLoggedInUser()).id, userRole: await getLoggedInUserOrganizationRole(organizationId), organizationId })
]);

return (
<div className="flex flex-col space-y-4 max-w-5xl mt-8">
<PageHeading
title="Activity"
subTitle="Organisational activity for all projects"
subTitle="Activity for all projects in this organization"
/>

<Suspense
fallback={
<T.P className="text-muted-foreground my-6">
Loading teams...
Loading activity...
</T.P>
}
>
<AllActivityDetails projectId="8c1e07ac-abaa-4258-b7ef-4c0ab1353b9c" projectSlug="testproj01" />
<AllActivityDetails organizationId={organizationId} allowedProjectIdsForUser={allowedProjectIdsForUser} />
</Suspense>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { Metadata } from "next";
import Link from "next/link";
import { Suspense } from "react";
import type { DashboardProps } from "../page";
import { AllProjectsTableWithPagination } from "./ProjectsWithPagination";
import { UserProjectsWithPagination } from "./ProjectsWithPagination";

export const metadata: Metadata = {
title: "Projects",
Expand Down Expand Up @@ -57,7 +57,7 @@ export default async function Page({
</T.P>
}
>
<AllProjectsTableWithPagination
<UserProjectsWithPagination
organizationId={organizationId}
searchParams={searchParams}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const statusColors: StatusColor = {
running_apply: 'bg-indigo-200/50 text-indigo-800 dark:bg-indigo-900/50 dark:text-indigo-200',
succeeded: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
failed: 'bg-red-200/50 text-red-800 dark:bg-red-900/50 dark:text-red-200',
discarded: 'bg-neutral-200 text-neutral-800 dark:bg-neutral-950 dark:text-neutral-200',
discarded: 'bg-neutral-200 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-200',
};


Expand Down
Loading

0 comments on commit e21aade

Please sign in to comment.