chore: add pre-commit hooks, tighten ESLint, activate Sentry DSN, publish CI coverage (Phase 1)
- Install husky v9 + lint-staged: pre-commit runs eslint --fix and prettier on staged files - Tighten ESLint base config: no-console→error, ban-ts-comment (ts-ignore banned, ts-expect-error with description allowed), reportUnusedDisableDirectives→error - Migrate web app from deprecated `next lint` to `eslint src/` with flat config and react-hooks plugin - Convert all 5 @ts-ignore to @ts-expect-error with descriptions, remove stale disable comments - Add NEXT_PUBLIC_SENTRY_DSN to docker-compose.prod.yml and .env.example - Add coverage artifact upload step to CI test job - Pre-existing violations (102 warnings) downgraded to warn in web config for Phase 2 cleanup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { OrderType, AllocationType, ProjectStatus } from "@capakraken/shared";
|
||||
import type { OrderType, AllocationType, ProjectStatus } from "@capakraken/shared";
|
||||
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
|
||||
import type { Project } from "@capakraken/shared";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
@@ -79,9 +79,12 @@ function projectToForm(project: Project): FormState {
|
||||
status: project.status,
|
||||
responsiblePerson: project.responsiblePerson ?? "",
|
||||
color: (project as unknown as { color?: string | null }).color ?? "",
|
||||
utilizationCategoryId: (project as unknown as { utilizationCategoryId?: string | null }).utilizationCategoryId ?? "",
|
||||
utilizationCategoryId:
|
||||
(project as unknown as { utilizationCategoryId?: string | null }).utilizationCategoryId ?? "",
|
||||
clientId: (project as unknown as { clientId?: string | null }).clientId ?? "",
|
||||
shoringThreshold: String((project as unknown as { shoringThreshold?: number | null }).shoringThreshold ?? 55),
|
||||
shoringThreshold: String(
|
||||
(project as unknown as { shoringThreshold?: number | null }).shoringThreshold ?? 55,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,10 +104,12 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
const [errors, setErrors] = useState<Partial<Record<keyof FormState, string>>>({});
|
||||
const [serverError, setServerError] = useState<string | null>(null);
|
||||
|
||||
const { data: utilizationCategories } = trpc.utilizationCategory.list.useQuery(undefined, { staleTime: 60_000 });
|
||||
const { data: utilizationCategories } = trpc.utilizationCategory.list.useQuery(undefined, {
|
||||
staleTime: 60_000,
|
||||
});
|
||||
const { data: clientList } = trpc.clientEntity.list.useQuery(undefined, { staleTime: 60_000 });
|
||||
|
||||
// @ts-ignore TS2589: tRPC infers union type too deeply for CreateProjectSchema with .refine()
|
||||
// @ts-expect-error TS2589: tRPC infers union type too deeply for CreateProjectSchema with .refine()
|
||||
const createMutation = trpc.project.create.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.project.listWithCosts.invalidate();
|
||||
@@ -193,7 +198,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
status: form.status as unknown as ProjectStatus,
|
||||
responsiblePerson: form.responsiblePerson.trim(),
|
||||
...(form.color ? { color: form.color } : {}),
|
||||
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
|
||||
...(form.utilizationCategoryId
|
||||
? { utilizationCategoryId: form.utilizationCategoryId }
|
||||
: {}),
|
||||
...(form.clientId ? { clientId: form.clientId } : {}),
|
||||
shoringThreshold: Number(form.shoringThreshold),
|
||||
},
|
||||
@@ -213,7 +220,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
dynamicFields: {},
|
||||
responsiblePerson: form.responsiblePerson.trim(),
|
||||
...(form.color ? { color: form.color } : {}),
|
||||
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
|
||||
...(form.utilizationCategoryId
|
||||
? { utilizationCategoryId: form.utilizationCategoryId }
|
||||
: {}),
|
||||
...(form.clientId ? { clientId: form.clientId } : {}),
|
||||
shoringThreshold: Number(form.shoringThreshold),
|
||||
});
|
||||
@@ -241,7 +250,12 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -282,7 +296,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
}
|
||||
/>
|
||||
{errors.shortCode && (
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">{errors.shortCode}</p>
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
{errors.shortCode}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -362,7 +378,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
className={errors.winProbability ? inputErrorClass : inputClass}
|
||||
/>
|
||||
{errors.winProbability && (
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">{errors.winProbability}</p>
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
{errors.winProbability}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -388,7 +406,8 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
<option value="">— Not specified —</option>
|
||||
{(utilizationCategories ?? []).map((cat) => (
|
||||
<option key={cat.id} value={cat.id}>
|
||||
{(cat as unknown as { code: string }).code} — {(cat as unknown as { name: string }).name}
|
||||
{(cat as unknown as { code: string }).code} —{" "}
|
||||
{(cat as unknown as { name: string }).name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -408,7 +427,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
{(clientList ?? []).map((c) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{(c as unknown as { name: string }).name}
|
||||
{(c as unknown as { code: string | null }).code ? ` [${(c as unknown as { code: string }).code}]` : ""}
|
||||
{(c as unknown as { code: string | null }).code
|
||||
? ` [${(c as unknown as { code: string }).code}]`
|
||||
: ""}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -434,7 +455,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
className={errors.startDate ? inputErrorClass : inputClass}
|
||||
/>
|
||||
{errors.startDate && (
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">{errors.startDate}</p>
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
{errors.startDate}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -469,7 +492,9 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
className={errors.budgetEur ? inputErrorClass : inputClass}
|
||||
/>
|
||||
{errors.budgetEur && (
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">{errors.budgetEur}</p>
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
{errors.budgetEur}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
@@ -576,7 +601,13 @@ export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps)
|
||||
disabled={isLoading}
|
||||
className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{isLoading ? (isEdit ? "Saving…" : "Creating…") : isEdit ? "Save Changes" : "Create Project"}
|
||||
{isLoading
|
||||
? isEdit
|
||||
? "Saving…"
|
||||
: "Creating…"
|
||||
: isEdit
|
||||
? "Save Changes"
|
||||
: "Create Project"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user