From 80572f5c3d80a0b287ca814f67400488634d5a4c Mon Sep 17 00:00:00 2001 From: "Claude Code (DevOps)" Date: Thu, 7 May 2026 19:51:47 -0300 Subject: [PATCH 1/4] chore(cleanup): remove pagina orfa PersonalizationSimulator Pagina sem rota associada no App.tsx. Calculadora 'didatica' duplicata do SimuladorWizard real. O proprio codigo admite no comentario: 'Estimativa didatica - para o calculo real, use o Simulador (wizard)'. Validacao previa: - Zero imports em src/ (verificado via grep) - Zero referencias em testes/specs/stories - Zero imports dinamicos com string concatenada - Sem rota /personalization-simulator no App.tsx Arquivos removidos: 1 (81 linhas) Refs auditoria: F1-5.x (paginas orfas) --- src/pages/PersonalizationSimulator.tsx | 81 -------------------------- 1 file changed, 81 deletions(-) delete mode 100644 src/pages/PersonalizationSimulator.tsx diff --git a/src/pages/PersonalizationSimulator.tsx b/src/pages/PersonalizationSimulator.tsx deleted file mode 100644 index b1698a61f..000000000 --- a/src/pages/PersonalizationSimulator.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * PersonalizationSimulator — simulador para visualizar cálculos de personalização sem criar mockup real. - * Útil para vendedores estimarem custos rapidamente. - */ -import { useState } from "react"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Button } from "@/components/ui/button"; -import { Calculator } from "lucide-react"; -import { PageSEO } from "@/components/seo/PageSEO"; - -export default function PersonalizationSimulator() { - const [width, setWidth] = useState(10); - const [height, setHeight] = useState(5); - const [colors, setColors] = useState(1); - const [quantity, setQuantity] = useState(100); - const [unitCost, setUnitCost] = useState(0.5); - const [setupCost, setSetupCost] = useState(50); - - const area = width * height; - const total = setupCost + unitCost * area * colors * quantity; - - return ( -
- -
-

- Simulador de Personalização -

-

Calcule estimativas de custo de gravação por área, cores e quantidade.

-
- - - - Parâmetros - - -
- - setWidth(+e.target.value)} /> -
-
- - setHeight(+e.target.value)} /> -
-
- - setColors(+e.target.value)} /> -
-
- - setQuantity(+e.target.value)} /> -
-
- - setUnitCost(+e.target.value)} /> -
-
- - setSetupCost(+e.target.value)} /> -
-
-
- - - - Resultado - - -

Área: {area} cm²

-

Total estimado: R$ {total.toFixed(2)}

-

- Estimativa didática — não consulta as tabelas oficiais de gravação. Para o cálculo - real (áreas, técnicas, faixas e valores cadastrados), use o Simulador (wizard). -

