From a63c1ec364640c3d8b81e4284f37fc495d4d2e27 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Fri, 8 May 2026 21:49:20 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=84=20label=20selector=20(with=20creat?= =?UTF-8?q?e=20label)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- messages/en-US.json | 1 + src/components/labels-selector.tsx | 117 +++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 8 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 2a6d1ee40..563569803 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1130,6 +1130,7 @@ "siteLabelsDescription": "Manage labels associated with this site.", "labelsNotFound": "Labels not found", "labelSearch": "Search labels", + "selectColor": "Select color", "createNewLabel": "Create new org label \"{label}\"", "inviteInvalidDescription": "The invite link is invalid.", "inviteErrorWrongUser": "Invite is not for this user", diff --git a/src/components/labels-selector.tsx b/src/components/labels-selector.tsx index 988a9e02b..ec8d7f270 100644 --- a/src/components/labels-selector.tsx +++ b/src/components/labels-selector.tsx @@ -1,6 +1,6 @@ import { orgQueries } from "@app/lib/queries"; import { useQuery } from "@tanstack/react-query"; -import { useMemo, useState } from "react"; +import { useActionState, useMemo, useState, useTransition } from "react"; import { Command, CommandEmpty, @@ -13,6 +13,18 @@ import { Checkbox } from "./ui/checkbox"; import { useTranslations } from "next-intl"; import { useDebounce } from "use-debounce"; import { type Selectedsite, SiteOnlineStatus } from "./site-selector"; +import { Button } from "./ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "./ui/select"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import type { CreateOrEditLabelResponse } from "@server/routers/labels/types"; +import type { AxiosResponse } from "axios"; type SelectedLabel = { name: string; @@ -24,17 +36,31 @@ export type LabelsSelectorProps = { orgId: string; selectedLabels: SelectedLabel[]; onSelectionChange: (sites: SelectedLabel[]) => void; + onCreateLabel: (newlabel: SelectedLabel) => Promise; +}; + +const LABEL_COLORS = { + red: "#ff6467", + green: "#05df72", + blue: "#51a2ff", + yellow: "#fdc744", + orange: "#ff8905", + purple: "#a684ff", + gray: "#b4b4b4" }; export function LabelsSelector({ orgId, selectedLabels, - onSelectionChange + onSelectionChange, + onCreateLabel }: LabelsSelectorProps) { const t = useTranslations(); const [labelSearchQuery, setlabelsSearchQuery] = useState(""); const [debouncedQuery] = useDebounce(labelSearchQuery, 150); + const api = createApiClient(useEnvContext()); + const { data: labels = [] } = useQuery( orgQueries.labels({ orgId, @@ -59,6 +85,29 @@ export function LabelsSelector({ [selectedLabels] ); + const colorValues = Object.values(LABEL_COLORS); + const randomColor = + colorValues[Math.floor(Math.random() * colorValues.length)]; + + const [, action, isPending] = useActionState(createLabel, null); + + async function createLabel(_: any, formData: FormData) { + const name = formData.get("name")?.toString(); + const color = formData.get("color")?.toString(); + const res = await api.post>( + `/org/${orgId}/labels`, + { name, color } + ); + + const { label } = res.data.data; + await onCreateLabel({ + labelId: label.labelId, + name: label.name, + color: label.color + }); + setlabelsSearchQuery(""); + } + return ( - + {labelSearchQuery.trim().length > 0 ? ( - <> - {t("createNewLabel", { - label: labelSearchQuery.trim() - })} - +
+ + {t("createNewLabel", { + label: labelSearchQuery.trim() + })} + + +
+ + + + + +
+
) : ( t("labelsNotFound") )}