From f775f954ad39c5370457768f758b68ab2f4794be Mon Sep 17 00:00:00 2001 From: Moheeb Zara Date: Tue, 4 Aug 2026 06:19:11 -0700 Subject: [PATCH] refactor: dead-code cleanup + drop the lib/types shim (slice 9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete orphaned components (theme-provider, task-claim-button), the unused roleDisplayName helper, the fully-dead cleanupRateLimits, the 6 never-imported validation schemas (signIn/signUp/createSpace/joinSpace — a replaced auth flow; proposalStatuses; updateAreaLeadRoleSchema). - Migrate all @/lib/types importers to the canonical @/types/domain and delete the compatibility shim. tsc 0, 635 tests green, lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/(app)/financials/page.tsx | 2 +- app/(app)/incidents/[id]/incident-actions.tsx | 2 +- app/(app)/incidents/[id]/page.tsx | 2 +- app/(app)/incidents/page.tsx | 2 +- app/(app)/policies/[slug]/page.tsx | 2 +- app/(app)/policies/[slug]/policy-actions.tsx | 2 +- app/(app)/policies/page.tsx | 2 +- app/(app)/proposals/[id]/page.tsx | 2 +- app/(app)/proposals/[id]/proposal-actions.tsx | 2 +- app/(app)/proposals/page.tsx | 2 +- app/(app)/proposals/proposal-badges.tsx | 2 +- components/task-claim-button.tsx | 64 ------------------- components/theme-provider.tsx | 11 ---- lib/role-labels.ts | 5 -- lib/security.ts | 12 ---- lib/types.ts | 7 -- lib/validations/auth.ts | 30 --------- lib/validations/governance.ts | 1 - lib/validations/index.ts | 1 - lib/validations/permissions.ts | 5 -- 20 files changed, 11 insertions(+), 147 deletions(-) delete mode 100644 components/task-claim-button.tsx delete mode 100644 components/theme-provider.tsx delete mode 100644 lib/types.ts delete mode 100644 lib/validations/auth.ts diff --git a/app/(app)/financials/page.tsx b/app/(app)/financials/page.tsx index 774b10f..f11684a 100644 --- a/app/(app)/financials/page.tsx +++ b/app/(app)/financials/page.tsx @@ -1,7 +1,7 @@ import { redirect } from 'next/navigation' import Link from 'next/link' import { createClient } from '@/lib/supabase/server' -import type { FinancialVisibility, Payment, Space } from '@/lib/types' +import type { FinancialVisibility, Payment, Space } from '@/types/domain' import { Receipt } from 'lucide-react' import { PageTitle } from '@/components/ui/page-title' import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty' diff --git a/app/(app)/incidents/[id]/incident-actions.tsx b/app/(app)/incidents/[id]/incident-actions.tsx index 47af991..b347c6e 100644 --- a/app/(app)/incidents/[id]/incident-actions.tsx +++ b/app/(app)/incidents/[id]/incident-actions.tsx @@ -8,7 +8,7 @@ import { addIncidentUpdate, appealIncident, } from '@/lib/actions' -import type { IncidentStatus } from '@/lib/types' +import type { IncidentStatus } from '@/types/domain' import { incidentStatuses, incidentUpdateVisibilities } from '@/lib/validations' type Props = { diff --git a/app/(app)/incidents/[id]/page.tsx b/app/(app)/incidents/[id]/page.tsx index 0d5ccaa..b8e0720 100644 --- a/app/(app)/incidents/[id]/page.tsx +++ b/app/(app)/incidents/[id]/page.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { redirect, notFound } from 'next/navigation' import { createClient } from '@/lib/supabase/server' -import type { Incident, IncidentUpdateRow } from '@/lib/types' +import type { Incident, IncidentUpdateRow } from '@/types/domain' import { IncidentActions } from './incident-actions' import { MarkdownBody } from '@/components/markdown' import { CommentThread } from '@/components/comments/comment-thread' diff --git a/app/(app)/incidents/page.tsx b/app/(app)/incidents/page.tsx index 170d6c4..1d9e068 100644 --- a/app/(app)/incidents/page.tsx +++ b/app/(app)/incidents/page.tsx @@ -2,7 +2,7 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { ShieldCheck } from 'lucide-react' import { createClient } from '@/lib/supabase/server' -import type { Incident, IncidentStatus } from '@/lib/types' +import type { Incident, IncidentStatus } from '@/types/domain' import { PageTitle } from '@/components/ui/page-title' import { Empty, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' diff --git a/app/(app)/policies/[slug]/page.tsx b/app/(app)/policies/[slug]/page.tsx index eb9fb4f..3a48d07 100644 --- a/app/(app)/policies/[slug]/page.tsx +++ b/app/(app)/policies/[slug]/page.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { redirect, notFound } from 'next/navigation' import { createClient } from '@/lib/supabase/server' -import type { Policy } from '@/lib/types' +import type { Policy } from '@/types/domain' import { PolicyActions } from './policy-actions' import { MarkdownBody } from '@/components/markdown' import { CommentThread } from '@/components/comments/comment-thread' diff --git a/app/(app)/policies/[slug]/policy-actions.tsx b/app/(app)/policies/[slug]/policy-actions.tsx index f82bbb5..3d119f1 100644 --- a/app/(app)/policies/[slug]/policy-actions.tsx +++ b/app/(app)/policies/[slug]/policy-actions.tsx @@ -4,7 +4,7 @@ import { useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { supersedePolicy, updatePolicyStatus } from '@/lib/actions' import { policyStatuses } from '@/lib/validations' -import type { Policy } from '@/lib/types' +import type { Policy } from '@/types/domain' export function PolicyActions({ policy }: { policy: Policy }) { const router = useRouter() diff --git a/app/(app)/policies/page.tsx b/app/(app)/policies/page.tsx index 0e46a9a..925d631 100644 --- a/app/(app)/policies/page.tsx +++ b/app/(app)/policies/page.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { createClient } from '@/lib/supabase/server' -import type { Policy, PolicyStatus } from '@/lib/types' +import type { Policy, PolicyStatus } from '@/types/domain' import { PageTitle } from '@/components/ui/page-title' export const dynamic = 'force-dynamic' diff --git a/app/(app)/proposals/[id]/page.tsx b/app/(app)/proposals/[id]/page.tsx index 5dfed39..5a6e650 100644 --- a/app/(app)/proposals/[id]/page.tsx +++ b/app/(app)/proposals/[id]/page.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { redirect, notFound } from 'next/navigation' import { createClient } from '@/lib/supabase/server' -import type { Proposal, ProposalVote } from '@/lib/types' +import type { Proposal, ProposalVote } from '@/types/domain' import { ProposalStatusBadge } from '../proposal-badges' import { ProposalActions } from './proposal-actions' import { MarkdownBody } from '@/components/markdown' diff --git a/app/(app)/proposals/[id]/proposal-actions.tsx b/app/(app)/proposals/[id]/proposal-actions.tsx index c4051fd..788921d 100644 --- a/app/(app)/proposals/[id]/proposal-actions.tsx +++ b/app/(app)/proposals/[id]/proposal-actions.tsx @@ -8,7 +8,7 @@ import { openProposal, withdrawProposal, } from '@/lib/actions' -import type { ProposalStatus, ProposalVote, VotePosition } from '@/lib/types' +import type { ProposalStatus, ProposalVote, VotePosition } from '@/types/domain' import { votePositions } from '@/lib/validations' type Props = { diff --git a/app/(app)/proposals/page.tsx b/app/(app)/proposals/page.tsx index adaa3f5..d64b7b4 100644 --- a/app/(app)/proposals/page.tsx +++ b/app/(app)/proposals/page.tsx @@ -2,7 +2,7 @@ import Link from 'next/link' import { redirect } from 'next/navigation' import { Vote } from 'lucide-react' import { createClient } from '@/lib/supabase/server' -import type { Proposal } from '@/lib/types' +import type { Proposal } from '@/types/domain' import { ProposalStatusBadge } from './proposal-badges' import { PageTitle } from '@/components/ui/page-title' import { Empty, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' diff --git a/app/(app)/proposals/proposal-badges.tsx b/app/(app)/proposals/proposal-badges.tsx index eea8670..e743992 100644 --- a/app/(app)/proposals/proposal-badges.tsx +++ b/app/(app)/proposals/proposal-badges.tsx @@ -1,4 +1,4 @@ -import type { ProposalStatus } from '@/lib/types' +import type { ProposalStatus } from '@/types/domain' const STATUS_STYLE: Record = { draft: 'text-muted-foreground bg-muted', diff --git a/components/task-claim-button.tsx b/components/task-claim-button.tsx deleted file mode 100644 index 832329f..0000000 --- a/components/task-claim-button.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client' - -import { useState } from 'react' -import { claimTask, completeTask } from '@/lib/actions' - -export function ClaimButton({ taskId }: { taskId: string }) { - const [loading, setLoading] = useState(false) - const [done, setDone] = useState(false) - - async function handleClaim() { - setLoading(true) - const result = await claimTask(taskId) - if (!result.error) setDone(true) - setLoading(false) - } - - if (done) { - return ( - - CLAIMED - - ) - } - - return ( - - ) -} - -export function CompleteButton({ taskId }: { taskId: string }) { - const [loading, setLoading] = useState(false) - const [done, setDone] = useState(false) - - async function handleComplete() { - setLoading(true) - const result = await completeTask(taskId) - if (!result.error) setDone(true) - setLoading(false) - } - - if (done) { - return ( - - DONE - - ) - } - - return ( - - ) -} diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx deleted file mode 100644 index 55c2f6e..0000000 --- a/components/theme-provider.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import * as React from 'react' -import { - ThemeProvider as NextThemesProvider, - type ThemeProviderProps, -} from 'next-themes' - -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { - return {children} -} diff --git a/lib/role-labels.ts b/lib/role-labels.ts index 9eae0be..66d3d15 100644 --- a/lib/role-labels.ts +++ b/lib/role-labels.ts @@ -47,8 +47,3 @@ export async function getRoleLabelMap( return map } -export function roleDisplayName(map: Record | null | undefined, role: string): string { - if (map && map[role]) return map[role].name - const d = DEFAULT_ROLE_LABELS[role as BuiltinRole] - return d ? d.name : role -} diff --git a/lib/security.ts b/lib/security.ts index d500287..e426756 100644 --- a/lib/security.ts +++ b/lib/security.ts @@ -139,18 +139,6 @@ export function checkRateLimit( return { allowed: true, remaining: maxRequests - entry.count, resetIn: entry.resetTime - now } } -/** - * Clear expired rate limit entries (call periodically) - */ -export function cleanupRateLimits(): void { - const now = Date.now() - for (const [key, entry] of rateLimitStore.entries()) { - if (now > entry.resetTime) { - rateLimitStore.delete(key) - } - } -} - /** * Validate content length */ diff --git a/lib/types.ts b/lib/types.ts deleted file mode 100644 index 0f87761..0000000 --- a/lib/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Compatibility shim. The hand-written domain types moved to `types/domain/*` - * (kept separate from the generated `types/database.ts`). This re-export keeps - * the `@/lib/types` specifier working for existing importers; prefer importing - * from `@/types/domain` in new code. - */ -export * from '@/types/domain' diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts deleted file mode 100644 index 45dac4c..0000000 --- a/lib/validations/auth.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { z } from 'zod' -import { emailField } from './primitives' - -export const signInSchema = z.object({ - email: emailField(), - password: z.string().min(1, 'Password is required'), -}) - -export const signUpSchema = z.object({ - email: emailField(), - password: z.string().min(8, 'Password must be at least 8 characters'), - fullName: z.string().min(1, 'Full name is required').max(100, 'Name is too long'), - action: z.enum(['create', 'join']), - spaceName: z.string().max(100).optional(), - spaceSlug: z.string().regex(/^[a-z0-9]+$/, 'Slug must be lowercase letters and numbers only').max(50).optional(), - city: z.string().max(100).optional(), - inviteCode: z.string().max(20).optional(), -}) - -export const createSpaceSchema = z.object({ - spaceName: z.string().min(1, 'Space name is required').max(100, 'Name is too long'), - spaceSlug: z.string().min(1, 'Slug is required').max(50).regex(/^[a-z0-9]+$/, 'Slug must be lowercase letters and numbers only'), - spaceCity: z.string().max(100).optional(), - displayName: z.string().min(1, 'Display name is required').max(100), -}) - -export const joinSpaceSchema = z.object({ - inviteCode: z.string().min(1, 'Invite code is required').max(20), - displayName: z.string().min(1, 'Display name is required').max(100), -}) diff --git a/lib/validations/governance.ts b/lib/validations/governance.ts index e22b6b4..0712851 100644 --- a/lib/validations/governance.ts +++ b/lib/validations/governance.ts @@ -4,7 +4,6 @@ import { flexibleDateTime } from './primitives' // ─── Governance: proposals ─────────────────────────────────────────────────── export const proposalTypes = ['bylaw_change','board_action','membership_vote','advisory_poll','recall','budget'] as const -export const proposalStatuses = ['draft','open','decided','withdrawn','expired'] as const export const thresholdRules = ['simple_majority','two_thirds','three_fourths','unanimous'] as const export const votePositions = ['yes','no','abstain','recused'] as const diff --git a/lib/validations/index.ts b/lib/validations/index.ts index 47d831c..cad6c7e 100644 --- a/lib/validations/index.ts +++ b/lib/validations/index.ts @@ -1,5 +1,4 @@ export * from './primitives' -export * from './auth' export * from './tasks' export * from './projects' export * from './members' diff --git a/lib/validations/permissions.ts b/lib/validations/permissions.ts index 5232738..bbf302c 100644 --- a/lib/validations/permissions.ts +++ b/lib/validations/permissions.ts @@ -24,11 +24,6 @@ export const createAreaLeadRoleSchema = z.object({ color: z.string().max(20).optional().nullable(), }) -export const updateAreaLeadRoleSchema = z.object({ - name: z.string().min(1).max(100).optional(), - color: z.string().max(20).optional().nullable(), -}) - export const assignAreaLeadSchema = z.object({ area_lead_role_id: z.string().uuid('Invalid role ID'), member_id: z.string().uuid('Invalid member ID'),