diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunDetails.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunDetails.tsx
index 7f46d6dd..bb02a679 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunDetails.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunDetails.tsx
@@ -5,7 +5,7 @@ import { Tables } from "@/lib/database.types";
import { motion } from "framer-motion";
import { RunsTable } from "./RunsTable";
-export default function RunDetails({ runs, project }: { runs: Tables<'digger_runs'>[], project: Tables<'projects'> }) {
+export default function RunsDetails({ runs, project }: { runs: Tables<'digger_runs'>[], project: Tables<'projects'> }) {
return (
-
+
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunsTable.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunsTable.tsx
index ed557536..dda58c42 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunsTable.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunsTable.tsx
@@ -1,14 +1,15 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tables } from "@/lib/database.types";
-import { motion } from "framer-motion";
+import { AnimatePresence, motion } from "framer-motion";
import { Activity } from "lucide-react";
import moment from "moment";
+import Link from "next/link";
-type StatusColor = {
+export type StatusColor = {
[key: string]: string;
};
-const statusColors: StatusColor = {
+export const statusColors: StatusColor = {
queued: 'bg-yellow-200/50 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-200',
'pending_approval': 'bg-blue-200/50 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200',
running: 'bg-purple-200/50 text-purple-800 dark:bg-purple-900/50 dark:text-purple-200',
@@ -17,56 +18,81 @@ const statusColors: StatusColor = {
failed: 'bg-red-200/50 text-red-800 dark:bg-red-900/50 dark:text-red-200',
};
-export const RunsTable = ({ runs }: { runs: Tables<'digger_runs'>[] }) => (
-
-
-
- Run ID
- Commit ID
- Status
- Date
- User
-
-
-
- {runs.length > 0 ? (
- runs.map((run) => (
-
- {run.id.length > 8 ? `${run.id.substring(0, 8)}...` : run.id}
- {run.commit_id}
-
-
- {run.status.toUpperCase()}
-
-
- {moment(run.created_at).fromNow()}
- {run.approval_author}
-
- ))
- ) : (
+const statusOrder = ['running', 'queued', 'pending_approval', 'approved', 'succeeded', 'failed'];
+
+export const RunsTable = ({ runs, projectSlug }: { runs: Tables<'digger_runs'>[], projectSlug: string }) => {
+ const sortedRuns = [...runs].sort((a, b) => {
+ const statusA = statusOrder.indexOf(a.status.toLowerCase());
+ const statusB = statusOrder.indexOf(b.status.toLowerCase());
+ if (statusA !== statusB) return statusA - statusB;
+ return moment(b.created_at).valueOf() - moment(a.created_at).valueOf();
+ });
+
+ return (
+
+
-
-
-
-
-
- No runs available
-
- Runs will appear here once they are initiated.
-
-
-
+ Run ID
+ Commit ID
+ Status
+ Date
+ User
- )}
-
-
-);
\ No newline at end of file
+
+
+
+ {sortedRuns.length > 0 ? (
+ sortedRuns.map((run) => (
+
+
+
+
+ {run.id.length > 8 ? `${run.id.substring(0, 8)}...` : run.id}
+
+
+
+ {run.commit_id}
+
+
+ {run.status.toUpperCase()}
+
+
+ {moment(run.created_at).fromNow()}
+ {run.approval_author}
+
+ ))
+ ) : (
+
+
+
+
+
+
+ No runs available
+
+ Runs will appear here once they are initiated.
+
+
+
+
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
index 0bab2001..48c622b9 100644
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/layout.tsx
@@ -1,10 +1,8 @@
-import { ApplicationLayoutShell } from '@/components/ApplicationLayoutShell';
-import { InternalNavbar } from '@/components/NavigationMenu/InternalNavbar';
-import { PageHeading } from '@/components/PageHeading';
-import { TabsNavigationV2 } from '@/components/TabsNavigation/TabsNavigation';
-import { getProjectTitleById, getSlimProjectBySlug } from '@/data/user/projects';
-import { projectSlugParamSchema } from '@/utils/zod-schemas/params';
-import { Suspense, type ReactNode } from 'react';
+import { PageHeading } from "@/components/PageHeading";
+import { TabsNavigationV2 } from "@/components/TabsNavigation/TabsNavigation";
+import { getProjectTitleById, getSlimProjectBySlug } from "@/data/user/projects";
+import { projectSlugParamSchema } from "@/utils/zod-schemas/params";
+import { Suspense } from "react";
async function ProjectPageHeading({ projectId }: { projectId: string }) {
@@ -16,18 +14,7 @@ async function ProjectPageHeading({ projectId }: { projectId: string }) {
/>
);
}
-
-export default async function ProjectLayout({
- params,
- children,
- navbar,
- sidebar,
-}: {
- children: ReactNode;
- params: unknown;
- navbar: ReactNode;
- sidebar: ReactNode;
-}) {
+export default async function ProjectPagesLayout({ params, children }: { params: unknown, children: React.ReactNode }) {
const { projectSlug } = projectSlugParamSchema.parse(params);
const project = await getSlimProjectBySlug(projectSlug);
@@ -45,28 +32,16 @@ export default async function ProjectLayout({
href: `/project/${projectSlug}/settings`,
},
];
+ return <>
+
+ {children}
+ >
- return (
-
-
-
-
- );
-}
+}
\ No newline at end of file
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/default.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/default.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/default.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/default.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/loading.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/loading.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/loading.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/loading.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/settings/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/settings/page.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@navbar/settings/page.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@navbar/settings/page.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@sidebar/ProjectSidebar.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@sidebar/ProjectSidebar.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@sidebar/ProjectSidebar.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@sidebar/ProjectSidebar.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@sidebar/default.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@sidebar/default.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/@sidebar/default.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/@sidebar/default.tsx
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/layout.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/layout.tsx
new file mode 100644
index 00000000..db06528e
--- /dev/null
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/layout.tsx
@@ -0,0 +1,33 @@
+import { ApplicationLayoutShell } from '@/components/ApplicationLayoutShell';
+import { InternalNavbar } from '@/components/NavigationMenu/InternalNavbar';
+import { Suspense, type ReactNode } from 'react';
+
+
+export default async function ProjectLayout({
+ params,
+ children,
+ navbar,
+ sidebar,
+}: {
+ children: ReactNode;
+ params: unknown;
+ navbar: ReactNode;
+ sidebar: ReactNode;
+}) {
+
+ return (
+
+
+
+
+
+ {navbar}
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/RunDetails.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/RunDetails.tsx
new file mode 100644
index 00000000..e60583ac
--- /dev/null
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/RunDetails.tsx
@@ -0,0 +1,80 @@
+'use client';
+
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Tables } from "@/lib/database.types";
+import { AnimatePresence, motion } from 'framer-motion';
+import { ChevronDown } from 'lucide-react';
+import React, { useState } from 'react';
+import { statusColors } from "../../(specific-project-pages)/RunsTable";
+
+export const RunDetails: React.FC<{ run: Tables<'digger_runs'> }> = ({ run }) => {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ return (
+
+
+
+
+ Run {run.id}
+
+
+ {run.status.toUpperCase()}
+
+
+
+
+
+
Commit: {run.commit_id}
+
Date: {run.created_at}
+
+
+
+
+ {isExpanded && run.status && (
+
+ Plan Summary
+
+
+ {run.status}
+ Created
+
+
+ {run.status}
+ Updated
+
+
+ {run.status}
+ Deleted
+
+
+
+ )}
+
+
+
+
+ );
+};
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx
new file mode 100644
index 00000000..bd604628
--- /dev/null
+++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx
@@ -0,0 +1,53 @@
+
+import { PageHeading } from "@/components/PageHeading";
+import { T } from "@/components/ui/Typography";
+import { getRunById } from "@/data/user/runs";
+import {
+ runIdParamSchema
+} from "@/utils/zod-schemas/params";
+import type { Metadata } from "next";
+import { Suspense } from "react";
+import { RunDetails } from "./RunDetails";
+
+export const metadata: Metadata = {
+ title: "Projects",
+ description: "You can create projects within teams, or within your organization.",
+};
+
+type RunDetailPageProps = {
+ params: {
+ runId: string;
+ };
+};
+
+
+export default async function RunDetailPage({
+ params,
+
+}: RunDetailPageProps) {
+ const { runId } = runIdParamSchema.parse(params);
+ const run = await getRunById(runId);
+
+ return (
+
+
+
+
+
+ {
+
+ Loading run details...
+
+ }
+ >
+
+
+ }
+
+ );
+}
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/runs/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/page.tsx
similarity index 100%
rename from src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/runs/page.tsx
rename to src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/page.tsx
diff --git a/src/utils/zod-schemas/params.ts b/src/utils/zod-schemas/params.ts
index cff995bf..d8956607 100644
--- a/src/utils/zod-schemas/params.ts
+++ b/src/utils/zod-schemas/params.ts
@@ -20,3 +20,7 @@ export const projectParamSchema = z.object({
export const projectSlugParamSchema = z.object({
projectSlug: z.string(),
});
+
+export const runIdParamSchema = z.object({
+ runId: z.string().uuid(),
+});
diff --git a/supabase/migrations/20240726134603_run_approval_flow.sql b/supabase/migrations/20240726134603_run_approval_flow.sql
new file mode 100644
index 00000000..4ae5166d
--- /dev/null
+++ b/supabase/migrations/20240726134603_run_approval_flow.sql
@@ -0,0 +1,16 @@
+-- Create a new enum type for approval_status
+CREATE TYPE digger_run_approval_status AS ENUM (
+ 'pending_approval',
+ 'approved',
+ 'running',
+ 'queued',
+ 'succeeded',
+ 'failed'
+);
+
+-- Add the approval_status column to the digger_runs table
+ALTER TABLE "public"."digger_runs"
+ADD COLUMN "approval_status" digger_run_approval_status NOT NULL DEFAULT 'pending_approval';
+
+-- Create an index on the new column for better query performance
+CREATE INDEX idx_digger_runs_approval_status ON "public"."digger_runs" ("approval_status");
\ No newline at end of file