Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
576 changes: 3 additions & 573 deletions .tsc-baseline.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/components/admin/products/BulkImportDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,11 @@ export function BulkImportDialog({ open, onOpenChange, onComplete }: BulkImportD
const skuCount = allSkus.filter((s) => s === sku).length;
if (skuCount > 1) warnings.push('SKU duplicado dentro do arquivo');

// `mapped` holds CSV/Excel values typed as `unknown`; they are coerced above,
// so assert the assembled row to the typed ImportRow shape.
const importRow: ImportRow | undefined =
errors.length === 0
? {
? ({
sku,
name: String(mapped.name).trim(),
sale_price: mapped.sale_price ?? 0,
Expand Down Expand Up @@ -227,7 +229,7 @@ export function BulkImportDialog({ open, onOpenChange, onComplete }: BulkImportD
is_kit: mapped.is_kit ?? null,
gender: mapped.gender || null,
dimensions: mapped.dimensions || null,
}
} as ImportRow)
: undefined;

results.push({
Expand Down
4 changes: 3 additions & 1 deletion src/components/admin/products/ProductFormFullscreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ const STEPS: StepDef[] = [
fieldLabels: {},
},
{
// 'kits' is a live wizard step but is missing from StepDef['id'] (StepId);
// widen via unknown until StepId is extended to include it.
id: 'kits',
label: 'Kits',
description: 'Gestão de kits nativos',
icon: Boxes,
requiredFields: [],
fieldLabels: {},
} as StepDef,
} as unknown as StepDef,
Comment on lines +103 to +111
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Adicionar estado da etapa kits no stepReady para evitar desalinhamento do stepper.

Após incluir id: 'kits', STEPS fica com 9 etapas, mas o array stepReady continua com 8 posições (Line 267-277). Isso desloca o readiness de media/content e deixa a última etapa sem estado, causando regressão visual/funcional no fluxo.

💡 Ajuste sugerido
   const stepReady = useMemo(
     () => [
       Boolean(formValues.supplier_id && formValues.sku && formValues.name),
       Boolean((formValues.sale_price ?? 0) > 0),
       Boolean(formValues.packing_type),
       Boolean(formValues.ncm_code || formValues.ean),
       isEdit && !!productId,
       true,
+      true, // kits
       images.length > 0 || Boolean(formValues.video_url),
       Boolean(formValues.meta_title || formValues.meta_description || formValues.key_benefits),
     ],
     [formValues, images.length, isEdit, productId],
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/admin/products/ProductFormFullscreen.tsx` around lines 103 -
111, STEPS now includes a new step with id 'kits' but the stepReady array (the
readiness state initialized/managed around stepReady) still has only 8 entries
causing misalignment; update the stepReady initialization and any places that
derive its length to include the new 'kits' slot (i.e., make stepReady have 9
entries or dynamically derive its length from STEPS) and ensure the index/order
of entries matches STEPS so that 'media/content' and the final step map to the
correct readiness flag; adjust any code that seeds or slices stepReady to use
STEPS.length or to insert the 'kits' readiness at the correct position.

{
id: 'media',
label: 'Mídia',
Expand Down
14 changes: 9 additions & 5 deletions src/components/catalog/CatalogContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,21 @@ export const CatalogContent = memo(function CatalogContent({
? 'Tente ajustar seus filtros para ver mais resultados.'
: 'Explore nosso catálogo completo para encontrar o que procura.'
}
action={{
label: 'Limpar todos os filtros',
onClick: onResetFilters,
}}
action={
onResetFilters
? {
label: 'Limpar todos os filtros',
onClick: onResetFilters,
}
: undefined
}
/>
);
}

return (
<div className="relative space-y-8 pb-12 duration-500 animate-in fade-in">
<SparklineSalesProvider>
<SparklineSalesProvider productIds={paginatedProducts.map((p) => p.id)}>
{viewMode === 'grid' && (
<ProductGrid
products={paginatedProducts}
Expand Down
2 changes: 1 addition & 1 deletion src/components/catalog/CatalogHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function CatalogHeader({
</span>
<Button
variant="ghost"
size="xs"
size="sm"
onClick={onClearHistory}
className="h-6 gap-1 px-1.5 text-[10px] text-muted-foreground hover:text-destructive"
>
Expand Down
105 changes: 56 additions & 49 deletions src/components/categories/CategoryTreeNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ function TreeNode({
<div>
<div
className={cn(
"flex items-center gap-1 py-1.5 px-2 rounded-md cursor-pointer transition-colors",
"hover:bg-accent/50",
isSelected && "bg-primary/10 text-primary font-medium"
'flex cursor-pointer items-center gap-1 rounded-md px-2 py-1.5 transition-colors',
'hover:bg-accent/50',
isSelected && 'bg-primary/10 font-medium text-primary',
)}
style={{ paddingLeft: `${level * 16 + 8}px` }}
onClick={() => onSelect(node)}
Expand All @@ -54,12 +54,12 @@ function TreeNode({
e.stopPropagation();
onToggle(node.id);
}}
className="p-0.5 hover:bg-muted rounded"
className="rounded p-0.5 hover:bg-muted"
>
{isExpanded ? (
<ChevronDown className="w-4 h-4 text-muted-foreground" />
<ChevronDown className="h-4 w-4 text-muted-foreground" />
) : (
<ChevronRight className="w-4 h-4 text-muted-foreground" />
<ChevronRight className="h-4 w-4 text-muted-foreground" />
)}
</button>
) : (
Expand All @@ -69,13 +69,13 @@ function TreeNode({
{/* Ícone de pasta */}
{hasChildren ? (
isExpanded ? (
<FolderOpen className="w-4 h-4 text-warning" />
<FolderOpen className="h-4 w-4 text-warning" />
) : (
<Folder className="w-4 h-4 text-warning" />
<Folder className="h-4 w-4 text-warning" />
)
) : (
<div className="w-4 h-4 flex items-center justify-center">
<div className="w-1.5 h-1.5 rounded-full bg-muted-foreground/50" />
<div className="flex h-4 w-4 items-center justify-center">
<div className="h-1.5 w-1.5 rounded-full bg-muted-foreground/50" />
</div>
)}

Expand All @@ -84,7 +84,7 @@ function TreeNode({

{/* Badge com contagem de filhos */}
{hasChildren && (
<Badge variant="secondary" className="ml-auto text-xs px-1.5 py-0">
<Badge variant="secondary" className="ml-auto px-1.5 py-0 text-xs">
{node.children.length}
</Badge>
)}
Expand All @@ -93,7 +93,7 @@ function TreeNode({
{/* Filhos */}
{hasChildren && isExpanded && (
<div>
{node.children.map(child => (
{node.children.map((child) => (
<TreeNode
key={child.id}
node={child}
Expand Down Expand Up @@ -123,19 +123,17 @@ function SearchResult({
return (
<div
className={cn(
"flex items-center gap-2 py-2 px-3 rounded-md cursor-pointer transition-colors",
"hover:bg-accent/50",
isSelected && "bg-primary/10 text-primary"
'flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 transition-colors',
'hover:bg-accent/50',
isSelected && 'bg-primary/10 text-primary',
)}
onClick={onSelect}
>
<Folder className="w-4 h-4 text-warning flex-shrink-0" />
<Folder className="h-4 w-4 flex-shrink-0 text-warning" />
<div className="min-w-0 flex-1">
<p className="text-sm font-medium truncate">{category.name}</p>
<p className="truncate text-sm font-medium">{category.name}</p>
{category.tree_structure && (
<p className="text-xs text-muted-foreground truncate">
{category.tree_structure}
</p>
<p className="truncate text-xs text-muted-foreground">{category.tree_structure}</p>
)}
</div>
<Badge variant="outline" className="text-xs">
Expand All @@ -149,7 +147,7 @@ export function CategoryTreeNavigation({
onSelectCategory,
selectedCategoryId,
showSearch = true,
maxHeight = "400px",
maxHeight = '400px',
className,
}: CategoryTreeNavigationProps) {
const { tree, searchCategories, isLoading, error, stats } = useCategoriesTree();
Expand All @@ -159,7 +157,7 @@ export function CategoryTreeNavigation({

// Toggle expandir/colapsar
const handleToggle = useCallback((id: string) => {
setExpandedIds(prev => {
setExpandedIds((prev) => {
const next = new Set(prev);
if (next.has(id)) {
next.delete(id);
Expand All @@ -171,19 +169,25 @@ export function CategoryTreeNavigation({
}, []);

// Selecionar categoria
const handleSelect = useCallback((category: CategoryNode | CategoryTreeItem) => {
onSelectCategory?.(category);
}, [onSelectCategory]);
const handleSelect = useCallback(
(category: CategoryNode | CategoryTreeItem) => {
onSelectCategory?.(category);
},
[onSelectCategory],
);

// Buscar
const handleSearch = useCallback((query: string) => {
setSearchQuery(query);
if (query.trim()) {
setSearchResults(searchCategories(query));
} else {
setSearchResults([]);
}
}, [searchCategories]);
const handleSearch = useCallback(
(query: string) => {
setSearchQuery(query);
if (query.trim()) {
setSearchResults(searchCategories(query));
} else {
setSearchResults([]);
}
},
[searchCategories],
);

// Limpar busca
const clearSearch = useCallback(() => {
Expand All @@ -195,7 +199,7 @@ export function CategoryTreeNavigation({
const expandAll = useCallback(() => {
const allIds = new Set<string>();
const addIds = (nodes: CategoryNode[]) => {
nodes.forEach(node => {
nodes.forEach((node) => {
if (node.children.length > 0) {
allIds.add(node.id);
addIds(node.children);
Expand All @@ -214,7 +218,7 @@ export function CategoryTreeNavigation({
// Loading state
if (isLoading) {
return (
<div className={cn("space-y-2 p-4", className)}>
<div className={cn('space-y-2 p-4', className)}>
<Skeleton className="h-8 w-full" />
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-6 w-1/2" />
Expand All @@ -226,7 +230,7 @@ export function CategoryTreeNavigation({
// Error state
if (error) {
return (
<div className={cn("p-4 text-center text-destructive", className)}>
<div className={cn('p-4 text-center text-destructive', className)}>
<p className="text-sm">Erro ao carregar categorias</p>
<p className="text-xs text-muted-foreground">{error}</p>
</div>
Expand All @@ -236,18 +240,18 @@ export function CategoryTreeNavigation({
// Empty state
if (tree.length === 0) {
return (
<div className={cn("p-4 text-center text-muted-foreground", className)}>
<Folder className="w-12 h-12 mx-auto mb-2 opacity-50" />
<div className={cn('p-4 text-center text-muted-foreground', className)}>
<Folder className="mx-auto mb-2 h-12 w-12 opacity-50" />
<p className="text-sm">Nenhuma categoria encontrada</p>
</div>
);
}

return (
<div className={cn("flex flex-col", className)}>
<div className={cn('flex flex-col', className)}>
{/* Header com busca */}
{showSearch && (
<div className="p-3 border-b space-y-2">
<div className="space-y-2 border-b p-3">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
Expand All @@ -257,17 +261,20 @@ export function CategoryTreeNavigation({
className="pl-8 pr-8"
/>
{searchQuery && (
<button aria-label="Fechar"
<button
aria-label="Fechar"
onClick={clearSearch}
className="absolute right-2.5 top-2.5 text-muted-foreground hover:text-foreground"
aria-label="Fechar">
>
<X className="h-4 w-4" />
</button>
)}
</div>

<div className="flex items-center justify-between text-xs text-muted-foreground">
<span>{stats.total} categorias em {stats.levels} níveis</span>
<span>
{stats.total} categorias em {stats.levels} níveis
</span>
<div className="flex gap-1">
<Button variant="ghost" size="sm" className="h-6 text-xs" onClick={expandAll}>
Expandir
Expand All @@ -286,10 +293,10 @@ export function CategoryTreeNavigation({
{/* Resultados de busca */}
{searchQuery && searchResults.length > 0 ? (
<div className="space-y-1">
<p className="text-xs text-muted-foreground px-2 mb-2">
<p className="mb-2 px-2 text-xs text-muted-foreground">
{searchResults.length} resultado{searchResults.length !== 1 ? 's' : ''}
</p>
{searchResults.map(cat => (
{searchResults.map((cat) => (
<SearchResult
key={cat.id}
category={cat}
Expand All @@ -299,14 +306,14 @@ export function CategoryTreeNavigation({
))}
</div>
) : searchQuery && searchResults.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
<Search className="w-8 h-8 mx-auto mb-2 opacity-50" />
<div className="py-8 text-center text-muted-foreground">
<Search className="mx-auto mb-2 h-8 w-8 opacity-50" />
<p className="text-sm">Nenhuma categoria encontrada</p>
<p className="text-xs">Tente outra busca</p>
</div>
) : (
/* Árvore de categorias */
tree.map(node => (
tree.map((node) => (
<TreeNode
key={node.id}
node={node}
Expand Down
Loading
Loading