From b315020b3788419b0931c26843e2a00b6f95b8d0 Mon Sep 17 00:00:00 2001 From: Oliver Bryan <04oliverbryan@gmail.com> Date: Sat, 10 Jan 2026 17:00:39 +0000 Subject: [PATCH] max status length of 24 --- .../backend/src/routes/organisation/update.ts | 7 ++ .../src/components/organisations-dialog.tsx | 80 +++++++++++++------ packages/shared/src/index.ts | 2 + 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/routes/organisation/update.ts b/packages/backend/src/routes/organisation/update.ts index b76435c..f3781d9 100644 --- a/packages/backend/src/routes/organisation/update.ts +++ b/packages/backend/src/routes/organisation/update.ts @@ -1,3 +1,4 @@ +import { ISSUE_STATUS_MAX_LENGTH } from "@issue/shared"; import type { BunRequest } from "bun"; import { getOrganisationById, updateOrganisation } from "../../db/queries"; @@ -20,6 +21,12 @@ export default async function organisationUpdate(req: BunRequest) { if (statuses.length === 0) { return new Response("statuses must have at least one status", { status: 400 }); } + + if (statuses.some((s) => s.length > ISSUE_STATUS_MAX_LENGTH)) { + return new Response(`status must be <= ${ISSUE_STATUS_MAX_LENGTH} characters`, { + status: 400, + }); + } } catch { return new Response("invalid statuses format (must be JSON array)", { status: 400 }); } diff --git a/packages/frontend/src/components/organisations-dialog.tsx b/packages/frontend/src/components/organisations-dialog.tsx index b9f000d..ab8fa2a 100644 --- a/packages/frontend/src/components/organisations-dialog.tsx +++ b/packages/frontend/src/components/organisations-dialog.tsx @@ -1,4 +1,8 @@ -import type { OrganisationMemberResponse, OrganisationResponse } from "@issue/shared"; +import { + ISSUE_STATUS_MAX_LENGTH, + type OrganisationMemberResponse, + type OrganisationResponse, +} from "@issue/shared"; import { ChevronDown, ChevronUp, Plus, X } from "lucide-react"; import type { ReactNode } from "react"; import { useCallback, useEffect, useState } from "react"; @@ -36,6 +40,7 @@ function OrganisationsDialog({ const [statuses, setStatuses] = useState([]); const [isCreatingStatus, setIsCreatingStatus] = useState(false); const [newStatusName, setNewStatusName] = useState(""); + const [statusError, setStatusError] = useState(null); const [statusToRemove, setStatusToRemove] = useState(null); const [reassignToStatus, setReassignToStatus] = useState(""); @@ -185,9 +190,16 @@ function OrganisationsDialog({ const handleCreateStatus = async () => { const trimmed = newStatusName.trim().toUpperCase(); if (!trimmed) return; + + if (trimmed.length > ISSUE_STATUS_MAX_LENGTH) { + setStatusError(`status must be <= ${ISSUE_STATUS_MAX_LENGTH} characters`); + return; + } + if (statuses.includes(trimmed)) { setNewStatusName(""); setIsCreatingStatus(false); + setStatusError(null); return; } @@ -195,6 +207,7 @@ function OrganisationsDialog({ await updateStatuses(newStatuses); setNewStatusName(""); setIsCreatingStatus(false); + setStatusError(null); }; const handleRemoveStatusClick = (status: string) => { @@ -440,34 +453,53 @@ function OrganisationsDialog({ {isAdmin && (isCreatingStatus ? ( -
- setNewStatusName(e.target.value)} - placeholder="Status name" - className="flex-1" - onKeyDown={(e) => { - if (e.key === "Enter") { - void handleCreateStatus(); - } else if (e.key === "Escape") { - setIsCreatingStatus(false); - setNewStatusName(""); +
+
+ { + setNewStatusName(e.target.value); + if (statusError) setStatusError(null); + }} + placeholder="Status name" + className="flex-1" + onKeyDown={(e) => { + if (e.key === "Enter") { + void handleCreateStatus(); + } else if (e.key === "Escape") { + setIsCreatingStatus(false); + setNewStatusName(""); + setStatusError(null); + } + }} + autoFocus + /> + + > + + +
+ {statusError && ( +

+ {statusError} +

+ )}
) : ( diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 7d5aa97..54cbf03 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -42,4 +42,6 @@ export { UserSelectSchema, } from "./schema"; +export const ISSUE_STATUS_MAX_LENGTH = 24; + export { calculateBreakTimeMs, calculateWorkTimeMs, isTimerRunning } from "./utils/time-tracking";