diff --git a/apps/web/src/app/(app)/projects/ProjectsClient.tsx b/apps/web/src/app/(app)/projects/ProjectsClient.tsx index a0af1b4..8c41373 100644 --- a/apps/web/src/app/(app)/projects/ProjectsClient.tsx +++ b/apps/web/src/app/(app)/projects/ProjectsClient.tsx @@ -669,7 +669,7 @@ export function ProjectsClient() { > Edit - + View → diff --git a/apps/web/src/components/admin/ManagementLevelsClient.tsx b/apps/web/src/components/admin/ManagementLevelsClient.tsx index 3eee28a..435c5fd 100644 --- a/apps/web/src/components/admin/ManagementLevelsClient.tsx +++ b/apps/web/src/components/admin/ManagementLevelsClient.tsx @@ -189,7 +189,7 @@ export function ManagementLevelsClient() { setConfirmDeleteLevel(level.id)} - className="text-xs text-red-500 hover:text-red-700 font-medium" + className="app-action-delete" > Delete diff --git a/apps/web/src/components/admin/RateCardsClient.tsx b/apps/web/src/components/admin/RateCardsClient.tsx index bc8c10b..ddb6be1 100644 --- a/apps/web/src/components/admin/RateCardsClient.tsx +++ b/apps/web/src/components/admin/RateCardsClient.tsx @@ -531,7 +531,7 @@ export function RateCardsClient() { setConfirmDeleteLine(line.id)} - className="text-xs text-red-500 hover:text-red-700 font-medium" + className="app-action-delete" > Delete diff --git a/apps/web/src/components/admin/UsersClient.tsx b/apps/web/src/components/admin/UsersClient.tsx index 1289e56..5686d31 100644 --- a/apps/web/src/components/admin/UsersClient.tsx +++ b/apps/web/src/components/admin/UsersClient.tsx @@ -669,7 +669,7 @@ export function UsersClient() { setDeleteTarget({ userId: user.id, userName: user.name ?? user.email })} - className="text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 font-medium" + className="app-action-delete" title="Permanently delete user" > Delete diff --git a/apps/web/src/components/allocations/AllocationsClient.tsx b/apps/web/src/components/allocations/AllocationsClient.tsx index af7c53d..5255784 100644 --- a/apps/web/src/components/allocations/AllocationsClient.tsx +++ b/apps/web/src/components/allocations/AllocationsClient.tsx @@ -552,12 +552,12 @@ export function AllocationsClient() { })} - openEdit(alloc)} className="text-xs font-medium text-blue-600 hover:text-blue-800 hover:underline dark:text-blue-300 dark:hover:text-blue-200">Edit + openEdit(alloc)} className="app-action-edit">Edit setConfirmDelete({ single: alloc })} disabled={singleDeletePending} - className="text-xs font-medium text-red-500 hover:text-red-700 hover:underline disabled:opacity-50 dark:text-red-300 dark:hover:text-red-200" + className="app-action-delete disabled:opacity-50" > Delete @@ -977,7 +977,7 @@ export function AllocationsClient() { openEdit(demand as AllocationWithDetails)} - className="text-xs font-medium text-blue-600 hover:text-blue-800 hover:underline dark:text-blue-300 dark:hover:text-blue-200" + className="app-action-edit" > Edit @@ -985,7 +985,7 @@ export function AllocationsClient() { type="button" onClick={() => setConfirmDelete({ single: demand as AllocationWithDetails })} disabled={singleDeletePending} - className="text-xs font-medium text-red-500 hover:text-red-700 hover:underline disabled:opacity-50 dark:text-red-300 dark:hover:text-red-200" + className="app-action-delete disabled:opacity-50" > Delete diff --git a/apps/web/src/components/blueprints/BlueprintFieldCatalog.tsx b/apps/web/src/components/blueprints/BlueprintFieldCatalog.tsx index 5a6dc25..192d8d5 100644 --- a/apps/web/src/components/blueprints/BlueprintFieldCatalog.tsx +++ b/apps/web/src/components/blueprints/BlueprintFieldCatalog.tsx @@ -18,8 +18,7 @@ import type { CatalogField } from "~/lib/blueprint-field-catalog.js"; // Styles // --------------------------------------------------------------------------- -const INPUT_CLS = - "px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500"; +const INPUT_CLS = "app-input"; const BTN_PRIMARY = "px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50"; diff --git a/apps/web/src/components/blueprints/BlueprintFieldEditor.tsx b/apps/web/src/components/blueprints/BlueprintFieldEditor.tsx index 9441857..830732b 100644 --- a/apps/web/src/components/blueprints/BlueprintFieldEditor.tsx +++ b/apps/web/src/components/blueprints/BlueprintFieldEditor.tsx @@ -18,8 +18,7 @@ const FIELD_TYPES: { value: FieldType; label: string }[] = [ { value: FieldType.EMAIL, label: "Email" }, ]; -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm"; +const INPUT_CLS = "app-input"; const BTN_PRIMARY = "px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50"; @@ -27,8 +26,7 @@ const BTN_PRIMARY = const BTN_SECONDARY = "px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 text-sm font-medium"; -const BTN_DANGER = - "px-2 py-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded text-sm transition-colors"; +const BTN_DANGER = "app-action-danger-btn"; function makeEmptyField(order: number): BlueprintFieldDefinition { return { diff --git a/apps/web/src/components/blueprints/BlueprintsClient.tsx b/apps/web/src/components/blueprints/BlueprintsClient.tsx index 5644a2f..a80e4db 100644 --- a/apps/web/src/components/blueprints/BlueprintsClient.tsx +++ b/apps/web/src/components/blueprints/BlueprintsClient.tsx @@ -14,8 +14,7 @@ import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js"; import { useTableSort } from "~/hooks/useTableSort.js"; import { useViewPrefs } from "~/hooks/useViewPrefs.js"; -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm"; +const INPUT_CLS = "app-input"; const BTN_PRIMARY = "px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50"; @@ -430,7 +429,7 @@ export function BlueprintsClient() { } }} disabled={deleteMutation.isPending} - className="text-xs text-red-500 hover:text-red-700 disabled:opacity-50" + className="app-action-delete disabled:opacity-50" > Delete diff --git a/apps/web/src/components/blueprints/FieldCard.tsx b/apps/web/src/components/blueprints/FieldCard.tsx index 0d451ff..48937e6 100644 --- a/apps/web/src/components/blueprints/FieldCard.tsx +++ b/apps/web/src/components/blueprints/FieldCard.tsx @@ -59,8 +59,7 @@ interface FieldCardProps { // Component // --------------------------------------------------------------------------- -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm"; +const INPUT_CLS = "app-input"; export function FieldCard({ field, overrides, onChange }: FieldCardProps) { const [expanded, setExpanded] = useState(false); diff --git a/apps/web/src/components/blueprints/RolePresetsEditor.tsx b/apps/web/src/components/blueprints/RolePresetsEditor.tsx index 21253ce..ca37997 100644 --- a/apps/web/src/components/blueprints/RolePresetsEditor.tsx +++ b/apps/web/src/components/blueprints/RolePresetsEditor.tsx @@ -4,11 +4,8 @@ import { useState } from "react"; import type { StaffingRequirement } from "@capakraken/shared"; import { uuid } from "~/lib/uuid.js"; -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm"; - -const BTN_DANGER = - "px-2 py-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded text-sm transition-colors"; +const INPUT_CLS = "app-input"; +const BTN_DANGER = "app-action-danger-btn"; function makeEmptyPreset(): StaffingRequirement { return { diff --git a/apps/web/src/components/estimates/EstimateWizard.tsx b/apps/web/src/components/estimates/EstimateWizard.tsx index 8aa73f7..6b750ea 100644 --- a/apps/web/src/components/estimates/EstimateWizard.tsx +++ b/apps/web/src/components/estimates/EstimateWizard.tsx @@ -14,10 +14,9 @@ import { ResourceCombobox } from "~/components/ui/ResourceCombobox.js"; import { trpc } from "~/lib/trpc/client.js"; import { uuid } from "~/lib/uuid.js"; -const INPUT_CLS = - "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100"; -const SELECT_CLS = INPUT_CLS; -const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500"; +const INPUT_CLS = "app-input"; +const SELECT_CLS = "app-select w-full"; +const LABEL_CLS = "app-label"; const STEP_LABELS = ["Setup", "Assumptions", "Scope", "Staffing", "Review"]; interface AssumptionRow { diff --git a/apps/web/src/components/estimates/EstimateWorkspaceDraftEditor.tsx b/apps/web/src/components/estimates/EstimateWorkspaceDraftEditor.tsx index 7071d83..3c0c068 100644 --- a/apps/web/src/components/estimates/EstimateWorkspaceDraftEditor.tsx +++ b/apps/web/src/components/estimates/EstimateWorkspaceDraftEditor.tsx @@ -39,9 +39,8 @@ interface ResourceListView { resources: ResourceOption[]; } -const INPUT_CLS = - "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100"; -const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500"; +const INPUT_CLS = "app-input"; +const LABEL_CLS = "app-label"; function toNumber(value: string) { const parsed = Number.parseFloat(value); diff --git a/apps/web/src/components/estimates/editors/AssumptionEditor.tsx b/apps/web/src/components/estimates/editors/AssumptionEditor.tsx index e992c83..6e2ab7e 100644 --- a/apps/web/src/components/estimates/editors/AssumptionEditor.tsx +++ b/apps/web/src/components/estimates/editors/AssumptionEditor.tsx @@ -2,9 +2,8 @@ import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; -const INPUT_CLS = - "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100"; -const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500"; +const INPUT_CLS = "app-input"; +const LABEL_CLS = "app-label"; export interface EditableAssumption { id?: string; diff --git a/apps/web/src/components/estimates/editors/DemandLineEditor.tsx b/apps/web/src/components/estimates/editors/DemandLineEditor.tsx index faa4187..0f12e6d 100644 --- a/apps/web/src/components/estimates/editors/DemandLineEditor.tsx +++ b/apps/web/src/components/estimates/editors/DemandLineEditor.tsx @@ -13,9 +13,8 @@ import type { EstimateResourceSnapshotView } from "~/components/estimates/Estima import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; import { formatMoney } from "~/lib/format.js"; -const INPUT_CLS = - "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100"; -const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500"; +const INPUT_CLS = "app-input"; +const LABEL_CLS = "app-label"; function toNumber(value: string) { const parsed = Number.parseFloat(value); diff --git a/apps/web/src/components/estimates/editors/ScopeItemEditor.tsx b/apps/web/src/components/estimates/editors/ScopeItemEditor.tsx index 8ef9d7f..c273932 100644 --- a/apps/web/src/components/estimates/editors/ScopeItemEditor.tsx +++ b/apps/web/src/components/estimates/editors/ScopeItemEditor.tsx @@ -4,9 +4,8 @@ import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; import { isSpreadsheetFile } from "~/lib/excel.js"; import { parseScopeImport } from "~/lib/scopeImportParser.js"; -const INPUT_CLS = - "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100"; -const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500"; +const INPUT_CLS = "app-input"; +const LABEL_CLS = "app-label"; export interface EditableScopeItem { id?: string; diff --git a/apps/web/src/components/notifications/CreateTaskModal.tsx b/apps/web/src/components/notifications/CreateTaskModal.tsx index 6c0b3e6..e189331 100644 --- a/apps/web/src/components/notifications/CreateTaskModal.tsx +++ b/apps/web/src/components/notifications/CreateTaskModal.tsx @@ -238,7 +238,7 @@ export function CreateTaskModal({ onClose, onSuccess }: CreateTaskModalProps) { setUserId("")} - className="text-xs text-red-500 hover:text-red-700" + className="app-action-delete" > Clear diff --git a/apps/web/src/components/projects/ProjectDemandsTable.tsx b/apps/web/src/components/projects/ProjectDemandsTable.tsx index 7a3544e..9003d23 100644 --- a/apps/web/src/components/projects/ProjectDemandsTable.tsx +++ b/apps/web/src/components/projects/ProjectDemandsTable.tsx @@ -166,7 +166,7 @@ export function ProjectDemandsTable({ demands, project }: ProjectDemandsTablePro setEditTarget(demand as unknown as AllocationWithDetails)} - className="text-xs font-medium text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200" + className="app-action-edit" > Edit diff --git a/apps/web/src/components/projects/ProjectWizard.tsx b/apps/web/src/components/projects/ProjectWizard.tsx index a5b2621..4384630 100644 --- a/apps/web/src/components/projects/ProjectWizard.tsx +++ b/apps/web/src/components/projects/ProjectWizard.tsx @@ -30,13 +30,9 @@ const ALLOCATION_TYPE_OPTIONS = [ { value: "EXT", label: "EXT" }, ] as const; -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm w-full"; - -const SELECT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm w-full bg-white"; - -const LABEL_CLS = "block text-xs font-medium text-gray-600 mb-1"; +const INPUT_CLS = "app-input"; +const SELECT_CLS = "app-select w-full"; +const LABEL_CLS = "app-label"; const BTN_PRIMARY = "px-5 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50 transition-colors"; @@ -44,8 +40,7 @@ const BTN_PRIMARY = const BTN_SECONDARY = "px-5 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 text-sm font-medium transition-colors"; -const BTN_DANGER = - "px-2 py-1 text-red-500 hover:text-red-700 hover:bg-red-50 rounded text-sm transition-colors"; +const BTN_DANGER = "app-action-danger-btn"; // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/apps/web/src/components/projects/ScenarioPlanner.tsx b/apps/web/src/components/projects/ScenarioPlanner.tsx index 2059f33..cf2b90e 100644 --- a/apps/web/src/components/projects/ScenarioPlanner.tsx +++ b/apps/web/src/components/projects/ScenarioPlanner.tsx @@ -501,7 +501,7 @@ function ScenarioRowEditor({ onRestore(row.key)} - className="text-xs text-blue-600 hover:text-blue-800 underline" + className="app-action-edit" > Restore diff --git a/apps/web/src/components/resources/BulkEditModal.tsx b/apps/web/src/components/resources/BulkEditModal.tsx index 6503624..704f6ef 100644 --- a/apps/web/src/components/resources/BulkEditModal.tsx +++ b/apps/web/src/components/resources/BulkEditModal.tsx @@ -5,8 +5,7 @@ import { FieldType } from "@capakraken/shared"; import type { BlueprintFieldDefinition } from "@capakraken/shared"; import { trpc } from "~/lib/trpc/client.js"; -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm"; +const INPUT_CLS = "app-input"; interface Props { selectedIds: string[]; diff --git a/apps/web/src/components/roles/RolesClient.tsx b/apps/web/src/components/roles/RolesClient.tsx index 196005c..3d723d9 100644 --- a/apps/web/src/components/roles/RolesClient.tsx +++ b/apps/web/src/components/roles/RolesClient.tsx @@ -280,7 +280,7 @@ export function RolesClient() { setConfirmDelete(role); setActionError(null); }} - className="text-xs text-red-500 hover:text-red-700" + className="app-action-delete" > Delete diff --git a/apps/web/src/components/ui/CustomFieldFilterBar.tsx b/apps/web/src/components/ui/CustomFieldFilterBar.tsx index 5a3424b..c0095ea 100644 --- a/apps/web/src/components/ui/CustomFieldFilterBar.tsx +++ b/apps/web/src/components/ui/CustomFieldFilterBar.tsx @@ -11,8 +11,7 @@ interface Props { onSetFilter: (key: string, value: string, type: FieldType) => void; } -const INPUT_CLS = - "px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm bg-white"; +const INPUT_CLS = "app-input"; export function CustomFieldFilterBar({ filterableFields, activeFilters, onSetFilter }: Props) { if (filterableFields.length === 0) return null;