Skip to content

Commit

Permalink
WIP duplicate runs views
Browse files Browse the repository at this point in the history
  • Loading branch information
ZIJ committed Aug 12, 2024
1 parent 66bc7c5 commit dd85b68
Show file tree
Hide file tree
Showing 8 changed files with 453 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { FreeTrialComponent } from '@/components/SubscriptionCards';
import { Skeleton } from '@/components/ui/skeleton';
import { getIsStripeTestMode } from '@/utils/server/stripe-utils';
import { differenceInDays } from 'date-fns';
import { FileText, Home, Layers, MessageCircle, Users } from 'lucide-react';
import { Activity, FileText, Home, Layers, MessageCircle, Users } from 'lucide-react';


const isStripeTestMode = getIsStripeTestMode()
Expand Down Expand Up @@ -91,6 +91,11 @@ async function OrganizationSidebarInternal({
href={`/org/${organizationId}/teams`}
icon={<Users className="size-4 text-foreground" />}
/>
<SidebarLink
label="Activity"
href={`/org/${organizationId}/activity`}
icon={<Activity className="size-4 text-foreground" />}
/>
<SidebarLink
label="Docs"
href={`https://docs.digger.dev/team/getting-started/gha-aws`}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
'use client';

import { getRunsByProjectId } from "@/data/user/runs";
import { supabaseUserClientComponentClient } from "@/supabase-clients/user/supabaseUserClientComponentClient";
import { useQuery } from "@tanstack/react-query";
import { useEffect } from "react";

export default function AllActivityDetails({
projectId,
projectSlug
}: {
projectId: string;
projectSlug: string;
}) {

const { data: runs, refetch, isLoading } = useQuery(
['runs', projectId],
async () => {
console.log('runs fetching');
return getRunsByProjectId(projectId);
},
{
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();

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

/*
if (isLoading) {
return (
<div className="w-full">
<div className="space-y-4">
{[...Array(3)].map((_, index) => (
<div key={index} className="flex space-x-4">
<Skeleton className="h-12 w-1/4" />
<Skeleton className="h-12 w-1/4" />
<Skeleton className="h-12 w-1/4" />
<Skeleton className="h-12 w-1/4" />
</div>
))}
</div>
</div>
);
}
if (!runs) {
return <Card>
<CardHeader>
<CardTitle>Project Runs</CardTitle>
<CardDescription>View all runs for this project</CardDescription>
</CardHeader>
<CardContent>
<p>No runs found</p>
</CardContent>
</Card>
}
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.1 }}
>
<Card className="w-full">
<motion.div
initial={{ opacity: 0, y: -5 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.15, delay: 0.1 }}
>
<CardHeader>
<CardTitle>Project Runs</CardTitle>
<CardDescription>View all runs for this project</CardDescription>
</CardHeader>
</motion.div>
<CardContent>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.15, delay: 0.2 }}
>
<AllActivityTable runs={runs} projectSlug={projectSlug} />
</motion.div>
</CardContent>
</Card>
</motion.div>
);
*/
return (
<div>TEST</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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";

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 }) => {
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 (
<Table>
<TableHeader>
<TableRow>
<TableHead className="text-left">Run ID</TableHead>
<TableHead className="text-left">Commit ID</TableHead>
<TableHead className="text-left">Status</TableHead>
<TableHead className="text-left">Last updated</TableHead>
<TableHead className="text-left">User</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<AnimatePresence>
{sortedRuns.length > 0 ? (
sortedRuns.map((run) => (
<motion.tr
key={run.id}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.3 }}
>
<TableCell>
<Link href={`/project/${projectSlug}/runs/${run.id}`}>
<span className="cursor-pointer hover:underline">
{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>
</TableCell>
<TableCell>{moment(run.updated_at).fromNow()}</TableCell>
<TableCell>{run.approval_author}</TableCell>
</motion.tr>
))
) : (
<TableRow>
<TableCell colSpan={5} className="w-full justify-center">
<motion.div
className="flex flex-col items-center justify-center mx-auto max-w-96 h-64 text-center"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<motion.div
className="rounded-full bg-gray-100 p-4 dark:bg-gray-800"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<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>
</p>
</motion.div>
</TableCell>
</TableRow>
)}
</AnimatePresence>
</TableBody>
</Table>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"use client";
import { T } from '@/components/ui/Typography';
import { Card } from '@/components/ui/card';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
type ColumnDef,
type SortingState,
} from '@tanstack/react-table';
import moment from 'moment';
import Link from 'next/link';
import { useState } from 'react';

type TeamWithAdminName = {
adminName: string | null | undefined;
created_at: string | null;
id: number;
name: string;
organization_id: string;
};

type Props = {
teams: TeamWithAdminName[];
};

export function OrganizationTeamsTable({ teams }: Props) {
const columns: ColumnDef<TeamWithAdminName>[] = [
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => (
<Link href={`/org/${row.original.organization_id}/team/${row.original.id}`} className='hover:underline'>
{row.getValue('name')}
</Link>
),
},
{
accessorKey: 'created_at',
header: 'Created At',
cell: ({ row }) => {
const date = moment(row.getValue('created_at') as string);
return date.format('MMM DD YYYY, HH:mm [hrs]');
},
},
{
accessorKey: 'adminName',
header: 'Created by',
cell: ({ row }) => row.getValue('adminName') || 'unknown',
},
];

const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data: teams,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onSortingChange: setSorting,
state: {
sorting,
},
});

if (teams.length === 0) {
return (
<Card>
<div className='flex justify-center w-full items-center h-[180px]'>
<T.Subtle>No teams found</T.Subtle>
</div>
</Card>
);
}

return (
<div className="w-full">
<Card className="p-4">
<Table>
<TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => (
<TableHead key={header.id} className="text-left font-normal text-sm text-muted-foreground">
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map(row => (
<TableRow key={row.id} className="hover:bg-muted/50">
{row.getVisibleCells().map(cell => (
<TableCell key={cell.id} className="py-2">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</Card>
</div>
);
}
Loading

0 comments on commit dd85b68

Please sign in to comment.