-
-
-
- ); -} From 2c6da29a6957eddf83f011798cc7db4f98a1ac60 Mon Sep 17 00:00:00 2001 From: "Claude Code (DevOps)" Date: Thu, 7 May 2026 19:52:05 -0300 Subject: [PATCH 2/4] chore(cleanup): remove ProductRegistrationPage e pasta product-registration/ Pagina orfa: nenhuma rota em App.tsx aponta pra ela. A rota /admin/cadastros usa um ProductsManager diferente, em @/components/admin/ProductsManager - nao usa nada de product-registration/. A pasta inteira product-registration/ era usada APENAS por ProductRegistrationPage.tsx (verificado via grep recursivo). Seguindo o principio de nao deixar codigo morto, deleto tudo junto. Validacao previa: - Zero imports do barrel @/components/product-registration em outros arquivos - Zero importacao individual de cada arquivo da pasta fora dela mesma - Zero referencias em supabase/functions, scripts/, e2e/ Arquivos removidos: 9 (750 linhas) - src/pages/ProductRegistrationPage.tsx (39 linhas) - src/components/product-registration/index.ts (2) - src/components/product-registration/ProductRegistrationForm.tsx (69) - src/components/product-registration/BulkImportPanel.tsx (165) - src/components/product-registration/BulkImportSteps.tsx (172) - src/components/product-registration/productFormTypes.ts (48) - src/components/product-registration/sections/BasicDataSection.tsx (68) - src/components/product-registration/sections/DetailsSection.tsx (70) - src/components/product-registration/sections/MediaAttributesSection.tsx (117) Refs auditoria: F1-5.x (paginas orfas) --- .../product-registration/BulkImportPanel.tsx | 165 ----------------- .../product-registration/BulkImportSteps.tsx | 172 ------------------ .../ProductRegistrationForm.tsx | 69 ------- src/components/product-registration/index.ts | 2 - .../product-registration/productFormTypes.ts | 48 ----- .../sections/BasicDataSection.tsx | 68 ------- .../sections/DetailsSection.tsx | 70 ------- .../sections/MediaAttributesSection.tsx | 117 ------------ src/pages/ProductRegistrationPage.tsx | 39 ---- 9 files changed, 750 deletions(-) delete mode 100644 src/components/product-registration/BulkImportPanel.tsx delete mode 100644 src/components/product-registration/BulkImportSteps.tsx delete mode 100644 src/components/product-registration/ProductRegistrationForm.tsx delete mode 100644 src/components/product-registration/index.ts delete mode 100644 src/components/product-registration/productFormTypes.ts delete mode 100644 src/components/product-registration/sections/BasicDataSection.tsx delete mode 100644 src/components/product-registration/sections/DetailsSection.tsx delete mode 100644 src/components/product-registration/sections/MediaAttributesSection.tsx delete mode 100644 src/pages/ProductRegistrationPage.tsx diff --git a/src/components/product-registration/BulkImportPanel.tsx b/src/components/product-registration/BulkImportPanel.tsx deleted file mode 100644 index 6d4bb4037..000000000 --- a/src/components/product-registration/BulkImportPanel.tsx +++ /dev/null @@ -1,165 +0,0 @@ -/** - * BulkImportPanel — Refactored to use useBulkImportFile hook. - * UI-only: delegates file parsing/mapping logic to the hook. - */ -import { useEffect } from 'react'; -import { Upload, Download, FileSpreadsheet, FileUp, Table as TableIcon } from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Card, CardContent } from '@/components/ui/card'; -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { useProductRegistration } from '@/hooks/useProductRegistration'; -import { useBulkImportFile } from '@/hooks/useBulkImportFile'; -import { MappingStep, PreviewStep, ImportingStep, ResultsStep } from './BulkImportSteps'; -import { cn } from '@/lib/utils'; -import { useState } from 'react'; - -export function BulkImportPanel() { - const { - generateTemplate, - importProducts, - importProgress, - loadReferenceData, - } = useProductRegistration(); - - const { - importMode, setImportMode, - step, setStep, - parsedData, - columnMappings, - isDragging, setIsDragging, - fileInputRef, - updateMapping, - isMappingComplete, - transformData, - handleFileSelect, - handleDrop, - resetImport, - } = useBulkImportFile(); - - const [importResults, setImportResults] = useState<{ - succeeded: number; - failed: number; - errors: Array<{ row: number; errors: string[] }>; - } | null>(null); - - useEffect(() => { loadReferenceData(); }, [loadReferenceData]); - - const startImport = async () => { - setStep('importing'); - const data = transformData(); - const results = await importProducts(data); - setImportResults(results); - setStep('results'); - }; - - const handleReset = () => { - resetImport(); - setImportResults(null); - }; - - return ( -
- {/* Mode selection */} - {step === 'upload' && ( - setImportMode(v as 'template' | 'custom')}> - - - - Usar Template - - - - Mapeamento Manual - - - - - - - Template Recomendado - - Baixe nosso template CSV/XLSX com todos os campos pré-configurados. - Preencha os dados e faça o upload para importação rápida. - - - - - - - - - Mapeamento Personalizado - - Faça upload de qualquer planilha e mapeie as colunas manualmente - para os campos do produto. - - - - - )} - - {/* Upload area */} - {step === 'upload' && ( - { e.preventDefault(); setIsDragging(true); }} - onDragLeave={() => setIsDragging(false)} - onDrop={handleDrop} - onClick={() => fileInputRef.current?.click()} - > - - -

Arraste seu arquivo aqui

-

ou clique para selecionar

-

Formatos suportados: CSV, XLSX, XLS

- -
-
- )} - - {/* Mapping */} - {step === 'mapping' && parsedData && ( - setStep('preview')} - /> - )} - - {/* Preview */} - {step === 'preview' && parsedData && ( - setStep(importMode === 'custom' ? 'mapping' : 'upload')} - onStart={startImport} - /> - )} - - {/* Importing */} - {step === 'importing' && importProgress && ( - - )} - - {/* Results */} - {step === 'results' && importResults && ( - - )} -
- ); -} diff --git a/src/components/product-registration/BulkImportSteps.tsx b/src/components/product-registration/BulkImportSteps.tsx deleted file mode 100644 index 60c31ca71..000000000 --- a/src/components/product-registration/BulkImportSteps.tsx +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Import step components extracted from BulkImportPanel - */ -import { Loader2, CheckCircle2, AlertCircle, Upload, X, ArrowRight } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; -import { Progress } from "@/components/ui/progress"; -import { Badge } from "@/components/ui/badge"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { PRODUCT_FIELDS, type ColumnMapping } from "@/hooks/useProductRegistration"; -import { cn } from "@/lib/utils"; -import { Table as TableIcon } from "lucide-react"; - -interface ParsedData { - headers: string[]; - rows: Record[]; - fileName: string; -} - -interface MappingStepProps { - parsedData: ParsedData; - columnMappings: ColumnMapping[]; - updateMapping: (source: string, target: string) => void; - isMappingComplete: () => boolean; - onCancel: () => void; - onContinue: () => void; -} - -export function MappingStep({ parsedData, columnMappings, updateMapping, isMappingComplete, onCancel, onContinue }: MappingStepProps) { - return ( - - - Mapeamento de Colunas - Arquivo: {parsedData.fileName} ({parsedData.rows.length} linhas) - - - - - - - Coluna do Arquivo - Exemplo - Campo no Sistema - - - - {columnMappings.map(mapping => { - const sampleValue = parsedData.rows[0]?.[mapping.sourceColumn]; - const field = PRODUCT_FIELDS.find(f => f.key === mapping.targetField); - return ( - - {mapping.sourceColumn} - {String(sampleValue || '-')} - - - - - ); - })} - -
-
-
- - -
-
-
- ); -} - -interface PreviewStepProps { - parsedData: ParsedData; - onBack: () => void; - onStart: () => void; -} - -export function PreviewStep({ parsedData, onBack, onStart }: PreviewStepProps) { - return ( - - - Pré-visualização - {parsedData.rows.length} produtos serão importados - - - - - #NomeSKUPreçoCategoria - - {parsedData.rows.slice(0, 10).map((row, index) => ( - - {index + 1} - {String(row.name || '-')} - {String(row.sku || '-')} - {String(row.price || '-')} - {String(row.category_name || row.category_id || '-')} - - ))} - -
- {parsedData.rows.length > 10 &&

e mais {parsedData.rows.length - 10} produtos...

} -
-
- - -
-
-
- ); -} - -interface ProgressStepProps { - progress: { total: number; processed: number; succeeded: number; failed: number }; -} - -export function ImportingStep({ progress }: ProgressStepProps) { - return ( - - Importando Produtos... - - -
- {progress.processed} de {progress.total} - {progress.succeeded} sucesso / {progress.failed} falhas -
-
-
- ); -} - -interface ResultsStepProps { - results: { succeeded: number; failed: number; errors: Array<{ row: number; errors: string[] }> }; - onReset: () => void; -} - -export function ResultsStep({ results, onReset }: ResultsStepProps) { - return ( - - - - {results.failed === 0 ? : } - Importação Concluída - - - -
- {results.succeeded} sucesso - {results.failed > 0 && {results.failed} falhas} -
- {results.errors.length > 0 && ( - - {results.errors.map((error, index) => ( -
-

Linha {error.row}

-
    {error.errors.map((e, i) =>
  • {e}
  • )}
-
- ))} -
- )} - -
-
- ); -} diff --git a/src/components/product-registration/ProductRegistrationForm.tsx b/src/components/product-registration/ProductRegistrationForm.tsx deleted file mode 100644 index 7daf435e4..000000000 --- a/src/components/product-registration/ProductRegistrationForm.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * ProductRegistrationForm — Orchestrator (refactored) - * Sections extracted to ./sections/ - */ -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Loader2, Save } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Form } from "@/components/ui/form"; -import { Skeleton } from "@/components/ui/skeleton"; -import { useProductRegistration, type ProductFormData } from "@/hooks/useProductRegistration"; -import { productFormSchema, type ProductFormSchema } from "./productFormTypes"; -import { BasicDataSection } from "./sections/BasicDataSection"; -import { MediaAttributesSection } from "./sections/MediaAttributesSection"; -import { DetailsSection } from "./sections/DetailsSection"; - -interface ProductRegistrationFormProps { - onSuccess?: (product: unknown) => void; - onCancel?: () => void; -} - -export function ProductRegistrationForm({ onSuccess, onCancel }: ProductRegistrationFormProps) { - const { isSubmitting, referenceData, loadReferenceData, createProduct } = useProductRegistration(); - - const form = useForm({ - resolver: zodResolver(productFormSchema), - defaultValues: { - name: "", sku: "", price: 0, supplier_id: "", category_id: "", - images: [], colors: [], materials: [], description: "", short_description: "", - is_active: true, is_kit: false, min_quantity: 1, stock: 0, technique_ids: [], tag_ids: [], - }, - }); - - useEffect(() => { loadReferenceData(); }, [loadReferenceData]); - - const onSubmit = async (data: ProductFormSchema) => { - const result = await createProduct(data as ProductFormData); - if (result) { form.reset(); onSuccess?.(result); } - }; - - if (referenceData.isLoading) { - return ( -
- - - - -
- ); - } - - return ( -
- - - - - -
- {onCancel && } - -
- - - ); -} diff --git a/src/components/product-registration/index.ts b/src/components/product-registration/index.ts deleted file mode 100644 index b629d3672..000000000 --- a/src/components/product-registration/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { ProductRegistrationForm } from './ProductRegistrationForm'; -export { BulkImportPanel } from './BulkImportPanel'; diff --git a/src/components/product-registration/productFormTypes.ts b/src/components/product-registration/productFormTypes.ts deleted file mode 100644 index 439436994..000000000 --- a/src/components/product-registration/productFormTypes.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Shared types for product registration form - */ -import { z } from "zod"; - -export const productFormSchema = z.object({ - name: z.string().min(3, "Nome deve ter pelo menos 3 caracteres"), - sku: z.string().min(2, "SKU deve ter pelo menos 2 caracteres"), - price: z.number().min(0.01, "Preço deve ser maior que zero"), - supplier_id: z.string().min(1, "Selecione um fornecedor"), - category_id: z.string().min(1, "Selecione uma categoria"), - images: z.array(z.object({ - url: z.string().url("URL inválida"), - alt_text: z.string().optional(), - is_primary: z.boolean().optional(), - image_type: z.enum(["main", "gallery", "detail", "mockup"]).optional(), - })).min(1, "Adicione pelo menos uma imagem"), - colors: z.array(z.string()).min(1, "Selecione pelo menos uma cor"), - materials: z.array(z.string()).min(1, "Selecione pelo menos um material"), - description: z.string().optional(), - short_description: z.string().optional(), - cost_price: z.number().optional(), - subcategory_id: z.string().optional(), - brand: z.string().optional(), - model: z.string().optional(), - weight_grams: z.number().optional(), - width_cm: z.number().optional(), - height_cm: z.number().optional(), - depth_cm: z.number().optional(), - min_quantity: z.number().optional(), - stock: z.number().optional(), - lead_time_days: z.number().optional(), - is_kit: z.boolean().optional(), - is_active: z.boolean().optional(), - technique_ids: z.array(z.string()).optional(), - tag_ids: z.array(z.string()).optional(), -}); - -export type ProductFormSchema = z.infer; - -export interface ReferenceDataState { - isLoading: boolean; - suppliers: Array<{ id: string; name: string; code?: string }>; - categories: Array<{ id: string; name: string }>; - colors: Array<{ id: string; color_name: string; hex_code?: string }>; - materials: Array<{ id: string; material_name: string }>; - techniques: Array<{ id: string; name: string }>; -} diff --git a/src/components/product-registration/sections/BasicDataSection.tsx b/src/components/product-registration/sections/BasicDataSection.tsx deleted file mode 100644 index 69a391a85..000000000 --- a/src/components/product-registration/sections/BasicDataSection.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * BasicDataSection — Dados básicos + Fornecedor/Categoria do formulário de registro - */ -import { Input } from "@/components/ui/input"; -import { - Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; -import { - FormControl, FormField, FormItem, FormLabel, FormMessage, -} from "@/components/ui/form"; -import { FormSection } from "@/components/ui/FormSection"; -import type { UseFormReturn } from "react-hook-form"; -import type { ProductFormSchema, ReferenceDataState } from "../productFormTypes"; - -interface BasicDataSectionProps { - form: UseFormReturn; - referenceData: ReferenceDataState; -} - -export function BasicDataSection({ form, referenceData }: BasicDataSectionProps) { - return ( - <> - -
- ( - Nome do Produto * - )} /> - ( - SKU * - )} /> - ( - Preço (R$) * - field.onChange(parseFloat(e.target.value) || 0)} /> - - )} /> - ( - Preço de Custo (R$) - field.onChange(parseFloat(e.target.value) || undefined)} /> - - )} /> -
-
- - -
- ( - Fornecedor * - - )} /> - ( - Categoria * - - )} /> -
-
- - ); -} diff --git a/src/components/product-registration/sections/DetailsSection.tsx b/src/components/product-registration/sections/DetailsSection.tsx deleted file mode 100644 index 1246a7289..000000000 --- a/src/components/product-registration/sections/DetailsSection.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/** - * DetailsSection — Descrição, Dimensões, Estoque - */ -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { Switch } from "@/components/ui/switch"; -import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; -import { FormSection } from "@/components/ui/FormSection"; -import type { UseFormReturn } from "react-hook-form"; -import type { ProductFormSchema } from "../productFormTypes"; - -interface DetailsSectionProps { - form: UseFormReturn; -} - -export function DetailsSection({ form }: DetailsSectionProps) { - return ( - <> - -
- ( - Descrição Curta - )} /> - ( - Descrição Completa