Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Slug validation on update deployment #15

Merged
merged 42 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c1a51fc
wip: merge origin main
zacharyblasczyk Sep 5, 2024
69586cc
fix conflict
zacharyblasczyk Sep 5, 2024
cbb310c
Init Slug Validation
zacharyblasczyk Sep 5, 2024
5c4fed7
Merge remote-tracking branch 'origin' into zacharyb/slug-validation
zacharyblasczyk Sep 5, 2024
25d08e7
fix
zacharyblasczyk Sep 5, 2024
dd622de
tried to invalidate the system id that generates the deploymet by pas…
zacharyblasczyk Sep 5, 2024
17ae503
fix
zacharyblasczyk Sep 5, 2024
51aebbf
trying to fix
zacharyblasczyk Sep 5, 2024
225f662
revert
zacharyblasczyk Sep 9, 2024
2571c9a
Merge remote-tracking branch 'origin' into zacharyb/slug-validation
zacharyblasczyk Sep 9, 2024
c37efa0
fix workspace creation, default admin role
zacharyblasczyk Sep 9, 2024
6532707
validation
zacharyblasczyk Sep 10, 2024
4066620
cleanup
zacharyblasczyk Sep 10, 2024
4affbeb
cleanup
zacharyblasczyk Sep 10, 2024
c9ac07b
format
zacharyblasczyk Sep 10, 2024
ec29188
fix
zacharyblasczyk Sep 10, 2024
c19956c
fix merge conflicts
zacharyblasczyk Sep 13, 2024
3a1ab99
fixed most of the issues. handling createRelease next
zacharyblasczyk Sep 14, 2024
b352909
small fix
zacharyblasczyk Sep 14, 2024
3b3e14b
fix
zacharyblasczyk Sep 14, 2024
3c82aff
revert
zacharyblasczyk Sep 14, 2024
5a58d9f
revert
zacharyblasczyk Sep 14, 2024
8e3cb89
fix
zacharyblasczyk Sep 14, 2024
dbaabbb
revert
zacharyblasczyk Sep 14, 2024
95528ca
fix
zacharyblasczyk Sep 14, 2024
ccf5e78
revert
zacharyblasczyk Sep 14, 2024
bb8776d
revert
zacharyblasczyk Sep 14, 2024
f1e92a0
fix
zacharyblasczyk Sep 14, 2024
a2d406a
revert
zacharyblasczyk Sep 14, 2024
b63cc01
missed file
zacharyblasczyk Sep 14, 2024
ccf65e8
fix
zacharyblasczyk Sep 14, 2024
b5837fd
fix deployment
zacharyblasczyk Sep 14, 2024
1859a6c
fix
zacharyblasczyk Sep 14, 2024
96dc37c
merge
zacharyblasczyk Sep 17, 2024
227bc42
update
zacharyblasczyk Sep 17, 2024
bf4bdd2
Merge remote-tracking branch 'origin' into zacharyb/slug-validation
zacharyblasczyk Sep 17, 2024
732fecf
revert
zacharyblasczyk Sep 17, 2024
b3aeb43
fix
zacharyblasczyk Sep 17, 2024
7056e2c
onSubmit
zacharyblasczyk Sep 17, 2024
3f6216b
fix props
zacharyblasczyk Sep 17, 2024
2bca56c
merge conflict
zacharyblasczyk Sep 18, 2024
c3ee5b7
fix merge
zacharyblasczyk Sep 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const DeploymentOptionsDropdown: React.FC<{
name: string;
slug: string;
description: string;
}> = (props) => {
systemId: string;
}> = ({ systemId, ...props }) => {
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);

