From e6f7c2e70f46c32d75e83082980a96e660f336b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sat, 25 Jul 2026 17:22:20 -0300 Subject: [PATCH] Tooling: prettier como autoridade de formatacao (organize-imports deterministico, scripts format/format:check, normalizacao mecanica de 312 arquivos; artefatos gerados e migrations excluidos via .prettierignore) --- .impeccable/config.json | 12 +- ...19T10-15-47Z__src-app-campaign-campanha.md | 33 +- ...19T11-03-23Z__src-app-campaign-campanha.md | 32 +- ...10-58Z__src-app-campaign-campanha-login.md | 32 +- ...src-components-campaign-NucleusInsights.md | 3 + ...55-17Z__src-app-campaign-campanha-login.md | 32 +- ...47-04Z__src-app-campaign-campanha-login.md | 32 +- ..._src-app-campaign-campanha-app-page-tsx.md | 32 +- ...20T00-01-43Z__src-app-campaign-campanha.md | 32 +- ...mponents-campaign-plazalistoverview-tsx.md | 35 +- ...ents-campaign-plazamappanel-tsx-cenario.md | 36 +- ...2026-07-24T20-30-52Z__a11-ranking-votos.md | 32 +- ...2026-07-24T20-48-41Z__a11-ranking-votos.md | 32 +- ...2026-07-24T21-01-02Z__a11-ranking-votos.md | 30 +- ...ampaign-municipalitygoalaccountcard-tsx.md | 40 +- ...omponents-campaign-municipalitylist-tsx.md | 27 +- .impeccable/live/config.json | 5 +- .prettierignore | 32 + .prettierrc.json | 3 +- .zed/settings.json | 6 +- docs/TESTING.md | 2 +- docs/plans/escala-dry-pos-c2.md | 5 + docs/plans/inteligencia-campanha.md | 2 +- docs/plans/mapa-bahia-geometrias.md | 20 +- docs/plans/ranking-votos-municipio.md | 1 + docs/plans/sistema-listas-campanha.md | 2 +- docs/plans/tabela-ti-inicio.md | 1 + docs/plans/zonas-por-municipio.md | 1 + knip.json | 8 +- package.json | 5 + pnpm-lock.yaml | 828 ++++++++++++++++++ scripts/build-bahia-demographics.mjs | 10 +- scripts/build-bahia-geometries.mjs | 19 +- scripts/build-bahia-tse-city-codes.mjs | 11 +- scripts/build-election-aggregates.mjs | 25 +- scripts/import-projecao.mjs | 47 +- scripts/seed-posts.mjs | 11 +- scripts/seed-tse-results.mjs | 16 +- .../campanha/(app)/apoiadores/[id]/page.tsx | 2 +- .../(app)/apoiadores/importar/page.tsx | 2 +- .../campanha/(app)/apoiadores/novo/page.tsx | 2 +- .../campanha/(app)/assessores/[id]/page.tsx | 10 +- .../campanha/(app)/assessores/page.tsx | 6 +- .../campanha/(app)/conceitos/page.tsx | 4 +- .../campanha/(app)/demandas/[slug]/page.tsx | 2 +- .../campanha/(app)/demandas/nova/page.tsx | 2 +- .../campanha/(app)/dobradinhas/page.tsx | 2 +- .../campanha/(app)/liderancas/[id]/page.tsx | 12 +- .../campanha/(app)/liderancas/nova/page.tsx | 15 +- .../campanha/(app)/liderancas/page.tsx | 4 +- .../[slug]/MunicipalityDetailTabs.tsx | 2 +- .../(app)/municipios/[slug]/editar/page.tsx | 2 +- .../campanha/(app)/municipios/[slug]/page.tsx | 6 +- .../(app)/municipios/expected-votes/route.ts | 2 +- .../campanha/(app)/municipios/page.tsx | 8 +- .../(app)/organizacoes/[slug]/page.tsx | 6 +- .../campanha/(app)/organizacoes/nova/page.tsx | 7 +- .../campanha/(app)/organizacoes/page.tsx | 2 +- src/app/(campaign)/campanha/(app)/page.tsx | 4 +- .../planos/[slug]/ActionPlanOverviewTab.tsx | 4 +- .../(app)/planos/[slug]/editar/page.tsx | 10 +- .../(app)/planos/[slug]/updateFormActions.ts | 3 +- .../(app)/planos/contactSearchActions.ts | 7 +- .../campanha/(app)/planos/novo/page.tsx | 7 +- .../(campaign)/campanha/actions/actionPlan.ts | 6 +- .../(campaign)/campanha/actions/advisor.ts | 4 +- .../campanha/actions/leaderSupporter.ts | 2 +- .../campanha/actions/municipality.ts | 5 +- .../campanha/actions/municipalityUpdate.ts | 7 +- .../(campaign)/campanha/actions/password.ts | 40 +- .../(campaign)/campanha/actions/supporter.ts | 2 - .../campanha/actions/supporterImport.ts | 2 +- .../(campaign)/campanha/actions/votePledge.ts | 5 +- .../campanha/convite/[token]/page.tsx | 6 +- src/app/(frontend)/[type]/[category]/page.tsx | 2 +- src/app/(frontend)/[type]/page.tsx | 2 +- .../(frontend)/abaixo-assinado/[id]/page.tsx | 16 +- .../actions/submitPetitionSignature.ts | 2 +- src/app/(frontend)/api/revalidate/route.ts | 5 +- src/app/(frontend)/layout.tsx | 10 +- src/app/(payload)/layout.tsx | 2 +- src/collections/ActionPlan.ts | 8 +- src/collections/CampaignDemand.ts | 2 +- src/collections/CampaignUser.ts | 2 +- src/collections/Contact.ts | 11 +- src/collections/Leadership.ts | 2 +- src/collections/Municipality.ts | 17 +- src/collections/Organization.ts | 2 +- src/collections/Post.ts | 2 +- src/collections/StateDeputy.ts | 2 +- src/collections/Tag.ts | 2 +- src/collections/VotePledge.ts | 27 +- src/components/PetitionForm.tsx | 27 +- src/components/PetitionSuccessDialog.tsx | 4 +- src/components/PostCard.tsx | 2 +- src/components/SignatureCounter.tsx | 4 +- src/components/ThemeProvider.tsx | 2 +- src/components/WhatsappForm.tsx | 4 +- .../campaign/actionPlan/ActionPlanCard.tsx | 4 +- .../actionPlan/ActionPlanDemandFields.tsx | 4 +- .../campaign/actionPlan/ActionPlanFilters.tsx | 6 +- .../campaign/actionPlan/ActionPlanForm.tsx | 16 +- .../actionPlan/ActionPlanLifecycleDialog.tsx | 2 +- .../actionPlan/ActionPlanResultForm.tsx | 6 +- .../actionPlan/ActionPlanTaskChecklist.tsx | 2 +- .../actionPlan/ActionPlanTaskFields.tsx | 12 +- .../actionPlan/ActionPlanUpdateFeed.tsx | 2 +- .../actionPlan/ActionPlanUpdateForm.tsx | 2 +- .../advisor/AdvisorDebouncedTextCell.tsx | 5 +- .../advisor/AdvisorMunicipalityCell.tsx | 12 +- .../advisor/AdvisorPasswordResetButton.tsx | 4 +- .../campaign/advisor/AdvisorsTable.tsx | 26 +- .../campaign/auth/CampaignProfileSettings.tsx | 2 +- .../campaign/dashboard/CampaignDashboard.tsx | 7 +- .../campaign/dashboard/RecentVisitTracker.tsx | 12 +- .../campaign/invite/CampaignInviteForm.tsx | 12 +- .../campaign/leadership/LeaderContactForm.tsx | 2 +- .../leadership/LeaderContactsPanel.tsx | 4 +- .../campaign/leadership/LeadershipForm.tsx | 5 +- .../leadership/LeadershipInternalForm.tsx | 5 +- src/components/campaign/map/BahiaMap.tsx | 4 +- .../campaign/map/MapFeatureReadout.tsx | 2 +- .../campaign/map/MunicipalityMapPanel.tsx | 10 +- .../map/MunicipalityMapPanelDynamic.tsx | 5 +- .../municipality/MunicipalityBaselineCard.tsx | 4 +- .../MunicipalityCandidateComparisonTable.tsx | 6 +- .../municipality/MunicipalityDossier.tsx | 2 +- .../MunicipalityEstimateScenarioContext.tsx | 12 +- .../MunicipalityGoalAccountCard.tsx | 25 +- .../municipality/MunicipalityHeaderFilter.tsx | 2 +- .../municipality/MunicipalityHoverTooltip.tsx | 2 +- .../MunicipalityLeadershipsPanel.tsx | 2 +- .../municipality/MunicipalityList.tsx | 9 +- .../MunicipalityListAdvisorsControl.tsx | 7 +- .../MunicipalityListGoalCoverageCell.tsx | 10 +- .../municipality/MunicipalityListOverview.tsx | 12 +- .../MunicipalityListTrendControl.tsx | 4 +- .../municipality/MunicipalityPledgesPanel.tsx | 4 +- .../municipality/MunicipalitySortableHead.tsx | 2 +- .../municipality/MunicipalityStrategyForm.tsx | 5 +- .../municipality/MunicipalityUpdateFeed.tsx | 8 +- .../municipality/MunicipalityUpdateForm.tsx | 2 +- .../MunicipalityZoneNeighborhoodsCard.tsx | 14 +- .../municipality/TerritoryOverviewTable.tsx | 16 +- .../organization/OrganizationForm.tsx | 5 +- .../campaign/shared/AsyncSearchCombobox.tsx | 5 +- .../campaign/shared/CampaignSearchInput.tsx | 15 +- .../campaign/shared/CampaignUserAvatar.tsx | 2 +- .../campaign/shared/ConsentText.tsx | 3 +- .../campaign/shared/ContactCombobox.tsx | 4 +- .../campaign/shared/StrictCombobox.tsx | 6 +- .../campaign/shell/CampaignSidebar.tsx | 13 +- .../campaign/shell/InstallPwaToast.tsx | 10 +- src/components/campaign/shell/nav.ts | 5 +- .../supporter/RemoveSupporterDataButton.tsx | 8 +- .../campaign/supporter/SupporterFilters.tsx | 4 +- .../campaign/supporter/SupporterForm.tsx | 20 +- .../campaign/supporter/SupporterList.tsx | 4 +- .../campaign/supporter/SupporterShareKit.tsx | 2 +- .../supporter/VoteIntentionControl.tsx | 7 +- .../votePledge/PledgeEstimateForm.tsx | 2 +- src/components/ui/Alert.tsx | 4 +- src/components/ui/AlertDialog.tsx | 8 +- src/components/ui/Avatar.tsx | 4 +- src/components/ui/Badge.tsx | 2 +- src/components/ui/Checkbox.tsx | 2 +- src/components/ui/Command.tsx | 18 +- src/components/ui/Drawer.tsx | 12 +- src/components/ui/Empty.tsx | 2 +- src/components/ui/Popover.tsx | 4 +- src/components/ui/Progress.tsx | 2 +- src/components/ui/Sheet.tsx | 15 +- src/components/ui/Sidebar.tsx | 17 +- src/components/ui/Table.tsx | 2 +- src/components/ui/Toaster.tsx | 8 +- src/components/ui/Toggle.tsx | 2 +- src/components/ui/ToggleGroup.tsx | 4 +- src/components/ui/breadcrumb.tsx | 54 +- src/components/ui/button.tsx | 2 +- src/components/ui/card.tsx | 53 +- src/components/ui/combobox.tsx | 70 +- src/components/ui/dialog.tsx | 22 +- src/components/ui/field.tsx | 10 +- src/components/ui/input-group.tsx | 98 +-- src/components/ui/label.tsx | 2 +- src/components/ui/native-select.tsx | 2 +- src/components/ui/separator.tsx | 14 +- src/components/ui/skeleton.tsx | 6 +- src/components/ui/tooltip.tsx | 20 +- src/globals/CampaignGoals.ts | 3 +- src/hooks/use-mobile.ts | 6 +- src/lib/advisorMunicipalityPortfolio.ts | 19 +- src/lib/bahiaElectionAggregates.ts | 3 +- src/lib/bahiaGeometries.ts | 4 - src/lib/bahiaGeometriesTypes.ts | 5 +- src/lib/bahiaMapStyle.ts | 4 +- src/lib/bahiaMunicipalityGeometries.ts | 8 +- src/lib/bahiaTerritories.ts | 1 - src/lib/bahiaTerritoryGeometries.ts | 12 +- src/lib/campaignIntelligenceConcepts.ts | 12 +- src/lib/campoParties.ts | 4 +- src/lib/contactSearchQuery.ts | 4 +- src/lib/electionPartySpectrum.ts | 2 +- src/lib/electionResults.ts | 13 +- src/lib/electionResultsBuild.ts | 4 +- src/lib/electionResultsParse.ts | 15 +- src/lib/facebookPixel.ts | 6 +- src/lib/municipalityVoteRank.ts | 3 +- src/lib/schemas/campaignPassword.ts | 2 +- src/lib/schemas/comment.ts | 1 - src/lib/schemas/contact.ts | 1 - src/lib/schemas/municipality.ts | 4 +- src/lib/schemas/petition-form.ts | 2 +- src/lib/schemas/supporter.ts | 1 - src/lib/schemas/whatsapp-form.ts | 2 +- src/lib/utils.ts | 4 +- src/lib/voteEstimate.ts | 1 - src/utilities/actionPlanDetailPageData.ts | 2 +- src/utilities/actionPlanFormData.ts | 20 +- src/utilities/actionPlanLeadershipOptions.ts | 5 +- src/utilities/actionPlanPageData.ts | 10 +- src/utilities/actionPlanViewModels.ts | 2 +- src/utilities/advisorData.ts | 2 +- src/utilities/campaignAccess.ts | 12 +- src/utilities/campaignDashboardData.ts | 2 +- src/utilities/campaignDemandData.ts | 5 +- src/utilities/campaignInviteOrigin.ts | 27 +- src/utilities/campaignInvitePageData.ts | 2 +- src/utilities/campaignInviteRepository.ts | 2 +- src/utilities/campaignPwaClient.ts | 9 +- src/utilities/campaignTime.ts | 1 - src/utilities/detailTabUi.ts | 9 +- src/utilities/drizzleBulk.ts | 6 +- src/utilities/electionResultsImport.ts | 10 +- src/utilities/goalCoverage.ts | 15 +- src/utilities/leaderContactsPageData.ts | 3 +- src/utilities/leadershipData.ts | 4 +- src/utilities/loadTerritoryOverview.ts | 10 +- src/utilities/municipalityDossierData.ts | 5 +- .../municipalityElectionGeography.ts | 10 +- src/utilities/municipalityGoalAccount.ts | 27 +- src/utilities/municipalityListFilters.ts | 18 +- src/utilities/municipalityListUrl.ts | 2 +- src/utilities/municipalityMapContract.ts | 2 +- src/utilities/municipalityMapData.ts | 39 +- src/utilities/municipalityMapNavigation.ts | 4 +- src/utilities/municipalityPageData.ts | 9 +- src/utilities/municipalityPotential.ts | 12 +- src/utilities/municipalityViewModels.ts | 25 +- src/utilities/organizationData.ts | 5 +- src/utilities/postgresTransactionLocks.ts | 4 +- src/utilities/revalidateRequest.ts | 3 +- src/utilities/stateDeputyData.ts | 4 +- src/utilities/supporterImportBulk.ts | 4 +- src/utilities/supporterImportToken.ts | 1 - src/utilities/supporterListSqlFilters.ts | 6 +- src/utilities/supporterPageData.ts | 4 +- src/utilities/supporterViewModels.ts | 6 +- src/utilities/voteEstimateScenarioFields.ts | 2 +- src/utilities/votePledgeViews.ts | 2 +- tests/e2e/campaignConcepts.e2e.spec.ts | 4 +- tests/e2e/fixtures/e2eTest.ts | 2 +- tests/helpers/tseFixtures.ts | 12 +- tests/int/api.int.spec.ts | 4 +- tests/int/bahiaGeometries.int.spec.ts | 5 +- tests/int/bahiaMunicipalityCodes.int.spec.ts | 6 +- .../bahiaMunicipalityDemographics.int.spec.ts | 2 +- tests/int/bahiaTerritories.int.spec.ts | 15 +- tests/int/bahiaTseCityCodes.int.spec.ts | 13 +- tests/int/bahiaTseZones.int.spec.ts | 8 +- tests/int/campaignActionPlan.int.spec.ts | 2 +- .../int/campaignAdvisorManagement.int.spec.ts | 6 +- tests/int/campaignAuth.int.spec.ts | 6 +- tests/int/campaignEntityListData.int.spec.ts | 4 +- tests/int/campaignFixtures.int.spec.ts | 16 +- tests/int/campaignGoalsAccess.int.spec.ts | 31 +- tests/int/campaignInvite.int.spec.ts | 2 +- tests/int/campaignInviteUi.int.spec.ts | 6 +- tests/int/campaignLeadership.int.spec.ts | 3 +- tests/int/electionResultsImport.int.spec.ts | 23 +- tests/int/formData.int.spec.ts | 2 +- tests/int/municipalityMapData.int.spec.ts | 2 +- tests/int/municipalityPageData.int.spec.ts | 62 +- tests/int/onda0Provision.int.spec.ts | 34 +- tests/int/petition-page-layout.int.spec.ts | 7 +- .../int/postgresTransactionLocks.int.spec.ts | 4 +- tests/int/relationship.int.spec.ts | 4 +- tests/int/schemaPrimitives.int.spec.ts | 2 +- tests/int/supporterListFilters.int.spec.ts | 5 +- tests/int/testDatabaseLease.int.spec.ts | 23 +- tests/unit/actionPlanUi.unit.spec.ts | 4 +- .../advisorMunicipalityPortfolio.unit.spec.ts | 2 +- .../unit/bahiaElectionAggregates.unit.spec.ts | 15 +- tests/unit/bahiaMapStyle.unit.spec.ts | 5 +- tests/unit/campaignComponents.unit.spec.ts | 6 +- .../campaignCompositionCleanup.unit.spec.ts | 1 - tests/unit/campaignConsent.unit.spec.ts | 12 +- .../campaignIntelligenceConcepts.unit.spec.ts | 7 +- .../campaignInviteArchitecture.unit.spec.ts | 4 +- .../campaignInviteInteractions.unit.spec.ts | 6 +- tests/unit/campaignInviteOrigin.unit.spec.ts | 4 +- .../unit/campaignMigrationSafety.unit.spec.ts | 4 +- .../unit/choroplethPercentScale.unit.spec.ts | 14 +- tests/unit/contactCityMigration.unit.spec.ts | 5 +- tests/unit/electionResults.unit.spec.ts | 36 +- tests/unit/municipalityList.unit.spec.ts | 7 +- tests/unit/municipalityPotential.unit.spec.ts | 84 +- .../municipalityStaffVoteTotal.unit.spec.ts | 6 +- tests/unit/projectionSheetParse.unit.spec.ts | 12 +- tests/unit/recentVisits.unit.spec.ts | 4 +- tests/unit/territoryOverview.unit.spec.ts | 56 +- tests/unit/voteEstimate.unit.spec.ts | 6 +- tsconfig.json | 18 +- 313 files changed, 2443 insertions(+), 1486 deletions(-) create mode 100644 .prettierignore diff --git a/.impeccable/config.json b/.impeccable/config.json index ffa72c0d..e048c55c 100644 --- a/.impeccable/config.json +++ b/.impeccable/config.json @@ -6,27 +6,21 @@ { "rule": "design-system-color", "value": "#111827", - "files": [ - "src/collections/CampaignUser.ts" - ], + "files": ["src/collections/CampaignUser.ts"], "createdAt": "2026-07-21T01:54:11.928Z", "reason": "Pre-existing inline hex in the password-reset EMAIL HTML template; transactional email cannot use CSS tokens" }, { "rule": "broken-image", "value": "*", - "files": [ - "src/components/MetaPixel.tsx" - ], + "files": ["src/components/MetaPixel.tsx"], "createdAt": "2026-07-23T22:40:00.000Z", "reason": "Meta's documented noscript tracking pixel: a hidden 1x1 whose src is a templated facebook.com/tr URL — not a rendered image" }, { "rule": "design-system-color", "value": "#c86112", - "files": [ - "src/app/(frontend)/styles.css" - ], + "files": ["src/app/(frontend)/styles.css"], "createdAt": "2026-07-25T02:48:38.641Z", "reason": "Pre-existing public-site body brand gradient (live in production, predates the hook); flagged while an unrelated print block was added" } diff --git a/.impeccable/critique/2026-07-19T10-15-47Z__src-app-campaign-campanha.md b/.impeccable/critique/2026-07-19T10-15-47Z__src-app-campaign-campanha.md index b1c3d937..ea6b97ca 100644 --- a/.impeccable/critique/2026-07-19T10-15-47Z__src-app-campaign-campanha.md +++ b/.impeccable/critique/2026-07-19T10-15-47Z__src-app-campaign-campanha.md @@ -8,25 +8,26 @@ p2_count: 2 timestamp: 2026-07-19T10-15-47Z slug: src-app-campaign-campanha --- + Method: dual-agent (A: 89c6189f-1341-4e15-8d45-e1aa7c6f5167 · B: 3e87667e-e21e-4a29-85b4-db65afcc49c5) # Critique: /campanha ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Pending/aria-live on filters; no freshness/"as of" on KPI aggregates | -| 2 | Match System / Real World | 4 | Domain language precise (Núcleo vs Zona TSE disambiguated) | -| 3 | User Control and Freedom | 3 | Clear filters/back; limited undo for irreversible paths | -| 4 | Consistency and Standards | 3 | Strong component discipline; role dashboards diverge in max-width/chrome | -| 5 | Error Prevention | 3 | Constrained selects + inline ZE validation | -| 6 | Recognition Rather Than Recall | 4 | Filter state in URL; labels on actions | -| 7 | Flexibility and Efficiency | 2 | No saved views; queues are one-by-one, no bulk | -| 8 | Aesthetic and Minimalist Design | 2 | geral dashboard = even wall of same-weight Cards | -| 9 | Error Recovery | 3 | Forgot-password names fallback; leadership empty does not | -| 10 | Help and Documentation | 1 | Almost no glossary/tooltips for ZE / engajado / estimativa | -| **Total** | | **28/40** | **Good** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------------------------------------------------ | +| 1 | Visibility of System Status | 3 | Pending/aria-live on filters; no freshness/"as of" on KPI aggregates | +| 2 | Match System / Real World | 4 | Domain language precise (Núcleo vs Zona TSE disambiguated) | +| 3 | User Control and Freedom | 3 | Clear filters/back; limited undo for irreversible paths | +| 4 | Consistency and Standards | 3 | Strong component discipline; role dashboards diverge in max-width/chrome | +| 5 | Error Prevention | 3 | Constrained selects + inline ZE validation | +| 6 | Recognition Rather Than Recall | 4 | Filter state in URL; labels on actions | +| 7 | Flexibility and Efficiency | 2 | No saved views; queues are one-by-one, no bulk | +| 8 | Aesthetic and Minimalist Design | 2 | geral dashboard = even wall of same-weight Cards | +| 9 | Error Recovery | 3 | Forgot-password names fallback; leadership empty does not | +| 10 | Help and Documentation | 1 | Almost no glossary/tooltips for ZE / engajado / estimativa | +| **Total** | | **28/40** | **Good** | ## Anti-Patterns Verdict @@ -49,36 +50,42 @@ A serious field desk with strong a11y/touch discipline and honest empty-state co ## Priority Issues ### [P1] Filter panel decision overload + **What:** `NucleusFilters` shows 6 fields at once on desktop (TI, município, ZE, cobertura, estimativa, prioridade) plus search. **Why it matters:** Primary triage tool under time pressure; violates clarity-under-pressure and >4 options rule. **Fix:** Keep territory + search primary; put coverage/estimate/priority behind "Mais filtros" at all breakpoints. **Suggested command:** `/impeccable distill` / `/impeccable layout` ### [P1] Hero-metric Card grids contradict own anti-references + **What:** General dashboard, NucleusListOverview, SupporterListOverview all use Card → description → big number grids. **Why it matters:** PRODUCT/DESIGN ban identical card grids / hero-metric templates; this is the strongest "template AI" tell. **Fix:** Compact secondary metrics into a stat row; reserve Card + priority lift for 1–2 decisive numbers; rank action queues. **Suggested command:** `/impeccable quieter` / `/impeccable layout` ### [P1] Side-tab accent on baseline row (detector) + **What:** `border-l-4 border-primary` on highlighted candidate in NucleusElectoralBaseline. **Why it matters:** Absolute ban (side-stripe >1px); deterministic AI-tell. **Fix:** Full border, background tint only, or leading mark — no thick left stripe. **Suggested command:** `/impeccable quieter` ### [P2] Liderança empty state is a dead end + **What:** "Nenhum núcleo" / "vínculo engajado" + warning icon, no CTA. **Why it matters:** Peer persona stranded; jargon without next step. **Fix:** Mirror forgot-password — name who to contact (coordenador / WhatsApp). **Suggested command:** `/impeccable clarify` / `/impeccable onboard` ### [P2] No freshness signal on aggregates + **What:** KPIs/overviews never show when data was computed. **Why it matters:** Field decisions need trust in recency. **Fix:** Relative "atualizado há…" near overview headers. **Suggested command:** `/impeccable harden` ### [P3] Split brand chrome (mobile red bar vs desktop stone rail) + **What:** Mobile top bar is solid Mandate Red; desktop sidebar is quiet rail. **Why it matters:** Breaks Signal Red Rule for Casey/Lia switching devices unless intentional "field mode." **Fix:** Document as field-mode signal or quiet the mobile chrome. diff --git a/.impeccable/critique/2026-07-19T11-03-23Z__src-app-campaign-campanha.md b/.impeccable/critique/2026-07-19T11-03-23Z__src-app-campaign-campanha.md index 4aca3562..e2ae0c2f 100644 --- a/.impeccable/critique/2026-07-19T11-03-23Z__src-app-campaign-campanha.md +++ b/.impeccable/critique/2026-07-19T11-03-23Z__src-app-campaign-campanha.md @@ -8,25 +8,26 @@ p3_count: 2 timestamp: 2026-07-19T11-03-23Z slug: src-app-campaign-campanha --- + Method: dual-agent (A: 43c7ddf0-03c4-40b6-86e4-167ed820fbc3 · B: c685e95f-8884-4bd6-9174-53d1a4613689) # Critique: /campanha ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 4 | Freshness on KPI surfaces; filter aria-busy/live region | -| 2 | Match System / Real World | 4 | Núcleo vs Zona TSE stated on list; domain language solid | -| 3 | User Control and Freedom | 4 | Differentiated AlertDialogs; clear filter clear/back | -| 4 | Consistency and Standards | 3 | List/dashboard on CampaignPageShell; detail/forms still ad-hoc max-w | -| 5 | Error Prevention | 3 | Constrained selects + ZE validation; otherwise unchanged | -| 6 | Recognition Rather Than Recall | 4 | URL filters; RecentlyVisited; freshness timestamps | -| 7 | Flexibility and Efficiency | 2 | No saved views; triage queues still one-by-one (CSV bulk only) | -| 8 | Aesthetic and Minimalist Design | 3 | Metric strips + one priority lift; MetricStrip still double-card surface | -| 9 | Error Recovery | 3 | Leadership empty has CTA; coordenador empty still prose-only | -| 10 | Help and Documentation | 2 | One Núcleo/ZE line; engajado / estimativa vocabulary still unexplained | -| **Total** | | **32/40** | **Good** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------------------------------------------------ | +| 1 | Visibility of System Status | 4 | Freshness on KPI surfaces; filter aria-busy/live region | +| 2 | Match System / Real World | 4 | Núcleo vs Zona TSE stated on list; domain language solid | +| 3 | User Control and Freedom | 4 | Differentiated AlertDialogs; clear filter clear/back | +| 4 | Consistency and Standards | 3 | List/dashboard on CampaignPageShell; detail/forms still ad-hoc max-w | +| 5 | Error Prevention | 3 | Constrained selects + ZE validation; otherwise unchanged | +| 6 | Recognition Rather Than Recall | 4 | URL filters; RecentlyVisited; freshness timestamps | +| 7 | Flexibility and Efficiency | 2 | No saved views; triage queues still one-by-one (CSV bulk only) | +| 8 | Aesthetic and Minimalist Design | 3 | Metric strips + one priority lift; MetricStrip still double-card surface | +| 9 | Error Recovery | 3 | Leadership empty has CTA; coordenador empty still prose-only | +| 10 | Help and Documentation | 2 | One Núcleo/ZE line; engajado / estimativa vocabulary still unexplained | +| **Total** | | **32/40** | **Good** | ## Anti-Patterns Verdict @@ -49,30 +50,35 @@ Prior P1s (filter wall, hero-metric card grids, side-tab) are closed. The surfac ## Priority Issues ### [P2] Domain jargon still opaque beyond one sentence + **What:** engajado, estimativa confirmada vs proposta, support statuses lack tooltips/help. **Why it matters:** Lia and first-timers translate under pressure; Help heuristic still weakest. **Fix:** Inline ? / first-visit hint on SupportStatusBadge and estimate labels. **Suggested command:** `/impeccable clarify` ### [P2] Dashboard queues cannot be triaged in bulk + **What:** Sem coordenador / stale updates / pending estimates are one-row links only. **Why it matters:** Alex under time pressure on a growing queue; CSV bulk exists for import but not daily triage. **Fix:** Multi-select + assign on the priority queue only. **Suggested command:** `/impeccable layout` ### [P2] Dashboard heading outline skips h2 + **What:** Detector + source: h1 "Visão geral" then QueueSection h3 before Indicadores h2. **Why it matters:** Screen-reader document outline; Sam persona. **Fix:** Promote Filas section title to h2, demote queue titles to h3; or insert sr-only/visible h2. **Suggested command:** `/impeccable audit` ### [P3] Coordenador empty state has no CTA + **What:** Prose-only empty vs leadership empty with Ver meu perfil. **Why it matters:** Last sibling of the old dead-end pattern. **Fix:** Mirror EmptyContent CTA pattern. **Suggested command:** `/impeccable onboard` ### [P3] MetricStrip double card surface + sidebar height transition + **What:** Overlay nested-cards on strip `bg-card` inside `bg-card`; Sidebar `transition-[width,height,padding]`. **Why it matters:** Mild visual nesting / layout jank risk — not task-blocking. **Fix:** Flatten inner metric cells to transparent; prefer transform/opacity on chrome. diff --git a/.impeccable/critique/2026-07-19T15-10-58Z__src-app-campaign-campanha-login.md b/.impeccable/critique/2026-07-19T15-10-58Z__src-app-campaign-campanha-login.md index 93022d5c..797ea4a7 100644 --- a/.impeccable/critique/2026-07-19T15-10-58Z__src-app-campaign-campanha-login.md +++ b/.impeccable/critique/2026-07-19T15-10-58Z__src-app-campaign-campanha-login.md @@ -6,25 +6,26 @@ p1_count: 2 timestamp: 2026-07-19T15-10-58Z slug: src-app-campaign-campanha-login --- + Method: dual-agent (A: cd378fde-c091-463a-8e89-ca6cecac8904 · B: 44f7668c-1143-41ec-a147-a2dc2f4d7f9c) # Critique: /campanha/login ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Pending state is clear (Spinner + “Entrando…”); auth failure is a single generic FieldError not tied to fields | -| 2 | Match System / Real World | 3 | “E-mail ou celular” matches dual auth, but leadership phone path vs email-only reset is invisible on this screen | -| 3 | User Control and Freedom | 3 | Forgot-password link exists; no show-password, no first-access/invite escape | -| 4 | Consistency and Standards | 3 | Auth shell duplicated across login/esqueci/redefinir; Entrar lacks `w-full` unlike sibling forms | -| 5 | Error Prevention | 2 | `inputMode="email"` on a dual identifier; validation authoritative only on server with generic messages | -| 6 | Recognition Rather Than Recall | 4 | Standard login IA; placeholder shows both formats | -| 7 | Flexibility and Efficiency | 2 | Single rigid credential path; no returning-user / PWA shortcut | -| 8 | Aesthetic and Minimalist Design | 4 | Restrained Field Desk card on muted; Signal Red budget respected | -| 9 | Error Recovery | 3 | Generic security error is correct but offers no next-step branch for phone-only / invite users | -| 10 | Help and Documentation | 1 | Zero first-timer guidance on login; forgot-password already explains phone→coordenador invite | -| **Total** | | **28/40** | **Good** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------- | +| 1 | Visibility of System Status | 3 | Pending state is clear (Spinner + “Entrando…”); auth failure is a single generic FieldError not tied to fields | +| 2 | Match System / Real World | 3 | “E-mail ou celular” matches dual auth, but leadership phone path vs email-only reset is invisible on this screen | +| 3 | User Control and Freedom | 3 | Forgot-password link exists; no show-password, no first-access/invite escape | +| 4 | Consistency and Standards | 3 | Auth shell duplicated across login/esqueci/redefinir; Entrar lacks `w-full` unlike sibling forms | +| 5 | Error Prevention | 2 | `inputMode="email"` on a dual identifier; validation authoritative only on server with generic messages | +| 6 | Recognition Rather Than Recall | 4 | Standard login IA; placeholder shows both formats | +| 7 | Flexibility and Efficiency | 2 | Single rigid credential path; no returning-user / PWA shortcut | +| 8 | Aesthetic and Minimalist Design | 4 | Restrained Field Desk card on muted; Signal Red budget respected | +| 9 | Error Recovery | 3 | Generic security error is correct but offers no next-step branch for phone-only / invite users | +| 10 | Help and Documentation | 1 | Zero first-timer guidance on login; forgot-password already explains phone→coordenador invite | +| **Total** | | **28/40** | **Good** | ## Anti-Patterns Verdict @@ -47,30 +48,35 @@ A clean, trustworthy ops gate that earns familiarity — then stalls for the peo ## Priority Issues ### [P1] Missing document heading landmark + - **What**: `CardTitle` is a `
`; page has no `

