diff --git a/docs/FUNCIONALIDADES_E_FERRAMENTAS.md b/docs/FUNCIONALIDADES_E_FERRAMENTAS.md
index 650dfb0bf..69e721650 100644
--- a/docs/FUNCIONALIDADES_E_FERRAMENTAS.md
+++ b/docs/FUNCIONALIDADES_E_FERRAMENTAS.md
@@ -257,7 +257,6 @@
| Regras de Personalização | `src/components/products/ProductPersonalizationRules.tsx` | React |
| Opções de Customização | `src/components/products/ProductCustomizationOptions.tsx` | React |
| Customization (subpasta) | `src/components/products/customization/` | React |
-| Simulador | `src/pages/PersonalizationSimulator.tsx` | React |
| Admin Personalização | `src/components/admin/ProductPersonalizationManager.tsx` | React |
| Grupo Personalização | `src/components/admin/GroupPersonalizationManager.tsx` | React |
| Gerenciador de Técnicas | `src/components/admin/TechniquesManager.tsx` | React, DnD Kit |
@@ -273,9 +272,6 @@
### 3.4 Registro de Produtos
| Funcionalidade | Arquivo Principal | Ferramentas/Bibliotecas |
|----------------|-------------------|-------------------------|
-| Página de Registro | `src/pages/ProductRegistrationPage.tsx` | React |
-| Formulário de Registro | `src/components/product-registration/ProductRegistrationForm.tsx` | React Hook Form |
-| Import em Massa | `src/components/product-registration/BulkImportPanel.tsx` | React, xlsx |
| Hook | `src/hooks/useProductRegistration.ts` | Supabase |
### 3.5 Histórico de Preços
@@ -950,7 +946,6 @@
### 22.3 Simulador de Personalização (Legacy)
| Funcionalidade | Arquivo Principal | Ferramentas/Bibliotecas |
|----------------|-------------------|-------------------------|
-| Página | `src/pages/PersonalizationSimulator.tsx` | React |
| Componentes | `src/components/simulator/` | React |
| Decision Matrix Chart | `src/components/simulator/DecisionMatrixChart.tsx` | Recharts |
| Margin Calculator | `src/components/simulator/MarginCalculatorCard.tsx` | React |
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.
-
-
-
-
- Baixar Template CSV
-
-
-
-
-
-
- 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 || '-')}
-
- updateMapping(mapping.sourceColumn, v === "_ignore" ? "" : v)}>
-
-
- Ignorar
- {PRODUCT_FIELDS.map(f => {f.label}{f.required && ' *'} )}
-
-
-
-
- );
- })}
-
-
-
-
-
-
- );
-}
-
-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
-
-
-
-
- # Nome SKU Preço Categoria
-
- {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...
}
-
-
- Voltar
- Iniciar Importação
-
-
-
- );
-}
-
-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} )}
-
- ))}
-
- )}
- Nova Importação
-
-
- );
-}
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 (
-
-
- );
-}
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 *
-
-
- {referenceData.suppliers.map(s => (
- {s.name}{s.code && ` (${s.code})`}
- ))}
-
- )} />
- (
- Categoria *
-
-
- {referenceData.categories.map(c => (
- {c.name}
- ))}
-
- )} />
-
-
- >
- );
-}
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
- )} />
-
-
-
-
-
- (
- Peso (g) field.onChange(parseInt(e.target.value) || undefined)} />
- )} />
- (
- Largura (cm) field.onChange(parseFloat(e.target.value) || undefined)} />
- )} />
- (
- Altura (cm) field.onChange(parseFloat(e.target.value) || undefined)} />
- )} />
- (
- Profundidade (cm) field.onChange(parseFloat(e.target.value) || undefined)} />
- )} />
-
-
-
-
-
- (
- Estoque Atual field.onChange(parseInt(e.target.value) || 0)} />
- )} />
- (
- Quantidade Mínima field.onChange(parseInt(e.target.value) || 1)} />
- )} />
- (
- Prazo de Entrega (dias) field.onChange(parseInt(e.target.value) || undefined)} />
- )} />
-
-
- (
- Produto Ativo
- )} />
- (
- É um Kit
- )} />
-
-
- >
- );
-}
diff --git a/src/components/product-registration/sections/MediaAttributesSection.tsx b/src/components/product-registration/sections/MediaAttributesSection.tsx
deleted file mode 100644
index 7e0fc1e53..000000000
--- a/src/components/product-registration/sections/MediaAttributesSection.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * MediaAttributesSection — Imagens, Cores, Materiais, Técnicas
- */
-import { useState } from "react";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Card, CardContent } from "@/components/ui/card";
-import { Badge } from "@/components/ui/badge";
-import { FormSection } from "@/components/ui/FormSection";
-import { ImagePlus, X } from "lucide-react";
-import { cn } from "@/lib/utils";
-import type { UseFormReturn } from "react-hook-form";
-import type { ProductFormSchema, ReferenceDataState } from "../productFormTypes";
-
-interface MediaAttributesSectionProps {
- form: UseFormReturn;
- referenceData: ReferenceDataState;
-}
-
-export function MediaAttributesSection({ form, referenceData }: MediaAttributesSectionProps) {
- const [newImageUrl, setNewImageUrl] = useState("");
- const images = form.watch("images");
- const selectedColors = form.watch("colors");
- const selectedMaterials = form.watch("materials");
- const selectedTechniques = form.watch("technique_ids") || [];
-
- const addImage = () => {
- if (!newImageUrl.trim()) return;
- try {
- new URL(newImageUrl);
- const current = form.getValues("images");
- form.setValue("images", [...current, { url: newImageUrl, is_primary: current.length === 0 }]);
- setNewImageUrl("");
- } catch { form.setError("images", { message: "URL inválida" }); }
- };
-
- const removeImage = (index: number) => {
- const current = form.getValues("images");
- const next = current.filter((_, i) => i !== index);
- if (current[index]?.is_primary && next.length > 0) next[0].is_primary = true;
- form.setValue("images", next);
- };
-
- const setPrimary = (index: number) => {
- form.setValue("images", form.getValues("images").map((img, i) => ({ ...img, is_primary: i === index })));
- };
-
- const toggle = (field: "colors" | "materials" | "technique_ids", id: string) => {
- const current = form.getValues(field) || [];
- form.setValue(field, current.includes(id) ? current.filter((v: string) => v !== id) : [...current, id]);
- };
-
- return (
- <>
- {/* Imagens */}
-
-
-
- setNewImageUrl(e.target.value)}
- onKeyDown={e => e.key === "Enter" && (e.preventDefault(), addImage())} />
- Adicionar
-
- {images.length > 0 && (
-
- {images.map((img, index) => (
-
-
- { (e.target as HTMLImageElement).src = "/placeholder.svg"; }} />
-
- setPrimary(index)}
- className={cn("text-xs", img.is_primary && "text-primary")}>{img.is_primary ? "Principal" : "Definir como principal"}
- removeImage(index)} className="h-6 w-6 text-destructive">
-
-
-
- ))}
-
- )}
- {form.formState.errors.images &&
{form.formState.errors.images.message}
}
-
-
-
- {/* Cores */}
-
-
- {referenceData.colors.map(color => (
- toggle("colors", color.id)}>
- {color.hex_code && }
- {color.color_name}
-
- ))}
-
- {form.formState.errors.colors && {form.formState.errors.colors.message}
}
-
-
- {/* Materiais */}
-
-
- {referenceData.materials.map(m => (
- toggle("materials", m.id)}>{m.material_name}
- ))}
-
- {form.formState.errors.materials && {form.formState.errors.materials.message}
}
-
-
- {/* Técnicas */}
-
-
- {referenceData.techniques.map(t => (
- toggle("technique_ids", t.id)}>{t.name}
- ))}
-
-
- >
- );
-}
diff --git a/src/components/quotes/QuoteCommentsThread.tsx b/src/components/quotes/QuoteCommentsThread.tsx
deleted file mode 100644
index 92ba5d35b..000000000
--- a/src/components/quotes/QuoteCommentsThread.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * QuoteCommentsThread — thread de comentários internos do orçamento.
- */
-import { useState } from "react";
-import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
-import { supabase } from "@/integrations/supabase/client";
-import { Button } from "@/components/ui/button";
-import { Textarea } from "@/components/ui/textarea";
-import { Skeleton } from "@/components/ui/skeleton";
-import { MessageSquare, Send } from "lucide-react";
-import { toast } from "sonner";
-
-interface QuoteCommentsThreadProps {
- quoteId: string;
-}
-
-export function QuoteCommentsThread({ quoteId }: QuoteCommentsThreadProps) {
- const [content, setContent] = useState("");
- const qc = useQueryClient();
-
- const { data, isLoading } = useQuery({
- queryKey: ["quote-comments", quoteId],
- queryFn: async () => {
- const { data, error } = await supabase
- .from("quote_comments")
- .select("*")
- .eq("quote_id", quoteId)
- .order("created_at", { ascending: true });
- if (error) throw error;
- return data || [];
- },
- });
-
- const create = useMutation({
- mutationFn: async () => {
- const { data: u } = await supabase.auth.getUser();
- if (!u.user) throw new Error("Não autenticado");
- const { error } = await supabase.from("quote_comments").insert({
- quote_id: quoteId,
- user_id: u.user.id,
- content: content.trim(),
- });
- if (error) throw error;
- },
- onSuccess: () => {
- setContent("");
- qc.invalidateQueries({ queryKey: ["quote-comments", quoteId] });
- },
- onError: (e: Error) => toast.error(e.message),
- });
-
- return (
-
-
- Comentários
-
-
- {isLoading ? (
-
- ) : !data?.length ? (
-
Sem comentários ainda.
- ) : (
-
- )}
-
-
-
-
- );
-}
diff --git a/src/components/quotes/QuoteHistoryTimeline.tsx b/src/components/quotes/QuoteHistoryTimeline.tsx
deleted file mode 100644
index a47bef8d8..000000000
--- a/src/components/quotes/QuoteHistoryTimeline.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * QuoteHistoryTimeline — timeline de eventos do orçamento (mudanças de status, edições, comentários).
- */
-import { useQuery } from "@tanstack/react-query";
-import { supabase } from "@/integrations/supabase/client";
-import { Skeleton } from "@/components/ui/skeleton";
-import { Clock, History } from "lucide-react";
-
-interface QuoteHistoryTimelineProps {
- quoteId: string;
-}
-
-export function QuoteHistoryTimeline({ quoteId }: QuoteHistoryTimelineProps) {
- const { data, isLoading } = useQuery({
- queryKey: ["quote-history", quoteId],
- queryFn: async () => {
- const { data, error } = await supabase
- .from("quote_history")
- .select("*")
- .eq("quote_id", quoteId)
- .order("created_at", { ascending: false });
- if (error) throw error;
- return data || [];
- },
- });
-
- if (isLoading) {
- return (
-
- {[0, 1, 2].map((i) => )}
-
- );
- }
-
- if (!data?.length) {
- return (
-
- Nenhum evento registrado.
-
- );
- }
-
- return (
-
- {data.map((event) => (
-
-
-
-
{event.action}
- {event.description &&
{event.description}
}
-
-
- {new Date(event.created_at).toLocaleString("pt-BR")}
-
-
-
- ))}
-
- );
-}
diff --git a/src/components/quotes/QuoteVersionsList.tsx b/src/components/quotes/QuoteVersionsList.tsx
deleted file mode 100644
index dc749db3f..000000000
--- a/src/components/quotes/QuoteVersionsList.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * QuoteVersionsList — lista todas as versões do orçamento (revisões).
- */
-import { useQuery } from "@tanstack/react-query";
-import { supabase } from "@/integrations/supabase/client";
-import { Skeleton } from "@/components/ui/skeleton";
-import { Badge } from "@/components/ui/badge";
-import { GitBranch } from "lucide-react";
-
-interface QuoteVersionsListProps {
- quoteId: string;
- parentQuoteId?: string | null;
-}
-
-export function QuoteVersionsList({ quoteId, parentQuoteId }: QuoteVersionsListProps) {
- const rootId = parentQuoteId ?? quoteId;
-
- const { data, isLoading } = useQuery({
- queryKey: ["quote-versions", rootId],
- queryFn: async () => {
- const { data, error } = await supabase
- // rls-allow: lookup por quote.id; RLS valida ownership
- .from("quotes")
- .select("id, quote_number, version, status, created_at, is_latest_version")
- .or(`id.eq.${rootId},parent_quote_id.eq.${rootId}`)
- .order("version", { ascending: false });
- if (error) throw error;
- return data || [];
- },
- });
-
- if (isLoading) return ;
- if (!data?.length) return null;
-
- return (
-
- );
-}
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 (
-
- );
-}
diff --git a/src/pages/ProductRegistrationPage.tsx b/src/pages/ProductRegistrationPage.tsx
deleted file mode 100644
index 909794610..000000000
--- a/src/pages/ProductRegistrationPage.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * ProductRegistrationPage — wrapper de página para o formulário de cadastro de produtos.
- */
-import { ProductRegistrationForm } from "@/components/product-registration/ProductRegistrationForm";
-import { BulkImportPanel } from "@/components/product-registration/BulkImportPanel";
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import { PageSEO } from "@/components/seo/PageSEO";
-import { Package, Upload } from "lucide-react";
-
-export default function ProductRegistrationPage() {
- return (
-
-
-
-
-
-
-
- Individual
-
-
- Importação em massa
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/pages/QuoteDetailPage.tsx b/src/pages/QuoteDetailPage.tsx
deleted file mode 100644
index 3509a9681..000000000
--- a/src/pages/QuoteDetailPage.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * QuoteDetailPage — detalhe completo de um orçamento (itens, histórico, comentários, versões).
- * Inclui banner read-only quando convertido em pedido + badge de pedido vinculado.
- */
-import { useParams } from "react-router-dom";
-import { useQuery } from "@tanstack/react-query";
-import { supabase } from "@/integrations/supabase/client";
-import { Skeleton } from "@/components/ui/skeleton";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import { QuoteHistoryTimeline } from "@/components/quotes/QuoteHistoryTimeline";
-import { QuoteCommentsThread } from "@/components/quotes/QuoteCommentsThread";
-import { QuoteVersionsList } from "@/components/quotes/QuoteVersionsList";
-import { QuoteOrderBadge } from "@/components/quotes/QuoteOrderBadge";
-import { QuoteConvertToOrder } from "@/components/quotes/QuoteConvertToOrder";
-import { PageSEO } from "@/components/seo/PageSEO";
-import { Badge } from "@/components/ui/badge";
-import { Lock } from "lucide-react";
-import { TooltipProvider } from "@/components/ui/tooltip";
-
-export default function QuoteDetailPage() {
- const { id } = useParams<{ id: string }>();
-
- const { data: quote, isLoading, refetch } = useQuery({
- queryKey: ["quote", id],
- enabled: !!id,
- queryFn: async () => {
- // rls-allow: lookup por id; RLS valida ownership
- const { data, error } = await supabase.from("quotes").select("*").eq("id", id!).single();
- if (error) throw error;
- return data;
- },
- });
-
- if (isLoading) return ;
- if (!quote) return Orçamento não encontrado.
;
-
- const isConverted = quote.status === "converted";
-
- return (
-
-
-
-
-
-
-
-
Orçamento {quote.quote_number}
-
-
-
- {quote.client_name || quote.client_company || "Sem cliente"} · R$ {Number(quote.total ?? 0).toFixed(2)}
-
-
-
- {quote.status}
- refetch()} />
-
-
-
- {isConverted && (
-
-
-
-
Orçamento convertido em pedido
-
- Este orçamento está em modo somente leitura. Para alterações, edite o pedido vinculado.
-
-
-
- )}
-
-
-
- Histórico
- Comentários
- Versões
-
-
- Linha do tempo
-
-
-
-
-
- Versões
-
-
-
-
- );
-}