return (
Expand All @@ -37,7 +38,7 @@ export const DeploymentOptionsDropdown: React.FC<{
forceMount
>
<DropdownMenuGroup>
<EditDeploymentDialog {...props}>
<EditDeploymentDialog {...props} systemId={systemId}>
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
<TbEdit className="mr-2" />
Edit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import React, { useState } from "react";
import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import isEqual from "lodash/isEqual";
import { useForm } from "react-hook-form";
Expand All @@ -24,41 +23,81 @@ import {
FormField,
FormItem,
FormLabel,
FormMessage,
RootFormMessage,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Textarea } from "@ctrlplane/ui/textarea";

import { api } from "~/trpc/react";

const deploymentForm = z.object({
id: z.string().uuid(),
name: z.string().min(3).max(255),
slug: z.string().min(3).max(255),
description: z.string().optional(),
const deploymentFormSchema = z.object({
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
systemId: z.string().uuid({ message: "Invalid system ID format." }),
id: z.string().uuid({ message: "Invalid ID format." }),
name: z
.string()
.min(3, { message: "Name must be at least 3 characters long." })
.max(255, { message: "Name must be at most 255 characters long." }),
slug: z
.string()
.min(3, { message: "Slug must be at least 3 characters long." })
.max(255, { message: "Slug must be at most 255 characters long." }),
description: z
.string()
.max(255, { message: "Description must be at most 255 characters long." })
.optional()
.refine((val) => !val || val.length >= 3, {
message: "Description must be at least 3 characters long if provided.",
}),
});

type DeploymentFormValues = z.infer<typeof deploymentForm>;
type DeploymentFormValues = z.infer<typeof deploymentFormSchema>;

export const EditDeploymentDialog: React.FC<
DeploymentFormValues & { children?: React.ReactNode }
> = ({ id, name, slug, description, children }) => {
const router = useRouter();
const update = api.deployment.update.useMutation();
> = ({ systemId, id, name, slug, description, children }) => {
const [open, setOpen] = useState(false);
const utils = api.useUtils();

const form = useForm({
resolver: zodResolver(deploymentForm),
defaultValues: { id, name, slug, description },
resolver: zodResolver(deploymentFormSchema),
defaultValues: { systemId, id, name, slug, description },
mode: "onChange",
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
});

const onSubmit = form.handleSubmit(async (data) => {
setOpen(false);
const isDataChanged = !isEqual(data, { name, slug, description });
if (!isDataChanged) return;
const update = api.deployment.update.useMutation({
onError: (error) => {
if (error.message.includes("violates unique constraint")) {
form.setError("root", {
type: "manual",
message:
"A deployment with this slug already exists. Please choose a different slug.",
});
return;
}
form.setError("root", {
type: "manual",
message: "An unexpected error occurred. Please try again.",
});
},
onSuccess: () => {
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
utils.deployment.invalidate();
utils.deployment.bySystemId.invalidate(systemId);
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
setOpen(false);
},
});

await update.mutateAsync({ id, data });
const onSubmit = form.handleSubmit((data) => {
const isDataChanged = !isEqual(data, { name, slug, description });
if (!isDataChanged) {
setOpen(false);
return;
}

router.refresh();
update.mutate({
id,
data: { name: data.name, slug: data.slug, description: data.description },
});
});

return (
Expand All @@ -85,6 +124,7 @@ export const EditDeploymentDialog: React.FC<
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
Expand All @@ -97,6 +137,7 @@ export const EditDeploymentDialog: React.FC<
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
Expand All @@ -109,6 +150,7 @@ export const EditDeploymentDialog: React.FC<
<FormControl>
<Textarea {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
Expand All @@ -126,6 +168,7 @@ export const EditDeploymentDialog: React.FC<
</FormItem>
)}
/>
<RootFormMessage />
<DialogFooter>
<CopyButton textToCopy={id} />
<div className="flex-grow" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ const DeploymentTable: React.FC<{
latestRelease: { id: string; version: string; createdAt: Date } | null;
}
>;
}> = ({ systemSlug, deployments, environments }) => {
systemId: string;
}> = ({ systemSlug, deployments, environments, systemId }) => {
const { workspaceSlug } = useParams<{ workspaceSlug: string }>();
return (
<div className="w-full overflow-x-auto">
Expand Down Expand Up @@ -162,7 +163,7 @@ const DeploymentTable: React.FC<{
<Button size="icon" variant="ghost">
<TbRocket />
</Button>
<DeploymentOptionsDropdown {...r} />
<DeploymentOptionsDropdown {...r} systemId={systemId} />
</div>
</td>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default async function SystemDeploymentsPage({
deployments={deployments}
environments={environments}
systemSlug={params.systemSlug}
systemId={system.id}
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
/>
</div>
);
Expand Down
14 changes: 13 additions & 1 deletion packages/ui/src/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ const FormMessage = React.forwardRef<
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
Expand All @@ -187,6 +187,17 @@ const FormMessage = React.forwardRef<
});
FormMessage.displayName = "FormMessage";

const RootFormMessage = () => {
const { formState } = useFormContext();
const error = formState.errors.root;

if (!error) return null;

return (
<p className="text-sm font-medium text-destructive">{error.message}</p>
);
};

export {
useForm,
useFormField,
Expand All @@ -197,6 +208,7 @@ export {
FormDescription,
FormMessage,
FormField,
RootFormMessage,
};

export { useFieldArray } from "react-hook-form";
Loading
Loading