`. Brand row is plain `font-semibold` text. - **Why it matters**: Screen-reader users get no clear “where am I” on a critical entry screen; weak WCAG structure. - **Fix**: Promote “Acessar painel” (or a sr-only “Entrar — Campanha”) to a real `

`. - **Suggested command**: `/impeccable harden /campanha/login` ### [P1] Phone-first leadership mismatch + - **What**: Identifier uses `inputMode="email"` while leadership auth is phone-as-username; “Esqueceu a senha?” leads to email-only reset that already tells phone users to ask a coordinator — but login itself is silent. - **Why it matters**: Field leadership gets the wrong keyboard, fails auth, then hits an email form that abandons them. - **Fix**: Prefer `inputMode="tel"` or pattern-aware mode; add one line of login copy mirroring `ForgotPasswordForm` (“Liderança: celular cadastrado. Sem senha? Peça convite ao coordenador.”). - **Suggested command**: `/impeccable clarify /campanha/login` ### [P2] Monolithic auth error with no recovery branches + - **What**: `state.error` (“E-mail, celular ou senha inválidos.” / “Dados inválidos.”) sits in one `FieldError` above submit, not linked via `aria-describedby` / `aria-invalid`. - **Why it matters**: Under pressure users retype everything; no path to invite vs reset. - **Fix**: Keep message generic for security; add `aria-invalid` + recovery links (esqueci-senha / first-access invite guidance). - **Suggested command**: `/impeccable harden /campanha/login` ### [P2] No first-timer guidance on the login surface + - **What**: “Campanha” + Megaphone + “Acessar painel” assumes prior context; invite flow undiscoverable. - **Why it matters**: Jordan abandons; leadership thinks the product is broken when they need a WhatsApp invite. - **Fix**: One sentence under CardDescription: who this is for + what to do on first access. - **Suggested command**: `/impeccable onboard /campanha/login` or `/impeccable clarify /campanha/login` ### [P3] Submit width inconsistent with sibling auth forms + - **What**: Login `Button` is `min-h-11` without `w-full`; forgot/reset use `min-h-11 w-full`. - **Why it matters**: Thumb-target inconsistency on phone; visual drift across auth chain. - **Fix**: Add `w-full`; extract shared `CampaignAuthPageShell` (already on RS+ backlog). diff --git a/.impeccable/critique/2026-07-19T15-22-00Z__src-components-campaign-NucleusInsights.md b/.impeccable/critique/2026-07-19T15-22-00Z__src-components-campaign-NucleusInsights.md index 3f736f80..38e2033d 100644 --- a/.impeccable/critique/2026-07-19T15-22-00Z__src-components-campaign-NucleusInsights.md +++ b/.impeccable/critique/2026-07-19T15-22-00Z__src-components-campaign-NucleusInsights.md @@ -5,13 +5,16 @@ **Detector:** clean ## Strengths + - Reuses Alert stack and tokens `estimate-confirmed` / `pending` consistent with Gap and Tendência. - One-line veredito + support numbers match design-ref Baseline-Eleitoral-2022. - Overview aggregation mirrors existing gap/trend lines. ## Issues addressed in polish + - P1: Duplicated confirmed-alert class strings → extracted `confirmedInsightAlertClass`. ## Minor (deferred) + - Band label (reduto/consolidado/oportunidade) not shown in detail Alert title — product may want chip later. - Overview distribution counts may be zero-heavy on small filters — acceptable for v1. diff --git a/.impeccable/critique/2026-07-19T15-55-17Z__src-app-campaign-campanha-login.md b/.impeccable/critique/2026-07-19T15-55-17Z__src-app-campaign-campanha-login.md index c63cf729..de3b90f7 100644 --- a/.impeccable/critique/2026-07-19T15-55-17Z__src-app-campaign-campanha-login.md +++ b/.impeccable/critique/2026-07-19T15-55-17Z__src-app-campaign-campanha-login.md @@ -7,25 +7,26 @@ p2_count: 2 timestamp: 2026-07-19T15-55-17Z slug: src-app-campaign-campanha-login --- + Method: dual-agent (A: 103923c2-0d00-49d5-9902-eee91515fbc5 · B: c71afcf4-ae05-432e-900f-a93610cb29a4) # Critique: /campanha/login (post-polish) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 4 | Spinner + aria-live; FieldError alert + aria-invalid/describedby on both fields | -| 2 | Match System / Real World | 3 | Dual-path copy clear; description still leads with email before phone; forgot remains email-only | -| 3 | User Control and Freedom | 3 | Forgot link exists; no show-password; no staff/liderança switch | -| 4 | Consistency and Standards | 4 | CampaignAuthPageShell + shared heading class; w-full matches siblings | -| 5 | Error Prevention | 3 | Dynamic inputMode after typing; empty field still inputMode=text | -| 6 | Recognition Rather Than Recall | 4 | Phone-first placeholder; standard login IA | -| 7 | Flexibility and Efficiency | 2 | Single rigid credential path | -| 8 | Aesthetic and Minimalist Design | 4 | Restrained Field Desk; Signal Red budget OK; header slightly dense | -| 9 | Error Recovery | 3 | Generic error + leadership recovery hint; hint not in aria-describedby | -| 10 | Help and Documentation | 3 | First-access WhatsApp tip onboarded; phone recovery still partly error-gated | -| **Total** | | **32/40** | **Good** (+4 vs prior 28) | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------ | +| 1 | Visibility of System Status | 4 | Spinner + aria-live; FieldError alert + aria-invalid/describedby on both fields | +| 2 | Match System / Real World | 3 | Dual-path copy clear; description still leads with email before phone; forgot remains email-only | +| 3 | User Control and Freedom | 3 | Forgot link exists; no show-password; no staff/liderança switch | +| 4 | Consistency and Standards | 4 | CampaignAuthPageShell + shared heading class; w-full matches siblings | +| 5 | Error Prevention | 3 | Dynamic inputMode after typing; empty field still inputMode=text | +| 6 | Recognition Rather Than Recall | 4 | Phone-first placeholder; standard login IA | +| 7 | Flexibility and Efficiency | 2 | Single rigid credential path | +| 8 | Aesthetic and Minimalist Design | 4 | Restrained Field Desk; Signal Red budget OK; header slightly dense | +| 9 | Error Recovery | 3 | Generic error + leadership recovery hint; hint not in aria-describedby | +| 10 | Help and Documentation | 3 | First-access WhatsApp tip onboarded; phone recovery still partly error-gated | +| **Total** | | **32/40** | **Good** (+4 vs prior 28) | ## Anti-Patterns Verdict @@ -48,30 +49,35 @@ Prior P1/P2 gaps closed: real h1, mobilizing title, phone-first placeholder, fir ## Priority Issues ### [P2] Empty-field keyboard not phone-first + - **What:** identifierInputMode('') → 'text'; tel only after ≥2 digits. - **Why:** Leadership first tap still gets QWERTY. - **Fix:** Default inputMode=tel when empty; switch to email on @. - **Suggested command:** `/impeccable adapt /campanha/login` ### [P2] No show-password toggle + - **What:** password type only. - **Why:** One-handed field re-entry under glare. - **Fix:** Reveal toggle with aria-pressed, keep min-h-11. - **Suggested command:** `/impeccable harden /campanha/login` ### [P3] Recovery hint outside aria-describedby + - **What:** Only login-credentials-error is described; CAMPAIGN_LEADERSHIP_LOGIN_RECOVERY_HINT is a sibling p. - **Why:** SR may miss phone-only guidance. - **Fix:** Append hint id to aria-describedby. - **Suggested command:** `/impeccable harden /campanha/login` ### [P3] “Esqueceu a senha?” still email-first funnel + - **What:** Link unlabeled as email-only; phone users hit forgot then discover coordinator path. - **Why:** Reflex after one failed attempt. - **Fix:** Sub-label or always-visible CAMPAIGN_LEADERSHIP_PHONE_ACCESS_HINT near link. - **Suggested command:** `/impeccable clarify /campanha/login` ### [P3] Emotional register still flat vs “inspiring” + - **What:** Clear ops gate; no trust/solidarity line. - **Why:** Field Desk asks for socialist/clear/inspiring. - **Fix:** One grounded trust line without kitsch. diff --git a/.impeccable/critique/2026-07-19T21-47-04Z__src-app-campaign-campanha-login.md b/.impeccable/critique/2026-07-19T21-47-04Z__src-app-campaign-campanha-login.md index a9ecdf6e..c28c3b1d 100644 --- a/.impeccable/critique/2026-07-19T21-47-04Z__src-app-campaign-campanha-login.md +++ b/.impeccable/critique/2026-07-19T21-47-04Z__src-app-campaign-campanha-login.md @@ -6,25 +6,26 @@ p1_count: 2 timestamp: 2026-07-19T21-47-04Z slug: src-app-campaign-campanha-login --- + # Critique: /campanha/login Method: dual-agent (A: 8a8b1ae0-9a8a-45f7-907f-54afcdf62db8 · B: 3899acad-4f36-4817-91d6-4135d72d2e96) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Pending state clear; no other status chrome | -| 2 | Match System / Real World | 3 | PT-BR + e-mail/celular fit; megaphone mark generic | -| 3 | User Control and Freedom | 2 | Only “Esqueceu a senha?”; no exit to public site | -| 4 | Consistency and Standards | 2 | h1 left vs centered muted copy; leading clash | -| 5 | Error Prevention | 2 | required + inputMode help; dual identifier still easy to mistype | -| 6 | Recognition Rather Than Recall | 3 | Labels/placeholder clear | -| 7 | Flexibility and Efficiency | 3 | E-mail or phone + adaptive inputMode | -| 8 | Aesthetic and Minimalist Design | 2 | Header rhythm/alignment + always-on help prose | -| 9 | Error Recovery | 3 | FieldError + leadership recovery hint | -| 10 | Help and Documentation | 3 | Help exists; always-visible first-access competes | -| **Total** | | **26/40** | **Acceptable** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ---------------------------------------------------------------- | +| 1 | Visibility of System Status | 3 | Pending state clear; no other status chrome | +| 2 | Match System / Real World | 3 | PT-BR + e-mail/celular fit; megaphone mark generic | +| 3 | User Control and Freedom | 2 | Only “Esqueceu a senha?”; no exit to public site | +| 4 | Consistency and Standards | 2 | h1 left vs centered muted copy; leading clash | +| 5 | Error Prevention | 2 | required + inputMode help; dual identifier still easy to mistype | +| 6 | Recognition Rather Than Recall | 3 | Labels/placeholder clear | +| 7 | Flexibility and Efficiency | 3 | E-mail or phone + adaptive inputMode | +| 8 | Aesthetic and Minimalist Design | 2 | Header rhythm/alignment + always-on help prose | +| 9 | Error Recovery | 3 | FieldError + leadership recovery hint | +| 10 | Help and Documentation | 3 | Help exists; always-visible first-access competes | +| **Total** | | **26/40** | **Acceptable** | ## Anti-Patterns Verdict @@ -47,25 +48,30 @@ Competent Field Desk palette discipline, phone-first form craft — undermined b ## Priority Issues ### P1 — Title misaligned with subtitle + Campaign `[data-theme='campaign'] h1 { text-align: left }` overrides CardHeader `text-center`. Title glyph start ~28px left of centered subtitle. **Fix:** Scope left-align to app chrome, or `text-center` on auth heading. **Command:** `/impeccable typeset` or `/impeccable layout` ### P1 — Subtitle vertical rhythm inverted + CardHeader `gap-1` (4px) between title and description; orphan `

` inherits base `leading-7` (28px) while CardDescription stays ~20px. Title glued to howto; second muted block breathes like article body. **Fix:** One muted block with explicit leading-snug; bump title→desc gap; never bare `

