diff --git a/.gitignore b/.gitignore index 166ab38..5dbe477 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,13 @@ out/ *.o *.pyc __pycache__/ +*.tsbuildinfo + +# Turbo cache +.turbo/ + +# Infra (moved to lunarfront-infra repo) +infra/ # IDE .idea/ diff --git a/packages/admin/src/components/accounts/payment-method-form.tsx b/packages/admin/src/components/accounts/payment-method-form.tsx index 1177b18..45e9be6 100644 --- a/packages/admin/src/components/accounts/payment-method-form.tsx +++ b/packages/admin/src/components/accounts/payment-method-form.tsx @@ -1,7 +1,6 @@ import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { PaymentMethodCreateSchema } from '@lunarfront/shared/schemas' -import type { PaymentMethodCreateInput } from '@lunarfront/shared/schemas' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' diff --git a/packages/admin/src/components/accounts/tax-exemption-form.tsx b/packages/admin/src/components/accounts/tax-exemption-form.tsx index d5a9d78..09dec43 100644 --- a/packages/admin/src/components/accounts/tax-exemption-form.tsx +++ b/packages/admin/src/components/accounts/tax-exemption-form.tsx @@ -1,7 +1,6 @@ import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { TaxExemptionCreateSchema } from '@lunarfront/shared/schemas' -import type { TaxExemptionCreateInput } from '@lunarfront/shared/schemas' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' diff --git a/packages/admin/src/components/lessons/template-section-builder.tsx b/packages/admin/src/components/lessons/template-section-builder.tsx index f622b18..408a530 100644 --- a/packages/admin/src/components/lessons/template-section-builder.tsx +++ b/packages/admin/src/components/lessons/template-section-builder.tsx @@ -1,8 +1,6 @@ -import { useState } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' -import { Label } from '@/components/ui/label' import { Trash2, Plus, ChevronUp, ChevronDown } from 'lucide-react' interface TemplateItemRow { diff --git a/packages/admin/src/components/repairs/generate-pdf.ts b/packages/admin/src/components/repairs/generate-pdf.ts index 4c46441..4df3b2b 100644 --- a/packages/admin/src/components/repairs/generate-pdf.ts +++ b/packages/admin/src/components/repairs/generate-pdf.ts @@ -1,5 +1,4 @@ import jsPDF from 'jspdf' -import { api } from '@/lib/api-client' import type { RepairTicket, RepairLineItem, RepairNote } from '@/types/repair' const STATUS_LABELS: Record = { diff --git a/packages/admin/src/components/repairs/pdf-modal.tsx b/packages/admin/src/components/repairs/pdf-modal.tsx index 51d481e..0ca5526 100644 --- a/packages/admin/src/components/repairs/pdf-modal.tsx +++ b/packages/admin/src/components/repairs/pdf-modal.tsx @@ -9,9 +9,9 @@ import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' -import { FileText, Download, Check, Eye, Lock } from 'lucide-react' +import { FileText, Download, Check, Eye } from 'lucide-react' import { toast } from 'sonner' -import type { RepairTicket, RepairLineItem, RepairNote } from '@/types/repair' +import type { RepairTicket, RepairLineItem } from '@/types/repair' interface FileRecord { id: string @@ -65,7 +65,7 @@ export function PdfModal({ ticket, lineItems, ticketId }: PdfModalProps) { function toggleNote(id: string) { setSelectedNoteIds((prev) => { const next = new Set(prev) - next.has(id) ? next.delete(id) : next.add(id) + if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) } @@ -73,7 +73,7 @@ export function PdfModal({ ticket, lineItems, ticketId }: PdfModalProps) { function togglePhoto(id: string) { setSelectedPhotoIds((prev) => { const next = new Set(prev) - next.has(id) ? next.delete(id) : next.add(id) + if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) } @@ -102,7 +102,7 @@ export function PdfModal({ ticket, lineItems, ticketId }: PdfModalProps) { ) toast.success('PDF generated and saved to documents') setOpen(false) - } catch (err) { + } catch { toast.error('Failed to generate PDF') } finally { setGenerating(false) diff --git a/packages/admin/src/components/repairs/status-progress.tsx b/packages/admin/src/components/repairs/status-progress.tsx index 85da7a2..83430a9 100644 --- a/packages/admin/src/components/repairs/status-progress.tsx +++ b/packages/admin/src/components/repairs/status-progress.tsx @@ -1,4 +1,4 @@ -import { Check, Truck, ClipboardList, Search, Clock, ThumbsUp, Wrench, Package, HandMetal, Ban, FilePlus } from 'lucide-react' +import { Check, ClipboardList, Search, Clock, ThumbsUp, Wrench, Package, HandMetal, Ban, FilePlus } from 'lucide-react' const STEPS = [ { key: 'new', label: 'New', icon: FilePlus }, diff --git a/packages/admin/src/components/repairs/ticket-photos.tsx b/packages/admin/src/components/repairs/ticket-photos.tsx index 21f7e5f..602a196 100644 --- a/packages/admin/src/components/repairs/ticket-photos.tsx +++ b/packages/admin/src/components/repairs/ticket-photos.tsx @@ -4,7 +4,7 @@ import { queryOptions } from '@tanstack/react-query' import { api } from '@/lib/api-client' import { useAuthStore } from '@/stores/auth.store' import { Button } from '@/components/ui/button' -import { FileText, ImageIcon, Plus, Trash2 } from 'lucide-react' +import { FileText, Plus, Trash2 } from 'lucide-react' import { toast } from 'sonner' function AuthImage({ path, alt, className, onClick }: { path: string; alt: string; className?: string; onClick?: () => void }) { diff --git a/packages/admin/src/components/shared/avatar-upload.tsx b/packages/admin/src/components/shared/avatar-upload.tsx index 6655649..e214d46 100644 --- a/packages/admin/src/components/shared/avatar-upload.tsx +++ b/packages/admin/src/components/shared/avatar-upload.tsx @@ -1,5 +1,5 @@ import { useRef, useState } from 'react' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { useQuery, useQueryClient } from '@tanstack/react-query' import { queryOptions } from '@tanstack/react-query' import { api } from '@/lib/api-client' import { useAuthStore } from '@/stores/auth.store' diff --git a/packages/admin/src/routes/_authenticated/accounts/$accountId/enrollments.tsx b/packages/admin/src/routes/_authenticated/accounts/$accountId/enrollments.tsx index 42c5aea..b366be5 100644 --- a/packages/admin/src/routes/_authenticated/accounts/$accountId/enrollments.tsx +++ b/packages/admin/src/routes/_authenticated/accounts/$accountId/enrollments.tsx @@ -1,7 +1,6 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router' import { useQuery } from '@tanstack/react-query' import { enrollmentListOptions } from '@/api/lessons' -import { memberListOptions } from '@/api/members' import { DataTable, type Column } from '@/components/shared/data-table' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' @@ -32,10 +31,6 @@ function AccountEnrollmentsTab() { const navigate = useNavigate() const hasPermission = useAuthStore((s) => s.hasPermission) - // Get member IDs for this account so we can filter enrollments - const { data: membersData } = useQuery(memberListOptions(accountId, { page: 1, limit: 100, order: 'asc' })) - const memberIds = (membersData?.data ?? []).map((m) => m.id) - const { data, isLoading } = useQuery({ ...enrollmentListOptions({ accountId, page: 1, limit: 100 }), enabled: !!accountId, diff --git a/packages/admin/src/routes/_authenticated/accounts/new.tsx b/packages/admin/src/routes/_authenticated/accounts/new.tsx index c384d11..060446d 100644 --- a/packages/admin/src/routes/_authenticated/accounts/new.tsx +++ b/packages/admin/src/routes/_authenticated/accounts/new.tsx @@ -24,7 +24,8 @@ function NewAccountPage() { const memberIsMinor = data.memberIsMinor as boolean | undefined // Create account (without member fields) - const { memberFirstName: _, memberLastName: __, memberEmail: ___, memberPhone: ____, memberDateOfBirth: _____, memberIsMinor: ______, ...accountData } = data + const memberKeys = new Set(['memberFirstName', 'memberLastName', 'memberEmail', 'memberPhone', 'memberDateOfBirth', 'memberIsMinor']) + const accountData: Record = Object.fromEntries(Object.entries(data).filter(([k]) => !memberKeys.has(k))) // Auto-generate account name from member if not provided if (!accountData.name && memberFirstName && memberLastName) { diff --git a/packages/admin/src/routes/_authenticated/files/index.tsx b/packages/admin/src/routes/_authenticated/files/index.tsx index b28310f..063447b 100644 --- a/packages/admin/src/routes/_authenticated/files/index.tsx +++ b/packages/admin/src/routes/_authenticated/files/index.tsx @@ -10,18 +10,17 @@ import { FolderTree } from '@/components/storage/folder-tree' import { FileIcon, formatFileSize } from '@/components/storage/file-icons' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Label } from '@/components/ui/label' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' -import { FolderPlus, Upload, Search, Folder, ChevronRight, MoreVertical, Trash2, Download, Shield } from 'lucide-react' +import { FolderPlus, Upload, Folder, ChevronRight, MoreVertical, Trash2, Download, Shield } from 'lucide-react' import { toast } from 'sonner' import { useAuthStore } from '@/stores/auth.store' import { FolderPermissionsDialog } from '@/components/storage/folder-permissions-dialog' -import type { StorageFolder, StorageFile } from '@/types/storage' +import type { StorageFile } from '@/types/storage' export const Route = createFileRoute('/_authenticated/files/')({ validateSearch: (search: Record) => ({ @@ -69,16 +68,6 @@ function FileManagerPage() { onError: (err) => toast.error(err.message), }) - const deleteFolderMutation = useMutation({ - mutationFn: storageFolderMutations.delete, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: storageFolderKeys.all }) - setSelectedFolderId(null) - toast.success('Folder deleted') - }, - onError: (err) => toast.error(err.message), - }) - const deleteFileMutation = useMutation({ mutationFn: storageFileMutations.delete, onSuccess: () => { diff --git a/packages/admin/src/routes/_authenticated/help.tsx b/packages/admin/src/routes/_authenticated/help.tsx index 6b57659..d67406f 100644 --- a/packages/admin/src/routes/_authenticated/help.tsx +++ b/packages/admin/src/routes/_authenticated/help.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' import { useState } from 'react' -import { getWikiCategories, getWikiPage, type WikiPage } from '@/wiki' +import { getWikiCategories, getWikiPage } from '@/wiki' import { Input } from '@/components/ui/input' import { Search, ChevronDown, ChevronRight } from 'lucide-react' diff --git a/packages/admin/src/routes/_authenticated/inventory/$productId.tsx b/packages/admin/src/routes/_authenticated/inventory/$productId.tsx index 7e12897..cb10925 100644 --- a/packages/admin/src/routes/_authenticated/inventory/$productId.tsx +++ b/packages/admin/src/routes/_authenticated/inventory/$productId.tsx @@ -24,7 +24,7 @@ import { ArrowLeft, Plus, Pencil, Star, Trash2 } from 'lucide-react' import { toast } from 'sonner' import { cn } from '@/lib/utils' import { useAuthStore } from '@/stores/auth.store' -import type { InventoryUnit, ProductSupplier, StockReceipt, UnitCondition, UnitStatus } from '@/types/inventory' +import type { InventoryUnit, ProductSupplier, UnitCondition, UnitStatus } from '@/types/inventory' const CONDITION_CLASSES: Record = { new: 'bg-blue-100 text-blue-800 border border-blue-300', @@ -494,7 +494,7 @@ function ProductDetailPage() { // ── Suppliers tab ───────────────────────────────────────────────────────────── function SuppliersTab({ - productId, + productId: _productId, linkedSuppliers, addOpen, setAddOpen, editTarget, setEditTarget, diff --git a/packages/admin/src/routes/_authenticated/lessons/enrollments/$enrollmentId.tsx b/packages/admin/src/routes/_authenticated/lessons/enrollments/$enrollmentId.tsx index ba0d769..e904845 100644 --- a/packages/admin/src/routes/_authenticated/lessons/enrollments/$enrollmentId.tsx +++ b/packages/admin/src/routes/_authenticated/lessons/enrollments/$enrollmentId.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { createFileRoute, useNavigate, Link } from '@tanstack/react-router' +import { createFileRoute, useNavigate } from '@tanstack/react-router' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { enrollmentDetailOptions, enrollmentMutations, enrollmentKeys, diff --git a/packages/admin/src/routes/_authenticated/lessons/schedule/index.tsx b/packages/admin/src/routes/_authenticated/lessons/schedule/index.tsx index 4cf5849..f3bb71c 100644 --- a/packages/admin/src/routes/_authenticated/lessons/schedule/index.tsx +++ b/packages/admin/src/routes/_authenticated/lessons/schedule/index.tsx @@ -90,7 +90,7 @@ const instructorColumns: Column[] = [ }, ] -function InstructorsTab({ canAdmin, search }: { canAdmin: boolean; search: any }) { +function InstructorsTab({ canAdmin, search: _search }: { canAdmin: boolean; search: any }) { const navigate = useNavigate() const queryClient = useQueryClient() const { params, setPage, setSearch, setSort } = usePagination() @@ -169,7 +169,7 @@ const lessonTypeColumns: Column[] = [ { key: 'is_active', header: 'Status', render: (lt) => {lt.isActive ? 'Active' : 'Inactive'} }, ] -function LessonTypesTab({ canAdmin, search }: { canAdmin: boolean; search: any }) { +function LessonTypesTab({ canAdmin, search: _search }: { canAdmin: boolean; search: any }) { const queryClient = useQueryClient() const { params, setPage, setSearch, setSort } = usePagination() const [searchInput, setSearchInput] = useState(params.q ?? '') @@ -298,7 +298,7 @@ const gradingScaleColumns: Column[] = [ { key: 'is_active', header: 'Status', render: (gs) => {gs.isActive ? 'Active' : 'Inactive'} }, ] -function GradingScalesTab({ canAdmin, search }: { canAdmin: boolean; search: any }) { +function GradingScalesTab({ canAdmin, search: _search }: { canAdmin: boolean; search: any }) { const queryClient = useQueryClient() const { params, setPage, setSort } = usePagination() const [createOpen, setCreateOpen] = useState(false) diff --git a/packages/admin/src/routes/_authenticated/lessons/sessions/$sessionId.tsx b/packages/admin/src/routes/_authenticated/lessons/sessions/$sessionId.tsx index 7d324e7..c5752e5 100644 --- a/packages/admin/src/routes/_authenticated/lessons/sessions/$sessionId.tsx +++ b/packages/admin/src/routes/_authenticated/lessons/sessions/$sessionId.tsx @@ -67,7 +67,7 @@ function SessionDetailPage() { enabled: !!session?.enrollmentId, }) - const { data: instructorData } = useQuery({ + useQuery({ ...instructorDetailOptions(session?.substituteInstructorId ?? enrollment?.instructorId ?? ''), enabled: !!(session?.substituteInstructorId ?? enrollment?.instructorId), }) diff --git a/packages/admin/src/routes/_authenticated/lessons/sessions/index.tsx b/packages/admin/src/routes/_authenticated/lessons/sessions/index.tsx index 008ffbe..d0d5833 100644 --- a/packages/admin/src/routes/_authenticated/lessons/sessions/index.tsx +++ b/packages/admin/src/routes/_authenticated/lessons/sessions/index.tsx @@ -11,7 +11,6 @@ import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Search, LayoutList, CalendarDays, ChevronLeft, ChevronRight } from 'lucide-react' -import { useAuthStore } from '@/stores/auth.store' import type { LessonSession } from '@/types/lesson' export const Route = createFileRoute('/_authenticated/lessons/sessions/')({ diff --git a/packages/admin/src/routes/_authenticated/members/$memberId.tsx b/packages/admin/src/routes/_authenticated/members/$memberId.tsx index fbbdf7c..639a538 100644 --- a/packages/admin/src/routes/_authenticated/members/$memberId.tsx +++ b/packages/admin/src/routes/_authenticated/members/$memberId.tsx @@ -11,7 +11,6 @@ import { IdentifierForm, type IdentifierFiles } from '@/components/accounts/iden import { DataTable, type Column } from '@/components/shared/data-table' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Skeleton } from '@/components/ui/skeleton' import { ArrowLeft, Plus, Trash2, CreditCard } from 'lucide-react' @@ -19,7 +18,7 @@ import { toast } from 'sonner' import { AvatarUpload } from '@/components/shared/avatar-upload' import { useAuthStore } from '@/stores/auth.store' import { cn } from '@/lib/utils' -import type { Member, MemberIdentifier } from '@/types/account' +import type { Member } from '@/types/account' import type { Enrollment } from '@/types/lesson' function memberDetailOptions(id: string) { diff --git a/packages/admin/src/routes/_authenticated/repair-batches/$batchId.tsx b/packages/admin/src/routes/_authenticated/repair-batches/$batchId.tsx index 0a475d6..0dfda66 100644 --- a/packages/admin/src/routes/_authenticated/repair-batches/$batchId.tsx +++ b/packages/admin/src/routes/_authenticated/repair-batches/$batchId.tsx @@ -1,13 +1,13 @@ import { createFileRoute, useParams, useNavigate } from '@tanstack/react-router' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' -import { repairBatchDetailOptions, repairBatchMutations, repairBatchKeys, repairBatchTicketsOptions, repairLineItemListOptions } from '@/api/repairs' +import { repairBatchDetailOptions, repairBatchMutations, repairBatchKeys, repairBatchTicketsOptions } from '@/api/repairs' import { usePagination } from '@/hooks/use-pagination' import { DataTable, type Column } from '@/components/shared/data-table' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' -import { ArrowLeft, Check, X, Plus, FileText, Download } from 'lucide-react' +import { ArrowLeft, Check, X, Plus, FileText } from 'lucide-react' import { BatchStatusProgress } from '@/components/repairs/batch-status-progress' import { toast } from 'sonner' import { useAuthStore } from '@/stores/auth.store' diff --git a/packages/admin/src/routes/_authenticated/settings.tsx b/packages/admin/src/routes/_authenticated/settings.tsx index 330cdc9..4b55974 100644 --- a/packages/admin/src/routes/_authenticated/settings.tsx +++ b/packages/admin/src/routes/_authenticated/settings.tsx @@ -12,9 +12,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' -import { AvatarUpload } from '@/components/shared/avatar-upload' import { Switch } from '@/components/ui/switch' -import { moduleListOptions, moduleMutations, moduleKeys, type ModuleConfig } from '@/api/modules' +import { moduleListOptions, moduleMutations, moduleKeys } from '@/api/modules' import { Save, Plus, Trash2, MapPin, Building, ImageIcon, Blocks, Lock } from 'lucide-react' import { toast } from 'sonner' diff --git a/packages/admin/src/routes/_authenticated/users.tsx b/packages/admin/src/routes/_authenticated/users.tsx index 6b2c698..a2d2122 100644 --- a/packages/admin/src/routes/_authenticated/users.tsx +++ b/packages/admin/src/routes/_authenticated/users.tsx @@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useState } from 'react' import { api } from '@/lib/api-client' -import { userListOptions, userRolesOptions, userKeys, userMutations, type UserRecord } from '@/api/users' +import { userListOptions, userRolesOptions, userMutations, type UserRecord } from '@/api/users' import { roleListOptions, rbacMutations } from '@/api/rbac' import { usePagination } from '@/hooks/use-pagination' import { DataTable, type Column } from '@/components/shared/data-table' diff --git a/packages/admin/src/routes/_authenticated/vault/index.tsx b/packages/admin/src/routes/_authenticated/vault/index.tsx index 1fd5c03..07a7fee 100644 --- a/packages/admin/src/routes/_authenticated/vault/index.tsx +++ b/packages/admin/src/routes/_authenticated/vault/index.tsx @@ -17,12 +17,12 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { - KeyRound, Lock, Unlock, Plus, MoreVertical, Trash2, Eye, EyeOff, Copy, + KeyRound, Lock, Plus, MoreVertical, Trash2, Eye, EyeOff, Copy, FolderKey, Shield, ShieldAlert, User2, Globe, StickyNote, } from 'lucide-react' import { toast } from 'sonner' import { CategoryPermissionsDialog } from '@/components/vault/category-permissions-dialog' -import type { VaultCategory, VaultEntry } from '@/types/vault' +import type { VaultEntry } from '@/types/vault' export const Route = createFileRoute('/_authenticated/vault/')({ validateSearch: (search: Record) => ({ @@ -36,10 +36,6 @@ export const Route = createFileRoute('/_authenticated/vault/')({ }) function VaultPage() { - const queryClient = useQueryClient() - const hasPermission = useAuthStore((s) => s.hasPermission) - const { params } = usePagination() - const { data: status, isLoading: statusLoading } = useQuery(vaultStatusOptions()) if (statusLoading) {