From 5adf521b1ae0d221ed815b66f2d762d357920b85 Mon Sep 17 00:00:00 2001 From: Oliver Bryan <04oliverbryan@gmail.com> Date: Sat, 10 Jan 2026 18:00:49 +0000 Subject: [PATCH] added *_MAX_LENGTH variables project wide --- .../src/components/create-organisation.tsx | 53 +++++++++++-------- .../src/components/create-project.tsx | 21 ++++++-- packages/shared/src/index.ts | 10 ++++ packages/shared/src/schema.ts | 32 ++++++++--- 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/packages/frontend/src/components/create-organisation.tsx b/packages/frontend/src/components/create-organisation.tsx index eb2b52a..893d321 100644 --- a/packages/frontend/src/components/create-organisation.tsx +++ b/packages/frontend/src/components/create-organisation.tsx @@ -1,3 +1,4 @@ +import { ORG_DESCRIPTION_MAX_LENGTH, ORG_NAME_MAX_LENGTH, ORG_SLUG_MAX_LENGTH } from "@issue/shared"; import { type FormEvent, useState } from "react"; import { useAuthenticatedSession } from "@/components/session-provider"; import { Button } from "@/components/ui/button"; @@ -62,8 +63,9 @@ export function CreateOrganisation({ setError(null); setSubmitAttempted(true); - if (name.trim() === "" || name.trim().length > 16) return; - if (slug.trim() === "" || slug.trim().length > 16) return; + if (name.trim() === "" || name.trim().length > ORG_NAME_MAX_LENGTH) return; + if (slug.trim() === "" || slug.trim().length > ORG_SLUG_MAX_LENGTH) return; + if (description.trim().length > ORG_DESCRIPTION_MAX_LENGTH) return; if (!user.id) { setError("you must be logged in to create an organisation"); @@ -104,7 +106,7 @@ export function CreateOrganisation({ {trigger || } - + Create Organisation {/* Enter the details for the new organisation. */} @@ -122,15 +124,16 @@ export function CreateOrganisation({ setSlug(slugify(nextName)); } }} - validate={(v) => - v.trim() === "" - ? "Cannot be empty" - : v.trim().length > 16 - ? "Too long (16 character limit)" - : undefined - } + validate={(v) => { + if (v.trim() === "") return "Cannot be empty"; + if (v.trim().length > ORG_NAME_MAX_LENGTH) { + return `Too long (${ORG_NAME_MAX_LENGTH} character limit)`; + } + return undefined; + }} submitAttempted={submitAttempted} placeholder="Demo Organisation" + maxLength={ORG_NAME_MAX_LENGTH} /> - v.trim() === "" - ? "Cannot be empty" - : v.trim().length > 16 - ? "Too long (16 character limit)" - : undefined - } + validate={(v) => { + if (v.trim() === "") return "Cannot be empty"; + if (v.trim().length > ORG_SLUG_MAX_LENGTH) { + return `Too long (${ORG_SLUG_MAX_LENGTH} character limit)`; + } + return undefined; + }} submitAttempted={submitAttempted} placeholder="demo-organisation" + maxLength={ORG_SLUG_MAX_LENGTH} /> setDescription(e.target.value)} - validate={(v) => - v.trim().length > 2048 ? "Too long (2048 character limit)" : undefined - } + validate={(v) => { + if (v.trim().length > ORG_DESCRIPTION_MAX_LENGTH) { + return `Too long (${ORG_DESCRIPTION_MAX_LENGTH} character limit)`; + } + return undefined; + }} submitAttempted={submitAttempted} placeholder="What is this organisation for?" + maxLength={ORG_DESCRIPTION_MAX_LENGTH} />
@@ -179,9 +187,10 @@ export function CreateOrganisation({ disabled={ submitting || name.trim() === "" || - name.trim().length > 16 || + name.trim().length > ORG_NAME_MAX_LENGTH || slug.trim() === "" || - slug.trim().length > 16 + slug.trim().length > ORG_SLUG_MAX_LENGTH || + description.trim().length > ORG_DESCRIPTION_MAX_LENGTH } > {submitting ? "Creating..." : "Create"} diff --git a/packages/frontend/src/components/create-project.tsx b/packages/frontend/src/components/create-project.tsx index 21c2a4b..0a468a4 100644 --- a/packages/frontend/src/components/create-project.tsx +++ b/packages/frontend/src/components/create-project.tsx @@ -1,4 +1,4 @@ -import type { ProjectRecord } from "@issue/shared"; +import { PROJECT_NAME_MAX_LENGTH, type ProjectRecord } from "@issue/shared"; import { type FormEvent, useState } from "react"; import { useAuthenticatedSession } from "@/components/session-provider"; import { Button } from "@/components/ui/button"; @@ -61,7 +61,12 @@ export function CreateProject({ setError(null); setSubmitAttempted(true); - if (name.trim() === "" || key.trim() === "" || key.length > 4) { + if ( + name.trim() === "" || + name.trim().length > PROJECT_NAME_MAX_LENGTH || + key.trim() === "" || + key.length > 4 + ) { return; } @@ -115,7 +120,7 @@ export function CreateProject({ )} - + Create Project @@ -132,9 +137,16 @@ export function CreateProject({ setKey(keyify(nextName)); } }} - validate={(v) => (v.trim() === "" ? "Cannot be empty" : undefined)} + validate={(v) => { + if (v.trim() === "") return "Cannot be empty"; + if (v.trim().length > PROJECT_NAME_MAX_LENGTH) { + return `Too long (${PROJECT_NAME_MAX_LENGTH} character limit)`; + } + return undefined; + }} submitAttempted={submitAttempted} placeholder="Demo Project" + maxLength={PROJECT_NAME_MAX_LENGTH} /> PROJECT_NAME_MAX_LENGTH && submitAttempted) || ((key.trim() === "" || key.length > 4) && submitAttempted) } > diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 54cbf03..755d396 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -42,6 +42,16 @@ export { UserSelectSchema, } from "./schema"; +export const ORG_NAME_MAX_LENGTH = 256; +export const ORG_DESCRIPTION_MAX_LENGTH = 1024; +export const ORG_SLUG_MAX_LENGTH = 64; + +export const PROJECT_NAME_MAX_LENGTH = 256; +export const PROJECT_DESCRIPTION_MAX_LENGTH = 1024; +export const PROJECT_SLUG_MAX_LENGTH = 64; + +export const ISSUE_TITLE_MAX_LENGTH = 256; +export const ISSUE_DESCRIPTION_MAX_LENGTH = 2048; export const ISSUE_STATUS_MAX_LENGTH = 24; export { calculateBreakTimeMs, calculateWorkTimeMs, isTimerRunning } from "./utils/time-tracking"; diff --git a/packages/shared/src/schema.ts b/packages/shared/src/schema.ts index 2b6f5c6..b60836d 100644 --- a/packages/shared/src/schema.ts +++ b/packages/shared/src/schema.ts @@ -1,6 +1,17 @@ import { integer, pgTable, timestamp, uniqueIndex, varchar } from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import type { z } from "zod"; +import { + ISSUE_DESCRIPTION_MAX_LENGTH, + ISSUE_STATUS_MAX_LENGTH, + ISSUE_TITLE_MAX_LENGTH, + ORG_DESCRIPTION_MAX_LENGTH, + ORG_NAME_MAX_LENGTH, + ORG_SLUG_MAX_LENGTH, + PROJECT_DESCRIPTION_MAX_LENGTH, + PROJECT_NAME_MAX_LENGTH, + PROJECT_SLUG_MAX_LENGTH, +} from "./index"; export const User = pgTable("User", { id: integer().primaryKey().generatedAlwaysAsIdentity(), @@ -14,10 +25,13 @@ export const User = pgTable("User", { export const Organisation = pgTable("Organisation", { id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 256 }).notNull(), - description: varchar({ length: 1024 }), - slug: varchar({ length: 64 }).notNull().unique(), - statuses: varchar({ length: 64 }).array().notNull().default(["TO DO", "IN PROGRESS", "REVIEW", "DONE"]), + name: varchar({ length: ORG_NAME_MAX_LENGTH }).notNull(), + description: varchar({ length: ORG_DESCRIPTION_MAX_LENGTH }), + slug: varchar({ length: ORG_SLUG_MAX_LENGTH }).notNull().unique(), + statuses: varchar({ length: ISSUE_STATUS_MAX_LENGTH }) + .array() + .notNull() + .default(["TO DO", "IN PROGRESS", "REVIEW", "DONE", "ARCHIVED", "MERGED"]), createdAt: timestamp({ withTimezone: false }).defaultNow(), updatedAt: timestamp({ withTimezone: false }).defaultNow(), }); @@ -37,7 +51,9 @@ export const OrganisationMember = pgTable("OrganisationMember", { export const Project = pgTable("Project", { id: integer().primaryKey().generatedAlwaysAsIdentity(), key: varchar({ length: 4 }).notNull(), - name: varchar({ length: 256 }).notNull(), + name: varchar({ length: PROJECT_NAME_MAX_LENGTH }).notNull(), + description: varchar({ length: PROJECT_DESCRIPTION_MAX_LENGTH }), + slug: varchar({ length: PROJECT_SLUG_MAX_LENGTH }).notNull().unique(), organisationId: integer() .notNull() .references(() => Organisation.id), @@ -79,9 +95,9 @@ export const Issue = pgTable( number: integer("number").notNull(), - title: varchar({ length: 256 }).notNull(), - description: varchar({ length: 2048 }).notNull(), - status: varchar({ length: 64 }).notNull().default("TO DO"), + title: varchar({ length: ISSUE_TITLE_MAX_LENGTH }).notNull(), + description: varchar({ length: ISSUE_DESCRIPTION_MAX_LENGTH }).notNull(), + status: varchar({ length: ISSUE_STATUS_MAX_LENGTH }).notNull().default("TO DO"), creatorId: integer() .notNull()