Skip to content

Commit dd04a30

Browse files
feature / add settings and connect runs to db
1 parent cf5aed7 commit dd04a30

File tree

9 files changed

+398
-111
lines changed

9 files changed

+398
-111
lines changed

src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/ProjectSettings.tsx

+156-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,68 @@
11
'use client';
22

3+
import { InputTags } from "@/components/InputTags";
4+
import { Button } from "@/components/ui/button";
35
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
6+
import { Checkbox } from "@/components/ui/checkbox";
7+
import { Input } from "@/components/ui/input";
8+
import { Label } from "@/components/ui/label";
9+
import { updateProjectSettingsAction } from "@/data/user/projects";
10+
import { useSAToastMutation } from "@/hooks/useSAToastMutation";
11+
import { Tables } from "@/lib/database.types";
412
import { motion } from "framer-motion";
13+
import { useState } from "react";
14+
import { Controller, useForm } from "react-hook-form";
15+
16+
type ProjectSettingsProps = {
17+
project: Tables<'projects'>;
18+
repositoryName: string | null;
19+
};
20+
21+
type ProjectSettingsFormData = {
22+
terraformWorkingDir: string;
23+
labels: string[];
24+
managedState: boolean;
25+
};
26+
27+
export default function ProjectSettings({ project, repositoryName }: ProjectSettingsProps) {
28+
const [isSubmitting, setIsSubmitting] = useState(false);
29+
30+
const { control, handleSubmit, formState: { isDirty } } = useForm<ProjectSettingsFormData>({
31+
defaultValues: {
32+
terraformWorkingDir: project.terraform_working_dir || '',
33+
labels: project.labels || [],
34+
managedState: project.is_managing_state || false,
35+
},
36+
});
37+
38+
const updateProjectSettingsMutation = useSAToastMutation(
39+
async (data: ProjectSettingsFormData) => {
40+
const result = await updateProjectSettingsAction({
41+
projectId: project.id,
42+
terraformWorkingDir: data.terraformWorkingDir,
43+
labels: data.labels,
44+
managedState: data.managedState,
45+
});
46+
return result;
47+
},
48+
{
49+
loadingMessage: "Updating project settings...",
50+
successMessage: "Project settings updated successfully!",
51+
errorMessage: "Failed to update project settings",
52+
}
53+
);
54+
55+
const onSubmit = async (data: ProjectSettingsFormData) => {
56+
setIsSubmitting(true);
57+
try {
58+
await updateProjectSettingsMutation.mutateAsync(data);
59+
} catch (error) {
60+
console.error("Error updating project settings:", error);
61+
} finally {
62+
setIsSubmitting(false);
63+
}
64+
};
565

6-
export default function ProjectSettings() {
766
return (
867
<motion.div
968
initial={{ opacity: 0, y: 10 }}
@@ -12,24 +71,103 @@ export default function ProjectSettings() {
1271
transition={{ duration: 0.1 }}
1372
>
1473
<Card className="w-full">
15-
<motion.div
16-
initial={{ opacity: 0, y: -5 }}
17-
animate={{ opacity: 1, y: 0 }}
18-
transition={{ duration: 0.15, delay: 0.1 }}
19-
>
20-
<CardHeader>
21-
<CardTitle>Project Settings</CardTitle>
22-
<CardDescription>Manage settings for your project</CardDescription>
23-
</CardHeader>
24-
</motion.div>
74+
<CardHeader>
75+
<CardTitle>Project Settings</CardTitle>
76+
<CardDescription>Manage settings for your project</CardDescription>
77+
</CardHeader>
2578
<CardContent>
26-
<motion.div
27-
initial={{ opacity: 0 }}
28-
animate={{ opacity: 1 }}
29-
transition={{ duration: 0.15, delay: 0.2 }}
30-
>
31-
{/* Add your project settings management component here */}
32-
</motion.div>
79+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
80+
<div className="grid grid-cols-2 gap-6">
81+
<motion.div
82+
initial={{ opacity: 0 }}
83+
animate={{ opacity: 1 }}
84+
transition={{ duration: 0.15, delay: 0.1 }}
85+
>
86+
<Label htmlFor="name">Project Name</Label>
87+
<Input id="name" value={project.name} disabled />
88+
</motion.div>
89+
90+
<motion.div
91+
initial={{ opacity: 0 }}
92+
animate={{ opacity: 1 }}
93+
transition={{ duration: 0.15, delay: 0.2 }}
94+
>
95+
<Label htmlFor="repo">Repository</Label>
96+
<Input id="repo" value={repositoryName || 'N/A'} disabled />
97+
</motion.div>
98+
</div>
99+
100+
<motion.div
101+
initial={{ opacity: 0 }}
102+
animate={{ opacity: 1 }}
103+
transition={{ duration: 0.15, delay: 0.3 }}
104+
className="flex items-center space-x-2"
105+
>
106+
<Controller
107+
name="managedState"
108+
control={control}
109+
render={({ field }) => (
110+
<Checkbox
111+
id="managedState"
112+
checked={field.value}
113+
onCheckedChange={field.onChange}
114+
disabled
115+
/>
116+
)}
117+
/>
118+
<Label htmlFor="managedState">Managed State</Label>
119+
</motion.div>
120+
121+
<motion.div
122+
initial={{ opacity: 0 }}
123+
animate={{ opacity: 1 }}
124+
transition={{ duration: 0.15, delay: 0.4 }}
125+
>
126+
<Label htmlFor="terraformWorkingDir">Terraform Working Directory</Label>
127+
<Controller
128+
name="terraformWorkingDir"
129+
control={control}
130+
render={({ field }) => (
131+
<Input id="terraformWorkingDir" {...field} />
132+
)}
133+
/>
134+
</motion.div>
135+
136+
<motion.div
137+
initial={{ opacity: 0 }}
138+
animate={{ opacity: 1 }}
139+
transition={{ duration: 0.15, delay: 0.5 }}
140+
>
141+
<Label htmlFor="labels">Labels</Label>
142+
<Controller
143+
name="labels"
144+
control={control}
145+
render={({ field }) => (
146+
<InputTags
147+
id="labels"
148+
value={field.value}
149+
onChange={field.onChange}
150+
placeholder="Add labels"
151+
className="mt-1"
152+
/>
153+
)}
154+
/>
155+
</motion.div>
156+
157+
<motion.div
158+
initial={{ opacity: 0 }}
159+
animate={{ opacity: 1 }}
160+
transition={{ duration: 0.15, delay: 0.6 }}
161+
className="flex justify-end"
162+
>
163+
<Button
164+
type="submit"
165+
disabled={!isDirty || isSubmitting || updateProjectSettingsMutation.isLoading}
166+
>
167+
{isSubmitting || updateProjectSettingsMutation.isLoading ? "Saving..." : "Save Changes"}
168+
</Button>
169+
</motion.div>
170+
</form>
33171
</CardContent>
34172
</Card>
35173
</motion.div>
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
'use client';
22

33
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
4+
import { Tables } from "@/lib/database.types";
45
import { motion } from "framer-motion";
5-
import { Run, RunsTable } from "./RunsTable";
6+
import { RunsTable } from "./RunsTable";
67

7-
export default function RunsDetails({ runs }: { runs: Run[] }) {
8+
export default function RunDetails({ runs, project }: { runs: Tables<'digger_runs'>[], project: Tables<'projects'> }) {
89
return (
910
<motion.div
1011
initial={{ opacity: 0, y: 10 }}

src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/RunsTable.tsx

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
2-
3-
export type Run = {
4-
runId: string;
5-
commitId: string;
6-
status: string;
7-
date: string;
8-
user: string;
9-
};
2+
import { Tables } from "@/lib/database.types";
3+
import { motion } from "framer-motion";
4+
import { Activity } from "lucide-react";
5+
import moment from "moment";
106

117
type StatusColor = {
128
[key: string]: string;
139
};
1410

1511
const statusColors: StatusColor = {
1612
queued: 'bg-yellow-200/50 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-200',
17-
'pending approval': 'bg-blue-200/50 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200',
13+
'pending_approval': 'bg-blue-200/50 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200',
1814
running: 'bg-purple-200/50 text-purple-800 dark:bg-purple-900/50 dark:text-purple-200',
1915
approved: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
2016
succeeded: 'bg-green-200/50 text-green-800 dark:bg-green-900/50 dark:text-green-200',
2117
failed: 'bg-red-200/50 text-red-800 dark:bg-red-900/50 dark:text-red-200',
2218
};
2319

24-
export const RunsTable = ({ runs }: { runs: Run[] }) => (
20+
export const RunsTable = ({ runs }: { runs: Tables<'digger_runs'>[] }) => (
2521
<Table>
2622
<TableHeader>
2723
<TableRow>
@@ -35,21 +31,40 @@ export const RunsTable = ({ runs }: { runs: Run[] }) => (
3531
<TableBody>
3632
{runs.length > 0 ? (
3733
runs.map((run) => (
38-
<TableRow key={run.runId}>
39-
<TableCell>{run.runId}</TableCell>
40-
<TableCell>{run.commitId}</TableCell>
34+
<TableRow key={run.id}>
35+
<TableCell >{run.id.length > 8 ? `${run.id.substring(0, 8)}...` : run.id}</TableCell>
36+
<TableCell>{run.commit_id}</TableCell>
4137
<TableCell>
4238
<span className={`px-2 py-1 rounded-full text-xs font-medium ${statusColors[run.status.toLowerCase()] || ''}`}>
4339
{run.status.toUpperCase()}
4440
</span>
4541
</TableCell>
46-
<TableCell>{run.date}</TableCell>
47-
<TableCell>{run.user}</TableCell>
42+
<TableCell>{moment(run.created_at).fromNow()}</TableCell>
43+
<TableCell>{run.approval_author}</TableCell>
4844
</TableRow>
4945
))
5046
) : (
5147
<TableRow>
52-
<TableCell colSpan={5} className="text-center">No runs available</TableCell>
48+
<TableCell colSpan={5}>
49+
<motion.div
50+
className="flex flex-col items-center justify-center h-64 text-center"
51+
initial={{ opacity: 0, y: 20 }}
52+
animate={{ opacity: 1, y: 0 }}
53+
transition={{ duration: 0.5 }}
54+
>
55+
<motion.div
56+
className="rounded-full bg-gray-100 p-4 dark:bg-gray-800"
57+
whileHover={{ scale: 1.05 }}
58+
whileTap={{ scale: 0.95 }}
59+
>
60+
<Activity className="h-8 w-8 text-gray-400" />
61+
</motion.div>
62+
<h3 className="mt-4 text-lg font-semibold text-gray-900 dark:text-gray-100">No runs available</h3>
63+
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
64+
Runs will appear here once they are initiated.
65+
</p>
66+
</motion.div>
67+
</TableCell>
5368
</TableRow>
5469
)}
5570
</TableBody>

0 commit comments

Comments
 (0)