From b53f80d31711f4f35d9519e908675e095df3c20f Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 3 Jul 2026 09:59:49 -0400 Subject: [PATCH] use column for default view --- messages/en-US.json | 2 +- server/db/pg/schema/schema.ts | 1 + server/db/sqlite/schema/schema.ts | 3 +++ server/routers/launcher/createLauncherView.ts | 13 ++---------- server/routers/launcher/deleteLauncherView.ts | 3 +-- .../routers/launcher/launcherDefaultView.ts | 21 +++++++------------ server/routers/launcher/types.ts | 3 +-- server/routers/launcher/updateLauncherView.ts | 6 +++--- 8 files changed, 19 insertions(+), 33 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 45393e184..90be25679 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -3621,7 +3621,7 @@ "resourceLauncherSiteGroupingDisabled": "Site grouping is unavailable at this scale. Filter by site to group a smaller set.", "resourceLauncherLabelGroupingDisabled": "Label grouping is unavailable at this scale.", "resourceLauncherCompactModeHint": "Showing a simplified list for faster browsing. Use search or filters to narrow results.", - "resourceLauncherCompactGroupingHint": "Apply site or label filters to enable grouping in compact mode.", + "resourceLauncherCompactGroupingHint": "Apply site or label filters to enable grouping.", "resourceLauncherCopiedToClipboard": "Copied to clipboard", "resourceLauncherCopiedAccessDescription": "Resource access has been copied to your clipboard.", "resourceLauncherViewNamePlaceholder": "View name", diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index 683c572b6..5375e4fc2 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -232,6 +232,7 @@ export const launcherViews = pgTable("launcherViews", { }), name: varchar("name").notNull(), config: text("config").notNull(), + isDefault: boolean("isDefault").notNull().default(false), createdAt: varchar("createdAt").notNull(), updatedAt: varchar("updatedAt").notNull() }); diff --git a/server/db/sqlite/schema/schema.ts b/server/db/sqlite/schema/schema.ts index 28797bcd9..5f0c2f9a7 100644 --- a/server/db/sqlite/schema/schema.ts +++ b/server/db/sqlite/schema/schema.ts @@ -233,6 +233,9 @@ export const launcherViews = sqliteTable("launcherViews", { }), name: text("name").notNull(), config: text("config").notNull(), + isDefault: integer("isDefault", { mode: "boolean" }) + .notNull() + .default(false), createdAt: text("createdAt").notNull(), updatedAt: text("updatedAt").notNull() }); diff --git a/server/routers/launcher/createLauncherView.ts b/server/routers/launcher/createLauncherView.ts index 4c77cd4f0..195dfc555 100644 --- a/server/routers/launcher/createLauncherView.ts +++ b/server/routers/launcher/createLauncherView.ts @@ -7,7 +7,6 @@ import moment from "moment"; import { fromZodError } from "zod-validation-error"; import { z } from "zod"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; -import { isLauncherDefaultOverrideViewName } from "./launcherDefaultView"; import { launcherViewConfigSchema } from "./types"; const createLauncherViewBodySchema = z.strictObject({ @@ -41,15 +40,6 @@ export async function createLauncherView( ); } - if (isLauncherDefaultOverrideViewName(parsed.data.name)) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "This view name is reserved" - ) - ); - } - if (parsed.data.orgWide) { const canCreateOrgWide = await checkUserActionPermission( ActionsEnum.createOrgWideLauncherView, @@ -89,7 +79,8 @@ export async function createLauncherView( ), createdAt: created.createdAt, updatedAt: created.updatedAt, - isOrgWide: created.userId == null + isOrgWide: created.userId == null, + isDefault: created.isDefault }, success: true, error: false, diff --git a/server/routers/launcher/deleteLauncherView.ts b/server/routers/launcher/deleteLauncherView.ts index 41d7dd214..8377872fb 100644 --- a/server/routers/launcher/deleteLauncherView.ts +++ b/server/routers/launcher/deleteLauncherView.ts @@ -6,7 +6,6 @@ import { and, eq } from "drizzle-orm"; import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; -import { isLauncherDefaultOverrideViewName } from "./launcherDefaultView"; export async function deleteLauncherView( req: Request, @@ -47,7 +46,7 @@ export async function deleteLauncherView( ); } - if (isLauncherDefaultOverrideViewName(existing.name)) { + if (existing.isDefault) { return next( createHttpError( HttpCode.BAD_REQUEST, diff --git a/server/routers/launcher/launcherDefaultView.ts b/server/routers/launcher/launcherDefaultView.ts index 4d180bd1d..a0c2fd066 100644 --- a/server/routers/launcher/launcherDefaultView.ts +++ b/server/routers/launcher/launcherDefaultView.ts @@ -2,17 +2,12 @@ import { db, launcherViews } from "@server/db"; import { and, eq, isNull } from "drizzle-orm"; import moment from "moment"; import { - LAUNCHER_DEFAULT_OVERRIDE_VIEW_NAME, launcherViewConfigSchema, type LauncherDefaultViewOverrides, type LauncherViewConfig, type LauncherViewRecord } from "./types"; -export function isLauncherDefaultOverrideViewName(name: string) { - return name === LAUNCHER_DEFAULT_OVERRIDE_VIEW_NAME; -} - export function mapViewRow( row: typeof launcherViews.$inferSelect ): LauncherViewRecord { @@ -24,16 +19,15 @@ export function mapViewRow( config: launcherViewConfigSchema.parse(JSON.parse(row.config)), createdAt: row.createdAt, updatedAt: row.updatedAt, - isOrgWide: row.userId == null + isOrgWide: row.userId == null, + isDefault: row.isDefault }; } export function extractDefaultViewOverrides( rows: Array ): LauncherDefaultViewOverrides { - const overrideRows = rows.filter((row) => - isLauncherDefaultOverrideViewName(row.name) - ); + const overrideRows = rows.filter((row) => row.isDefault); const personalRow = overrideRows.find((row) => row.userId !== null); const orgWideRow = overrideRows.find((row) => row.userId === null); @@ -47,9 +41,7 @@ export function extractDefaultViewOverrides( export function listVisibleLauncherViews( rows: Array ): LauncherViewRecord[] { - return rows - .filter((row) => !isLauncherDefaultOverrideViewName(row.name)) - .map(mapViewRow); + return rows.filter((row) => !row.isDefault).map(mapViewRow); } export async function findDefaultViewOverride( @@ -63,7 +55,7 @@ export async function findDefaultViewOverride( .where( and( eq(launcherViews.orgId, orgId), - eq(launcherViews.name, LAUNCHER_DEFAULT_OVERRIDE_VIEW_NAME), + eq(launcherViews.isDefault, true), orgWide ? isNull(launcherViews.userId) : eq(launcherViews.userId, userId) @@ -106,7 +98,8 @@ export async function upsertDefaultViewOverride({ .values({ orgId, userId: orgWide ? null : userId, - name: LAUNCHER_DEFAULT_OVERRIDE_VIEW_NAME, + name: "", + isDefault: true, config: JSON.stringify(config), createdAt: now, updatedAt: now diff --git a/server/routers/launcher/types.ts b/server/routers/launcher/types.ts index 9f9701c96..4a986118c 100644 --- a/server/routers/launcher/types.ts +++ b/server/routers/launcher/types.ts @@ -89,6 +89,7 @@ export type LauncherViewRecord = { createdAt: string; updatedAt: string; isOrgWide: boolean; + isDefault: boolean; }; export type LauncherDefaultViewOverrides = { @@ -181,8 +182,6 @@ export function parseIdListParam(value: string | undefined): number[] { export const DEFAULT_LAUNCHER_VIEW_ID = "default" as const; -export const LAUNCHER_DEFAULT_OVERRIDE_VIEW_NAME = "__default__"; - export type LauncherViewSelection = | { type: "default" } | { type: "saved"; viewId: number }; diff --git a/server/routers/launcher/updateLauncherView.ts b/server/routers/launcher/updateLauncherView.ts index 757a16dac..e4373151f 100644 --- a/server/routers/launcher/updateLauncherView.ts +++ b/server/routers/launcher/updateLauncherView.ts @@ -9,7 +9,6 @@ import moment from "moment"; import { fromZodError } from "zod-validation-error"; import { z } from "zod"; import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; -import { isLauncherDefaultOverrideViewName } from "./launcherDefaultView"; import { launcherViewConfigSchema } from "./types"; const updateLauncherViewBodySchema = z.strictObject({ @@ -67,7 +66,7 @@ export async function updateLauncherView( ); } - if (isLauncherDefaultOverrideViewName(existing.name)) { + if (existing.isDefault) { return next( createHttpError( HttpCode.BAD_REQUEST, @@ -145,7 +144,8 @@ export async function updateLauncherView( ), createdAt: updated.createdAt, updatedAt: updated.updatedAt, - isOrgWide: updated.userId == null + isOrgWide: updated.userId == null, + isDefault: updated.isDefault }, success: true, error: false,