diff --git a/docs/TECH-DEBT.md b/docs/TECH-DEBT.md index 7f01cf95..a99afe9f 100644 --- a/docs/TECH-DEBT.md +++ b/docs/TECH-DEBT.md @@ -6,7 +6,7 @@ Ledger of known engineering debt. Items large enough to be scheduled work become | Item | Location | Type | Risk | Effort | Priority | Status | Ref | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | --------------------------- | ----------------------------------------------------------------------------------------- | -------------------- | -| 164 unused exports + 60 unused types reported by knip (warn-level; 158 at Pass 1) | god modules, ui kit families | dead code | low | M | P2 | open — Pass 2 W4b flips `exports` to error after W4a kills the biggest offender | Pass 2 W4b | +| 164 unused exports + 60 unused types reported by knip (warn-level; 158 at Pass 1) | god modules, ui kit families | dead code | low | M | P2 | closed 2026-07-25 (Pass 2 W4b): 0 findings; `exports`/`types`/`enumMembers` at **error** in CI; tests entered the knip graph; `duplicates` off (only hits were the 6 deliberate access aliases `canUpdateX = canReadX`) | Pass 2 W4b | | knip loader noise: `Error loading src/payload.config.ts` (server-only guard) | knip run output | tooling noise | none (exit 0, static graph resolves) | S | P3 | open | | | Missing-consent window fixture releases its exclusive lease during the observation window — readers can interleave (rare flake). Full fix: session-level advisory lock spanning delete→op→restore + updated fault-path pins | `tests/helpers/testDatabaseLease.ts` (`withMissingInviteConsentFixture`) | test-infra race | low (ms-wide window; shared leases added 2026-07-23 narrow it) | M | P2 | open | | | `users` collection has no roles (all admins omnipotent) | `src/collections/Users.ts` | RBAC | medium (small trusted admin set) | M (schema migration) | P1 before widening `/admin` | open | AGENTS Known Gaps #1 | diff --git a/knip.json b/knip.json index 3eb8e869..4d647cf9 100644 --- a/knip.json +++ b/knip.json @@ -1,21 +1,27 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", "entry": [ - "src/payload.config.ts", "src/migrations/index.ts", + "src/migrations/*.ts", "src/app/(payload)/admin/importMap.js", "scripts/*.mjs", - "eslint-local-rules.mjs" + "eslint-local-rules.mjs", + "tests/**/*.spec.ts", + "tests/helpers/serverOnly.ts" ], - "project": ["src/**/*.{ts,tsx,js}", "scripts/**/*.mjs"], - "ignore": ["src/payload-types.ts", "src/migrations/**"], + "project": [ + "src/**/*.{ts,tsx,js}", + "scripts/**/*.mjs", + "tests/**/*.ts" + ], + "ignore": [], "paths": { - "server-only": ["tests/helpers/serverOnly.ts"] + "server-only": [ + "tests/helpers/serverOnly.ts" + ] }, - "ignoreBinaries": ["playwright"], + "ignoreBinaries": [], "ignoreDependencies": [ - "tsx", - "sharp", "tailwindcss-animate", "tw-animate-css", "shadcn", @@ -23,9 +29,9 @@ "prettier" ], "rules": { - "exports": "warn", - "types": "warn", - "enumMembers": "warn", - "duplicates": "warn" + "exports": "error", + "types": "error", + "enumMembers": "error", + "duplicates": "off" } -} +} \ No newline at end of file diff --git a/src/app/(campaign)/campanha/(app)/apoiadores/novo/formActions.ts b/src/app/(campaign)/campanha/(app)/apoiadores/novo/formActions.ts index da6a06a9..5716dfd4 100644 --- a/src/app/(campaign)/campanha/(app)/apoiadores/novo/formActions.ts +++ b/src/app/(campaign)/campanha/(app)/apoiadores/novo/formActions.ts @@ -21,7 +21,7 @@ export type SupporterFormState = { revision?: number } -export type SupporterFormValues = { +type SupporterFormValues = { name?: string phone?: string email?: string diff --git a/src/app/(campaign)/campanha/actions/actionPlan.ts b/src/app/(campaign)/campanha/actions/actionPlan.ts index 011dff8a..a089d924 100644 --- a/src/app/(campaign)/campanha/actions/actionPlan.ts +++ b/src/app/(campaign)/campanha/actions/actionPlan.ts @@ -63,7 +63,7 @@ export const createActionPlanRecord = async ( ) } -export const updateActionPlanRecord = async ( +const updateActionPlanRecord = async ( payload: Payload, actor: CampaignUser, input: ActionPlanUpdateInput, @@ -134,7 +134,7 @@ const setActionPlanLifecycleStatusRecord = async ( { beginFailureMessage: 'Não foi possível atualizar o status do plano.' }, ) -export const toggleActionPlanTaskRecord = async ( +const toggleActionPlanTaskRecord = async ( payload: Payload, actor: CampaignUser, planId: number, @@ -176,7 +176,7 @@ export const toggleActionPlanTaskRecord = async ( { beginFailureMessage: 'Não foi possível atualizar a tarefa do plano.' }, ) -export const appendActionPlanUpdateRecord = async ( +const appendActionPlanUpdateRecord = async ( payload: Payload, actor: CampaignUser, planId: number, @@ -221,7 +221,7 @@ export const appendActionPlanUpdateRecord = async ( ) } -export const registerActionPlanResult = async ( +const registerActionPlanResult = async ( payload: Payload, actor: CampaignUser, planId: number, diff --git a/src/app/(campaign)/campanha/actions/demand.ts b/src/app/(campaign)/campanha/actions/demand.ts index 68085ed3..25177f7f 100644 --- a/src/app/(campaign)/campanha/actions/demand.ts +++ b/src/app/(campaign)/campanha/actions/demand.ts @@ -5,11 +5,9 @@ import type { Payload } from 'payload' import { campaignDemandCostSchema, campaignDemandCreateSchema, - campaignDemandDetailsUpdateSchema, campaignDemandTransitionSchema, type CampaignDemandCostInput, type CampaignDemandCreateInput, - type CampaignDemandDetailsUpdateInput, type CampaignDemandTransitionInput, } from '@/lib/schemas/campaignDemandInput' import type { CampaignUser } from '@/payload-types' @@ -50,33 +48,6 @@ export const createCampaignDemandRecord = async ( ) } -export const updateCampaignDemandDetailsRecord = async ( - payload: Payload, - actor: CampaignUser, - input: CampaignDemandDetailsUpdateInput, -) => { - const { id, ...data } = campaignDemandDetailsUpdateSchema.parse(input) - - return withPayloadTransaction( - payload, - async ({ req }) => { - const currentActor = await reloadCampaignActor(payload, actor, req) - await acquireTextAdvisoryLocks(payload, req, [`campaign-demand:${id}`]) - - return payload.update({ - collection: 'campaignDemand', - id, - data, - depth: 0, - user: currentActor, - overrideAccess: false, - req, - }) - }, - { beginFailureMessage: 'Não foi possível iniciar a atualização da demanda.' }, - ) -} - export const transitionCampaignDemandRecord = async ( payload: Payload, actor: CampaignUser, @@ -215,11 +186,6 @@ export const createCampaignDemand = async (input: CampaignDemandCreateInput) => return createCampaignDemandRecord(payload, actor, input) } -export const updateCampaignDemandDetails = async (input: CampaignDemandDetailsUpdateInput) => { - const { payload, actor } = await getCampaignActionContext() - return updateCampaignDemandDetailsRecord(payload, actor, input) -} - export const transitionCampaignDemand = async (input: CampaignDemandTransitionInput) => { const { payload, actor } = await getCampaignActionContext() return transitionCampaignDemandRecord(payload, actor, input) diff --git a/src/app/(campaign)/campanha/actions/leaderSupporter.ts b/src/app/(campaign)/campanha/actions/leaderSupporter.ts index bf90c4ad..a398783e 100644 --- a/src/app/(campaign)/campanha/actions/leaderSupporter.ts +++ b/src/app/(campaign)/campanha/actions/leaderSupporter.ts @@ -28,7 +28,7 @@ export type LeaderSupporterFormState = { revision?: number } -export type LeaderSupporterFormValues = { +type LeaderSupporterFormValues = { name?: string phone?: string city?: string @@ -144,7 +144,7 @@ export const createLeaderSupporterRecord = async ( } } -export const createLeaderSupporter = async (input: unknown) => { +const createLeaderSupporter = async (input: unknown) => { const { payload, actor } = await getCampaignActionContext() return createLeaderSupporterRecord(payload, actor, input) } diff --git a/src/app/(campaign)/campanha/actions/organization.ts b/src/app/(campaign)/campanha/actions/organization.ts index 057a5120..058e766c 100644 --- a/src/app/(campaign)/campanha/actions/organization.ts +++ b/src/app/(campaign)/campanha/actions/organization.ts @@ -19,7 +19,7 @@ const organizationPolicy: StaffEntityPolicy = { conflictMessage: 'Já existe uma organização com este nome.', } -export const createOrganizationRecord = async ( +const createOrganizationRecord = async ( payload: Payload, actor: CampaignUser, input: OrganizationCreateInput, @@ -36,7 +36,7 @@ export const createOrganizationRecord = async ( ) } -export const updateOrganizationRecord = async ( +const updateOrganizationRecord = async ( payload: Payload, actor: CampaignUser, input: OrganizationUpdateInput, diff --git a/src/app/(campaign)/campanha/actions/stateDeputy.ts b/src/app/(campaign)/campanha/actions/stateDeputy.ts index 6e19c58d..f153a8bd 100644 --- a/src/app/(campaign)/campanha/actions/stateDeputy.ts +++ b/src/app/(campaign)/campanha/actions/stateDeputy.ts @@ -19,7 +19,7 @@ const stateDeputyPolicy: StaffEntityPolicy = { conflictMessage: 'Já existe uma dobradinha com este nome.', } -export const createStateDeputyRecord = async ( +const createStateDeputyRecord = async ( payload: Payload, actor: CampaignUser, input: StateDeputyCreateInput, @@ -36,7 +36,7 @@ export const createStateDeputyRecord = async ( ) } -export const updateStateDeputyRecord = async ( +const updateStateDeputyRecord = async ( payload: Payload, actor: CampaignUser, input: StateDeputyUpdateInput, diff --git a/src/app/(campaign)/campanha/actions/supporter.ts b/src/app/(campaign)/campanha/actions/supporter.ts index e5cc506e..b70ec936 100644 --- a/src/app/(campaign)/campanha/actions/supporter.ts +++ b/src/app/(campaign)/campanha/actions/supporter.ts @@ -27,12 +27,7 @@ import { previewSupporterImportText as previewSupporterImportTextAction, } from './supporterImport' -export type { - SupporterImportOkRow, - SupporterImportPreviewResult, - SupporterImportPreviewRow, - SupporterImportRowStatus, -} from '@/utilities/supporterImport' + const getFreshStaffActor = ( payload: Payload, @@ -46,7 +41,7 @@ const getFreshStaffActor = ( req, ) -export const assertMunicipalityManagement = async ( +const assertMunicipalityManagement = async ( payload: Payload, actor: CampaignUser, municipalityID: number, diff --git a/src/components/campaign/actionPlan/ActionPlanForm.tsx b/src/components/campaign/actionPlan/ActionPlanForm.tsx index d049801d..733dfcf0 100644 --- a/src/components/campaign/actionPlan/ActionPlanForm.tsx +++ b/src/components/campaign/actionPlan/ActionPlanForm.tsx @@ -64,7 +64,7 @@ export type ActionPlanFormFieldsProps = { const editableStatuses = ['rascunho', 'planejado'] as const -export const ActionPlanFormFields = ({ +const ActionPlanFormFields = ({ municipalityOptions, organizationOptions, advisorOptions = [], diff --git a/src/components/campaign/leadership/SupportStatusBadge.tsx b/src/components/campaign/leadership/SupportStatusBadge.tsx index 5458f4e0..17200f52 100644 --- a/src/components/campaign/leadership/SupportStatusBadge.tsx +++ b/src/components/campaign/leadership/SupportStatusBadge.tsx @@ -26,12 +26,6 @@ const statusPresentation = { }, } as const -export const supportStatusLabel = (status: SupportStatus): string => - statusPresentation[status].label - -export const supportStatusSummary = (status: SupportStatus): string => - statusPresentation[status].summary - export const SupportStatusBadge = ({ status }: { status: SupportStatus }) => { const presentation = statusPresentation[status] diff --git a/src/components/campaign/map/BahiaMap.tsx b/src/components/campaign/map/BahiaMap.tsx index f54b71e5..8ed58798 100644 --- a/src/components/campaign/map/BahiaMap.tsx +++ b/src/components/campaign/map/BahiaMap.tsx @@ -26,7 +26,7 @@ import { import { cn } from '@/lib/utils' import { Spinner } from '@/components/ui/Spinner' -export type { BahiaMapFillMode, ChoroplethValues } + const BAHIA_BOUNDS: L.LatLngBoundsExpression = [ [-18.5, -46.8], diff --git a/src/components/campaign/shared/CampaignListPagination.tsx b/src/components/campaign/shared/CampaignListPagination.tsx index 8a8fa034..2d2607de 100644 --- a/src/components/campaign/shared/CampaignListPagination.tsx +++ b/src/components/campaign/shared/CampaignListPagination.tsx @@ -10,7 +10,7 @@ import { } from '@/components/ui/Pagination' import { cn } from '@/lib/utils' -export const getPaginationPages = (page: number, totalPages: number): number[] => { +const getPaginationPages = (page: number, totalPages: number): number[] => { const pages = new Set([1, totalPages, page - 1, page, page + 1]) return [...pages] .filter((candidate) => candidate >= 1 && candidate <= totalPages) diff --git a/src/components/campaign/shell/CampaignPageShell.tsx b/src/components/campaign/shell/CampaignPageShell.tsx index 8d1ce2d7..346b2b25 100644 --- a/src/components/campaign/shell/CampaignPageShell.tsx +++ b/src/components/campaign/shell/CampaignPageShell.tsx @@ -2,7 +2,7 @@ import type { ComponentProps, ReactNode } from 'react' import { cn } from '@/lib/utils' -export const campaignPageShellClassName = 'mx-auto flex w-full max-w-screen-2xl flex-col gap-8' +const campaignPageShellClassName = 'mx-auto flex w-full max-w-screen-2xl flex-col gap-8' export const campaignPrioritySurfaceClassName = 'shadow-[0_4px_24px_rgb(28_25_23/0.08)] ring-1 ring-foreground/10' diff --git a/src/components/ui/Alert.tsx b/src/components/ui/Alert.tsx index 91b80268..2ce1de70 100644 --- a/src/components/ui/Alert.tsx +++ b/src/components/ui/Alert.tsx @@ -56,8 +56,4 @@ const AlertDescription = ({ className, ...props }: React.ComponentProps<'div'>) /> ) -const AlertAction = ({ className, ...props }: React.ComponentProps<'div'>) => ( -
-) - -export { Alert, AlertTitle, AlertDescription, AlertAction } +export { Alert, AlertTitle, AlertDescription, } diff --git a/src/components/ui/AlertDialog.tsx b/src/components/ui/AlertDialog.tsx index 39adf001..f66273c1 100644 --- a/src/components/ui/AlertDialog.tsx +++ b/src/components/ui/AlertDialog.tsx @@ -14,11 +14,11 @@ export const AlertDialogTrigger = ( props: React.ComponentProps, ) => -export const AlertDialogPortal = ( +const AlertDialogPortal = ( props: React.ComponentProps, ) => -export const AlertDialogOverlay = ({ +const AlertDialogOverlay = ({ className, ...props }: React.ComponentProps) => ( diff --git a/src/components/ui/Avatar.tsx b/src/components/ui/Avatar.tsx index 2ef23ead..e59ab19d 100644 --- a/src/components/ui/Avatar.tsx +++ b/src/components/ui/Avatar.tsx @@ -48,40 +48,4 @@ const AvatarFallback = ({ /> ) -const AvatarBadge = ({ className, ...props }: React.ComponentProps<'span'>) => ( - svg]:hidden', - 'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2', - 'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2', - className, - )} - {...props} - /> -) - -const AvatarGroup = ({ className, ...props }: React.ComponentProps<'div'>) => ( -
-) - -const AvatarGroupCount = ({ className, ...props }: React.ComponentProps<'div'>) => ( -
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3', - className, - )} - {...props} - /> -) - -export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge } +export { Avatar, AvatarImage, AvatarFallback, } diff --git a/src/components/ui/Command.tsx b/src/components/ui/Command.tsx index 8fbfe083..bfc64f57 100644 --- a/src/components/ui/Command.tsx +++ b/src/components/ui/Command.tsx @@ -87,17 +87,6 @@ const CommandList = ({ /> ) -const CommandEmpty = ({ - className, - ...props -}: React.ComponentProps) => ( - -) - const CommandGroup = ({ className, ...props @@ -112,17 +101,6 @@ const CommandGroup = ({ /> ) -const CommandSeparator = ({ - className, - ...props -}: React.ComponentProps) => ( - -) - const CommandItem = ({ className, children, @@ -141,25 +119,14 @@ const CommandItem = ({ ) -const CommandShortcut = ({ className, ...props }: React.ComponentProps<'span'>) => ( - -) - export { Command, CommandDialog, CommandInput, CommandList, - CommandEmpty, + CommandGroup, CommandItem, - CommandShortcut, - CommandSeparator, + + } diff --git a/src/components/ui/Drawer.tsx b/src/components/ui/Drawer.tsx index 77697e23..242147db 100644 --- a/src/components/ui/Drawer.tsx +++ b/src/components/ui/Drawer.tsx @@ -54,10 +54,6 @@ const Drawer = ({ ) } -const DrawerTrigger = ({ ...props }: DrawerPrimitive.Trigger.Props) => ( - -) - const DrawerPortal = ({ ...props }: DrawerPrimitive.Portal.Props) => ( ) @@ -172,10 +168,10 @@ const DrawerDescription = ({ className, ...props }: DrawerPrimitive.Description. export { Drawer, - DrawerPortal, - DrawerOverlay, - DrawerSwipeHandle, - DrawerTrigger, + + + + DrawerClose, DrawerContent, DrawerHeader, diff --git a/src/components/ui/Pagination.tsx b/src/components/ui/Pagination.tsx index 160d38af..058f09a2 100644 --- a/src/components/ui/Pagination.tsx +++ b/src/components/ui/Pagination.tsx @@ -1,8 +1,7 @@ import * as React from 'react' import { cn } from '@/lib/utils' -import { Button } from '@/components/ui/button' -import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from 'lucide-react' +import { MoreHorizontalIcon } from 'lucide-react' const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (