feat: better UI for submitting labels

This commit is contained in:
PiquelChips
2025-08-11 14:03:30 +02:00
parent 1904a3d1e9
commit 429c1e4cd8
7 changed files with 6526 additions and 25 deletions
@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -27,9 +28,15 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Settings2 } from "lucide-react"; import { HelpCircle, Plus, Settings2, X } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -42,7 +49,7 @@ const schema = z
wildcardDomain: z.string(), wildcardDomain: z.string(),
port: z.number(), port: z.number(),
previewLimit: z.number(), previewLimit: z.number(),
previewLabels: z.string(), previewLabels: z.array(z.string()).optional(),
previewHttps: z.boolean(), previewHttps: z.boolean(),
previewPath: z.string(), previewPath: z.string(),
previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]),
@@ -82,7 +89,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
wildcardDomain: "*.traefik.me", wildcardDomain: "*.traefik.me",
port: 3000, port: 3000,
previewLimit: 3, previewLimit: 3,
previewLabels: "", previewLabels: [],
previewHttps: false, previewHttps: false,
previewPath: "/", previewPath: "/",
previewCertificateType: "none", previewCertificateType: "none",
@@ -104,7 +111,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
buildArgs: data.previewBuildArgs || "", buildArgs: data.previewBuildArgs || "",
wildcardDomain: data.previewWildcard || "*.traefik.me", wildcardDomain: data.previewWildcard || "*.traefik.me",
port: data.previewPort || 3000, port: data.previewPort || 3000,
previewLabels: data.previewLabels || "", previewLabels: data.previewLabels || [],
previewLimit: data.previewLimit || 3, previewLimit: data.previewLimit || 3,
previewHttps: data.previewHttps || false, previewHttps: data.previewHttps || false,
previewPath: data.previewPath || "/", previewPath: data.previewPath || "/",
@@ -204,22 +211,86 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="previewLabels" name="previewLabels"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem className="md:col-span-2">
<FormLabel>Labels</FormLabel> <div className="flex items-center gap-2">
<FormControl> <FormLabel>Preview Labels</FormLabel>
<Input <TooltipProvider>
placeholder="enhancement,needs-review (Leave empty for all labels)" <Tooltip>
{...field} <TooltipTrigger asChild>
/> <HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
</FormControl> </TooltipTrigger>
<FormMessage /> <TooltipContent>
</FormItem> <p>
)} Add a labels that will trigger a preview deployment
/> for a pull request. If no labels are specified, all
pull requests will trigger a preview deployment.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="flex flex-wrap gap-2 mb-2">
{field.value?.map((label, index) => (
<Badge
key={index}
variant="secondary"
className="flex items-center gap-1"
>
{label}
<X
className="size-3 cursor-pointer hover:text-destructive"
onClick={() => {
const newLabels = [...(field.value || [])];
newLabels.splice(index, 1);
field.onChange(newLabels);
}}
/>
</Badge>
))}
</div>
<div className="flex gap-2">
<FormControl>
<Input
placeholder="Enter a label (e.g. enhancements, needs-review)"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
const input = e.currentTarget;
const label = input.value.trim();
if (label) {
field.onChange([...(field.value || []), label]);
input.value = "";
}
}
}}
/>
</FormControl>
<Button
type="button"
variant="outline"
size="icon"
onClick={() => {
const input = document.querySelector(
'input[placeholder*="Enter a label"]',
) as HTMLInputElement;
const label = input.value.trim();
if (label) {
field.onChange([...(field.value || []), label]);
input.value = "";
}
}}
>
<Plus className="size-4" />
</Button>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="previewLimit" name="previewLimit"
@@ -1 +0,0 @@
ALTER TABLE "application" ADD COLUMN "previewLabels" text;
@@ -0,0 +1 @@
ALTER TABLE "application" ADD COLUMN "previewLabels" text[];
File diff suppressed because it is too large Load Diff
+7
View File
@@ -743,6 +743,13 @@
"when": 1754259281559, "when": 1754259281559,
"tag": "0105_clumsy_quicksilver", "tag": "0105_clumsy_quicksilver",
"breakpoints": true "breakpoints": true
},
{
"idx": 106,
"version": "7",
"when": 1754912062243,
"tag": "0106_purple_maggott",
"breakpoints": true
} }
] ]
} }
+2 -3
View File
@@ -443,12 +443,11 @@ export default async function handler(
for (const app of secureApps) { for (const app of secureApps) {
// check for labels // check for labels
if (app?.previewLabels != "") { if (app?.previewLabels.size() > 0) {
const previewLabels = app?.previewLabels?.split(",");
let hasLabel: boolean = false; let hasLabel: boolean = false;
const labels = githubBody?.pull_request?.labels; const labels = githubBody?.pull_request?.labels;
for (const label of labels) { for (const label of labels) {
if (previewLabels.contains(label.name)) { if (app?.previewLabels.contains(label.name)) {
hasLabel = true; hasLabel = true;
break; break;
} }
+1 -1
View File
@@ -79,7 +79,7 @@ export const applications = pgTable("application", {
previewEnv: text("previewEnv"), previewEnv: text("previewEnv"),
watchPaths: text("watchPaths").array(), watchPaths: text("watchPaths").array(),
previewBuildArgs: text("previewBuildArgs"), previewBuildArgs: text("previewBuildArgs"),
previewLabels: text("previewLabels"), previewLabels: text("previewLabels").array(),
previewWildcard: text("previewWildcard"), previewWildcard: text("previewWildcard"),
previewPort: integer("previewPort").default(3000), previewPort: integer("previewPort").default(3000),
previewHttps: boolean("previewHttps").notNull().default(false), previewHttps: boolean("previewHttps").notNull().default(false),