` under campaign theme without leading override. **Command:** `/impeccable layout` + `/impeccable typeset` ### P2 — Split-screen + public publications: recommend No + Login is ops gate (noindex); posts are public register. Phone users get scroll soup. Motivation ≠ news feed on unlock. **Soft maybe:** one quiet purpose line under mark — not a publication panel. **Command:** `/impeccable shape` only if product insists on panel (internal voice, not posts) ### P2 — Generic Megaphone mark + Not Solla / Field Desk identity. **Command:** `/impeccable polish` (reuse campaign-logo) ### P3 — Always-on first-access paragraph + Progressive disclosure for returning users. **Command:** `/impeccable distill` or `/impeccable clarify` diff --git a/.impeccable/critique/2026-07-19T23-22-03Z__src-app-campaign-campanha-app-page-tsx.md b/.impeccable/critique/2026-07-19T23-22-03Z__src-app-campaign-campanha-app-page-tsx.md index 043498e7..ca1446ee 100644 --- a/.impeccable/critique/2026-07-19T23-22-03Z__src-app-campaign-campanha-app-page-tsx.md +++ b/.impeccable/critique/2026-07-19T23-22-03Z__src-app-campaign-campanha-app-page-tsx.md @@ -7,23 +7,24 @@ p2_count: 3 timestamp: 2026-07-19T23-22-03Z slug: src-app-campaign-campanha-app-page-tsx --- + Method: dual-agent (A: design review · B: detector + Playwright screenshots) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | KPIs têm `CampaignDataFreshness`; filas não mostram idade dos itens | -| 2 | Match System / Real World | 3 | Linguagem de núcleo/fila encaixa; **"Lideranças" sem contexto** | -| 3 | User Control and Freedom | 3 | **"Limpar"** em Visitados é ghost sem ícone nem `aria-label` claro | -| 4 | Consistency and Standards | 2 | **Lista de núcleos usa `lg:grid-cols-2`; dashboard geral não** | -| 5 | Error Prevention | 3 | Dashboard read-only; sem riscos destrutivos aqui | -| 6 | Recognition Rather Than Recall | 3 | Visitados ajuda recall; **cores do mapa não são decodificáveis** | -| 7 | Flexibility and Efficiency | 2 | **Coluna única + `max-w-screen-2xl`** desperdiça desktop largo | -| 8 | Aesthetic and Minimalist Design | 2 | **Três subseções vazias nas filas**; cards com muito respiro interno | -| 9 | Error Recovery | 3 | N/A nesta tela | -| 10 | Help and Documentation | 2 | Status de apoio e escala do mapa sem glossário/legenda visual | -| **Total** | | **27/40** | **Acceptable — melhorias significativas antes de sentir "pronto"** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | -------------------------------------------------------------------- | +| 1 | Visibility of System Status | 3 | KPIs têm `CampaignDataFreshness`; filas não mostram idade dos itens | +| 2 | Match System / Real World | 3 | Linguagem de núcleo/fila encaixa; **"Lideranças" sem contexto** | +| 3 | User Control and Freedom | 3 | **"Limpar"** em Visitados é ghost sem ícone nem `aria-label` claro | +| 4 | Consistency and Standards | 2 | **Lista de núcleos usa `lg:grid-cols-2`; dashboard geral não** | +| 5 | Error Prevention | 3 | Dashboard read-only; sem riscos destrutivos aqui | +| 6 | Recognition Rather Than Recall | 3 | Visitados ajuda recall; **cores do mapa não são decodificáveis** | +| 7 | Flexibility and Efficiency | 2 | **Coluna única + `max-w-screen-2xl`** desperdiça desktop largo | +| 8 | Aesthetic and Minimalist Design | 2 | **Três subseções vazias nas filas**; cards com muito respiro interno | +| 9 | Error Recovery | 3 | N/A nesta tela | +| 10 | Help and Documentation | 2 | Status de apoio e escala do mapa sem glossário/legenda visual | +| **Total** | | **27/40** | **Acceptable — melhorias significativas antes de sentir "pronto"** | ## Anti-Patterns Verdict @@ -46,26 +47,31 @@ O dashboard tem ingredientes certos para o Field Desk (fila prioritária, faixa ## Priority Issues ### [P1] Desktop não aproveita largura; ultra-wide deixa vazio à direita + - **Why:** `CampaignPageShell` usa `flex-col gap-8` sem breakpoint grid; `mr-auto max-w-screen-2xl` prende conteúdo à esquerda em monitores largos. - **Fix:** Reflow `GeneralDashboard` em `lg:grid` (ex.: filas + KPIs à esquerda, mapa sticky + eventos à direita), espelhando `NucleusListOverview`. Considerar `mx-auto` ou remover cap em `xl:` quando houver grid de 2 colunas. - **Suggested command:** `/impeccable layout` ### [P1] Filas de ação vazias ocupam espaço sem valor + - **Why:** `QueueSection` sempre renderiza; com tudo zerado o usuário lê três mensagens + três badges "0". - **Fix:** Se todas as filas vazias, um único estado positivo ("Nada pendente — filas em dia"); renderizar subseções só quando `items.length > 0`. - **Suggested command:** `/impeccable quieter` ### [P2] Escala do mapa sem cores + - **Why:** `choroplethFillColor` implementa gradiente rose→vermelho; UI só mostra números em `ChoroplethMapControls`. - **Fix:** Barra de legenda com swatches usando o mesmo ramp; rótulos 0 e máximo. - **Suggested command:** `/impeccable clarify` ### [P2] "Lideranças" sem framing + - **Why:** Linha solta com badges de status + contagem; não fica claro que são contatos de liderança em todos os núcleos por status de apoio. - **Fix:** Card com título + descrição; link para lista filtrada; tooltip/glossário nos status. - **Suggested command:** `/impeccable clarify` ### [P2] Visitados recentemente — bullets e botão Limpar + - **Why:** `

    ` sem `list-none` (padrão do projeto em outras listas); "Limpar" ghost compete com o título sem affordance de ação destrutiva/secundária. - **Fix:** `list-none m-0 p-0`; botão outline ou ícone + `aria-label="Limpar histórico de visitas"`. - **Suggested command:** `/impeccable polish` diff --git a/.impeccable/critique/2026-07-20T00-01-43Z__src-app-campaign-campanha.md b/.impeccable/critique/2026-07-20T00-01-43Z__src-app-campaign-campanha.md index 6b47af2e..78bf3eb2 100644 --- a/.impeccable/critique/2026-07-20T00-01-43Z__src-app-campaign-campanha.md +++ b/.impeccable/critique/2026-07-20T00-01-43Z__src-app-campaign-campanha.md @@ -8,23 +8,24 @@ p3_count: 2 timestamp: 2026-07-20T00-01-43Z slug: src-app-campaign-campanha --- + Method: dual-agent (A: 22ac0b39-b0f9-4111-b890-4c4125d1218b · B: bacccead-08e1-4dce-9b0d-4fe7984b1853) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | KPIs têm `CampaignDataFreshness`; filas não mostram idade do item na linha | -| 2 | Match System / Real World | 4 | Linguagem de núcleo/fila encaixa; `LeadershipStatusCard` contextualiza status | -| 3 | User Control and Freedom | 4 | `RecentlyVisitedCard` com `aria-label` no limpar; liderança tem CTA no empty | -| 4 | Consistency and Standards | 3 | Dashboard `geral` alinhado ao grid coordenador; empty de eventos ainda é `

    ` solto | -| 5 | Error Prevention | 3 | Dashboard read-only; sem riscos destrutivos nesta tela | -| 6 | Recognition Rather Than Recall | 4 | `ChoroplethLegend` + visitados recentes; glossário ainda só em `title` | -| 7 | Flexibility and Efficiency | 2 | Fila prioritária ainda é um clique por núcleo; sem triagem em lote | -| 8 | Aesthetic and Minimalist Design | 3 | Top row 3:2:1 funciona; dois `CampaignMetricStrip` + mapa sem header | -| 9 | Error Recovery | 3 | Empty de liderança com CTA; coordenador sem próximo passo | -| 10 | Help and Documentation | 2 | Status de apoio e estimativa sem affordance touch (`?`/Popover) | -| **Total** | | **32/40** | **Good — fundação sólida; flexibilidade e ajuda ainda abaixo do ideal** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------ | +| 1 | Visibility of System Status | 3 | KPIs têm `CampaignDataFreshness`; filas não mostram idade do item na linha | +| 2 | Match System / Real World | 4 | Linguagem de núcleo/fila encaixa; `LeadershipStatusCard` contextualiza status | +| 3 | User Control and Freedom | 4 | `RecentlyVisitedCard` com `aria-label` no limpar; liderança tem CTA no empty | +| 4 | Consistency and Standards | 3 | Dashboard `geral` alinhado ao grid coordenador; empty de eventos ainda é `

    ` solto | +| 5 | Error Prevention | 3 | Dashboard read-only; sem riscos destrutivos nesta tela | +| 6 | Recognition Rather Than Recall | 4 | `ChoroplethLegend` + visitados recentes; glossário ainda só em `title` | +| 7 | Flexibility and Efficiency | 2 | Fila prioritária ainda é um clique por núcleo; sem triagem em lote | +| 8 | Aesthetic and Minimalist Design | 3 | Top row 3:2:1 funciona; dois `CampaignMetricStrip` + mapa sem header | +| 9 | Error Recovery | 3 | Empty de liderança com CTA; coordenador sem próximo passo | +| 10 | Help and Documentation | 2 | Status de apoio e estimativa sem affordance touch (`?`/Popover) | +| **Total** | | **32/40** | **Good — fundação sólida; flexibilidade e ajuda ainda abaixo do ideal** | ## Anti-Patterns Verdict @@ -49,26 +50,31 @@ O dashboard `geral` saiu da pilha vertical ingênua para um desk operacional: fi ## Priority Issues ### [P2] Triagem em lote ainda ausente na fila prioritária + - **Why:** Cada item “Sem coordenador” é um link isolado; 15 núcleos = 15 idas e voltas. - **Fix:** Multi-select só em `withoutCoordinator` + barra “Atribuir coordenador (N)” reutilizando shell de designação existente. - **Suggested command:** `/impeccable layout` ### [P2] Jargão de domínio opaco no touch + - **Why:** `SupportStatusBadge`, estimativa confirmada vs proposta, métricas do mapa dependem de `title` (hover) — inútil no celular. - **Fix:** `CampaignGlossaryHint` com gatilho `?` (`min-h-11`) em badges e labels de KPI. - **Suggested command:** `/impeccable clarify` ### [P2] Outline de headings pula `h2` em Filas + - **Why:** `h1` → `CardTitle` Filas (não heading) → `h3` nas subfilas → depois `h2` Indicadores — ordem invertida para leitores de tela. - **Fix:** Promover “Filas de ação” a `h2` semântico (`id="action-queues"` já existe). - **Suggested command:** `/impeccable audit` ### [P3] Empty do coordenador sem CTA + - **Why:** Texto explica o bloqueio mas não oferece ação (liderança já tem “Ver meu perfil”). - **Fix:** Espelhar `LeadershipDashboard` empty com `EmptyContent` + CTA. - **Suggested command:** `/impeccable onboard` ### [P3] Recentes compactos escondem timestamp + - **Why:** Modo `compact` põe idade só em `sr-only`; Alex não distingue visita recente de antiga. - **Fix:** Mostrar `text-xs text-muted-foreground` truncado no modo compacto. - **Suggested command:** `/impeccable polish` diff --git a/.impeccable/critique/2026-07-23T21-29-20Z__src-components-campaign-plazalistoverview-tsx.md b/.impeccable/critique/2026-07-23T21-29-20Z__src-components-campaign-plazalistoverview-tsx.md index bc3f78d1..5811cd59 100644 --- a/.impeccable/critique/2026-07-23T21-29-20Z__src-components-campaign-plazalistoverview-tsx.md +++ b/.impeccable/critique/2026-07-23T21-29-20Z__src-components-campaign-plazalistoverview-tsx.md @@ -8,23 +8,24 @@ p3_count: 1 timestamp: 2026-07-23T21-29-20Z slug: src-components-campaign-plazalistoverview-tsx --- + Method: dual-agent (A: c0c0346d-83f2-4aa7-b728-c883635b063e · B: 104c4dcf-5d13-4351-b6fe-db8d2d632179) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 2 | Número muda com filtros, mas nunca diz que é o cenário Média; mapa pode divergir | -| 2 | Match System / Real World | 2 | “Faixa” e “no conjunto” não nomeiam pessimista–otimista nem o recorte filtrado | -| 3 | User Control and Freedom | 3 | Agregado read-only correto; filtros controlam o conjunto; sem seletor de cenário no strip | -| 4 | Consistency and Standards | 2 | Dashboard: “Votos estimados” + célula própria p/ sem estimativa; lista empacota no meio | -| 5 | Error Prevention | 2 | `—` quando total=0; faixa some se falta um extremo (pós-backfill só central) | -| 6 | Recognition Rather Than Recall | 1 | Exige lembrar A10: default=média; Faixa=P–O; conjunto=filtrado | -| 7 | Flexibility and Efficiency | 2 | Denso p/ Alex; cenário só no mapa / Popover da linha | -| 8 | Aesthetic and Minimalist Design | 3 | Field Desk limpo; ênfase justificada; leve hero-KPI | -| 9 | Error Recovery | 2 | Qualidade do total (`sem estimativa`) enterrada em Declarações | -| 10 | Help and Documentation | 1 | Sem microcopy “Média” / “Pessimista–Otimista” | -| **Total** | | **20/40** | **Acceptable — craft visual ok; informação do cenário fraca** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ----------------------------------------------------------------------------------------- | +| 1 | Visibility of System Status | 2 | Número muda com filtros, mas nunca diz que é o cenário Média; mapa pode divergir | +| 2 | Match System / Real World | 2 | “Faixa” e “no conjunto” não nomeiam pessimista–otimista nem o recorte filtrado | +| 3 | User Control and Freedom | 3 | Agregado read-only correto; filtros controlam o conjunto; sem seletor de cenário no strip | +| 4 | Consistency and Standards | 2 | Dashboard: “Votos estimados” + célula própria p/ sem estimativa; lista empacota no meio | +| 5 | Error Prevention | 2 | `—` quando total=0; faixa some se falta um extremo (pós-backfill só central) | +| 6 | Recognition Rather Than Recall | 1 | Exige lembrar A10: default=média; Faixa=P–O; conjunto=filtrado | +| 7 | Flexibility and Efficiency | 2 | Denso p/ Alex; cenário só no mapa / Popover da linha | +| 8 | Aesthetic and Minimalist Design | 3 | Field Desk limpo; ênfase justificada; leve hero-KPI | +| 9 | Error Recovery | 2 | Qualidade do total (`sem estimativa`) enterrada em Declarações | +| 10 | Help and Documentation | 1 | Sem microcopy “Média” / “Pessimista–Otimista” | +| **Total** | | **20/40** | **Acceptable — craft visual ok; informação do cenário fraca** | ## Anti-Patterns Verdict @@ -36,7 +37,7 @@ Method: dual-agent (A: c0c0346d-83f2-4aa7-b728-c883635b063e · B: 104c4dcf-5d13- ## Overall Impression -O strip está certo em *estrutura* A10 (média grande + faixa secundária, não três KPIs) e no lugar certo (acima da tabela, no recorte filtrado). Falha em *rotular a semântica*: o número hero não diz “Média”, “Faixa” não diz pessimista–otimista, e o mapa acima pode mostrar outro cenário sem o strip confessar o lock em `central`. Em reunião, Marcos não sabe se está olhando chão, caso-base ou teto. +O strip está certo em _estrutura_ A10 (média grande + faixa secundária, não três KPIs) e no lugar certo (acima da tabela, no recorte filtrado). Falha em _rotular a semântica_: o número hero não diz “Média”, “Faixa” não diz pessimista–otimista, e o mapa acima pode mostrar outro cenário sem o strip confessar o lock em `central`. Em reunião, Marcos não sabe se está olhando chão, caso-base ou teto. ## What's Working @@ -47,31 +48,37 @@ O strip está certo em *estrutura* A10 (média grande + faixa secundária, não ## Priority Issues ### [P1] Número grande sem rótulo “Média” + - **Why:** Default A10 é `central`; a UI só diz “Votos estimados no conjunto”. Coordenação pergunta “isso é o otimista?”. - **Fix:** Label ou detail explícito: “Média” / “Cenário média (padrão)”. - **Suggested command:** `/impeccable clarify` ### [P1] “Faixa X–Y” sem pessimista–otimista + - **Why:** Pode ser CI, meta ou goals; conflita com vocabulário A10 já nos forms/mapa. - **Fix:** “Pessimista X · Otimista Y” ou mini `VoteEstimateScenarioStrip` endpoints. - **Suggested command:** `/impeccable clarify` ### [P1] Mapa Cenário vs strip dessincronizado + - **Why:** Mapa (acima) troca P/M/O; strip fica em central sem cue — duas verdades na mesma scroll. - **Fix:** Âncora explícita (“sempre média”) **ou** strip segue o cenário do mapa (estado local compartilhado). - **Suggested command:** `/impeccable layout` ### [P2] `sem estimativa` enterrado em Declarações + - **Why:** Qualidade do hero number some no meio da célula; dashboard já tem célula própria. - **Fix:** Footnote sob o total ou alinhar layout ao dashboard. - **Suggested command:** `/impeccable layout` ### [P2] “No conjunto” subespecificado + - **Why:** Não diz “Praças filtradas”; fácil ler como estadual/escopo total. - **Fix:** “Média nas Praças filtradas” (resolve P1+P2 de naming). - **Suggested command:** `/impeccable clarify` ### [P3] Linguagem visual ≠ linhas da tabela + - **Why:** Linhas usam ScenarioStrip; overview só tipografia. - **Fix:** Strip compacto endpoints no detail. - **Suggested command:** `/impeccable polish` diff --git a/.impeccable/critique/2026-07-23T21-52-39Z__src-components-campaign-plazamappanel-tsx-cenario.md b/.impeccable/critique/2026-07-23T21-52-39Z__src-components-campaign-plazamappanel-tsx-cenario.md index c7d766e0..922decde 100644 --- a/.impeccable/critique/2026-07-23T21-52-39Z__src-components-campaign-plazamappanel-tsx-cenario.md +++ b/.impeccable/critique/2026-07-23T21-52-39Z__src-components-campaign-plazamappanel-tsx-cenario.md @@ -8,23 +8,24 @@ p3_count: 1 timestamp: 2026-07-23T21-52-39Z slug: src-components-campaign-plazamappanel-tsx-cenario --- + Method: dual-agent (A: c3613972-8d1f-41bb-b210-501b0ddc14c9 · B: 8ae72814-5de3-4010-9ebb-7c9d64d8ccec) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 1 | Ano default 2022: mudar Cenário não pinta o mapa; efeito no overview abaixo do fold | -| 2 | Match System / Real World | 1 | “overview” / “preenchimento” ≠ linguagem de campo; toolbar diz “filtro do mapa” | -| 3 | User Control and Freedom | 3 | Três opções reversíveis; baixo risco | -| 4 | Consistency and Standards | 2 | Mesmo NativeSelect que Ano/Escala, mas escopo de página (não só mapa) | -| 5 | Error Prevention | 1 | Posição convida o modelo errado; disclaimer é pós-fato | -| 6 | Recognition Rather Than Recall | 2 | Labels P/M/O ok; regra “só 2026” exige ler o rodapé | -| 7 | Flexibility and Efficiency | 2 | Sync global é eficiente se entendido; sem atalho Ano→2026 | -| 8 | Aesthetic and Minimalist Design | 2 | Quarto controle + disclaimer xs no estado comum (Ano≠2026) | -| 9 | Error Recovery | 2 | Mapa “não fez nada” sem live region no overview | -| 10 | Help and Documentation | 2 | Hint existe; copy de engenheiro; sempre ligada no first paint | -| **Total** | | **18/40** | **Poor — labels A10 ok; assento + disclaimer quebram o controle** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ----------------------------------------------------------------------------------- | +| 1 | Visibility of System Status | 1 | Ano default 2022: mudar Cenário não pinta o mapa; efeito no overview abaixo do fold | +| 2 | Match System / Real World | 1 | “overview” / “preenchimento” ≠ linguagem de campo; toolbar diz “filtro do mapa” | +| 3 | User Control and Freedom | 3 | Três opções reversíveis; baixo risco | +| 4 | Consistency and Standards | 2 | Mesmo NativeSelect que Ano/Escala, mas escopo de página (não só mapa) | +| 5 | Error Prevention | 1 | Posição convida o modelo errado; disclaimer é pós-fato | +| 6 | Recognition Rather Than Recall | 2 | Labels P/M/O ok; regra “só 2026” exige ler o rodapé | +| 7 | Flexibility and Efficiency | 2 | Sync global é eficiente se entendido; sem atalho Ano→2026 | +| 8 | Aesthetic and Minimalist Design | 2 | Quarto controle + disclaimer xs no estado comum (Ano≠2026) | +| 9 | Error Recovery | 2 | Mapa “não fez nada” sem live region no overview | +| 10 | Help and Documentation | 2 | Hint existe; copy de engenheiro; sempre ligada no first paint | +| **Total** | | **18/40** | **Poor — labels A10 ok; assento + disclaimer quebram o controle** | ## Anti-Patterns Verdict @@ -47,26 +48,31 @@ A10 labels e sync via context estão certos. O bug de UX é estrutural: **Cenár ## Priority Issues ### [P1] Assento na toolbar do mapa implica choropleth quando Ano≠2026 + - **Why:** First paint comum: muda Cenário → mapa igual → sensação de broken. -- **Fix:** Relocar ao overview; **ou** desabilitar/ghost + explicar; **ou** Cenário força Ano→2026; **ou** esconder no mapa quando ≠2026 *e* hospedar o controle onde o efeito é visível. +- **Fix:** Relocar ao overview; **ou** desabilitar/ghost + explicar; **ou** Cenário força Ano→2026; **ou** esconder no mapa quando ≠2026 _e_ hospedar o controle onde o efeito é visível. - **Suggested command:** `/impeccable layout` (ou `/impeccable shape`) ### [P1] Disclaimer em português de engenheiro + - **Why:** “overview” não está na UI; “preenchimento” é jargão de mapa. - **Fix:** Uma linha de resultado (“Muda os totais acima da lista. No mapa, só com Ano 2026.”) — ou matar a linha relocando o controle. - **Suggested command:** `/impeccable clarify` / `/impeccable distill` ### [P2] Feel the action: efeito fora do viewport + - **Why:** Mapa acima; overview/lista abaixo. Em mobile parece no-op. - **Fix:** Highlight/pending no overview; ou co-localizar o controle. - **Suggested command:** `/impeccable animate` + layout ### [P2] Always-show na fila do mapa vs hide -- **Why:** Sync global é racional; *nesta* fila treina o affordance errado. + +- **Why:** Sync global é racional; _nesta_ fila treina o affordance errado. - **Fix:** Não esconder sem relocação; se global, não vestir como filtro do mapa. - **Suggested command:** `/impeccable shape` ### [P3] Densidade Ano / Escala / Cenário / Comparar + - **Why:** Quarto peer + disclaimer wrap em narrow (Casey). - **Suggested command:** `/impeccable quieter` diff --git a/.impeccable/critique/2026-07-24T20-30-52Z__a11-ranking-votos.md b/.impeccable/critique/2026-07-24T20-30-52Z__a11-ranking-votos.md index fc6c1a12..84d89e25 100644 --- a/.impeccable/critique/2026-07-24T20-30-52Z__a11-ranking-votos.md +++ b/.impeccable/critique/2026-07-24T20-30-52Z__a11-ranking-votos.md @@ -6,6 +6,7 @@ p1_count: 2 timestamp: 2026-07-24T20-30-52Z slug: a11-ranking-votos --- + # Critique — A11 posição em votos **Alvos:** `MunicipalityBaselineCard`, `MunicipalityList`, `MunicipalitySortableHead`, `MunicipalityFilters` @@ -13,19 +14,19 @@ slug: a11-ranking-votos ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Pending/dim ok; sort active mostly chevron — no “ordenado por…” summary | -| 2 | Match System / Real World | 2 | “% da votação” unexplained on list; “Praça” leftovers; seed empty-state | -| 3 | User Control and Freedom | 3 | Limpar is all-or-nothing (sort resets only with full clear) | -| 4 | Consistency and Standards | 2 | Mobile “% da votação” vs desktop “Votos 2022”; card vs table readout order | -| 5 | Error Prevention | 3 | Detail separates dual-% well; list has no equivalent guardrail | -| 6 | Recognition Rather Than Recall | 2 | List % without denominator; definition lives on detail | -| 7 | Flexibility and Efficiency | 3 | Default votos desc is right mesa lens; inactive columns under-afforded | -| 8 | Aesthetic and Minimalist Design | 3 | Restrained desk; quiet secondary lines also hide meaning | -| 9 | Error Recovery | 2 | “—” for missing rank; engineer empty baseline copy | -| 10 | Help and Documentation | 2 | Baseline intro good; list column gives zero definition | -| **Total** | | **25/40** | **Acceptable — vocabulary/consistency is the drag** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | -------------------------------------------------------------------------- | +| 1 | Visibility of System Status | 3 | Pending/dim ok; sort active mostly chevron — no “ordenado por…” summary | +| 2 | Match System / Real World | 2 | “% da votação” unexplained on list; “Praça” leftovers; seed empty-state | +| 3 | User Control and Freedom | 3 | Limpar is all-or-nothing (sort resets only with full clear) | +| 4 | Consistency and Standards | 2 | Mobile “% da votação” vs desktop “Votos 2022”; card vs table readout order | +| 5 | Error Prevention | 3 | Detail separates dual-% well; list has no equivalent guardrail | +| 6 | Recognition Rather Than Recall | 2 | List % without denominator; definition lives on detail | +| 7 | Flexibility and Efficiency | 3 | Default votos desc is right mesa lens; inactive columns under-afforded | +| 8 | Aesthetic and Minimalist Design | 3 | Restrained desk; quiet secondary lines also hide meaning | +| 9 | Error Recovery | 2 | “—” for missing rank; engineer empty baseline copy | +| 10 | Help and Documentation | 2 | Baseline intro good; list column gives zero definition | +| **Total** | | **25/40** | **Acceptable — vocabulary/consistency is the drag** | ## Anti-Patterns Verdict @@ -48,26 +49,31 @@ Default sort by concentration is the right product move. The biggest opportunity ## Priority Issues ### [P1] Label / metric / sort vocabulary collision + - **Why:** Under pressure, header “Votos 2022” vs cell “% da votação” vs mobile Ordenar invites wrong comparisons. - **Fix:** One shared term (e.g. Concentração 2022) across column, Ordenar, and primary readout; secondary = votos · Nº de 435. - **Suggested command:** `/impeccable clarify` ### [P1] Dual-% trap still live on list → detail path + - **Why:** List shows unexplained %; detail introduces local % of válidos — disclaimer one click too late. - **Fix:** Column hint/caption: “% da votação estadual do candidato”; keep local dominance secondary. - **Suggested command:** `/impeccable clarify` ### [P2] Card vs table VotePositionReadout hierarchy diverge + - **Why:** Same data, different story (table prioritizes share; card is middot soup). - **Fix:** Same stack both layouts: concentration → votos · rank. - **Suggested command:** `/impeccable distill` ### [P2] “Praça” + engineer empty state + - **Why:** Remodel is Município; “Rode o seed…” fails closed for staff. - **Fix:** Município language; staff-safe empty (“Sem baseline TSE neste ambiente”). - **Suggested command:** `/impeccable harden` ### [P3] Active sort under-announced for sighted scan + - **Why:** Chevron alone is easy to miss in dense headers. - **Fix:** Active weight + optional “Ordenado por concentração 2022…”. - **Suggested command:** `/impeccable polish` diff --git a/.impeccable/critique/2026-07-24T20-48-41Z__a11-ranking-votos.md b/.impeccable/critique/2026-07-24T20-48-41Z__a11-ranking-votos.md index a5a2b01a..436108e7 100644 --- a/.impeccable/critique/2026-07-24T20-48-41Z__a11-ranking-votos.md +++ b/.impeccable/critique/2026-07-24T20-48-41Z__a11-ranking-votos.md @@ -6,6 +6,7 @@ p1_count: 2 timestamp: 2026-07-24T20-48-41Z slug: a11-ranking-votos --- + # Critique — A11 posição em votos (pós-polish) **Alvos:** BaselineCard, List, SortableHead, Filters @@ -13,19 +14,19 @@ slug: a11-ranking-votos ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Sort summary ok; dormant chevrons faint | -| 2 | Match System / Real World | 3 | Concentração unified; Praças leftovers in visit/scope | -| 3 | User Control and Freedom | 3 | Limpar nuclear | -| 4 | Consistency and Standards | 3 | Mobile/desktop labels aligned | -| 5 | Error Prevention | 3 | Dual-% taught; cells still noisy | -| 6 | Recognition Rather Than Recall | 3 | Definition in body/cells, not on header | -| 7 | Flexibility and Efficiency | 3 | Default votos desc right | -| 8 | Aesthetic and Minimalist Design | 2 | Per-cell “da votação estadual” + “de 435” | -| 9 | Error Recovery | 3 | Staff-safe empty baseline | -| 10 | Help and Documentation | 3 | Help misplaced (cells vs header) | -| **Total** | | **29/40** | **Good** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ----------------------------------------------------- | +| 1 | Visibility of System Status | 3 | Sort summary ok; dormant chevrons faint | +| 2 | Match System / Real World | 3 | Concentração unified; Praças leftovers in visit/scope | +| 3 | User Control and Freedom | 3 | Limpar nuclear | +| 4 | Consistency and Standards | 3 | Mobile/desktop labels aligned | +| 5 | Error Prevention | 3 | Dual-% taught; cells still noisy | +| 6 | Recognition Rather Than Recall | 3 | Definition in body/cells, not on header | +| 7 | Flexibility and Efficiency | 3 | Default votos desc right | +| 8 | Aesthetic and Minimalist Design | 2 | Per-cell “da votação estadual” + “de 435” | +| 9 | Error Recovery | 3 | Staff-safe empty baseline | +| 10 | Help and Documentation | 3 | Help misplaced (cells vs header) | +| **Total** | | **29/40** | **Good** | ## Anti-Patterns Verdict @@ -46,18 +47,23 @@ Vocabulary polish worked (+4 vs 25). Next jump is density: definitions once on t ## Priority Issues ### [P1] Per-cell “da votação estadual” (owner) + Cell = bare %. Explanation on Concentração header tooltip. → `/impeccable distill` ### [P1] “de 435” per row (owner) + Rank = `12º` only; catalog size once in header tooltip. → `/impeccable distill` ### [P2] Sort summary copy-heavy + Short status only; definition → header. → `/impeccable quieter` / polish ### [P2] Baseline dual-% fork + Demote local dominance. → `/impeccable layout` ### [P3] Dormant chevrons + Praças leftovers + → `/impeccable polish` / clarify ## Persona Red Flags diff --git a/.impeccable/critique/2026-07-24T21-01-02Z__a11-ranking-votos.md b/.impeccable/critique/2026-07-24T21-01-02Z__a11-ranking-votos.md index 823368af..9e719eff 100644 --- a/.impeccable/critique/2026-07-24T21-01-02Z__a11-ranking-votos.md +++ b/.impeccable/critique/2026-07-24T21-01-02Z__a11-ranking-votos.md @@ -6,6 +6,7 @@ p1_count: 1 timestamp: 2026-07-24T21-01-02Z slug: a11-ranking-votos --- + # Critique — A11 (pós density distill) **Data:** 2026-07-24 @@ -13,19 +14,19 @@ slug: a11-ranking-votos ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Sort status solid | -| 2 | Match System / Real World | 3 | Concentração vocabulary ok | -| 3 | User Control and Freedom | 3 | Limpar nuclear | -| 4 | Consistency and Standards | 3 | Help chrome fights distill | -| 5 | Error Prevention | 3 | Bare cells help | -| 6 | Recognition Rather Than Recall | 3 | Definition on ? not header | -| 7 | Flexibility and Efficiency | 3 | Default votos desc | -| 8 | Aesthetic and Minimalist Design | 3 | Cells good; ? residual | -| 9 | Error Recovery | 3 | Empty baseline ok | -| 10 | Help and Documentation | 2 | Wrong chrome: ? vs hover header | -| **Total** | | **30/40** | **Good** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------- | +| 1 | Visibility of System Status | 3 | Sort status solid | +| 2 | Match System / Real World | 3 | Concentração vocabulary ok | +| 3 | User Control and Freedom | 3 | Limpar nuclear | +| 4 | Consistency and Standards | 3 | Help chrome fights distill | +| 5 | Error Prevention | 3 | Bare cells help | +| 6 | Recognition Rather Than Recall | 3 | Definition on ? not header | +| 7 | Flexibility and Efficiency | 3 | Default votos desc | +| 8 | Aesthetic and Minimalist Design | 3 | Cells good; ? residual | +| 9 | Error Recovery | 3 | Empty baseline ok | +| 10 | Help and Documentation | 2 | Wrong chrome: ? vs hover header | +| **Total** | | **30/40** | **Good** | ## Anti-Patterns @@ -44,13 +45,16 @@ Cells landed. Open P1: remove CampaignInfoHint; explanation on header hover/focu ## Priority Issues ### [P1] Owner — no ? button; hover header (validated) + Remove hint chrome; Tooltip on sortable Concentração head. Mobile: no orphan ?. Baseline: same rule. → `/impeccable distill` ### [P2] Limpar nukes sort + → `/impeccable harden` ### [P3] Dormant chevrons after ? dies + → `/impeccable polish` ## Personas diff --git a/.impeccable/critique/2026-07-25T01-04-34Z__omponents-campaign-municipalitygoalaccountcard-tsx.md b/.impeccable/critique/2026-07-25T01-04-34Z__omponents-campaign-municipalitygoalaccountcard-tsx.md index dcc665bf..f59e9b60 100644 --- a/.impeccable/critique/2026-07-25T01-04-34Z__omponents-campaign-municipalitygoalaccountcard-tsx.md +++ b/.impeccable/critique/2026-07-25T01-04-34Z__omponents-campaign-municipalitygoalaccountcard-tsx.md @@ -6,23 +6,24 @@ p1_count: 3 timestamp: 2026-07-25T01-04-34Z slug: omponents-campaign-municipalitygoalaccountcard-tsx --- + Method: dual-agent (A: 92b63fbf-f200-4a00-9cc5-81db3f317b57 · B: 35312920-2be2-4bac-92aa-0fbb62c2a571) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 3 | Coverage %, deficit text, and provenance ("estimativa da mesa") are all stated plainly; the 0%-coverage `Progress` bar is a near-invisible 4px hairline, understating urgency at the most common early-campaign state. | -| 2 | Match Between System and Real World | 2 | "Teto do campo," "share intracampo," "roll-off," "campo" are coined composites — confirmed by the product's own coordinator calling these "conceitos complicados" *after* this exact tooltip fix shipped (`docs/plans/documentacao-conceitos-campanha.md`). | -| 3 | User Control and Freedom | 3 | Popover/tooltip dismiss cleanly (Escape, click-outside, blur); no traps. | -| 4 | Consistency and Standards | 2 | Two disclosure idioms collide in one card: `CampaignInfoHint` opens-on-click-and-stays-open; the metric `Tooltip` closes-on-click and never opens on click. A user primed by the "?" icon one line above will click a metric next and get nothing, or dismiss an open tooltip. | -| 5 | Error Prevention | 3 | n/a mostly (read-only diagnostics); sane zero/negative fallbacks ("Sem meta definida," "Sem teto do campo projetado..."). | -| 6 | Recognition Rather Than Recall | 2 | Dotted-underline is a fair desktop cue, but on touch there is **no cue at all** — confirmed by both assessments (Radix's own touch-suppression logic, and no tap affordance reachable at 390×844). | -| 7 | Flexibility and Efficiency of Use | 3 | `Tab` reaches every metric and opens its tooltip on focus with a visible ring — solid keyboard path, measured live. | -| 8 | Aesthetic and Minimalist Design | 3 | Restrained, on-brand, correctly dense for a staff surface; undermined live by the tooltip/popover collision covering sibling content (screenshotted at both desktop and 390px). | -| 9 | Error Recovery | 3 | n/a — no error states exercised; fallback copy is clear and non-technical. | -| 10 | Help and Documentation | 2 | Good *density* of contextual help (card popover + 4 inline tooltips), not yet *effective* — field evidence says the explanations didn't resolve confusion, and there's no link to deeper docs yet (that's roadmap E18). | -| **Total** | | **26/40** | **Acceptable — significant improvements needed before users are happy** | +| # | Heuristic | Score | Key Issue | +| --------- | ----------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | Visibility of System Status | 3 | Coverage %, deficit text, and provenance ("estimativa da mesa") are all stated plainly; the 0%-coverage `Progress` bar is a near-invisible 4px hairline, understating urgency at the most common early-campaign state. | +| 2 | Match Between System and Real World | 2 | "Teto do campo," "share intracampo," "roll-off," "campo" are coined composites — confirmed by the product's own coordinator calling these "conceitos complicados" _after_ this exact tooltip fix shipped (`docs/plans/documentacao-conceitos-campanha.md`). | +| 3 | User Control and Freedom | 3 | Popover/tooltip dismiss cleanly (Escape, click-outside, blur); no traps. | +| 4 | Consistency and Standards | 2 | Two disclosure idioms collide in one card: `CampaignInfoHint` opens-on-click-and-stays-open; the metric `Tooltip` closes-on-click and never opens on click. A user primed by the "?" icon one line above will click a metric next and get nothing, or dismiss an open tooltip. | +| 5 | Error Prevention | 3 | n/a mostly (read-only diagnostics); sane zero/negative fallbacks ("Sem meta definida," "Sem teto do campo projetado..."). | +| 6 | Recognition Rather Than Recall | 2 | Dotted-underline is a fair desktop cue, but on touch there is **no cue at all** — confirmed by both assessments (Radix's own touch-suppression logic, and no tap affordance reachable at 390×844). | +| 7 | Flexibility and Efficiency of Use | 3 | `Tab` reaches every metric and opens its tooltip on focus with a visible ring — solid keyboard path, measured live. | +| 8 | Aesthetic and Minimalist Design | 3 | Restrained, on-brand, correctly dense for a staff surface; undermined live by the tooltip/popover collision covering sibling content (screenshotted at both desktop and 390px). | +| 9 | Error Recovery | 3 | n/a — no error states exercised; fallback copy is clear and non-technical. | +| 10 | Help and Documentation | 2 | Good _density_ of contextual help (card popover + 4 inline tooltips), not yet _effective_ — field evidence says the explanations didn't resolve confusion, and there's no link to deeper docs yet (that's roadmap E18). | +| **Total** | | **26/40** | **Acceptable — significant improvements needed before users are happy** | ## Anti-Patterns Verdict @@ -45,30 +46,35 @@ The card itself is well-crafted and on-brand — restrained, honest about data p ## Priority Issues **[P1] Touch users cannot open the metric tooltips at all.** + - **Why it matters**: PRODUCT.md names "used in the field and on the go" as this surface's defining context, and this exact follow-up exists for comprehension. It's unreachable on the device class that context implies. - **What/evidence**: `@radix-ui/react-tooltip@1.2.8`'s `TooltipTrigger` explicitly ignores touch pointer events and gates focus-open on a pointer-down ref that a tap sets first — confirmed against the installed source, not inference. At 390×844, no tap affordance exists for any of the 4 metrics; the adjacent "Editar votos estimados" control works fine at the same width, so this reads as an oversight, not a deliberate mobile choice. - **Fix**: Add an explicit tap trigger — drop `asChild` for a focusable/clickable element with its own `onClick` toggling open, or give each metric a small click-openable affordance like `CampaignInfoHint`'s. - **Suggested command**: `/impeccable harden` **[P1] Tooltips render on top of the number/coverage block they explain.** + - **Why it matters**: The moment someone seeks clarification is the moment the clarified figure disappears — a direct "one thing at a time" violation at the card's highest-stakes row. - **What/evidence**: Reproduced independently by both assessments via hover, real keyboard focus, and screenshots at 1280×720 and 390×844 — the bubble (Radix collision-flips to `data-side="top"` since the trigger sits near the viewport bottom) fully covers "Cobertura da meta" / the progress bar / "Faltam X votos para a meta" at both widths. - **Fix**: Force collision-safe placement for this usage (`side="top"` with generous `sideOffset` plus bottom padding, or reorder the diagnostic row above the coverage block so there's room for the tooltip to open without overlap). - **Suggested command**: `/impeccable layout` **[P1] Tooltip/popover copy is dense, cross-referential jargon — and the real user already said it didn't help.** -- **Why it matters**: `docs/plans/documentacao-conceitos-campanha.md` quotes the actual coordinator calling these "conceitos complicados" *after* these exact tooltips shipped — the fix under critique didn't fix the problem it was built to fix. + +- **Why it matters**: `docs/plans/documentacao-conceitos-campanha.md` quotes the actual coordinator calling these "conceitos complicados" _after_ these exact tooltips shipped — the fix under critique didn't fix the problem it was built to fix. - **What/evidence**: e.g. the "Teto do campo" explanation is one ~40-word sentence with three technical terms and no break; "Share intracampo"'s explanation requires recalling "Captura"'s definition mid-read, but the two tooltips can never be open at the same time (opening one closes the other) — a genuine working-memory violation. - **Fix**: Split each explanation into a short lead sentence (what it measures) + a visually secondary formula line; bold only the one term each tooltip defines; stop cross-referencing metrics that can't be open simultaneously — state the difference inline instead of by reference. - **Suggested command**: `/impeccable clarify` **[P2] Two incompatible disclosure idioms coexist in one card.** -- **Why it matters**: Breaking Consistency *within* a single component teaches the wrong mental model in real time — a user who just learned "click the small icon" naturally tries clicking a metric next and gets nothing or an unwanted dismissal. + +- **Why it matters**: Breaking Consistency _within_ a single component teaches the wrong mental model in real time — a user who just learned "click the small icon" naturally tries clicking a metric next and gets nothing or an unwanted dismissal. - **What/evidence**: `CampaignInfoHint` = click-to-open-and-stay-open Popover; `GoalAccountMetric` = hover/focus Tooltip that closes on click (Radix default), never opens on click. - **Fix**: Pick one idiom for this card, or make the "click for detail" vs. "hover/focus for definition" visual distinction stronger than dotted-underline vs. circle-icon alone. - **Suggested command**: `/impeccable clarify` **[P3] The 0%-coverage progress bar is nearly invisible.** + - **Why it matters**: Understates urgency exactly when it should read loudest (0% coverage is the most common early-campaign state). - **Fix**: Give the near-zero state deliberate visual weight instead of relying solely on the adjacent "0%" text. - **Suggested command**: `/impeccable polish` @@ -91,6 +97,6 @@ The card itself is well-crafted and on-brand — restrained, honest about data p ## Questions to Consider -- If the coordinator needed a *separate* documentation surface (E18) days after these exact tooltips shipped specifically because they didn't resolve the confusion, is hover/focus disclosure the right mechanism for this density of explanation at all — or does the card need one worked example inline before a glossary page ever ships? +- If the coordinator needed a _separate_ documentation surface (E18) days after these exact tooltips shipped specifically because they didn't resolve the confusion, is hover/focus disclosure the right mechanism for this density of explanation at all — or does the card need one worked example inline before a glossary page ever ships? - "Captura" and "Share intracampo" exist as two numbers whose entire tooltip copy is dedicated to explaining how they differ from each other — should this be one metric with a denominator toggle instead of two numbers that each require reading the other's footnote? -- As E9–E17 add more diagnostic metrics to this row, does the card need a settled disclosure model *now* — before there are 8 metrics fighting over two incompatible idioms instead of 4? +- As E9–E17 add more diagnostic metrics to this row, does the card need a settled disclosure model _now_ — before there are 8 metrics fighting over two incompatible idioms instead of 4? diff --git a/.impeccable/critique/2026-07-25T02-37-03Z__src-components-campaign-municipalitylist-tsx.md b/.impeccable/critique/2026-07-25T02-37-03Z__src-components-campaign-municipalitylist-tsx.md index 6b81b4a9..7a1c4c22 100644 --- a/.impeccable/critique/2026-07-25T02-37-03Z__src-components-campaign-municipalitylist-tsx.md +++ b/.impeccable/critique/2026-07-25T02-37-03Z__src-components-campaign-municipalitylist-tsx.md @@ -6,25 +6,26 @@ p1_count: 3 timestamp: 2026-07-25T02-37-03Z slug: src-components-campaign-municipalitylist-tsx --- + # Critique — B16 Municipality Header Filters Method: dual-agent (A: 8cdcb249-95fe-4f3d-8937-081b10b442bc · B: c641769d-fc5c-4a83-9442-4329cff5cf96) ## Design Health Score -| # | Heuristic | Score | Key Issue | -|---|-----------|-------|-----------| -| 1 | Visibility of System Status | 2 | Control lag until RSC (fixed post-critique: optimistic) | -| 2 | Match System / Real World | 3 | Priority on Município column | -| 3 | User Control and Freedom | 3 | Limpar + Todos clear | -| 4 | Consistency and Standards | 2 | Header vs mobile select duality | -| 5 | Error Prevention | 3 | Constrained options | -| 6 | Recognition Rather Than Recall | 2 | Icon-only funnels (Priority now labeled) | -| 7 | Flexibility and Efficiency | 3 | Column-local filters | -| 8 | Aesthetic and Minimalist Design | 3 | Slim bar succeeds | -| 9 | Error Recovery | 2 | Limpar primary recovery | -| 10 | Help and Documentation | 2 | Filter meanings sparse | -| **Total** | | **25/40** | **Acceptable** | +| # | Heuristic | Score | Key Issue | +| --------- | ------------------------------- | --------- | ------------------------------------------------------- | +| 1 | Visibility of System Status | 2 | Control lag until RSC (fixed post-critique: optimistic) | +| 2 | Match System / Real World | 3 | Priority on Município column | +| 3 | User Control and Freedom | 3 | Limpar + Todos clear | +| 4 | Consistency and Standards | 2 | Header vs mobile select duality | +| 5 | Error Prevention | 3 | Constrained options | +| 6 | Recognition Rather Than Recall | 2 | Icon-only funnels (Priority now labeled) | +| 7 | Flexibility and Efficiency | 3 | Column-local filters | +| 8 | Aesthetic and Minimalist Design | 3 | Slim bar succeeds | +| 9 | Error Recovery | 2 | Limpar primary recovery | +| 10 | Help and Documentation | 2 | Filter meanings sparse | +| **Total** | | **25/40** | **Acceptable** | ## Anti-Patterns Verdict diff --git a/.impeccable/live/config.json b/.impeccable/live/config.json index a5453592..bbe52400 100644 --- a/.impeccable/live/config.json +++ b/.impeccable/live/config.json @@ -1,8 +1,5 @@ { - "files": [ - "src/app/(frontend)/layout.tsx", - "src/app/(campaign)/layout.tsx" - ], + "files": ["src/app/(frontend)/layout.tsx", "src/app/(campaign)/layout.tsx"], "insertBefore": "", "commentSyntax": "jsx", "cspChecked": true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..04fe99c7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,32 @@ +# Build outputs and tool caches +.next/ +out/ +build/ +playwright-report/ +test-results/ +data/ +pnpm-lock.yaml + +# Generated files (owned by their generators, not by Prettier) +src/payload-types.ts +src/app/(payload)/admin/importMap.js + +# Frozen history — migrations are never edited retroactively +src/migrations/ + +# Committed data artifacts (rebuilt by scripts; pretty-printing would explode them +# and the next regeneration would revert it) +src/lib/geometries/ +src/lib/electionAggregates/ +src/lib/bahiaMunicipalityDemographics.ts +src/lib/bahiaTseCityCodes.ts +src/lib/bahiaMunicipalityCodes.ts +src/lib/bahiaTseZones.ts +tests/fixtures/ +output/ + +# Static assets and vendored agent material +public/ +.agents/ +.cursor/ +docs/sheets/ diff --git a/.prettierrc.json b/.prettierrc.json index cb8ee267..0a75c504 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,6 @@ "singleQuote": true, "trailingComma": "all", "printWidth": 100, - "semi": false + "semi": false, + "plugins": ["prettier-plugin-organize-imports"] } diff --git a/.zed/settings.json b/.zed/settings.json index b91b0933..690bef03 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -1,11 +1,7 @@ { "languages": { "CSS": { - "language_servers": [ - "tailwindcss-intellisense-css", - "!vscode-css-language-server", - "..." - ] + "language_servers": ["tailwindcss-intellisense-css", "!vscode-css-language-server", "..."] } } } diff --git a/docs/TESTING.md b/docs/TESTING.md index 42b08b1c..e151354b 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -24,7 +24,7 @@ Modules the 2026-07 hardening phases touch, and where their behavior is pinned: | Leader supporter create | linked-municipality scope; out-of-scope rejection; staff rejection | `collectionAccessLockdown.int.spec.ts` | | | Posts visibility/caching | `isPostVisible` fail-closed; cached listing | (indirect via routes) | no dedicated `posts` spec — backlog | | List URL contracts (Pass 2 W0, pinned 2026-07-25) | `campaignListUrl` canonicalize/clamp/redirect; supporter + actionPlan parse/serialize/href; 5 entity parsers | `campaignListUrl`, `supporterUi`, `actionPlanUi`, `campaignEntityListParsers` (unit) | municipality list URL pinned separately in `municipalityUi.unit.spec.ts` | -| Form-action ladders (Pass 2 W4d) | `runCampaignFormAction`: success spread, boundary/zod/safe/generic mapping, values+revision echo | `campaignFormActions.unit.spec.ts` | per-route ladders are thin shells; behavior pinned at the wrapper | +| Form-action ladders (Pass 2 W4d) | `runCampaignFormAction`: success spread, boundary/zod/safe/generic mapping, values+revision echo | `campaignFormActions.unit.spec.ts` | per-route ladders are thin shells; behavior pinned at the wrapper | | WhatsApp subscription consent (Pass 2 D3) | fail-closed without `whatsapp-inscricao`; happy path links subscription → keyed consent | `submitWhatsapp.int.spec.ts` | | | Vote trend + election number format (Pass 2 W4a survivors) | `computeVoteTrend` pair priority/±10% band; `formatElectionNumber` E18 fraction defect | `voteTrend.unit.spec.ts` | | | Entity list loaders (Pass 2 W0) | scope (leader empty/denied), marker-narrowed q, kind/status filters, pagination shape | `campaignEntityListData.int.spec.ts` | advisor loader runs post-gate with `overrideAccess: true` (gate asserted upstream) | diff --git a/docs/plans/escala-dry-pos-c2.md b/docs/plans/escala-dry-pos-c2.md index ac42ef82..7d1f0145 100644 --- a/docs/plans/escala-dry-pos-c2.md +++ b/docs/plans/escala-dry-pos-c2.md @@ -127,23 +127,28 @@ flowchart TD Todas as cinco fases foram implementadas e validadas localmente (`tsc --noEmit`, `pnpm lint`, `pnpm test:int`, `pnpm test:unit`) contra `teqo_test`. Sem regressões nas rotas de núcleos/apoiadores/planos já entregues. ### Fase 1 — UX alinhada + - `SupporterForm` agora usa `StrictCombobox` + `municipalityComboboxOptions()` no município (validação continua em `optionalBahiaCity`). - `RemoveSupporterDataButton` trocou `window.confirm` por `AlertDialog` no padrão de `ArchiveNucleusDialog.tsx`. ### Fase 2 — DRY com núcleos / leadership + - `src/utilities/campaignListUrl.ts` (novo): `firstValue`, `normalizedText`, `strictDecimalInteger`, `inspectRawListParams`, `resolveListUrl`, `buildListHref`. `nucleusUi.ts`, `supporterUi.ts` e `actionPlanUi.ts` delegam para ele (domínio permanece nos módulos de domínio). - `src/components/campaign/CampaignListPagination.tsx` (novo) + `getPaginationPages`: substitui `NucleusPagination`, `SupporterPagination` e `ActionPlanPagination` (os três foram removidos). Listagens de núcleos/apoiadores/planos e o teste unitário `campaignNucleusUi` apontam para o novo componente. - `src/utilities/campaignFormFields.ts` (novo): `fieldError` / `errorProps` compartilhados por `SupporterForm` e `LeadershipForm` (cada form liga um `errorProps` local ao seu prefixo de id). - `src/utilities/campaignFormActionError.ts` (novo): `mapCampaignFormActionError` consolida FormDataBoundaryError → Zod → safelist → genérico. `apoiadores/novo/formActions.ts`, `apoiadores/[id]/formActions.ts` e `nucleos/[slug]/leadershipFormActions.ts` usam o mapper. ### Fase 3 — KPI da lista + - `src/utilities/supporterListOverviewAggregate.ts` (novo): `computeSupporterListOverviewAggregate` faz um único `SELECT COUNT(*) FILTER (...)` via drizzle na sessão da txn Payload, espelhando `buildSupporterListWhere` + a constraint de acesso (`nucleus_id IN (...)` para coordenador, sem constraint para geral, `none` para liderança). `loadSupporterListOverviewData` passou de 3× `payload.count` para um round-trip. Testes int cobrem geral, coordenador (escopo + filtro de cidade) e base vazia. ### Fase 4 — Import em escala + - `src/utilities/supporterImportBulk.ts` (novo): `bulkInsertSupporterImport` insere contacts e supporters via `payload.db.drizzle` na sessão da txn Payload (`getPostgresTransactionDatabase`), em chunks de 500, com `ON CONFLICT DO NOTHING` no índice unique `(contact_id, nucleus_id)`. Substitui até 10k `payload.create` por poucos inserts bulk. - `src/collections/Contact.ts`: `enforceUniqueContactPhone` honra `context.skipContactPhoneInvariant` **fail-closed** — só respeita quando `req.transactionID` está ativo (sem txn, os locks xact-level não podem estar segurados → throw). `upsertContactByPhone` passa o context (o caller já segurou os locks), eliminando o re-lock/re-check redundante do path single-create. ### Fase 5 — Preview sem round-trip + - `src/collections/SupporterImportBatch.ts` (novo, admin hidden, acesso `geral`/admin) + migration `20260719_011015_add_supporter_import_batch`: staging efêmero do conjunto `ok` no servidor. - `src/utilities/supporterImportToken.ts` (novo): token HMAC-SHA256(`${batchId}.${actorID}.${expiresAt}`, PAYLOAD_SECRET) com `timingSafeEqual` e TTL 10 min. `previewSupporterImportText` agora retorna `{ counts, sampleRows (100), errorReportCsv, importToken }` e estageia o conjunto `ok` em `supporterImportBatch`. `confirmSupporterImportRecord` aceita `importToken` (schema trocou `rows` por `importToken`), verifica HMAC + ator + expiração, consome o lote (single-use, delete após commit) e alimenta o bulk insert. O wizard parou de guardar/reenviar as rows. - Testes int: fluxo preview→confirm; token tampered (assinatura alterada) rejeitado; token de outro ator rejeitado; reuso de token consumido rejeitado. diff --git a/docs/plans/inteligencia-campanha.md b/docs/plans/inteligencia-campanha.md index a0da162f..98a608a4 100644 --- a/docs/plans/inteligencia-campanha.md +++ b/docs/plans/inteligencia-campanha.md @@ -55,7 +55,7 @@ Regras transversais (de §6.2/§6.4, viram comportamento do produto): **barato-a | --- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | -------- | --------- | | E8 | **[Conta da cadeira](conta-da-cadeira.md)** | Global `campaignGoals`; potencial por município (válidos projetados, captura do campo, share intracampo via `campoParties.ts`, roll-off, headroom); decomposição meta estadual→município com sanity por TI; cobertura Σ(estimativa do cenário — default média via **A10**)÷meta no dashboard/lista; denominadores sempre válidos projetados (I-B) | O-A | B | ~2d | 2 | | E9 | **[Fila de alocação](fila-de-alocacao.md)** | Lista de decisão com as 7 colunas (§5.2 do relatório: votos em jogo, meta/cobertura/delta, classe, LQ/captura, rede+frescor, competição, tendência) + **% da própria votação** (âncora da mesa, via A11); ordenação default por déficit descoberto (default do staff quando houver metas); ordenação de risco (defesa por frescor); "coluna da vergonha" | O-B, O-F | B | ~1,5d | 2 | -| C12 | **[Registro-fundação](registro-fundacao.md)** | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin` + demandas/custo vinculado; collection `allocationDecision`; garantia "nunca sobrescrever" | O-E, O-G(base) | B | ~2d | 2 | +| C12 | **[Registro-fundação](registro-fundacao.md)** | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin` + demandas/custo vinculado; collection `allocationDecision`; garantia "nunca sobrescrever" | O-E, O-G(base) | B | ~2d | 2 | | E16 | **[Dossiê do município](dossie-municipio.md)** | Dossiê pré-visita/print compondo série+captura, pledges/cobertura, lideranças, conjuntura (`politicalTrend`), forças/riscos, dobradinhas, sinais recentes e perfil A8; emendas quando houver dado (G11) — pedido explícito de campo (O6 do CUSTOMER.md) | O6 (campo) | B | ~1d | 2 | | E10 | **[Classificação territorial relativa](classificacao-territorial-relativa.md)** | Recalibra defesa/ataque/indecisa/perdida com âncoras relativas (LQ vs. share estadual próprio; múltiplos do share da cadeira marginal), multi-eixo (dominância+importância+competição+teto do campo); substitui limiares 35/20/10 de `electionInsights.ts` para DF | O-D | B | ~1d | 3 | | B13 | **[Escala relativa no mapa](escala-relativa-mapa.md)** | Modo de escala novo: quantis do próprio candidato (5 classes) default, LQ e rank no município; símbolo proporcional por votos em jogo sobre o polígono; % dos válidos mantido como opção; roll-off como métrica adicional | O-C | B | ~2d | 3 | diff --git a/docs/plans/mapa-bahia-geometrias.md b/docs/plans/mapa-bahia-geometrias.md index 3ce30538..1ab18da6 100644 --- a/docs/plans/mapa-bahia-geometrias.md +++ b/docs/plans/mapa-bahia-geometrias.md @@ -80,16 +80,16 @@ Hierarquia TSE: município → (1+) zona → (muitas) seção; a relação munic ## Entrega as-built (B2, 2026-07-18) -| Artefato | Caminho | Notas | -| -------- | ------- | ----- | -| TopoJSON municípios | `src/lib/geometries/bahia-municipalities.topo.json` | ~132 KB; 417 features; object `municipalities` | -| TopoJSON TIs | `src/lib/geometries/bahia-identity-territories.topo.json` | ~15 KB; 27 features; object `territories` (dissolução IBGE) | -| Tabela nome→`codarea` | `src/lib/bahiaMunicipalityCodes.ts` | + `codeForMunicipality` / `municipalityForCode` | -| Helpers runtime | `src/lib/bahiaGeometries.ts` | `topojson-client` `feature()` + Maps no load do módulo | -| Fixture oficial | `tests/fixtures/bahia-municipality-codes.official.json` | evidence SHA + bijeção | -| Testes int | `tests/int/bahiaMunicipalityCodes.int.spec.ts`, `tests/int/bahiaGeometries.int.spec.ts` | cobertura 417/27, teto de bytes, nomes de objects via módulo | -| Script | `scripts/build-bahia-geometries.mjs` | `pnpm build:geometries`; deps: `topojson-client` (runtime), `topojson-server` / `topojson-simplify` (dev) | -| Cache | `data/geometries/` | gitignored | +| Artefato | Caminho | Notas | +| --------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| TopoJSON municípios | `src/lib/geometries/bahia-municipalities.topo.json` | ~132 KB; 417 features; object `municipalities` | +| TopoJSON TIs | `src/lib/geometries/bahia-identity-territories.topo.json` | ~15 KB; 27 features; object `territories` (dissolução IBGE) | +| Tabela nome→`codarea` | `src/lib/bahiaMunicipalityCodes.ts` | + `codeForMunicipality` / `municipalityForCode` | +| Helpers runtime | `src/lib/bahiaGeometries.ts` | `topojson-client` `feature()` + Maps no load do módulo | +| Fixture oficial | `tests/fixtures/bahia-municipality-codes.official.json` | evidence SHA + bijeção | +| Testes int | `tests/int/bahiaMunicipalityCodes.int.spec.ts`, `tests/int/bahiaGeometries.int.spec.ts` | cobertura 417/27, teto de bytes, nomes de objects via módulo | +| Script | `scripts/build-bahia-geometries.mjs` | `pnpm build:geometries`; deps: `topojson-client` (runtime), `topojson-server` / `topojson-simplify` (dev) | +| Cache | `data/geometries/` | gitignored | **Cleanup `/simplify` já aplicado no PR da Fase 1:** reuso de `downloadToBuffer`; catch só de `UnknownMunicipalityError`; remoção de dead code de codegen (`stableStringifyTopo` enganoso, tipo `BahiaMunicipalityCode` não usado). **Não aplicado (→ B5):** lazy load split mun/TI; helper CLI compartilhado `ensureCachedDownload`. diff --git a/docs/plans/ranking-votos-municipio.md b/docs/plans/ranking-votos-municipio.md index ad838167..0d60375b 100644 --- a/docs/plans/ranking-votos-municipio.md +++ b/docs/plans/ranking-votos-municipio.md @@ -8,6 +8,7 @@ Appetite: ~0,5–1 dia eng; sem migration, sem collection, sem server action nov Responsável: — Revisões: + - 2026-07-24 (entrega + polish pós-critique): helper `municipalityVoteRank`; baseline + coluna lista; default da lista = `votos` desc (omitido na URL) — **substituído pelo default `deficit` desc do E9 ✓ no mesmo dia**, `?sort=votos` continua disponível; `?sort=name` para alfabético; share lidera a célula; rank no mobile; % dos válidos demoted. B15 amplia keys restantes. - 2026-07-24 (auditoria pre-implementação): B15 ainda ausente no código — esta entrega introduz `sort`/`dir` mínimo só para `name`|`votos` (fallback do plano); B15 amplia as demais keys depois. Seção Dados → decisão → apresentação preenchida. Questões em aberto fechadas: lista só 2022; sem delta de rank 2018→2022 na v1. diff --git a/docs/plans/sistema-listas-campanha.md b/docs/plans/sistema-listas-campanha.md index cde88d6a..28975f2b 100644 --- a/docs/plans/sistema-listas-campanha.md +++ b/docs/plans/sistema-listas-campanha.md @@ -41,7 +41,7 @@ Gate: W0 ([pinagem-superficies-lista.md](pinagem-superficies-lista.md)) verde ## Rabbit holes -- **Generalizar `MunicipalityHeaderFilter` inteiro no D1.** As facetas de município (435 opções + busca) são as mais complexas; a generalização é a *interface* (filterDef + slot), a implementação rica continua específica até um segundo consumidor pedir. +- **Generalizar `MunicipalityHeaderFilter` inteiro no D1.** As facetas de município (435 opções + busca) são as mais complexas; a generalização é a _interface_ (filterDef + slot), a implementação rica continua específica até um segundo consumidor pedir. - **DataTable do shadcn como base.** Consultar composição, mas a casa já tem `ui/Table` + DESIGN.md; introduzir TanStack Table trocaria o modelo server-first por client state — não. - **Mexer na semântica de URL "aproveitando".** Congelada (B18 depende dela). diff --git a/docs/plans/tabela-ti-inicio.md b/docs/plans/tabela-ti-inicio.md index e0bba7df..6ad54f80 100644 --- a/docs/plans/tabela-ti-inicio.md +++ b/docs/plans/tabela-ti-inicio.md @@ -8,6 +8,7 @@ Appetite: ~1 dia eng; sem migration, sem collection Responsável: — ### Revisão 2026-07-24 (auditoria → implementação) + - **A11 ausente confirmado:** `municipalityVoteRank.ts` não existe no repo → `% da própria votação` calculado localmente em `computeTerritoryRollup` (Σ TI ÷ Σ estadual 2022). A11 unifica o helper de share quando entrar. - **Access:** loader `loadTerritoryOverview` usa `overrideAccess: true` (a `canReadMunicipality` do advisor escopa por `advisors contains user.id`) — exposição é agregado TI-level (somas/contagens), nunca PII por município; advisor vê a tabela completa (leitura regional é contexto). Confirmado contra o plano. - **Split client/server:** o rollup puro ficou em `territoryOverview.ts` (client-safe, sem `server-only`) para a tabela client importar `sortTerritoryRows`/tipos; o loader server-only foi para `loadTerritoryOverview.ts`. diff --git a/docs/plans/zonas-por-municipio.md b/docs/plans/zonas-por-municipio.md index 85423d3a..28a1f6e2 100644 --- a/docs/plans/zonas-por-municipio.md +++ b/docs/plans/zonas-por-municipio.md @@ -6,6 +6,7 @@ Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha A → A2) Responsável: — Revisões: + - 2026-07-18 (entrega): cadastro estático `bahiaTseZones` a partir do TSE 2024 `detalhe_votacao_munzona` BA (SHA-256 no cabeçalho); motor `territorySuggestions` + chips opt-in no coordenador `NucleusTerritoryAndZonesFields`; sem migration. Corrigido drift factual: a validação server-side existente chama-se `validateTerritoryAndZones` (não `validateGeographyAndZones`). Reuso de `canonicalizeMunicipalityName` / aliases de A3 na geração do dataset. - 2026-07-18 (as-built / pós-simplify): `TseZoneInput` só controlado; `NucleusTerritoryFields` removido (só `NucleusTerritoryAndZonesFields`); `buildTerritorySuggestions` devolve também `outsideZones`; helpers de membership `isTseZoneOf*` removidos (usar `.includes` nos arrays); caches módulo `citiesByZone` / `zonesByTerritory`; chips com props discriminadas `kind: 'zone'|'city'`; teto UI de 8 + “+N sugestões”. diff --git a/knip.json b/knip.json index 4413b266..151e41bb 100644 --- a/knip.json +++ b/knip.json @@ -15,13 +15,7 @@ "server-only": ["tests/helpers/serverOnly.ts"] }, "ignoreBinaries": [], - "ignoreDependencies": [ - "tailwindcss-animate", - "tw-animate-css", - "shadcn", - "eslint-config-next", - "prettier" - ], + "ignoreDependencies": ["tailwindcss-animate", "tw-animate-css", "shadcn", "eslint-config-next"], "rules": { "exports": "error", "types": "error", diff --git a/package.json b/package.json index ede7df0c..18bfffc2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "build:tse-city-codes": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" node scripts/build-bahia-tse-city-codes.mjs", "dev": "node scripts/guard-dev-db.mjs && cross-env NODE_OPTIONS=--no-deprecation next dev", "devsafe": "rm -rf .next && node scripts/guard-dev-db.mjs && cross-env NODE_OPTIONS=--no-deprecation next dev", + "format": "prettier --write .", + "format:check": "prettier --check .", + "check:cycles": "madge --circular --extensions ts,tsx --ts-config tsconfig.json src", "generate:importmap": "cross-env NODE_OPTIONS=\"--no-deprecation --conditions=react-server\" payload generate:importmap", "generate:types": "cross-env NODE_OPTIONS=\"--no-deprecation --conditions=react-server\" payload generate:types", "migrate": "cross-env NODE_OPTIONS=\"--no-deprecation --conditions=react-server\" payload migrate", @@ -119,9 +122,11 @@ "husky": "^9.1.7", "jsdom": "28.0.0", "knip": "^6.27.0", + "madge": "^8.0.0", "pg": "^8.22.0", "postcss": "^8.5.6", "prettier": "^3.4.2", + "prettier-plugin-organize-imports": "^4.3.0", "schema-dts": "^2.0.0", "tailwindcss": "^4.1.18", "topojson-server": "^3.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 527093e5..2912c656 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -192,6 +192,9 @@ importers: knip: specifier: ^6.27.0 version: 6.27.0 + madge: + specifier: ^8.0.0 + version: 8.0.0(@typescript/typescript6@6.0.2) pg: specifier: ^8.22.0 version: 8.22.0 @@ -201,6 +204,9 @@ importers: prettier: specifier: ^3.4.2 version: 3.8.1 + prettier-plugin-organize-imports: + specifier: ^4.3.0 + version: 4.3.0(@typescript/typescript6@6.0.2)(prettier@3.8.1) schema-dts: specifier: ^2.0.0 version: 2.0.0(@typescript/typescript6@6.0.2) @@ -331,10 +337,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -348,6 +362,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-jsx@7.28.6': resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} engines: {node: '>=6.9.0'} @@ -410,6 +429,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + '@base-ui/react@1.6.0': resolution: {integrity: sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==} engines: {node: '>=14.0.0'} @@ -474,6 +497,14 @@ packages: '@date-fns/tz@1.2.0': resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} + '@dependents/detective-less@5.0.3': + resolution: {integrity: sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA==} + engines: {node: '>=18'} + + '@discoveryjs/json-ext@1.1.0': + resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} + engines: {node: '>=14.17.0'} + '@dnd-kit/accessibility@3.1.1': resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} peerDependencies: @@ -3010,6 +3041,22 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@ts-graphviz/adapter@2.0.6': + resolution: {integrity: sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==} + engines: {node: '>=18'} + + '@ts-graphviz/ast@2.0.7': + resolution: {integrity: sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==} + engines: {node: '>=18'} + + '@ts-graphviz/common@2.1.5': + resolution: {integrity: sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==} + engines: {node: '>=18'} + + '@ts-graphviz/core@2.0.7': + resolution: {integrity: sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==} + engines: {node: '>=18'} + '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} @@ -3161,6 +3208,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.65.0': + resolution: {integrity: sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@8.55.0': resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3171,6 +3224,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.65.0': + resolution: {integrity: sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.55.0': resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3182,12 +3241,22 @@ packages: resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.65.0': + resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.55.0': resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.65.0': + resolution: {integrity: sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.55.0': resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3199,6 +3268,10 @@ packages: resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.65.0': + resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} @@ -3514,6 +3587,21 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vue/compiler-core@3.5.40': + resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + + '@vue/compiler-dom@3.5.40': + resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + + '@vue/compiler-sfc@3.5.40': + resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + + '@vue/compiler-ssr@3.5.40': + resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==} + + '@vue/shared@3.5.40': + resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3581,10 +3669,16 @@ packages: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-module-path@2.2.0: + resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -3635,6 +3729,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-module-types@6.0.2: + resolution: {integrity: sha512-6KuK/7nZ/2Qh7sGuVEiwxjCxzTY2Pdb5mTo5z1e6/J8BA0tvjR7G8vQJKrQMTqwmnA3UPEyKIFX4YUS1DO1Hvw==} + engines: {node: '>=18'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -3676,6 +3774,9 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.9.19: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true @@ -3687,6 +3788,9 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -3719,6 +3823,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -3795,6 +3902,10 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -3814,6 +3925,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -3852,6 +3967,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -3859,6 +3978,13 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -4026,6 +4152,10 @@ packages: babel-plugin-macros: optional: true + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -4041,6 +4171,9 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -4057,6 +4190,11 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dependency-tree@11.5.0: + resolution: {integrity: sha512-K9zBwKDZrot3RkxizugpVSdImxULAg4Ycp3+ydy2r561k96oiiw6nfsOR15fwNDQ5BF2UXe+2JFM/H5Xz4MGQg==} + engines: {node: '>=18'} + hasBin: true + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -4072,6 +4210,49 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detective-amd@6.1.0: + resolution: {integrity: sha512-fmI6LGMvotqd49QaA3ZYw+q0aGp2yXmMjzIuY6fH9j9YFIXY/73yDhMwhX9cPbhWd+AH06NH1Di/LKOuCH0Ubg==} + engines: {node: '>=18'} + hasBin: true + + detective-cjs@6.1.1: + resolution: {integrity: sha512-pSh7mkCKEtLlmANqLu3KDFS3NV8Hx41jy/JF1/gAWOgU+Uo5QTkeI1tWNP4dWGo4L0E9j18Ez9EPsTleautKqA==} + engines: {node: '>=18'} + + detective-es6@5.0.2: + resolution: {integrity: sha512-+qHHGYhjupiVs4rnIpI9nZ5B130A4AmE35ZX1w33hb46vcZ7T3jfDbvmPw0FhWtMHn5BS5HHu7ZtnZ53bMcXZA==} + engines: {node: '>=18'} + + detective-postcss@8.0.4: + resolution: {integrity: sha512-DZ7M/hWPZyr17ZUdoQ+TVXaPj70mYr4XXrAE+GeJbca44haCvZgb191L/jLJmFYewhxRJuBd4lUtNSu986TXag==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4.47 + + detective-sass@6.0.2: + resolution: {integrity: sha512-i3xpXHDKS0qI2aFW4asQ7fqlPK00ndOVZELvQapFJCaF0VxYmsNWtd0AmvXbTLMk7bfO5VdIeorhY9KfmHVoVA==} + engines: {node: '>=18'} + + detective-scss@5.0.2: + resolution: {integrity: sha512-9JOEMZ8pDh3ShXmftq7hoQqqJsClaGgxo1hghfCeFlmKf5TC/Twtwb0PAaK8dXwpg9Z0uCmEYSrCxO+kel2eEg==} + engines: {node: '>=18'} + + detective-stylus@5.0.1: + resolution: {integrity: sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==} + engines: {node: '>=18'} + + detective-typescript@14.1.2: + resolution: {integrity: sha512-bIeEn0eVi/JRsE1YizBR2ilnMlWRAIBJJ6kXCKNFxEEWhUcEY3R6I3KYIAy48ieURbD1hcb3Ebvl8AqeoPMSzg==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + + detective-vue2@2.3.0: + resolution: {integrity: sha512-3gwbZPqVTm9sL9XdZsgEJ7x4x99O853VVZHapQAiEkGuMJMpFPjHDrecSgfqnS5JW3FJfYXesLZGvUOibjn49g==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -4230,6 +4411,10 @@ packages: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.24.3: + resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==} + engines: {node: '>=10.13.0'} + entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} @@ -4311,6 +4496,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-next@15.4.11: resolution: {integrity: sha512-JgvFtb0R8tFyGRRzPZhX4mX+bgllh0v8WP7VxlRXWrh829ONZCi+Bv8OGH4QeQjmFIvZ7hgdQBRXHPf2TYgvsA==} peerDependencies: @@ -4409,6 +4599,10 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4446,6 +4640,9 @@ packages: estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -4548,6 +4745,11 @@ packages: resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} engines: {node: '>=20'} + filing-cabinet@5.5.1: + resolution: {integrity: sha512-PzLBTChlVPn6LnNxF0KWs+XqPziVh3Sfmz/3TXOymHxu6a9yhrDcQn7YwgpcRM6mqhR2WHVGPR8RU4fmcF1IVA==} + engines: {node: '>=18'} + hasBin: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -4643,6 +4845,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-amd-module-type@6.0.2: + resolution: {integrity: sha512-7zShVYAYtMnj9S65CfN+hvpBCByfuB1OY8xID01nZEzXTZbx4YyysAfi+nMl95JSR6odt4q8TCj2W63KAoyVLQ==} + engines: {node: '>=18'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -4663,6 +4869,9 @@ packages: resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} engines: {node: '>=14.16'} + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -4715,6 +4924,11 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gonzales-pe@4.3.0: + resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} + engines: {node: '>=0.6.0'} + hasBin: true + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4973,6 +5187,10 @@ packages: engines: {node: '>=14.16'} hasBin: true + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -4996,6 +5214,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + is-obj@3.0.0: resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} engines: {node: '>=12'} @@ -5014,6 +5236,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + is-regexp@3.1.0: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} engines: {node: '>=12'} @@ -5046,6 +5272,10 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -5054,6 +5284,10 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-url-superb@4.0.0: + resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} + engines: {node: '>=10'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -5298,6 +5532,10 @@ packages: lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} @@ -5325,6 +5563,16 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + madge@8.0.0: + resolution: {integrity: sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: ^5.4.4 + peerDependenciesMeta: + typescript: + optional: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -5488,6 +5736,16 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + module-definition@6.0.2: + resolution: {integrity: sha512-SvAU3lB0+Yjbq55yHY3wkRZBOh+fhU1SnIF3IFbTewv6mtAh7yUT8ACHAJ2mGIJ7tCes2QuCL/cl6m0JSZ/ArA==} + engines: {node: '>=18'} + hasBin: true + + module-lookup-amd@9.1.3: + resolution: {integrity: sha512-Jc3XmOaR9FdfMJSK8+vyLgsCkzm8z2L0NS6vrlRWi12DjS7MY7TMNE7E1yj8yXx837xtMDbKSSgcdXnFlJ2YLg==} + engines: {node: '>=18'} + hasBin: true + monaco-editor@0.55.1: resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==} @@ -5513,6 +5771,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -5564,6 +5827,10 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-source-walk@7.0.2: + resolution: {integrity: sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A==} + engines: {node: '>=18'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -5648,6 +5915,10 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} @@ -5688,6 +5959,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@2.1.0: + resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} + engines: {node: '>=6'} + parse-ms@4.0.0: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} @@ -5864,10 +6139,20 @@ packages: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} + postcss-values-parser@6.0.2: + resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.2.9 + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.22: + resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -5911,10 +6196,25 @@ packages: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} + precinct@12.3.2: + resolution: {integrity: sha512-JbJevI1K80z8e/WIyDt/4vUN/4qcfBSKKqOjJA4mosPPPb7zODKRJQV7YN7apVWN3k58nZYm/vEsLgEGYmnxwg==} + engines: {node: '>=18'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-organize-imports@4.3.0: + resolution: {integrity: sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw==} + peerDependencies: + prettier: '>=2.0' + typescript: '>=2.9' + vue-tsc: ^2.1.0 || 3 + peerDependenciesMeta: + vue-tsc: + optional: true + prettier@3.8.1: resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} @@ -5924,6 +6224,10 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-ms@7.0.1: + resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} + engines: {node: '>=10'} + pretty-ms@9.3.0: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} @@ -5967,6 +6271,9 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quote-unquote@1.0.0: + resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} + radix-ui@1.4.3: resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} peerDependencies: @@ -5988,6 +6295,10 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-datepicker@7.6.0: resolution: {integrity: sha512-9cQH6Z/qa4LrGhzdc3XoHbhrxNcMi9MKjZmYgF/1MNNaJwvdSjv3Xd+jjvrEEbKEf71ZgCA3n7fQbdwd70qCRw==} peerDependencies: @@ -6076,6 +6387,10 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -6104,9 +6419,22 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + requirejs-config-file@4.0.0: + resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} + engines: {node: '>=10.13.0'} + + requirejs@2.3.8: + resolution: {integrity: sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==} + engines: {node: '>=0.4.0'} + hasBin: true + reselect@5.2.0: resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} + resolve-dependency-path@4.0.1: + resolution: {integrity: sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ==} + engines: {node: '>=18'} + resolve-dir@1.0.1: resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} engines: {node: '>=0.10.0'} @@ -6123,10 +6451,19 @@ packages: engines: {node: '>= 0.4'} hasBin: true + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -6162,6 +6499,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -6180,6 +6520,11 @@ packages: sanitize-filename@1.6.3: resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + sass-lookup@6.1.2: + resolution: {integrity: sha512-GjmndmKQBtlPil79RK72L7yc5kDXZPCQeH97bP8R8DcxtXQJO6vECExb3WP/m6+cxaV9h4ZxrSRvCkPG2v/VSw==} + engines: {node: '>=18'} + hasBin: true + sass@1.77.4: resolution: {integrity: sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==} engines: {node: '>=14.0.0'} @@ -6363,6 +6708,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-to-array@2.3.0: + resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -6401,9 +6749,16 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + stringify-object@5.0.0: resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} engines: {node: '>=14.16'} @@ -6428,6 +6783,10 @@ packages: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -6456,6 +6815,11 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylus-lookup@6.1.2: + resolution: {integrity: sha512-O+Q/SJ8s1X2aMLh4213fQ9X/bND9M3dhSsyTRe+O1OXPcewGLiYmAtKCrnP7FDvDBaXB2ZHPkCt3zi4cJXBlCQ==} + engines: {node: '>=18'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -6489,6 +6853,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} @@ -6575,6 +6943,12 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-essentials@10.0.3: resolution: {integrity: sha512-/FrVAZ76JLTWxJOERk04fm8hYENDo0PWSP3YLQKxevLwWtxemGcl5JJEzN4iqfDlRve0ckyfFaOBu4xbNH/wZw==} peerDependencies: @@ -6583,6 +6957,10 @@ packages: typescript: optional: true + ts-graphviz@2.1.6: + resolution: {integrity: sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==} + engines: {node: '>=18'} + ts-morph@26.0.0: resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} @@ -6642,6 +7020,11 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -6873,6 +7256,13 @@ packages: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} + walkdir@0.4.1: + resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} + engines: {node: '>=6.0.0'} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -7193,8 +7583,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.6': @@ -7206,6 +7600,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -7283,6 +7681,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@base-ui/react@1.6.0(@date-fns/tz@1.2.0)(@types/react@19.2.14)(date-fns@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.29.7 @@ -7334,6 +7737,13 @@ snapshots: '@date-fns/tz@1.2.0': {} + '@dependents/detective-less@5.0.3': + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + '@discoveryjs/json-ext@1.1.0': {} + '@dnd-kit/accessibility@3.1.1(react@19.2.4)': dependencies: react: 19.2.4 @@ -9725,6 +10135,21 @@ snapshots: '@tokenizer/token@0.3.0': {} + '@ts-graphviz/adapter@2.0.6': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/ast@2.0.7': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/common@2.1.5': {} + + '@ts-graphviz/core@2.0.7': + dependencies: + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 @@ -9914,6 +10339,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.65.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) + '@typescript-eslint/types': 8.65.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.55.0': dependencies: '@typescript-eslint/types': 8.55.0 @@ -9923,6 +10357,10 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' + '@typescript-eslint/tsconfig-utils@8.65.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@typescript-eslint/type-utils@8.55.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(jiti@2.7.0))': dependencies: '@typescript-eslint/types': 8.55.0 @@ -9937,6 +10375,8 @@ snapshots: '@typescript-eslint/types@8.55.0': {} + '@typescript-eslint/types@8.65.0': {} + '@typescript-eslint/typescript-estree@8.55.0(@typescript/typescript6@6.0.2)': dependencies: '@typescript-eslint/project-service': 8.55.0(@typescript/typescript6@6.0.2) @@ -9952,6 +10392,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.65.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.65.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3) + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/visitor-keys': 8.65.0 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.55.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(jiti@2.7.0))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.7.0)) @@ -9968,6 +10423,11 @@ snapshots: '@typescript-eslint/types': 8.55.0 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.65.0': + dependencies: + '@typescript-eslint/types': 8.65.0 + eslint-visitor-keys: 5.0.1 + '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -10161,6 +10621,38 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@vue/compiler-core@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.40 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.40': + dependencies: + '@vue/compiler-core': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/compiler-sfc@3.5.40': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.40 + '@vue/compiler-dom': 3.5.40 + '@vue/compiler-ssr': 3.5.40 + '@vue/shared': 3.5.40 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.22 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.40': + dependencies: + '@vue/compiler-dom': 3.5.40 + '@vue/shared': 3.5.40 + + '@vue/shared@3.5.40': {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -10222,11 +10714,15 @@ snapshots: ansis@4.2.0: {} + any-promise@1.3.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + app-module-path@2.2.0: {} + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -10308,6 +10804,8 @@ snapshots: assertion-error@2.0.1: {} + ast-module-types@6.0.2: {} + ast-types-flow@0.0.8: {} ast-types@0.16.1: @@ -10340,6 +10838,8 @@ snapshots: balanced-match@4.0.4: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.9.19: {} bidi-js@1.0.3: @@ -10348,6 +10848,12 @@ snapshots: binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -10393,6 +10899,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -10468,6 +10979,10 @@ snapshots: dependencies: clsx: 2.1.1 + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -10484,6 +10999,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone@1.0.4: {} + clsx@2.1.1: {} cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): @@ -10522,10 +11039,16 @@ snapshots: commander@11.1.0: {} + commander@12.1.0: {} + commander@14.0.3: {} commander@2.20.3: {} + commander@7.2.0: {} + + commondir@1.0.1: {} + concat-map@0.0.1: {} console-table-printer@2.12.1: @@ -10665,6 +11188,8 @@ snapshots: optionalDependencies: babel-plugin-macros: 3.1.0 + deep-extend@0.6.0: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -10676,6 +11201,10 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + defaults@1.0.4: + dependencies: + clone: 1.0.4 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -10692,6 +11221,16 @@ snapshots: depd@2.0.0: {} + dependency-tree@11.5.0: + dependencies: + '@discoveryjs/json-ext': 1.1.0 + commander: 12.1.0 + filing-cabinet: 5.5.1 + precinct: 12.3.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + dequal@2.0.3: {} detect-file@1.0.0: {} @@ -10700,6 +11239,62 @@ snapshots: detect-node-es@1.1.0: {} + detective-amd@6.1.0: + dependencies: + ast-module-types: 6.0.2 + escodegen: 2.1.0 + get-amd-module-type: 6.0.2 + node-source-walk: 7.0.2 + + detective-cjs@6.1.1: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + + detective-es6@5.0.2: + dependencies: + node-source-walk: 7.0.2 + + detective-postcss@8.0.4(postcss@8.5.22): + dependencies: + is-url-superb: 4.0.0 + postcss: 8.5.22 + postcss-values-parser: 6.0.2(postcss@8.5.22) + + detective-sass@6.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-scss@5.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-stylus@5.0.1: {} + + detective-typescript@14.1.2(typescript@5.9.3): + dependencies: + '@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3) + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + detective-vue2@2.3.0(typescript@5.9.3): + dependencies: + '@dependents/detective-less': 5.0.3 + '@vue/compiler-sfc': 3.5.40 + detective-es6: 5.0.2 + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -10773,6 +11368,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.24.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + entities@6.0.1: {} entities@7.0.1: {} @@ -10982,6 +11582,14 @@ snapshots: escape-string-regexp@4.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-config-next@15.4.11(@typescript/typescript6@6.0.2)(eslint@9.39.2(jiti@2.7.0)): dependencies: '@next/eslint-plugin-next': 15.4.11 @@ -11129,6 +11737,8 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} + eslint@9.39.2(jiti@2.7.0): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.7.0)) @@ -11195,6 +11805,8 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/unist': 3.0.3 + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -11346,6 +11958,20 @@ snapshots: transitivePeerDependencies: - supports-color + filing-cabinet@5.5.1: + dependencies: + app-module-path: 2.2.0 + commander: 12.1.0 + enhanced-resolve: 5.24.3 + module-definition: 6.0.2 + module-lookup-amd: 9.1.3 + resolve: 1.22.12 + resolve-dependency-path: 4.0.1 + sass-lookup: 6.1.2 + stylus-lookup: 6.1.2 + tsconfig-paths: 4.2.0 + typescript: 5.9.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -11442,6 +12068,11 @@ snapshots: gensync@1.0.0-beta.2: {} + get-amd-module-type@6.0.2: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + get-caller-file@2.0.5: {} get-east-asian-width@1.5.0: {} @@ -11463,6 +12094,8 @@ snapshots: get-own-enumerable-keys@1.0.0: {} + get-own-enumerable-property-symbols@3.0.2: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -11524,6 +12157,10 @@ snapshots: globrex@0.1.2: {} + gonzales-pe@4.3.0: + dependencies: + minimist: 1.2.8 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -11759,6 +12396,8 @@ snapshots: dependencies: is-docker: 3.0.0 + is-interactive@1.0.0: {} + is-interactive@2.0.0: {} is-map@2.0.3: {} @@ -11774,6 +12413,8 @@ snapshots: is-number@7.0.0: {} + is-obj@1.0.1: {} + is-obj@3.0.0: {} is-plain-obj@4.1.0: {} @@ -11789,6 +12430,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-regexp@1.0.0: {} + is-regexp@3.1.0: {} is-set@2.0.3: {} @@ -11816,10 +12459,14 @@ snapshots: dependencies: which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} + is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} + is-url-superb@4.0.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -12049,6 +12696,11 @@ snapshots: lodash@4.17.23: {} + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + log-symbols@6.0.0: dependencies: chalk: 5.6.2 @@ -12072,6 +12724,25 @@ snapshots: lz-string@1.5.0: {} + madge@8.0.0(@typescript/typescript6@6.0.2): + dependencies: + chalk: 4.1.2 + commander: 7.2.0 + commondir: 1.0.1 + debug: 4.4.3 + dependency-tree: 11.5.0 + ora: 5.4.1 + pluralize: 8.0.0 + pretty-ms: 7.0.1 + rc: 1.2.8 + stream-to-array: 2.3.0 + ts-graphviz: 2.1.6 + walkdir: 0.4.1 + optionalDependencies: + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -12357,6 +13028,17 @@ snapshots: minimist@1.2.8: {} + module-definition@6.0.2: + dependencies: + ast-module-types: 6.0.2 + node-source-walk: 7.0.2 + + module-lookup-amd@9.1.3: + dependencies: + commander: 12.1.0 + requirejs: 2.3.8 + requirejs-config-file: 4.0.0 + monaco-editor@0.55.1: dependencies: dompurify: 3.2.7 @@ -12393,6 +13075,8 @@ snapshots: nanoid@3.3.11: {} + nanoid@3.3.16: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -12439,6 +13123,10 @@ snapshots: node-releases@2.0.27: {} + node-source-walk@7.0.2: + dependencies: + '@babel/parser': 7.29.0 + normalize-path@3.0.0: {} npm-run-path@4.0.1: @@ -12536,6 +13224,18 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + ora@8.2.0: dependencies: chalk: 5.6.2 @@ -12634,6 +13334,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@2.1.0: {} + parse-ms@4.0.0: {} parse-passwd@1.0.0: {} @@ -12835,12 +13537,25 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-values-parser@6.0.2(postcss@8.5.22): + dependencies: + color-name: 1.1.4 + is-url-superb: 4.0.0 + postcss: 8.5.22 + quote-unquote: 1.0.0 + postcss@8.4.31: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.22: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -12871,8 +13586,33 @@ snapshots: powershell-utils@0.1.0: {} + precinct@12.3.2: + dependencies: + '@dependents/detective-less': 5.0.3 + commander: 12.1.0 + detective-amd: 6.1.0 + detective-cjs: 6.1.1 + detective-es6: 5.0.2 + detective-postcss: 8.0.4(postcss@8.5.22) + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + detective-vue2: 2.3.0(typescript@5.9.3) + module-definition: 6.0.2 + node-source-walk: 7.0.2 + postcss: 8.5.22 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + prelude-ls@1.2.1: {} + prettier-plugin-organize-imports@4.3.0(@typescript/typescript6@6.0.2)(prettier@3.8.1): + dependencies: + prettier: 3.8.1 + typescript: '@typescript/typescript6@6.0.2' + prettier@3.8.1: {} pretty-format@27.5.1: @@ -12881,6 +13621,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-ms@7.0.1: + dependencies: + parse-ms: 2.1.0 + pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 @@ -12922,6 +13666,8 @@ snapshots: quick-format-unescaped@4.0.4: {} + quote-unquote@1.0.0: {} + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@radix-ui/primitive': 1.1.3 @@ -12994,6 +13740,13 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-datepicker@7.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@floating-ui/react': 0.27.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13085,6 +13838,12 @@ snapshots: react@19.2.4: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -13123,8 +13882,17 @@ snapshots: require-from-string@2.0.2: {} + requirejs-config-file@4.0.0: + dependencies: + esprima: 4.0.1 + stringify-object: 3.3.0 + + requirejs@2.3.8: {} + reselect@5.2.0: {} + resolve-dependency-path@4.0.1: {} + resolve-dir@1.0.1: dependencies: expand-tilde: 2.0.2 @@ -13140,12 +13908,24 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -13212,6 +13992,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -13231,6 +14013,11 @@ snapshots: dependencies: truncate-utf8-bytes: 1.0.2 + sass-lookup@6.1.2: + dependencies: + commander: 12.1.0 + enhanced-resolve: 5.24.3 + sass@1.77.4: dependencies: chokidar: 3.6.0 @@ -13514,6 +14301,10 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-to-array@2.3.0: + dependencies: + any-promise: 1.3.0 + streamsearch@1.1.0: {} strict-event-emitter@0.5.1: {} @@ -13580,11 +14371,21 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + stringify-object@5.0.0: dependencies: get-own-enumerable-keys: 1.0.0 @@ -13605,6 +14406,8 @@ snapshots: strip-final-newline@4.0.0: {} + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strip-json-comments@5.0.3: {} @@ -13623,6 +14426,10 @@ snapshots: stylis@4.2.0: {} + stylus-lookup@6.1.2: + dependencies: + commander: 12.1.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -13645,6 +14452,8 @@ snapshots: tapable@2.3.0: {} + tapable@2.3.3: {} + thread-stream@3.1.0: dependencies: real-require: 0.2.0 @@ -13726,10 +14535,21 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-essentials@10.0.3(@typescript/typescript6@6.0.2): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' + ts-graphviz@2.1.6: + dependencies: + '@ts-graphviz/adapter': 2.0.6 + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-graphviz/core': 2.0.7 + ts-morph@26.0.0: dependencies: '@ts-morph/common': 0.27.0 @@ -13810,6 +14630,8 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typescript@5.9.3: {} + typescript@6.0.3: {} typescript@7.0.2: @@ -14038,6 +14860,12 @@ snapshots: walk-up-path@4.0.0: {} + walkdir@0.4.1: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} webidl-conversions@8.0.1: {} diff --git a/scripts/build-bahia-demographics.mjs b/scripts/build-bahia-demographics.mjs index 2a03dfcb..bae6a71a 100644 --- a/scripts/build-bahia-demographics.mjs +++ b/scripts/build-bahia-demographics.mjs @@ -132,8 +132,7 @@ const buildDemographicsByCode = ({ ageCategoryRows, femaleRows, medianRows }) => for (const record of byCode.values()) { record.population = Object.values(record.ageBands).reduce((sum, value) => sum + value, 0) - record.sexShareFemale = - record.population > 0 ? record.femalePopulation / record.population : 0 + record.sexShareFemale = record.population > 0 ? record.femalePopulation / record.population : 0 delete record.femalePopulation } @@ -141,14 +140,15 @@ const buildDemographicsByCode = ({ ageCategoryRows, femaleRows, medianRows }) => } const buildModuleSource = (demographicsByCode, provenance) => { - const entries = [...demographicsByCode.entries()].sort(([left], [right]) => left.localeCompare(right)) + const entries = [...demographicsByCode.entries()].sort(([left], [right]) => + left.localeCompare(right), + ) const lines = entries .map(([code, record]) => { const bands = Object.entries(record.ageBands) .map(([band, value]) => `${JSON.stringify(band)}: ${value}`) .join(', ') - const median = - record.medianAge == null ? 'null' : JSON.stringify(record.medianAge) + const median = record.medianAge == null ? 'null' : JSON.stringify(record.medianAge) const sexShare = record.sexShareFemale.toFixed(6) return ` ${JSON.stringify(code)}: { population: ${record.population}, ageBands: { ${bands} }, sexShareFemale: ${sexShare}, medianAge: ${median} },` }) diff --git a/scripts/build-bahia-geometries.mjs b/scripts/build-bahia-geometries.mjs index 440d5684..f39adfc1 100644 --- a/scripts/build-bahia-geometries.mjs +++ b/scripts/build-bahia-geometries.mjs @@ -22,20 +22,18 @@ */ import { createHash } from 'node:crypto' -import { mkdir, readFile, writeFile, access } from 'node:fs/promises' +import { access, mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' -import { topology } from 'topojson-server' import { merge, quantize } from 'topojson-client' -import { presimplify, simplify, quantile } from 'topojson-simplify' +import { topology } from 'topojson-server' +import { presimplify, quantile, simplify } from 'topojson-simplify' -const { canonicalizeMunicipalityName, UnknownMunicipalityError } = await import( - '../src/lib/electionResults.ts' -) -const { bahiaMunicipalities, bahiaIdentityTerritoryRecords } = await import( - '../src/lib/bahiaTerritories.ts' -) +const { canonicalizeMunicipalityName, UnknownMunicipalityError } = + await import('../src/lib/electionResults.ts') +const { bahiaMunicipalities, bahiaIdentityTerritoryRecords } = + await import('../src/lib/bahiaTerritories.ts') const { downloadToBuffer } = await import('../src/lib/electionResultsZip.ts') const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..') @@ -224,7 +222,8 @@ const main = async () => { const memberGeometries = territory.municipalities.map((name) => { const code = codeByCanonical.get(name) const geometry = geometriesByCodarea.get(code) - if (!geometry) die(`Missing municipality geometry for ${name} (${code}) in territory ${territory.code}`) + if (!geometry) + die(`Missing municipality geometry for ${name} (${code}) in territory ${territory.code}`) return geometry }) return { diff --git a/scripts/build-bahia-tse-city-codes.mjs b/scripts/build-bahia-tse-city-codes.mjs index a2c8f201..f5113f39 100644 --- a/scripts/build-bahia-tse-city-codes.mjs +++ b/scripts/build-bahia-tse-city-codes.mjs @@ -16,13 +16,12 @@ */ import { createHash } from 'node:crypto' -import { mkdir, readFile, writeFile, access } from 'node:fs/promises' +import { access, mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' -const { canonicalizeMunicipalityName, UnknownMunicipalityError } = await import( - '../src/lib/electionResults.ts' -) +const { canonicalizeMunicipalityName, UnknownMunicipalityError } = + await import('../src/lib/electionResults.ts') const { bahiaMunicipalities } = await import('../src/lib/bahiaTerritories.ts') const { parseTseCsvBuffer } = await import('../src/lib/electionResultsCsv.ts') const { downloadToBuffer, readZipEntry } = await import('../src/lib/electionResultsZip.ts') @@ -160,7 +159,9 @@ const main = async () => { const missing = bahiaMunicipalities.filter((city) => !codeByCanonical.has(city)) if (missing.length > 0) { - die(`Missing TSE city codes for: ${missing.slice(0, 5).join(', ')}${missing.length > 5 ? '…' : ''}`) + die( + `Missing TSE city codes for: ${missing.slice(0, 5).join(', ')}${missing.length > 5 ? '…' : ''}`, + ) } const accessed = new Date().toISOString().slice(0, 10) diff --git a/scripts/build-election-aggregates.mjs b/scripts/build-election-aggregates.mjs index e085eac8..bf6e0c56 100644 --- a/scripts/build-election-aggregates.mjs +++ b/scripts/build-election-aggregates.mjs @@ -27,10 +27,10 @@ * writes only src/lib/electionAggregates/. */ +import { config as loadEnv } from 'dotenv' import { mkdir, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' -import { config as loadEnv } from 'dotenv' import { getPayload } from 'payload' import { assertLocalDatabase } from './assert-local-database.mjs' @@ -42,9 +42,8 @@ const payloadConfig = (await import('../src/payload.config.ts')).default const { BASELINE_TICKET_2022, ELECTION_YEAR_2022, FEDERAL_DEPUTY_OFFICE, HISTORICAL_SERIES_YEARS } = await import('../src/lib/electionResults.ts') const { municipalityCatalog } = await import('../src/lib/municipalityCatalog.ts') -const { municipalityElectionGeography } = await import( - '../src/utilities/municipalityElectionGeography.ts' -) +const { municipalityElectionGeography } = + await import('../src/utilities/municipalityElectionGeography.ts') const { loadCampoFederalVotesByCityZone, loadCandidateVotesByCityZone, @@ -84,7 +83,11 @@ for (const year of HISTORICAL_SERIES_YEARS) { loadCandidateVotesByCityZone(payload, cliReader, { year, candidateNumber }), loadValidVotesByCityZone(payload, cliReader, { year }), loadCampoFederalVotesByCityZone(payload, cliReader, { year }), - loadOfficeTallyByCityZone(payload, cliReader, { year, office: FEDERAL_DEPUTY_OFFICE, turn: '1' }), + loadOfficeTallyByCityZone(payload, cliReader, { + year, + office: FEDERAL_DEPUTY_OFFICE, + turn: '1', + }), ]) if (candidateVotes.size === 0) { @@ -119,8 +122,16 @@ for (const year of HISTORICAL_SERIES_YEARS) { // 2022-only: majoritarian (presidente/governador #13) votes + tally, used for // "teto do campo" and roll-off — no majoritária seeded for 2014/2018 (E8 audit). const majoritarianOffices = [ - { key: 'president', office: 'presidente', candidateNumber: BASELINE_TICKET_2022.president.candidateNumber }, - { key: 'governor', office: 'governador', candidateNumber: BASELINE_TICKET_2022.governor.candidateNumber }, + { + key: 'president', + office: 'presidente', + candidateNumber: BASELINE_TICKET_2022.president.candidateNumber, + }, + { + key: 'governor', + office: 'governador', + candidateNumber: BASELINE_TICKET_2022.governor.candidateNumber, + }, ] const majoritarianByOffice = {} diff --git a/scripts/import-projecao.mjs b/scripts/import-projecao.mjs index 5edb10e4..38157280 100644 --- a/scripts/import-projecao.mjs +++ b/scripts/import-projecao.mjs @@ -274,8 +274,13 @@ const { canonicalizeMunicipalityName, UnknownMunicipalityError, normalizeMunicip const { municipalityCatalogEntriesForCity } = await import('../src/lib/municipalityCatalog.ts') const { getMunicipalityFederalBaseline } = await import('../src/lib/bahiaElectionAggregates.ts') const { territoryForCity } = await import('../src/lib/bahiaTerritories.ts') -const { parseExpectationCell, mapSheetPriority, parseSheetNumber, parseSituationCell, splitNameCell } = - await import('../src/lib/projectionSheetParse.ts') +const { + parseExpectationCell, + mapSheetPriority, + parseSheetNumber, + parseSituationCell, + splitNameCell, +} = await import('../src/lib/projectionSheetParse.ts') const { getVoteEstimateOrderViolation, toVoteEstimateScenarioViewModel, @@ -512,12 +517,12 @@ for (const sheetRow of canonicalRows.values()) { priorityRaw: sheetRow.priorityRaw, hasStrategy: Boolean( sheetRow.situationRaw || - sheetRow.dobradinhasRaw || - sheetRow.leadershipsRaw || - sheetRow.advisorsRaw || - sheetRow.nextStepsRaw || - sheetRow.observationRaw || - prioritariaRow, + sheetRow.dobradinhasRaw || + sheetRow.leadershipsRaw || + sheetRow.advisorsRaw || + sheetRow.nextStepsRaw || + sheetRow.observationRaw || + prioritariaRow, ), }) continue @@ -988,8 +993,11 @@ if (staffPlans.size > 0) { const status = plan.existing ? `casa com usuário existente #${plan.existing.id} (${plan.existing.name}, ${plan.existing.role})` : `criar (papel ${plan.role}, sem credenciais)` - const variants = plan.entry.variants.size > 1 ? ` · variantes: ${formatVariants(plan.entry)}` : '' - console.log(` ${plan.name}: ${status} · ${plan.entry.municipalityIds.size} município(s)${variants}`) + const variants = + plan.entry.variants.size > 1 ? ` · variantes: ${formatVariants(plan.entry)}` : '' + console.log( + ` ${plan.name}: ${status} · ${plan.entry.municipalityIds.size} município(s)${variants}`, + ) } } @@ -997,8 +1005,11 @@ if (deputyPlans.size > 0) { console.log('\n— Dobradinhas (deputados estaduais) —') for (const plan of [...deputyPlans.values()].sort((a, b) => a.name.localeCompare(b.name))) { const status = plan.existing ? `casa com dobradinha existente #${plan.existing.id}` : 'criar' - const variants = plan.entry.variants.size > 1 ? ` · variantes: ${formatVariants(plan.entry)}` : '' - console.log(` ${plan.name}: ${status} · ${plan.entry.municipalityIds.size} município(s)${variants}`) + const variants = + plan.entry.variants.size > 1 ? ` · variantes: ${formatVariants(plan.entry)}` : '' + console.log( + ` ${plan.name}: ${status} · ${plan.entry.municipalityIds.size} município(s)${variants}`, + ) } } @@ -1025,7 +1036,9 @@ if (prioritariaOverrides.length > 0) { } if (skippedSegments.length > 0) { - console.log('\n— Segmentos descartados (notas/coletivos/incertezas — permanecem só na planilha) —') + console.log( + '\n— Segmentos descartados (notas/coletivos/incertezas — permanecem só na planilha) —', + ) for (const skipped of skippedSegments.slice(0, 80)) { console.log(` [${skipped.label}] ${skipped.column}: ${JSON.stringify(skipped.segment)}`) } @@ -1180,7 +1193,13 @@ try { municipalitiesUpdated += 1 } - return { usersCreated, deputiesCreated, leadershipsCreated, leadershipsLinked, municipalitiesUpdated } + return { + usersCreated, + deputiesCreated, + leadershipsCreated, + leadershipsLinked, + municipalitiesUpdated, + } }) console.log( diff --git a/scripts/seed-posts.mjs b/scripts/seed-posts.mjs index 79aff666..c2d89f49 100644 --- a/scripts/seed-posts.mjs +++ b/scripts/seed-posts.mjs @@ -23,10 +23,10 @@ * pnpm db:seed:posts * (against a remote DB, on purpose:) ALLOW_REMOTE_DB=true pnpm db:seed:posts */ -import { config as loadEnv } from 'dotenv' -import { JSDOM } from 'jsdom' import { convertHTMLToLexical, editorConfigFactory } from '@payloadcms/richtext-lexical' import { del as blobDel } from '@vercel/blob' +import { config as loadEnv } from 'dotenv' +import { JSDOM } from 'jsdom' import { getPayload } from 'payload' import { assertLocalDatabase } from './assert-local-database.mjs' @@ -223,7 +223,9 @@ async function fetchViaRestApi() { // --------------------------------------------------------------------------- const FALLBACK_CATEGORIES = ['noticias', 'uncategorized'] -const PERMALINK_RE = new RegExp(`^${BASE_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/\\d{4}/\\d{2}/\\d{2}/[^/]+/?$`) +const PERMALINK_RE = new RegExp( + `^${BASE_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/\\d{4}/\\d{2}/\\d{2}/[^/]+/?$`, +) async function fetchViaHtmlArchive() { /** @type {Article[]} */ @@ -272,8 +274,7 @@ async function fetchSinglePage(url) { const doc = new JSDOM(await res.text()).window.document const slug = url.replace(/\/$/, '').split('/').pop() - const contentEl = - doc.querySelector('.entry-content') || doc.querySelector('article') || doc.body + const contentEl = doc.querySelector('.entry-content') || doc.querySelector('article') || doc.body const time = doc.querySelector('time[datetime]') const firstImg = contentEl.querySelector('img') diff --git a/scripts/seed-tse-results.mjs b/scripts/seed-tse-results.mjs index 172a6278..cd5a1848 100644 --- a/scripts/seed-tse-results.mjs +++ b/scripts/seed-tse-results.mjs @@ -33,10 +33,10 @@ * TSE_CACHE_DIR=./data/tse pnpm db:seed:tse */ -import { createHash } from 'node:crypto' -import { mkdir, readFile, writeFile, access } from 'node:fs/promises' -import { join } from 'node:path' import { config as loadEnv } from 'dotenv' +import { createHash } from 'node:crypto' +import { access, mkdir, readFile, writeFile } from 'node:fs/promises' +import { join } from 'node:path' import { getPayload } from 'payload' import { assertLocalDatabase } from './assert-local-database.mjs' @@ -46,13 +46,11 @@ loadEnv({ path: '.env' }) const config = (await import('../src/payload.config.ts')).default const { parseTseCsvBuffer } = await import('../src/lib/electionResultsCsv.ts') -const { buildElectionResultsFromCsvRows, HISTORICAL_BASELINE_OFFICES } = await import( - '../src/lib/electionResultsBuild.ts' -) +const { buildElectionResultsFromCsvRows, HISTORICAL_BASELINE_OFFICES } = + await import('../src/lib/electionResultsBuild.ts') const { downloadToBuffer, readZipEntry } = await import('../src/lib/electionResultsZip.ts') -const { buildImportBundles, importElectionBundles } = await import( - '../src/utilities/electionResultsImport.ts' -) +const { buildImportBundles, importElectionBundles } = + await import('../src/utilities/electionResultsImport.ts') const SUPPORTED_YEARS = [2014, 2018, 2022] diff --git a/src/app/(campaign)/campanha/(app)/apoiadores/[id]/page.tsx b/src/app/(campaign)/campanha/(app)/apoiadores/[id]/page.tsx index be38e448..eb48fec9 100644 --- a/src/app/(campaign)/campanha/(app)/apoiadores/[id]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/apoiadores/[id]/page.tsx @@ -13,8 +13,8 @@ import { VoteIntentionControl } from '@/components/campaign/supporter/VoteIntent import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { campaignRoleLabels } from '@/utilities/campaignUserProfile' import { getCampaignUser } from '@/utilities/campaignAuth' +import { campaignRoleLabels } from '@/utilities/campaignUserProfile' import { loadSupporterDetailConsentData, loadSupporterDetailPageData, diff --git a/src/app/(campaign)/campanha/(app)/apoiadores/importar/page.tsx b/src/app/(campaign)/campanha/(app)/apoiadores/importar/page.tsx index 8ed0698f..97fa1023 100644 --- a/src/app/(campaign)/campanha/(app)/apoiadores/importar/page.tsx +++ b/src/app/(campaign)/campanha/(app)/apoiadores/importar/page.tsx @@ -4,8 +4,8 @@ import { redirect } from 'next/navigation' import { CampaignScopeBadge } from '@/components/campaign/shared/CampaignScopeBadge' import { SupporterImportWizard } from '@/components/campaign/supporter/SupporterImportWizard' import { Button } from '@/components/ui/button' -import { getCampaignUser } from '@/utilities/campaignAuth' import { isCampaignCoordinator } from '@/utilities/campaignAccess' +import { getCampaignUser } from '@/utilities/campaignAuth' export default async function ImportSupportersPage() { const user = await getCampaignUser() diff --git a/src/app/(campaign)/campanha/(app)/apoiadores/novo/page.tsx b/src/app/(campaign)/campanha/(app)/apoiadores/novo/page.tsx index d7a39fec..4c4d680c 100644 --- a/src/app/(campaign)/campanha/(app)/apoiadores/novo/page.tsx +++ b/src/app/(campaign)/campanha/(app)/apoiadores/novo/page.tsx @@ -7,8 +7,8 @@ import { createSupporterFormAction } from '@/app/(campaign)/campanha/(app)/apoia import { CampaignScopeBadge } from '@/components/campaign/shared/CampaignScopeBadge' import { SupporterForm } from '@/components/campaign/supporter/SupporterForm' import { Button } from '@/components/ui/button' -import { campaignRoleLabels } from '@/utilities/campaignUserProfile' import { getCampaignUser } from '@/utilities/campaignAuth' +import { campaignRoleLabels } from '@/utilities/campaignUserProfile' import { loadSupporterCreatePageData } from '@/utilities/supporterPageData' import { canAccessSupporterArea } from '@/utilities/supporterUi' diff --git a/src/app/(campaign)/campanha/(app)/assessores/[id]/page.tsx b/src/app/(campaign)/campanha/(app)/assessores/[id]/page.tsx index d41d6140..53aaff30 100644 --- a/src/app/(campaign)/campanha/(app)/assessores/[id]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/assessores/[id]/page.tsx @@ -9,15 +9,12 @@ import { AdvisorPasswordResetButton } from '@/components/campaign/advisor/Adviso import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' +import { formatBrazilianPhoneInput } from '@/lib/phone' import { isPlanilhaPlaceholderEmail } from '@/lib/schemas/advisor' import { loadAdvisorDetail } from '@/utilities/advisorData' import { isCampaignUnrestricted } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' -import { formatBrazilianPhoneInput } from '@/lib/phone' -import { - sendAdvisorPasswordResetFormAction, - updateAdvisorProfileFormAction, -} from '../formActions' +import { sendAdvisorPasswordResetFormAction, updateAdvisorProfileFormAction } from '../formActions' type AdvisorDetailPageProps = { params: Promise<{ id: string }> @@ -35,8 +32,7 @@ export default async function AdvisorDetailPage({ params }: AdvisorDetailPagePro const advisor = await loadAdvisorDetail(payload, advisorId) if (!advisor) notFound() - const email = - advisor.email && !isPlanilhaPlaceholderEmail(advisor.email) ? advisor.email : null + const email = advisor.email && !isPlanilhaPlaceholderEmail(advisor.email) ? advisor.email : null return ( diff --git a/src/app/(campaign)/campanha/(app)/assessores/page.tsx b/src/app/(campaign)/campanha/(app)/assessores/page.tsx index db4383f0..edad0044 100644 --- a/src/app/(campaign)/campanha/(app)/assessores/page.tsx +++ b/src/app/(campaign)/campanha/(app)/assessores/page.tsx @@ -2,14 +2,14 @@ import config from '@payload-config' import { redirect } from 'next/navigation' import { getPayload } from 'payload' +import { AdvisorsTable } from '@/components/campaign/advisor/AdvisorsTable' +import { CampaignListFooter } from '@/components/campaign/shared/CampaignListFooter' import { CampaignListPendingBoundary, CampaignListResults, } from '@/components/campaign/shared/CampaignListPending' -import { CampaignListFooter } from '@/components/campaign/shared/CampaignListFooter' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { CampaignSearchForm } from '@/components/campaign/shared/CampaignSearchForm' -import { AdvisorsTable } from '@/components/campaign/advisor/AdvisorsTable' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { advisorListHrefForPage, loadAdvisorListPageData, diff --git a/src/app/(campaign)/campanha/(app)/conceitos/page.tsx b/src/app/(campaign)/campanha/(app)/conceitos/page.tsx index 465c61db..56d121f1 100644 --- a/src/app/(campaign)/campanha/(app)/conceitos/page.tsx +++ b/src/app/(campaign)/campanha/(app)/conceitos/page.tsx @@ -72,8 +72,8 @@ export default async function CampaignConceptsPage() {

    Conceitos de inteligência

    - O que cada número da campanha mede e como é calculado. Só o que o produto já calcula - hoje — a lista cresce conforme novas análises entram. + O que cada número da campanha mede e como é calculado. Só o que o produto já calcula hoje + — a lista cresce conforme novas análises entram.

    diff --git a/src/app/(campaign)/campanha/(app)/demandas/[slug]/page.tsx b/src/app/(campaign)/campanha/(app)/demandas/[slug]/page.tsx index eb7d6915..7a3851fb 100644 --- a/src/app/(campaign)/campanha/(app)/demandas/[slug]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/demandas/[slug]/page.tsx @@ -4,8 +4,8 @@ import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { DemandWorkflowCard } from '@/components/campaign/demand/DemandWorkflowCard' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { diff --git a/src/app/(campaign)/campanha/(app)/demandas/nova/page.tsx b/src/app/(campaign)/campanha/(app)/demandas/nova/page.tsx index bbda7b57..69928bbf 100644 --- a/src/app/(campaign)/campanha/(app)/demandas/nova/page.tsx +++ b/src/app/(campaign)/campanha/(app)/demandas/nova/page.tsx @@ -4,8 +4,8 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { DemandForm } from '@/components/campaign/demand/DemandForm' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' diff --git a/src/app/(campaign)/campanha/(app)/dobradinhas/page.tsx b/src/app/(campaign)/campanha/(app)/dobradinhas/page.tsx index 27929058..bfa1114f 100644 --- a/src/app/(campaign)/campanha/(app)/dobradinhas/page.tsx +++ b/src/app/(campaign)/campanha/(app)/dobradinhas/page.tsx @@ -10,13 +10,13 @@ import { CampaignListPendingBoundary, CampaignListResults, } from '@/components/campaign/shared/CampaignListPending' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { CampaignSearchForm } from '@/components/campaign/shared/CampaignSearchForm' import { CampaignTable, CampaignTableHead, type CampaignTableColumn, } from '@/components/campaign/shared/CampaignTable' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' diff --git a/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx b/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx index 339726e4..fb4a72ea 100644 --- a/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx @@ -4,16 +4,20 @@ import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' -import { LeadershipInternalForm } from '@/components/campaign/leadership/LeadershipInternalForm' import { LeadershipInviteButtons } from '@/components/campaign/invite/LeadershipInviteButtons' -import { StateDeputyChips } from '@/components/campaign/stateDeputy/StateDeputyChips' +import { LeadershipInternalForm } from '@/components/campaign/leadership/LeadershipInternalForm' import { SupportStatusBadge } from '@/components/campaign/leadership/SupportStatusBadge' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' +import { StateDeputyChips } from '@/components/campaign/stateDeputy/StateDeputyChips' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' -import { loadOrganizationOptions, loadMunicipalityOptions, loadStateDeputyOptions } from '@/utilities/campaignRelationOptions' +import { + loadMunicipalityOptions, + loadOrganizationOptions, + loadStateDeputyOptions, +} from '@/utilities/campaignRelationOptions' import { loadLeadershipDetail } from '@/utilities/leadershipData' import { updateLeadershipInternalFormAction } from './formActions' diff --git a/src/app/(campaign)/campanha/(app)/liderancas/nova/page.tsx b/src/app/(campaign)/campanha/(app)/liderancas/nova/page.tsx index 59fa46fb..7450841e 100644 --- a/src/app/(campaign)/campanha/(app)/liderancas/nova/page.tsx +++ b/src/app/(campaign)/campanha/(app)/liderancas/nova/page.tsx @@ -4,12 +4,16 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { LeadershipForm } from '@/components/campaign/leadership/LeadershipForm' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' -import { loadOrganizationOptions, loadMunicipalityOptions, loadStateDeputyOptions } from '@/utilities/campaignRelationOptions' +import { + loadMunicipalityOptions, + loadOrganizationOptions, + loadStateDeputyOptions, +} from '@/utilities/campaignRelationOptions' import { createLeadershipFormAction } from './formActions' type NewLeadershipPageProps = { @@ -32,9 +36,12 @@ export default async function NewLeadershipPage({ searchParams }: NewLeadershipP ? rawSearchParams.municipality[0] : rawSearchParams.municipality const initialMunicipalityID = - rawInitialMunicipality && /^[1-9]\d*$/.test(rawInitialMunicipality) ? Number(rawInitialMunicipality) : null + rawInitialMunicipality && /^[1-9]\d*$/.test(rawInitialMunicipality) + ? Number(rawInitialMunicipality) + : null const initialMunicipalityIDs = - initialMunicipalityID && municipalityOptions.some((option) => option.id === initialMunicipalityID) + initialMunicipalityID && + municipalityOptions.some((option) => option.id === initialMunicipalityID) ? [initialMunicipalityID] : [] diff --git a/src/app/(campaign)/campanha/(app)/liderancas/page.tsx b/src/app/(campaign)/campanha/(app)/liderancas/page.tsx index 93c1c8e3..4e3e6da7 100644 --- a/src/app/(campaign)/campanha/(app)/liderancas/page.tsx +++ b/src/app/(campaign)/campanha/(app)/liderancas/page.tsx @@ -4,20 +4,20 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { getPayload } from 'payload' +import { SupportStatusBadge } from '@/components/campaign/leadership/SupportStatusBadge' import { CampaignListEmptyState } from '@/components/campaign/shared/CampaignListEmptyState' import { CampaignListFooter } from '@/components/campaign/shared/CampaignListFooter' import { CampaignListPendingBoundary, CampaignListResults, } from '@/components/campaign/shared/CampaignListPending' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { CampaignSearchForm } from '@/components/campaign/shared/CampaignSearchForm' import { CampaignTable, CampaignTableHead, type CampaignTableColumn, } from '@/components/campaign/shared/CampaignTable' -import { SupportStatusBadge } from '@/components/campaign/leadership/SupportStatusBadge' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' diff --git a/src/app/(campaign)/campanha/(app)/municipios/[slug]/MunicipalityDetailTabs.tsx b/src/app/(campaign)/campanha/(app)/municipios/[slug]/MunicipalityDetailTabs.tsx index 041d56d6..e1b2152d 100644 --- a/src/app/(campaign)/campanha/(app)/municipios/[slug]/MunicipalityDetailTabs.tsx +++ b/src/app/(campaign)/campanha/(app)/municipios/[slug]/MunicipalityDetailTabs.tsx @@ -1,4 +1,3 @@ -import { CampaignListPagination } from '@/components/campaign/shared/CampaignListPagination' import { CandidateComparePicker } from '@/components/campaign/map/CandidateComparePicker' import { MunicipalityBaselineCard } from '@/components/campaign/municipality/MunicipalityBaselineCard' import { MunicipalityCandidateComparisonTable } from '@/components/campaign/municipality/MunicipalityCandidateComparisonTable' @@ -10,6 +9,7 @@ import { MunicipalityStrategyCard } from '@/components/campaign/municipality/Mun import { MunicipalityUpdateFeed } from '@/components/campaign/municipality/MunicipalityUpdateFeed' import { MunicipalityUpdateForm } from '@/components/campaign/municipality/MunicipalityUpdateForm' import { MunicipalityZoneNeighborhoodsCard } from '@/components/campaign/municipality/MunicipalityZoneNeighborhoodsCard' +import { CampaignListPagination } from '@/components/campaign/shared/CampaignListPagination' import type { VoteEstimateScenarioViewModel } from '@/lib/voteEstimate' import type { getCampaignUser } from '@/utilities/campaignAuth' import { loadFederalCandidateOptions } from '@/utilities/electionCandidateOptions' diff --git a/src/app/(campaign)/campanha/(app)/municipios/[slug]/editar/page.tsx b/src/app/(campaign)/campanha/(app)/municipios/[slug]/editar/page.tsx index 418adb77..36143f10 100644 --- a/src/app/(campaign)/campanha/(app)/municipios/[slug]/editar/page.tsx +++ b/src/app/(campaign)/campanha/(app)/municipios/[slug]/editar/page.tsx @@ -4,9 +4,9 @@ import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { MunicipalityAdvisorsForm } from '@/components/campaign/municipality/MunicipalityAdvisorsForm' import { MunicipalityStrategyForm } from '@/components/campaign/municipality/MunicipalityStrategyForm' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' diff --git a/src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx b/src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx index cbfccd1a..ac7b9467 100644 --- a/src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx @@ -1,12 +1,12 @@ -import { Suspense } from 'react' import config from '@payload-config' import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { getPayload } from 'payload' +import { Suspense } from 'react' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' -import { MunicipalityTabNav } from '@/components/campaign/municipality/MunicipalityTabNav' import { RecentVisitTracker } from '@/components/campaign/dashboard/RecentVisitTracker' +import { MunicipalityTabNav } from '@/components/campaign/municipality/MunicipalityTabNav' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { isCampaignLeader } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' diff --git a/src/app/(campaign)/campanha/(app)/municipios/expected-votes/route.ts b/src/app/(campaign)/campanha/(app)/municipios/expected-votes/route.ts index 5867026b..8ce52719 100644 --- a/src/app/(campaign)/campanha/(app)/municipios/expected-votes/route.ts +++ b/src/app/(campaign)/campanha/(app)/municipios/expected-votes/route.ts @@ -4,11 +4,11 @@ import { z } from 'zod' import { municipalityStaffEditSafeMessages } from '@/app/(campaign)/campanha/(app)/municipios/municipalityStaffEditMessages' import { setMunicipalityExpectedVotes } from '@/app/(campaign)/campanha/actions/municipality' import { positiveRelationshipId } from '@/lib/schemas/primitives' +import { toVoteEstimateScenarioViewModel } from '@/lib/voteEstimate' import { CAMPAIGN_SESSION_EXPIRED_MESSAGE, mapCampaignFormActionError, } from '@/utilities/campaignFormActionError' -import { toVoteEstimateScenarioViewModel } from '@/lib/voteEstimate' import type { MunicipalityListExpectedVotesResponse } from './types' diff --git a/src/app/(campaign)/campanha/(app)/municipios/page.tsx b/src/app/(campaign)/campanha/(app)/municipios/page.tsx index 79a9a3af..72a0524d 100644 --- a/src/app/(campaign)/campanha/(app)/municipios/page.tsx +++ b/src/app/(campaign)/campanha/(app)/municipios/page.tsx @@ -6,14 +6,14 @@ import config from '@payload-config' import { redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignListFooter } from '@/components/campaign/shared/CampaignListFooter' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' -import { CampaignScopeBadge } from '@/components/campaign/shared/CampaignScopeBadge' +import { RecentVisitTracker } from '@/components/campaign/dashboard/RecentVisitTracker' import { MunicipalityEstimateScenarioProvider } from '@/components/campaign/municipality/MunicipalityEstimateScenarioContext' import { MunicipalityFilters } from '@/components/campaign/municipality/MunicipalityFilters' import { MunicipalityList } from '@/components/campaign/municipality/MunicipalityList' import { MunicipalityListOverview } from '@/components/campaign/municipality/MunicipalityListOverview' -import { RecentVisitTracker } from '@/components/campaign/dashboard/RecentVisitTracker' +import { CampaignListFooter } from '@/components/campaign/shared/CampaignListFooter' +import { CampaignScopeBadge } from '@/components/campaign/shared/CampaignScopeBadge' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { isCampaignCoordinator, isCampaignLeader, diff --git a/src/app/(campaign)/campanha/(app)/organizacoes/[slug]/page.tsx b/src/app/(campaign)/campanha/(app)/organizacoes/[slug]/page.tsx index dc5043d4..06c15d69 100644 --- a/src/app/(campaign)/campanha/(app)/organizacoes/[slug]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/organizacoes/[slug]/page.tsx @@ -4,8 +4,8 @@ import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { OrganizationForm } from '@/components/campaign/organization/OrganizationForm' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { actionPlanStatusLabels, actionPlanStatuses } from '@/lib/schemas/actionPlan' @@ -52,7 +52,9 @@ export default async function OrganizationDetailPage({ params }: OrganizationDet {organizationKindLabels[organization.kind]}

{organization.municipalityNames.length ? ( -

Atua em {organization.municipalityNames.join(', ')}

+

+ Atua em {organization.municipalityNames.join(', ')} +

) : null} {organization.notes ? (

{organization.notes}

diff --git a/src/app/(campaign)/campanha/(app)/organizacoes/nova/page.tsx b/src/app/(campaign)/campanha/(app)/organizacoes/nova/page.tsx index cc6454e0..ee979abe 100644 --- a/src/app/(campaign)/campanha/(app)/organizacoes/nova/page.tsx +++ b/src/app/(campaign)/campanha/(app)/organizacoes/nova/page.tsx @@ -4,8 +4,8 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { getPayload } from 'payload' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { OrganizationForm } from '@/components/campaign/organization/OrganizationForm' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Button } from '@/components/ui/button' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' @@ -35,7 +35,10 @@ export default async function NewOrganizationPage() {

- + ) } diff --git a/src/app/(campaign)/campanha/(app)/organizacoes/page.tsx b/src/app/(campaign)/campanha/(app)/organizacoes/page.tsx index 120c3b1f..e0fdd666 100644 --- a/src/app/(campaign)/campanha/(app)/organizacoes/page.tsx +++ b/src/app/(campaign)/campanha/(app)/organizacoes/page.tsx @@ -10,13 +10,13 @@ import { CampaignListPendingBoundary, CampaignListResults, } from '@/components/campaign/shared/CampaignListPending' -import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { CampaignSearchForm } from '@/components/campaign/shared/CampaignSearchForm' import { CampaignTable, CampaignTableHead, type CampaignTableColumn, } from '@/components/campaign/shared/CampaignTable' +import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' import { organizationKindLabels } from '@/lib/schemas/organization' diff --git a/src/app/(campaign)/campanha/(app)/page.tsx b/src/app/(campaign)/campanha/(app)/page.tsx index d6c2bf61..dea422f9 100644 --- a/src/app/(campaign)/campanha/(app)/page.tsx +++ b/src/app/(campaign)/campanha/(app)/page.tsx @@ -5,8 +5,8 @@ import { Suspense } from 'react' import { CampaignDashboard } from '@/components/campaign/dashboard/CampaignDashboard' import { LeaderContactsPanel } from '@/components/campaign/leadership/LeaderContactsPanel' -import { MunicipalityEstimateScenarioProvider } from '@/components/campaign/municipality/MunicipalityEstimateScenarioContext' import { MunicipalityMapPanelDynamic } from '@/components/campaign/map/MunicipalityMapPanelDynamic' +import { MunicipalityEstimateScenarioProvider } from '@/components/campaign/municipality/MunicipalityEstimateScenarioContext' import { TerritoryOverviewTable } from '@/components/campaign/municipality/TerritoryOverviewTable' import { campaignPrioritySurfaceClassName } from '@/components/campaign/shell/CampaignPageShell' import type { CampaignUser } from '@/payload-types' @@ -15,8 +15,8 @@ import { getCampaignUser } from '@/utilities/campaignAuth' import { getCampaignDashboardData } from '@/utilities/campaignDashboardData' import { loadFederalCandidateOptions } from '@/utilities/electionCandidateOptions' import { loadLeaderContactsPageData } from '@/utilities/leaderContactsPageData' -import { loadMunicipalityMapBundle } from '@/utilities/municipalityMapData' import { loadTerritoryOverview } from '@/utilities/loadTerritoryOverview' +import { loadMunicipalityMapBundle } from '@/utilities/municipalityMapData' export const dynamic = 'force-dynamic' diff --git a/src/app/(campaign)/campanha/(app)/planos/[slug]/ActionPlanOverviewTab.tsx b/src/app/(campaign)/campanha/(app)/planos/[slug]/ActionPlanOverviewTab.tsx index 5d4ea393..ef864d91 100644 --- a/src/app/(campaign)/campanha/(app)/planos/[slug]/ActionPlanOverviewTab.tsx +++ b/src/app/(campaign)/campanha/(app)/planos/[slug]/ActionPlanOverviewTab.tsx @@ -170,9 +170,7 @@ export const ActionPlanOverviewTab = ({ ))} ) : ( -

- Nenhuma demanda vinculada a este plano. -

+

Nenhuma demanda vinculada a este plano.

)} diff --git a/src/app/(campaign)/campanha/(app)/planos/[slug]/editar/page.tsx b/src/app/(campaign)/campanha/(app)/planos/[slug]/editar/page.tsx index 34bc0eb0..42a34c77 100644 --- a/src/app/(campaign)/campanha/(app)/planos/[slug]/editar/page.tsx +++ b/src/app/(campaign)/campanha/(app)/planos/[slug]/editar/page.tsx @@ -1,6 +1,6 @@ import config from '@payload-config' -import { getPayload } from 'payload' import { notFound, redirect } from 'next/navigation' +import { getPayload } from 'payload' import { searchActionPlanContactOptions, @@ -8,13 +8,13 @@ import { } from '@/app/(campaign)/campanha/(app)/planos/contactSearchActions' import { updateActionPlanFormAction } from '@/app/(campaign)/campanha/(app)/planos/formActions' import { ActionPlanForm } from '@/components/campaign/actionPlan/ActionPlanForm' +import { ActionPlanNotFoundError, getActionPlanEditPageData } from '@/utilities/actionPlanPageData' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' import { - ActionPlanNotFoundError, - getActionPlanEditPageData, -} from '@/utilities/actionPlanPageData' -import { loadOrganizationOptions, loadMunicipalityOptions } from '@/utilities/campaignRelationOptions' + loadMunicipalityOptions, + loadOrganizationOptions, +} from '@/utilities/campaignRelationOptions' import { getEligibleAdvisorOptions } from '@/utilities/municipalityViewModels' type EditActionPlanPageProps = { diff --git a/src/app/(campaign)/campanha/(app)/planos/[slug]/updateFormActions.ts b/src/app/(campaign)/campanha/(app)/planos/[slug]/updateFormActions.ts index ace18158..90e46e84 100644 --- a/src/app/(campaign)/campanha/(app)/planos/[slug]/updateFormActions.ts +++ b/src/app/(campaign)/campanha/(app)/planos/[slug]/updateFormActions.ts @@ -35,7 +35,8 @@ export const createActionPlanUpdateFormAction = async ( return mapCampaignFormActionError({ error, safeMessages, - genericMessage: 'Não foi possível enviar a atualização. Verifique seu acesso e tente novamente.', + genericMessage: + 'Não foi possível enviar a atualização. Verifique seu acesso e tente novamente.', }) } } diff --git a/src/app/(campaign)/campanha/(app)/planos/contactSearchActions.ts b/src/app/(campaign)/campanha/(app)/planos/contactSearchActions.ts index e19d2906..adf45f02 100644 --- a/src/app/(campaign)/campanha/(app)/planos/contactSearchActions.ts +++ b/src/app/(campaign)/campanha/(app)/planos/contactSearchActions.ts @@ -4,15 +4,12 @@ import config from '@payload-config' import { getPayload } from 'payload' import type { ContactComboboxOption } from '@/components/campaign/shared/ContactCombobox' -import { - isContactSearchQueryReady, - normalizeContactSearchQuery, -} from '@/lib/contactSearchQuery' -import { getCampaignUser } from '@/utilities/campaignAuth' +import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery' import { searchActionPlanLeadershipOptions, type ActionPlanLeadershipOption, } from '@/utilities/actionPlanLeadershipOptions' +import { getCampaignUser } from '@/utilities/campaignAuth' const CONTACT_OPTION_LIMIT = 20 diff --git a/src/app/(campaign)/campanha/(app)/planos/novo/page.tsx b/src/app/(campaign)/campanha/(app)/planos/novo/page.tsx index 5524caa7..6c5f25b5 100644 --- a/src/app/(campaign)/campanha/(app)/planos/novo/page.tsx +++ b/src/app/(campaign)/campanha/(app)/planos/novo/page.tsx @@ -1,6 +1,6 @@ import config from '@payload-config' -import { getPayload } from 'payload' import { redirect } from 'next/navigation' +import { getPayload } from 'payload' import { searchActionPlanContactOptions, @@ -10,7 +10,10 @@ import { createActionPlanFormAction } from '@/app/(campaign)/campanha/(app)/plan import { ActionPlanForm } from '@/components/campaign/actionPlan/ActionPlanForm' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignUser } from '@/utilities/campaignAuth' -import { loadOrganizationOptions, loadMunicipalityOptions } from '@/utilities/campaignRelationOptions' +import { + loadMunicipalityOptions, + loadOrganizationOptions, +} from '@/utilities/campaignRelationOptions' import { getEligibleAdvisorOptions } from '@/utilities/municipalityViewModels' export default async function NewActionPlanPage() { diff --git a/src/app/(campaign)/campanha/actions/actionPlan.ts b/src/app/(campaign)/campanha/actions/actionPlan.ts index a089d924..41ed2bf4 100644 --- a/src/app/(campaign)/campanha/actions/actionPlan.ts +++ b/src/app/(campaign)/campanha/actions/actionPlan.ts @@ -6,16 +6,16 @@ import { actionPlanCreateSchema, actionPlanDemandDraftsSchema, actionPlanUpdateSchema, - type ActionPlanDemandDraft, type ActionPlanCreateInput, + type ActionPlanDemandDraft, type ActionPlanUpdateInput, } from '@/lib/schemas/actionPlan' import type { CampaignUser } from '@/payload-types' -import { getCampaignActionContext, reloadCampaignActor } from '@/utilities/campaignActionContext' import { isCampaignStaff } from '@/utilities/campaignAccess' +import { getCampaignActionContext, reloadCampaignActor } from '@/utilities/campaignActionContext' +import { hookFilledCreateData } from '@/utilities/hookFilledData' import { withPayloadTransaction } from '@/utilities/payloadTransaction' import { acquireTextAdvisoryLocks } from '@/utilities/postgresTransactionLocks' -import { hookFilledCreateData } from '@/utilities/hookFilledData' const MAX_ACTION_PLAN_UPDATE_BODY_LENGTH = 4000 const MAX_ACTION_PLAN_RESULT_SUMMARY_LENGTH = 6000 diff --git a/src/app/(campaign)/campanha/actions/advisor.ts b/src/app/(campaign)/campanha/actions/advisor.ts index 4b1a5291..0349446d 100644 --- a/src/app/(campaign)/campanha/actions/advisor.ts +++ b/src/app/(campaign)/campanha/actions/advisor.ts @@ -100,7 +100,9 @@ const nextAdvisorIdsAfterMembership = ( if (assigned) { if (currentAdvisorIDs.length >= MAX_ADVISORS_PER_MUNICIPALITY) { - throw new Error(`Cada município aceita no máximo ${MAX_ADVISORS_PER_MUNICIPALITY} assessores.`) + throw new Error( + `Cada município aceita no máximo ${MAX_ADVISORS_PER_MUNICIPALITY} assessores.`, + ) } return [...currentAdvisorIDs, advisorId] } diff --git a/src/app/(campaign)/campanha/actions/leaderSupporter.ts b/src/app/(campaign)/campanha/actions/leaderSupporter.ts index a398783e..db57b166 100644 --- a/src/app/(campaign)/campanha/actions/leaderSupporter.ts +++ b/src/app/(campaign)/campanha/actions/leaderSupporter.ts @@ -5,6 +5,7 @@ import type { Payload } from 'payload' import { upsertContactByPhone } from '@/app/(campaign)/campanha/actions/supporter' import { checkboxFormValue, nullableRelationshipFormValue, optionalFormText } from '@/lib/formData' +import { sanitizeBrazilianPhoneInput } from '@/lib/phone' import { leaderSupporterCreateSchema } from '@/lib/schemas/supporter' import type { CampaignUser, Supporter } from '@/payload-types' import { getEngagedLeaderMunicipalityIds, isCampaignLeader } from '@/utilities/campaignAccess' @@ -17,7 +18,6 @@ import { mapCampaignFormActionError } from '@/utilities/campaignFormActionError' import { acquireContactPhoneLocks } from '@/utilities/contactPhoneInvariant' import type { PayloadTransactionRequest } from '@/utilities/payloadTransaction' import { withPayloadTransaction } from '@/utilities/payloadTransaction' -import { sanitizeBrazilianPhoneInput } from '@/lib/phone' import { isUniqueSupporterConflict } from '@/utilities/supporterErrors' export type LeaderSupporterFormState = { diff --git a/src/app/(campaign)/campanha/actions/municipality.ts b/src/app/(campaign)/campanha/actions/municipality.ts index 7fe2438a..a567cdc7 100644 --- a/src/app/(campaign)/campanha/actions/municipality.ts +++ b/src/app/(campaign)/campanha/actions/municipality.ts @@ -12,6 +12,7 @@ import { type MunicipalityPoliticalTrendInput, type MunicipalityStrategyUpdateInput, } from '@/lib/schemas/municipality' +import { normalizeVoteEstimateOnSave, toVoteEstimateScenarioViewModel } from '@/lib/voteEstimate' import type { CampaignUser } from '@/payload-types' import { getCampaignActionContext, @@ -21,10 +22,6 @@ import { import type { PayloadTransactionRequest } from '@/utilities/payloadTransaction' import { withPayloadTransaction } from '@/utilities/payloadTransaction' import { acquireTextAdvisoryLocks } from '@/utilities/postgresTransactionLocks' -import { - normalizeVoteEstimateOnSave, - toVoteEstimateScenarioViewModel, -} from '@/lib/voteEstimate' const getFreshStaffActor = ( payload: Payload, diff --git a/src/app/(campaign)/campanha/actions/municipalityUpdate.ts b/src/app/(campaign)/campanha/actions/municipalityUpdate.ts index aaa8bfc1..190252e3 100644 --- a/src/app/(campaign)/campanha/actions/municipalityUpdate.ts +++ b/src/app/(campaign)/campanha/actions/municipalityUpdate.ts @@ -2,11 +2,14 @@ import type { Payload } from 'payload' -import { municipalityUpdateCreateSchema, type MunicipalityUpdateCreateInput } from '@/lib/schemas/municipalityUpdate' +import { + municipalityUpdateCreateSchema, + type MunicipalityUpdateCreateInput, +} from '@/lib/schemas/municipalityUpdate' import type { CampaignUser } from '@/payload-types' import { getCampaignActionContext, reloadCampaignActor } from '@/utilities/campaignActionContext' -import { withPayloadTransaction } from '@/utilities/payloadTransaction' import { hookFilledCreateData } from '@/utilities/hookFilledData' +import { withPayloadTransaction } from '@/utilities/payloadTransaction' export const createMunicipalityUpdateRecord = async ( payload: Payload, diff --git a/src/app/(campaign)/campanha/actions/password.ts b/src/app/(campaign)/campanha/actions/password.ts index 2d478e04..fc97dc10 100644 --- a/src/app/(campaign)/campanha/actions/password.ts +++ b/src/app/(campaign)/campanha/actions/password.ts @@ -5,11 +5,22 @@ import { redirect } from 'next/navigation' import { getPayload } from 'payload' import { requiredFormSecret, requiredFormText } from '@/lib/formData' +import { normalizeBrazilianPhone } from '@/lib/phone' import { campaignChangePasswordSchema, campaignPasswordResetRequestSchema, campaignPasswordResetSchema, } from '@/lib/schemas/campaignPassword' +import { + campaignLoginCredentials, + getCampaignUser, + setCampaignAuthCookie, +} from '@/utilities/campaignAuth' +import { + CAMPAIGN_SESSION_EXPIRED_MESSAGE, + mapCampaignFormActionError, + type CampaignFormActionState, +} from '@/utilities/campaignFormActionError' import { assertCampaignEmailConfigured, CAMPAIGN_LEADERSHIP_FORGOT_PASSWORD_MESSAGE, @@ -17,17 +28,10 @@ import { CAMPAIGN_PASSWORD_RESET_INVALID_TOKEN_MESSAGE, isCampaignEmailConfigured, } from '@/utilities/campaignPasswordReset' -import { campaignLoginCredentials, getCampaignUser, setCampaignAuthCookie } from '@/utilities/campaignAuth' -import { - CAMPAIGN_SESSION_EXPIRED_MESSAGE, - mapCampaignFormActionError, - type CampaignFormActionState, -} from '@/utilities/campaignFormActionError' -import { normalizeBrazilianPhone } from '@/lib/phone' -export const requestCampaignPasswordReset = async ( - input: { email: string }, -): Promise => { +export const requestCampaignPasswordReset = async (input: { + email: string +}): Promise => { const parsed = campaignPasswordResetRequestSchema.safeParse(input) if (!parsed.success) { return mapCampaignFormActionError({ @@ -81,9 +85,11 @@ export const requestCampaignPasswordResetFormAction = async ( } } -const resetCampaignPassword = async ( - input: { token: string; password: string; passwordConfirmation: string }, -): Promise => { +const resetCampaignPassword = async (input: { + token: string + password: string + passwordConfirmation: string +}): Promise => { const parsed = campaignPasswordResetSchema.safeParse(input) if (!parsed.success) { return mapCampaignFormActionError({ @@ -140,9 +146,11 @@ export const resetCampaignPasswordFormAction = async ( } } -const changeCampaignPassword = async ( - input: { currentPassword: string; password: string; passwordConfirmation: string }, -): Promise => { +const changeCampaignPassword = async (input: { + currentPassword: string + password: string + passwordConfirmation: string +}): Promise => { const user = await getCampaignUser() if (!user) { return { message: CAMPAIGN_SESSION_EXPIRED_MESSAGE } diff --git a/src/app/(campaign)/campanha/actions/supporter.ts b/src/app/(campaign)/campanha/actions/supporter.ts index b70ec936..18d594c2 100644 --- a/src/app/(campaign)/campanha/actions/supporter.ts +++ b/src/app/(campaign)/campanha/actions/supporter.ts @@ -27,8 +27,6 @@ import { previewSupporterImportText as previewSupporterImportTextAction, } from './supporterImport' - - const getFreshStaffActor = ( payload: Payload, actor: CampaignUser, diff --git a/src/app/(campaign)/campanha/actions/supporterImport.ts b/src/app/(campaign)/campanha/actions/supporterImport.ts index f48ba345..0dc09787 100644 --- a/src/app/(campaign)/campanha/actions/supporterImport.ts +++ b/src/app/(campaign)/campanha/actions/supporterImport.ts @@ -3,6 +3,7 @@ import { parse } from 'csv-parse/sync' import type { Payload } from 'payload' +import { normalizeBrazilianPhone } from '@/lib/phone' import { resolveBahiaMunicipality, supporterImportConfirmSchema, @@ -17,7 +18,6 @@ import { import { acquireContactPhoneLocks } from '@/utilities/contactPhoneInvariant' import type { PayloadTransactionRequest } from '@/utilities/payloadTransaction' import { withPayloadTransaction } from '@/utilities/payloadTransaction' -import { normalizeBrazilianPhone } from '@/lib/phone' import { relationshipId } from '@/utilities/relationship' import { isPreviewErrorRow, diff --git a/src/app/(campaign)/campanha/actions/votePledge.ts b/src/app/(campaign)/campanha/actions/votePledge.ts index 242ecb39..0d4f6193 100644 --- a/src/app/(campaign)/campanha/actions/votePledge.ts +++ b/src/app/(campaign)/campanha/actions/votePledge.ts @@ -8,16 +8,13 @@ import { type DeclareVotesInput, type EstimateVotesInput, } from '@/lib/schemas/votePledge' +import { normalizeVoteEstimateOnSave, toVoteEstimateScenarioViewModel } from '@/lib/voteEstimate' import type { CampaignUser } from '@/payload-types' import { isCampaignStaff } from '@/utilities/campaignAccess' import { getCampaignActionContext, reloadCampaignActor } from '@/utilities/campaignActionContext' import { withPayloadTransaction } from '@/utilities/payloadTransaction' import { acquireTextAdvisoryLocks } from '@/utilities/postgresTransactionLocks' import { relationshipId, requireRelationshipId } from '@/utilities/relationship' -import { - normalizeVoteEstimateOnSave, - toVoteEstimateScenarioViewModel, -} from '@/lib/voteEstimate' const STAFF_DECLARE_REQUIRED = 'Somente a coordenação e a assessoria registram votos declarados.' const MUNICIPALITY_NOT_LINKED = diff --git a/src/app/(campaign)/campanha/convite/[token]/page.tsx b/src/app/(campaign)/campanha/convite/[token]/page.tsx index b104831c..cdf41b2b 100644 --- a/src/app/(campaign)/campanha/convite/[token]/page.tsx +++ b/src/app/(campaign)/campanha/convite/[token]/page.tsx @@ -8,8 +8,8 @@ import { redeemCampaignInviteLoginFormAction, } from '@/app/(campaign)/campanha/convite/[token]/formActions' import { CampaignInviteForm } from '@/components/campaign/invite/CampaignInviteForm' -import { ConsentText } from '@/components/campaign/shared/ConsentText' import { InvalidCampaignInvite } from '@/components/campaign/invite/InvalidCampaignInvite' +import { ConsentText } from '@/components/campaign/shared/ConsentText' import { getCampaignInvitePageData } from '@/utilities/campaignInvitePageData' export const dynamic = 'force-dynamic' @@ -48,7 +48,9 @@ export default async function CampaignInvitePage({ params }: CampaignInvitePageP preview.kind === 'login' ? redeemCampaignInviteLoginFormAction.bind(null, token) : redeemCampaignInviteAutofillFormAction.bind(null, token) - const consentText = preview.requiresConsent ? ConsentText({ data: preview.consentData }) : undefined + const consentText = preview.requiresConsent + ? ConsentText({ data: preview.consentData }) + : undefined return (
diff --git a/src/app/(frontend)/[type]/[category]/page.tsx b/src/app/(frontend)/[type]/[category]/page.tsx index 1df49ed4..ed08ba9a 100644 --- a/src/app/(frontend)/[type]/[category]/page.tsx +++ b/src/app/(frontend)/[type]/[category]/page.tsx @@ -3,7 +3,6 @@ import { SiteHeader } from '@/components/SiteHeader' import { Badge } from '@/components/ui/Badge' import { Separator } from '@/components/ui/separator' import { getCachedGlobal } from '@/utilities/globals' -import { stripTrailingSlash } from '@/utilities/seo' import { POST_TYPE_LABELS, getCategoryName, @@ -11,6 +10,7 @@ import { getVisiblePosts, isPostType, } from '@/utilities/posts' +import { stripTrailingSlash } from '@/utilities/seo' import type { Metadata } from 'next' import { notFound } from 'next/navigation' diff --git a/src/app/(frontend)/[type]/page.tsx b/src/app/(frontend)/[type]/page.tsx index c82ef637..2f1fc353 100644 --- a/src/app/(frontend)/[type]/page.tsx +++ b/src/app/(frontend)/[type]/page.tsx @@ -3,8 +3,8 @@ import { SiteHeader } from '@/components/SiteHeader' import { Badge } from '@/components/ui/Badge' import { Separator } from '@/components/ui/separator' import { getCachedGlobal } from '@/utilities/globals' -import { stripTrailingSlash } from '@/utilities/seo' import { POST_TYPE_LABELS, getVisiblePosts, isPostType } from '@/utilities/posts' +import { stripTrailingSlash } from '@/utilities/seo' import type { Metadata } from 'next' import { notFound } from 'next/navigation' diff --git a/src/app/(frontend)/abaixo-assinado/[id]/page.tsx b/src/app/(frontend)/abaixo-assinado/[id]/page.tsx index 30af3074..77d47e4c 100644 --- a/src/app/(frontend)/abaixo-assinado/[id]/page.tsx +++ b/src/app/(frontend)/abaixo-assinado/[id]/page.tsx @@ -1,17 +1,17 @@ -import { PetitionForm } from '@/components/PetitionForm' -import { MetaPixel } from '@/components/MetaPixel' -import { SignatureCounter } from '@/components/SignatureCounter' import { getSignatureCount } from '@/app/(frontend)/actions/getSignatureCount' +import { MetaPixel } from '@/components/MetaPixel' +import { PetitionForm } from '@/components/PetitionForm' +import { SignatureCounter } from '@/components/SignatureCounter' import { normalizeFacebookPixelId } from '@/lib/facebookPixel' -import { getCachedDocumentById, getPetitionIds } from '@/utilities/documents' -import { getCachedGlobal } from '@/utilities/globals' -import { extractFirstImageFromLexical } from '@/utilities/extractFirstImageFromLexical' -import { convertLexicalToHTML } from '@payloadcms/richtext-lexical/html' import type { Media } from '@/payload-types' +import { getCachedDocumentById, getPetitionIds } from '@/utilities/documents' +import { extractFirstImageFromLexical } from '@/utilities/extractFirstImageFromLexical' +import { getCachedGlobal } from '@/utilities/globals' +import { convertLexicalToHTML } from '@payloadcms/richtext-lexical/html' import type { Metadata } from 'next' -import type { Article, WithContext } from 'schema-dts' import Link from 'next/link' import { notFound } from 'next/navigation' +import type { Article, WithContext } from 'schema-dts' const MAX_DESCRIPTION_LENGTH = 200 diff --git a/src/app/(frontend)/actions/submitPetitionSignature.ts b/src/app/(frontend)/actions/submitPetitionSignature.ts index e00f7031..b4a01f66 100644 --- a/src/app/(frontend)/actions/submitPetitionSignature.ts +++ b/src/app/(frontend)/actions/submitPetitionSignature.ts @@ -1,7 +1,7 @@ 'use server' -import config from '@payload-config' import { petitionFormSchema, type PetitionFormInput } from '@/lib/schemas/petition-form' +import config from '@payload-config' import { getPayload } from 'payload' type SubmitPetitionSignatureInput = PetitionFormInput & { diff --git a/src/app/(frontend)/api/revalidate/route.ts b/src/app/(frontend)/api/revalidate/route.ts index 3a1adb3c..5f7e5b10 100644 --- a/src/app/(frontend)/api/revalidate/route.ts +++ b/src/app/(frontend)/api/revalidate/route.ts @@ -1,7 +1,7 @@ import { resolveRevalidateTag } from '@/utilities/revalidateRequest' -import { timingSafeEqual } from 'node:crypto' import { revalidateTag } from 'next/cache' import { NextResponse } from 'next/server' +import { timingSafeEqual } from 'node:crypto' /** * On-demand revalidation endpoint for content written straight to the database @@ -69,8 +69,7 @@ export async function POST(request: Request): Promise { const url = new URL(request.url) const queryTag = url.searchParams.get('tag') - const bodyResult = - queryTag != null && queryTag !== '' ? {} : await parseBodyTag(request) + const bodyResult = queryTag != null && queryTag !== '' ? {} : await parseBodyTag(request) if ('invalid' in bodyResult) { return NextResponse.json({ revalidated: false, error: 'Invalid JSON body' }, { status: 400 }) diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index 616ecb9f..5326abb9 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -1,13 +1,13 @@ +import { RefreshRouteOnSave } from '@/components/RefreshRouteOnSave' +import { ThemeProvider } from '@/components/ThemeProvider' import { getCachedDocumentById } from '@/utilities/documents' import { getCachedGlobal } from '@/utilities/globals' -import { RefreshRouteOnSave } from '@/components/RefreshRouteOnSave' +import { Analytics } from '@vercel/analytics/next' +import { SpeedInsights } from '@vercel/speed-insights/next' import { Metadata } from 'next' +import { Inter } from 'next/font/google' import React from 'react' import './styles.css' -import { ThemeProvider } from '@/components/ThemeProvider' -import { Inter } from 'next/font/google' -import { SpeedInsights } from '@vercel/speed-insights/next' -import { Analytics } from '@vercel/analytics/next' const inter = Inter({ subsets: ['latin'], variable: '--font-sans' }) diff --git a/src/app/(payload)/layout.tsx b/src/app/(payload)/layout.tsx index 8df141ae..4836b9a0 100644 --- a/src/app/(payload)/layout.tsx +++ b/src/app/(payload)/layout.tsx @@ -2,8 +2,8 @@ /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ import config from '@payload-config' import '@payloadcms/next/css' -import type { ServerFunctionClient } from 'payload' import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts' +import type { ServerFunctionClient } from 'payload' import React from 'react' import { importMap } from './admin/importMap.js' diff --git a/src/collections/ActionPlan.ts b/src/collections/ActionPlan.ts index 02e42552..a7cf1f77 100644 --- a/src/collections/ActionPlan.ts +++ b/src/collections/ActionPlan.ts @@ -6,19 +6,20 @@ import type { import { APIError } from 'payload' import { - actionPlanOriginLabels, - actionPlanOrigins, actionPlanKindLabels, actionPlanKinds, + actionPlanOriginLabels, + actionPlanOrigins, actionPlanStatusLabels, actionPlanStatuses, } from '@/lib/schemas/actionPlan' +import { slugify } from '@/lib/slug' import { canCreateActionPlan, canCreateActionPlanAdvisors, canDeleteActionPlan, - canManageCampaignStaffField, canManageActionPlanAdvisors, + canManageCampaignStaffField, canReadActionPlan, canReadCampaignStaffField, canSetActionPlanStatus, @@ -27,7 +28,6 @@ import { eligibleCampaignStaffWhere, } from '@/utilities/campaignAccess' import { relationshipId } from '@/utilities/relationship' -import { slugify } from '@/lib/slug' const trimmedText = (value: unknown): string => (typeof value === 'string' ? value.trim() : '') diff --git a/src/collections/CampaignDemand.ts b/src/collections/CampaignDemand.ts index 6f6f4231..0f49498b 100644 --- a/src/collections/CampaignDemand.ts +++ b/src/collections/CampaignDemand.ts @@ -13,6 +13,7 @@ import { campaignDemandTransitions, type CampaignDemandStatus, } from '@/lib/schemas/campaignDemand' +import { slugify } from '@/lib/slug' import { canCreateCampaignDemand, canDeleteCampaignDemand, @@ -24,7 +25,6 @@ import { isCampaignUnrestricted, } from '@/utilities/campaignAccess' import { relationshipId } from '@/utilities/relationship' -import { slugify } from '@/lib/slug' const trimmedText = (value: unknown): string => (typeof value === 'string' ? value.trim() : '') diff --git a/src/collections/CampaignUser.ts b/src/collections/CampaignUser.ts index 0d69d5ab..c766ac6d 100644 --- a/src/collections/CampaignUser.ts +++ b/src/collections/CampaignUser.ts @@ -1,3 +1,4 @@ +import { normalizeBrazilianPhone } from '@/lib/phone' import type { CampaignUser as CampaignUserDocument } from '@/payload-types' import { canCreateCampaignUserPhone, @@ -11,7 +12,6 @@ import { canUpdateCampaignUserPhone, } from '@/utilities/campaignAccess' import { buildCampaignPasswordResetUrl } from '@/utilities/campaignPasswordReset' -import { normalizeBrazilianPhone } from '@/lib/phone' import { APIError, type CollectionAfterReadHook, diff --git a/src/collections/Contact.ts b/src/collections/Contact.ts index d31af0de..40b3ece4 100644 --- a/src/collections/Contact.ts +++ b/src/collections/Contact.ts @@ -1,13 +1,10 @@ import { CitiesByState } from '@/lib/cities' +import { normalizeBrazilianPhone } from '@/lib/phone' +import { canManageContacts, canReadContacts } from '@/utilities/campaignAccess' import { acquireContactPhoneLocks, assertContactPhoneAvailable, } from '@/utilities/contactPhoneInvariant' -import { - canManageContacts, - canReadContacts, -} from '@/utilities/campaignAccess' -import { normalizeBrazilianPhone } from '@/lib/phone' import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload' import { APIError } from 'payload' @@ -35,9 +32,7 @@ const enforceUniqueContactPhone: CollectionBeforeChangeHook = async ({ // Phone-less contacts (e.g. name-only leadership imports) have no uniqueness to enforce. if (!phone) return data const oldPhone = - operation === 'update' && typeof originalDoc?.phone === 'string' - ? originalDoc.phone - : undefined + operation === 'update' && typeof originalDoc?.phone === 'string' ? originalDoc.phone : undefined await acquireContactPhoneLocks( req.payload, diff --git a/src/collections/Leadership.ts b/src/collections/Leadership.ts index 47550c33..5647fb71 100644 --- a/src/collections/Leadership.ts +++ b/src/collections/Leadership.ts @@ -1,6 +1,7 @@ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload' import { APIError } from 'payload' +import { MAX_LEADERSHIP_MUNICIPALITIES } from '@/lib/schemas/leadership' import { canCreateLeadership, canDeleteLeadership, @@ -10,7 +11,6 @@ import { canReadLeadership, canSetAdministrativeLeadershipField, } from '@/utilities/campaignAccess' -import { MAX_LEADERSHIP_MUNICIPALITIES } from '@/lib/schemas/leadership' import { relationshipId } from '@/utilities/relationship' const requireAtLeastOneMunicipality: CollectionBeforeValidateHook = ({ diff --git a/src/collections/Municipality.ts b/src/collections/Municipality.ts index f6b8eb7e..5653a9d1 100644 --- a/src/collections/Municipality.ts +++ b/src/collections/Municipality.ts @@ -5,6 +5,12 @@ import type { } from 'payload' import { APIError } from 'payload' +import { politicalTrendStatuses } from '@/lib/schemas/municipality' +import { + getVoteEstimateOrderViolation, + VOTE_ESTIMATE_ORDER_ERROR_MESSAGE, + type VoteEstimateScenarioFields, +} from '@/lib/voteEstimate' import { canAssignMunicipalityAdvisors, canCreateMunicipality, @@ -17,14 +23,8 @@ import { canUpdateMunicipality, eligibleCampaignStaffWhere, } from '@/utilities/campaignAccess' -import { politicalTrendStatuses } from '@/lib/schemas/municipality' import { politicalTrendLabels } from '@/utilities/municipalityLabels' import { relationshipId } from '@/utilities/relationship' -import { - getVoteEstimateOrderViolation, - VOTE_ESTIMATE_ORDER_ERROR_MESSAGE, - type VoteEstimateScenarioFields, -} from '@/lib/voteEstimate' import { voteEstimateScenarioGroupAccess, voteEstimateScenarioGroupFields, @@ -99,7 +99,10 @@ const validateMunicipalityAdvisors: CollectionBeforeValidateHook = async ({ data }) if (eligibleAdvisors.docs.length !== advisorIDs.length) { - throw new APIError('Cada assessor deve ter papel de Coordenador Geral, Assessor ou Candidato.', 400) + throw new APIError( + 'Cada assessor deve ter papel de Coordenador Geral, Assessor ou Candidato.', + 400, + ) } return data diff --git a/src/collections/Organization.ts b/src/collections/Organization.ts index 8dfbd0c5..86fb40e1 100644 --- a/src/collections/Organization.ts +++ b/src/collections/Organization.ts @@ -1,6 +1,7 @@ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload' import { APIError } from 'payload' +import { slugify } from '@/lib/slug' import { canCreateOrganization, canDeleteOrganization, @@ -8,7 +9,6 @@ import { canReadOrganization, canSetCampaignSystemField, } from '@/utilities/campaignAccess' -import { slugify } from '@/lib/slug' const trimmedText = (value: unknown): string => (typeof value === 'string' ? value.trim() : '') diff --git a/src/collections/Post.ts b/src/collections/Post.ts index ddaf1372..57a4248b 100644 --- a/src/collections/Post.ts +++ b/src/collections/Post.ts @@ -1,6 +1,6 @@ +import { slugify } from '@/lib/slug' import { payloadAdminOnly } from '@/utilities/campaignAccess' import { revalidateDocumentById, revalidatePostsListing } from '@/utilities/documents' -import { slugify } from '@/lib/slug' import type { CollectionConfig } from 'payload' const slug = 'post' diff --git a/src/collections/StateDeputy.ts b/src/collections/StateDeputy.ts index c7ada5f2..25054de0 100644 --- a/src/collections/StateDeputy.ts +++ b/src/collections/StateDeputy.ts @@ -1,6 +1,7 @@ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload' import { APIError } from 'payload' +import { slugify } from '@/lib/slug' import { canCreateStateDeputy, canDeleteStateDeputy, @@ -8,7 +9,6 @@ import { canReadStateDeputy, canSetCampaignSystemField, } from '@/utilities/campaignAccess' -import { slugify } from '@/lib/slug' const trimmedText = (value: unknown): string => (typeof value === 'string' ? value.trim() : '') diff --git a/src/collections/Tag.ts b/src/collections/Tag.ts index 23a10fcf..0630510d 100644 --- a/src/collections/Tag.ts +++ b/src/collections/Tag.ts @@ -1,6 +1,6 @@ +import { slugify } from '@/lib/slug' import { payloadAdminOnly } from '@/utilities/campaignAccess' import { revalidateDocumentById, revalidatePostsListing } from '@/utilities/documents' -import { slugify } from '@/lib/slug' import type { CollectionConfig } from 'payload' const slug = 'tag' diff --git a/src/collections/VotePledge.ts b/src/collections/VotePledge.ts index 4a2ca35c..c2226dd6 100644 --- a/src/collections/VotePledge.ts +++ b/src/collections/VotePledge.ts @@ -1,6 +1,11 @@ import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload' import { APIError } from 'payload' +import { + type VoteEstimateScenarioFields, + getVoteEstimateOrderViolation, + VOTE_ESTIMATE_ORDER_ERROR_MESSAGE, +} from '@/lib/voteEstimate' import { canCreateVotePledge, canDeleteVotePledge, @@ -12,11 +17,6 @@ import { payloadAdminOnly, } from '@/utilities/campaignAccess' import { relationshipId } from '@/utilities/relationship' -import { - type VoteEstimateScenarioFields, - getVoteEstimateOrderViolation, - VOTE_ESTIMATE_ORDER_ERROR_MESSAGE, -} from '@/lib/voteEstimate' import { voteEstimateScenarioGroupAccess, voteEstimateScenarioGroupFields, @@ -85,12 +85,17 @@ const validatePledgeIntegrity: CollectionBeforeChangeHook = async ({ overrideAccess: true, req, }) - const linkedMunicipalityIDs = (Array.isArray(leadership.municipalities) ? leadership.municipalities : []) + const linkedMunicipalityIDs = ( + Array.isArray(leadership.municipalities) ? leadership.municipalities : [] + ) .map(relationshipId) .filter((id): id is number => id !== null) if (!linkedMunicipalityIDs.includes(municipalityID)) { - throw new APIError('A liderança precisa estar vinculada ao município para declarar votos nele.', 409) + throw new APIError( + 'A liderança precisa estar vinculada ao município para declarar votos nele.', + 409, + ) } return data @@ -138,7 +143,13 @@ export const VotePledge: CollectionConfig = { admin: { group: 'Campanha', useAsTitle: 'id', - defaultColumns: ['leadership', 'municipality', 'declaredVotes', 'estimatedVotes.central', 'updatedAt'], + defaultColumns: [ + 'leadership', + 'municipality', + 'declaredVotes', + 'estimatedVotes.central', + 'updatedAt', + ], }, access: { create: canCreateVotePledge, diff --git a/src/components/PetitionForm.tsx b/src/components/PetitionForm.tsx index c68277cc..7e6dda27 100644 --- a/src/components/PetitionForm.tsx +++ b/src/components/PetitionForm.tsx @@ -1,5 +1,6 @@ 'use client' +import { submitPetitionSignature } from '@/app/(frontend)/actions/submitPetitionSignature' import { Field, FieldDescription, @@ -8,26 +9,22 @@ import { FieldLegend, FieldSet, } from '@/components/ui/field' +import { trackMetaLead } from '@/lib/facebookPixel' +import { petitionFormSchema, type PetitionFormInput } from '@/lib/schemas/petition-form' import { Petition } from '@/payload-types' import { zodResolver } from '@hookform/resolvers/zod' -import { FormProvider, type SubmitHandler, useForm } from 'react-hook-form' import { useState, useTransition } from 'react' -import { Textarea } from './ui/textarea' -import { Button } from './ui/button' -import { petitionFormSchema, type PetitionFormInput } from '@/lib/schemas/petition-form' -import { trackMetaLead } from '@/lib/facebookPixel' -import { submitPetitionSignature } from '@/app/(frontend)/actions/submitPetitionSignature' -import { NameInput } from './NameInput' -import { EmailInput } from './EmailInput' -import { PhoneInput } from './PhoneInput' -import { StateSelect } from './StateSelect' +import { FormProvider, useForm, type SubmitHandler } from 'react-hook-form' import { CitySelect } from './CitySelect' -import { PostalCodeInput } from './PostalCodeInput' +import { EmailInput } from './EmailInput' +import { NameInput } from './NameInput' import { PetitionSuccessDialog } from './PetitionSuccessDialog' -import { - SIGNATURE_CREATED_EVENT, - type SignatureCreatedDetail, -} from './SignatureCounter' +import { PhoneInput } from './PhoneInput' +import { PostalCodeInput } from './PostalCodeInput' +import { SIGNATURE_CREATED_EVENT, type SignatureCreatedDetail } from './SignatureCounter' +import { StateSelect } from './StateSelect' +import { Button } from './ui/button' +import { Textarea } from './ui/textarea' interface PetitionFormProps { id: string diff --git a/src/components/PetitionSuccessDialog.tsx b/src/components/PetitionSuccessDialog.tsx index f3e53aa1..6533944d 100644 --- a/src/components/PetitionSuccessDialog.tsx +++ b/src/components/PetitionSuccessDialog.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useState } from 'react' +import { WhatsAppIcon } from '@/components/socialIcons' import { Button } from '@/components/ui/button' import { Dialog, @@ -9,8 +9,8 @@ import { DialogDescription, DialogTitle, } from '@/components/ui/dialog' -import { WhatsAppIcon } from '@/components/socialIcons' import { XIcon } from 'lucide-react' +import { useEffect, useState } from 'react' interface PetitionSuccessDialogProps { open: boolean diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx index 07b2f5c0..6d09903c 100644 --- a/src/components/PostCard.tsx +++ b/src/components/PostCard.tsx @@ -1,6 +1,6 @@ -import type { Post } from '@/payload-types' import { Badge } from '@/components/ui/Badge' import { Card } from '@/components/ui/card' +import type { Post } from '@/payload-types' import { formatPostDate, getCategoryName, getPostCanonicalPath } from '@/utilities/posts' import Image from 'next/image' import Link from 'next/link' diff --git a/src/components/SignatureCounter.tsx b/src/components/SignatureCounter.tsx index 586112b4..0089cee9 100644 --- a/src/components/SignatureCounter.tsx +++ b/src/components/SignatureCounter.tsx @@ -1,8 +1,8 @@ 'use client' -import { useEffect, useRef, useState } from 'react' -import { cn } from '@/lib/utils' import { getSignatureCount } from '@/app/(frontend)/actions/getSignatureCount' +import { cn } from '@/lib/utils' +import { useEffect, useRef, useState } from 'react' const POLL_INTERVAL_MS = 10_000 const ANIMATION_DURATION_MS = 1200 diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index f1023296..f3541504 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -1,7 +1,7 @@ 'use client' -import * as React from 'react' import { ThemeProvider as NextThemesProvider } from 'next-themes' +import * as React from 'react' export function ThemeProvider({ children, diff --git a/src/components/WhatsappForm.tsx b/src/components/WhatsappForm.tsx index 41318f4b..89e385cd 100644 --- a/src/components/WhatsappForm.tsx +++ b/src/components/WhatsappForm.tsx @@ -1,5 +1,6 @@ 'use client' +import { submitWhatsapp } from '@/app/(frontend)/actions/submitWhatsapp' import { CitySelect } from '@/components/CitySelect' import { EmailInput } from '@/components/EmailInput' import { NameInput } from '@/components/NameInput' @@ -10,9 +11,8 @@ import { Field, FieldDescription, FieldGroup } from '@/components/ui/field' import { Textarea } from '@/components/ui/textarea' import { WhatsAppFormInput, whatsAppFormSchema } from '@/lib/schemas/whatsapp-form' import { zodResolver } from '@hookform/resolvers/zod' -import { FormProvider, SubmitHandler, useForm } from 'react-hook-form' import { useTransition } from 'react' -import { submitWhatsapp } from '@/app/(frontend)/actions/submitWhatsapp' +import { FormProvider, SubmitHandler, useForm } from 'react-hook-form' interface WhatsappFormProps { classname?: string diff --git a/src/components/campaign/actionPlan/ActionPlanCard.tsx b/src/components/campaign/actionPlan/ActionPlanCard.tsx index 17779ed9..d103efc8 100644 --- a/src/components/campaign/actionPlan/ActionPlanCard.tsx +++ b/src/components/campaign/actionPlan/ActionPlanCard.tsx @@ -1,12 +1,12 @@ -import Link from 'next/link' import { CalendarIcon, MapPinIcon, UserIcon } from 'lucide-react' +import Link from 'next/link' import { ActionPlanStatusBadge } from '@/components/campaign/actionPlan/ActionPlanStatusBadge' import { Badge } from '@/components/ui/Badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { actionPlanKindLabels } from '@/lib/schemas/actionPlan' -import { formatBahiaDateTimeLabel } from '@/utilities/campaignTime' import type { ActionPlanListViewModel } from '@/utilities/actionPlanViewModels' +import { formatBahiaDateTimeLabel } from '@/utilities/campaignTime' const formatWhen = (plan: ActionPlanListViewModel): string => plan.startAt ? formatBahiaDateTimeLabel(plan.startAt) : 'Data a definir' diff --git a/src/components/campaign/actionPlan/ActionPlanDemandFields.tsx b/src/components/campaign/actionPlan/ActionPlanDemandFields.tsx index 6e875cdd..6d8662d9 100644 --- a/src/components/campaign/actionPlan/ActionPlanDemandFields.tsx +++ b/src/components/campaign/actionPlan/ActionPlanDemandFields.tsx @@ -1,15 +1,15 @@ 'use client' -import { useId, useState } from 'react' import { PlusIcon, Trash2Icon } from 'lucide-react' +import { useId, useState } from 'react' import { Button } from '@/components/ui/button' import { Field, FieldDescription, FieldError, FieldLabel } from '@/components/ui/field' import { Input } from '@/components/ui/input' import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select' import { Textarea } from '@/components/ui/textarea' -import { campaignDemandKindLabels, campaignDemandKinds } from '@/lib/schemas/campaignDemand' import { MAX_ACTION_PLAN_DEMAND_DRAFTS } from '@/lib/schemas/actionPlan' +import { campaignDemandKindLabels, campaignDemandKinds } from '@/lib/schemas/campaignDemand' type DemandFieldValue = { key: string diff --git a/src/components/campaign/actionPlan/ActionPlanFilters.tsx b/src/components/campaign/actionPlan/ActionPlanFilters.tsx index 823a761f..6c4f2b35 100644 --- a/src/components/campaign/actionPlan/ActionPlanFilters.tsx +++ b/src/components/campaign/actionPlan/ActionPlanFilters.tsx @@ -1,17 +1,17 @@ 'use client' import { useCampaignListPending } from '@/components/campaign/shared/CampaignListPending' -import { useRef, useState, useTransition } from 'react' +import { ChevronDownIcon, FilterIcon, XIcon } from 'lucide-react' import Link from 'next/link' import { usePathname, useRouter } from 'next/navigation' -import { ChevronDownIcon, FilterIcon, XIcon } from 'lucide-react' +import { useRef, useState, useTransition } from 'react' import type { RelationOption } from '@/components/campaign/shared/RelationMultiSelect' import { Button } from '@/components/ui/button' import { Field, FieldLabel } from '@/components/ui/field' import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select' -import { cn } from '@/lib/utils' import { actionPlanKindLabels, actionPlanStatusLabels } from '@/lib/schemas/actionPlan' +import { cn } from '@/lib/utils' import { actionPlanTabLabels, actionPlanTabs, diff --git a/src/components/campaign/actionPlan/ActionPlanForm.tsx b/src/components/campaign/actionPlan/ActionPlanForm.tsx index 733dfcf0..2631099a 100644 --- a/src/components/campaign/actionPlan/ActionPlanForm.tsx +++ b/src/components/campaign/actionPlan/ActionPlanForm.tsx @@ -1,13 +1,19 @@ 'use client' -import { useActionState, useState } from 'react' import Link from 'next/link' +import { useActionState, useState } from 'react' -import { AsyncSearchCombobox } from '@/components/campaign/shared/AsyncSearchCombobox' import { ActionPlanDemandFields } from '@/components/campaign/actionPlan/ActionPlanDemandFields' -import { ContactCombobox, type ContactComboboxOption } from '@/components/campaign/shared/ContactCombobox' import { ActionPlanTaskFields } from '@/components/campaign/actionPlan/ActionPlanTaskFields' -import { RelationMultiSelect, type RelationOption } from '@/components/campaign/shared/RelationMultiSelect' +import { AsyncSearchCombobox } from '@/components/campaign/shared/AsyncSearchCombobox' +import { + ContactCombobox, + type ContactComboboxOption, +} from '@/components/campaign/shared/ContactCombobox' +import { + RelationMultiSelect, + type RelationOption, +} from '@/components/campaign/shared/RelationMultiSelect' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/Alert' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' @@ -23,9 +29,9 @@ import { } from '@/components/ui/field' import { Input } from '@/components/ui/input' import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select' +import { Spinner } from '@/components/ui/Spinner' import { Textarea } from '@/components/ui/textarea' import { ToggleGroup, ToggleGroupItem } from '@/components/ui/ToggleGroup' -import { Spinner } from '@/components/ui/Spinner' import { isContactSearchQueryReady } from '@/lib/contactSearchQuery' import { actionPlanKindLabels, diff --git a/src/components/campaign/actionPlan/ActionPlanLifecycleDialog.tsx b/src/components/campaign/actionPlan/ActionPlanLifecycleDialog.tsx index 6271fbb4..30f63ec8 100644 --- a/src/components/campaign/actionPlan/ActionPlanLifecycleDialog.tsx +++ b/src/components/campaign/actionPlan/ActionPlanLifecycleDialog.tsx @@ -1,7 +1,7 @@ 'use client' -import { useActionState } from 'react' import { CheckCircle2Icon, XCircleIcon } from 'lucide-react' +import { useActionState } from 'react' import { cancelActionPlanFormAction, diff --git a/src/components/campaign/actionPlan/ActionPlanResultForm.tsx b/src/components/campaign/actionPlan/ActionPlanResultForm.tsx index eef5c4f9..938ab56c 100644 --- a/src/components/campaign/actionPlan/ActionPlanResultForm.tsx +++ b/src/components/campaign/actionPlan/ActionPlanResultForm.tsx @@ -1,7 +1,7 @@ 'use client' -import { useActionState, useEffect } from 'react' import { useRouter } from 'next/navigation' +import { useActionState, useEffect } from 'react' import { toast } from 'sonner' import { registerActionPlanResultFormAction } from '@/app/(campaign)/campanha/(app)/planos/[slug]/resultFormActions' @@ -50,9 +50,7 @@ export const ActionPlanResultForm = ({ Resultado da ação - {recordedLabel ? ( -

{recordedLabel}

- ) : null} + {recordedLabel ?

{recordedLabel}

: null}
diff --git a/src/components/campaign/actionPlan/ActionPlanTaskChecklist.tsx b/src/components/campaign/actionPlan/ActionPlanTaskChecklist.tsx index 606aadfb..af3b2a10 100644 --- a/src/components/campaign/actionPlan/ActionPlanTaskChecklist.tsx +++ b/src/components/campaign/actionPlan/ActionPlanTaskChecklist.tsx @@ -5,8 +5,8 @@ import { toast } from 'sonner' import { toggleActionPlanTaskAction } from '@/app/(campaign)/campanha/(app)/planos/[slug]/taskActions' import { Checkbox } from '@/components/ui/Checkbox' -import { formatBahiaDateTimeLabel } from '@/utilities/campaignTime' import type { ActionPlanTaskViewModel } from '@/utilities/actionPlanViewModels' +import { formatBahiaDateTimeLabel } from '@/utilities/campaignTime' type ActionPlanTaskChecklistProps = { planId: number diff --git a/src/components/campaign/actionPlan/ActionPlanTaskFields.tsx b/src/components/campaign/actionPlan/ActionPlanTaskFields.tsx index 5980536c..d0ed72c5 100644 --- a/src/components/campaign/actionPlan/ActionPlanTaskFields.tsx +++ b/src/components/campaign/actionPlan/ActionPlanTaskFields.tsx @@ -1,9 +1,12 @@ 'use client' -import { useId, useState } from 'react' import { PlusIcon, Trash2Icon } from 'lucide-react' +import { useId, useState } from 'react' -import { ContactCombobox, type ContactComboboxOption } from '@/components/campaign/shared/ContactCombobox' +import { + ContactCombobox, + type ContactComboboxOption, +} from '@/components/campaign/shared/ContactCombobox' import { Button } from '@/components/ui/button' import { Field, FieldError, FieldLabel } from '@/components/ui/field' import { Input } from '@/components/ui/input' @@ -84,7 +87,10 @@ export const ActionPlanTaskFields = ({
{tasks.map((task, index) => ( -
+
(
diff --git a/src/components/campaign/actionPlan/ActionPlanUpdateForm.tsx b/src/components/campaign/actionPlan/ActionPlanUpdateForm.tsx index 65af121a..7b3e8eb7 100644 --- a/src/components/campaign/actionPlan/ActionPlanUpdateForm.tsx +++ b/src/components/campaign/actionPlan/ActionPlanUpdateForm.tsx @@ -1,7 +1,7 @@ 'use client' -import { useActionState, useEffect, useRef } from 'react' import { useRouter } from 'next/navigation' +import { useActionState, useEffect, useRef } from 'react' import { toast } from 'sonner' import { createActionPlanUpdateFormAction } from '@/app/(campaign)/campanha/(app)/planos/[slug]/updateFormActions' diff --git a/src/components/campaign/advisor/AdvisorDebouncedTextCell.tsx b/src/components/campaign/advisor/AdvisorDebouncedTextCell.tsx index 07d5f53a..c64328fe 100644 --- a/src/components/campaign/advisor/AdvisorDebouncedTextCell.tsx +++ b/src/components/campaign/advisor/AdvisorDebouncedTextCell.tsx @@ -6,12 +6,12 @@ import { toast } from 'sonner' import { Input } from '@/components/ui/input' import { Spinner } from '@/components/ui/Spinner' +import { formatBrazilianPhoneInput, sanitizeBrazilianPhoneInput } from '@/lib/phone' import { isPlanilhaPlaceholderEmail, planilhaPlaceholderEmailForAdvisor, } from '@/lib/schemas/advisor' import type { CampaignFormActionState } from '@/utilities/campaignFormActionError' -import { formatBrazilianPhoneInput, sanitizeBrazilianPhoneInput } from '@/lib/phone' const SAVE_DEBOUNCE_MS = 500 @@ -79,8 +79,7 @@ export const AdvisorDebouncedTextCell = ({ const displayNext = nextRaw.trim() if (displayNext === lastSaved.current) return - const persisted = - field === 'email' ? resolveEmailToPersist(nextRaw) : displayNext + const persisted = field === 'email' ? resolveEmailToPersist(nextRaw) : displayNext const formData = new FormData() formData.set('advisorId', String(advisorId)) diff --git a/src/components/campaign/advisor/AdvisorMunicipalityCell.tsx b/src/components/campaign/advisor/AdvisorMunicipalityCell.tsx index ffed8aab..6cc1f58e 100644 --- a/src/components/campaign/advisor/AdvisorMunicipalityCell.tsx +++ b/src/components/campaign/advisor/AdvisorMunicipalityCell.tsx @@ -342,7 +342,12 @@ export const AdvisorMunicipalityCell = ({ {chip.label} ) : ( - + { if ((event.target as HTMLElement).closest('[data-chip-remove]')) return inputRef.current?.focus() diff --git a/src/components/campaign/advisor/AdvisorPasswordResetButton.tsx b/src/components/campaign/advisor/AdvisorPasswordResetButton.tsx index 561ddc7e..89e2353d 100644 --- a/src/components/campaign/advisor/AdvisorPasswordResetButton.tsx +++ b/src/components/campaign/advisor/AdvisorPasswordResetButton.tsx @@ -51,7 +51,9 @@ export const AdvisorPasswordResetButton = ({ size="icon" className="size-10" disabled={disabled || isPending} - aria-label={disabled ? `${accessibleName} — ${PLACEHOLDER_RESET_MESSAGE}` : accessibleName} + aria-label={ + disabled ? `${accessibleName} — ${PLACEHOLDER_RESET_MESSAGE}` : accessibleName + } > {isPending ? (