diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 80bb36c8e..faaef1b6f 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -4,17 +4,7 @@ language: "pt-BR" -tone_instructions: | - Seja direto e prático. Use português brasileiro claro. - Foque em bugs reais, problemas de segurança, performance e regressões. - Evite nitpicks de estilo (formatação, espaçamento, etc). - Sinalize com prioridade alta: - - Credenciais, tokens ou secrets hardcoded - - Queries SQL com risco de injection - - Promises sem await ou catch - - Tipos any/unknown sem tratamento - - URLs de API hardcoded em vez de variáveis de ambiente - - Operações destrutivas em SQL (DROP/TRUNCATE) sem backup +tone_instructions: "PT-BR direto. Foco: bugs, segurança, performance, regressões. Evite nitpicks. Prioridade alta: secrets hardcoded, SQL injection, promises sem await/catch, any/unknown, URLs hardcoded, DROP/TRUNCATE sem backup." early_access: false diff --git a/.eslint-baseline.json b/.eslint-baseline.json index 4a7be76e2..1464f9a36 100644 --- a/.eslint-baseline.json +++ b/.eslint-baseline.json @@ -1,1929 +1,5 @@ { - "generatedAt": "2026-04-27T15:31:15.386Z", - "totalErrors": 1433, - "counts": { - "src/App.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/components/a11y/AccessibilityProvider.tsx": { - "no-undef": 1 - }, - "src/components/a11y/VisuallyHidden.tsx": { - "no-undef": 1 - }, - "src/components/admin/DiscountApprovalHeaderBadge.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/DiscountApprovalQueue.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/GroupPersonalizationManager.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/ImageUploadButton.tsx": { - "no-undef": 1 - }, - "src/components/admin/InlineEditField.tsx": { - "no-undef": 1 - }, - "src/components/admin/PasswordResetApproval.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/ProductPersonalizationManager.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/SortableItem.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/AutoTestIntervalCard.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/BridgeProductsPreviewPanel.tsx": { - "eqeqeq": 11 - }, - "src/components/admin/connections/ConnectionDetailsDialog.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/ConnectionErrorDetailsDialog.tsx": { - "eqeqeq": 2 - }, - "src/components/admin/connections/ConnectionTestDetailsDialog.tsx": { - "eqeqeq": 10 - }, - "src/components/admin/connections/ConnectionTestHistoryPanel.tsx": { - "eqeqeq": 3, - "no-undef": 1 - }, - "src/components/admin/connections/ConnectionTimelineDrawer.tsx": { - "eqeqeq": 3 - }, - "src/components/admin/connections/ConnectionsPulseBar.tsx": { - "@typescript-eslint/consistent-type-imports": 1 - }, - "src/components/admin/connections/CredentialCacheMetricsPanel.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/ErrorDetailsDialog.tsx": { - "eqeqeq": 8, - "no-undef": 2 - }, - "src/components/admin/connections/EventsMultiSelect.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/connections/ExplainModeContext.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/ExternalConnectionsSyncLogPanel.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/FailureWindowCard.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/IntegrationsHealthCard.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 4 - }, - "src/components/admin/connections/KeysValidationTab.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/connections/KpiExplainTooltip.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/connections/LastTestLine.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/LatencyBadge.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/MaskedSuffixBadge.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/SecretField.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/admin/connections/SecretImpactTooltip.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/SeverityFilterContext.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/TestAllConnectionsButton.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/TestProgressIndicator.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/components/admin/connections/WebhookPlaygroundPanel.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/connections/ZoneCommandPalette.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/connections/ZoneQuickNav.tsx": { - "no-undef": 1 - }, - "src/components/admin/connections/ZoneSection.tsx": { - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/components/admin/connections/secretNormalizers.ts": { - "prefer-const": 1 - }, - "src/components/admin/group-personalization/GroupComponentCard.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/admin/group-personalization/GroupLocationCard.tsx": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/components/admin/hooks/useGroupPersonalization.ts": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/admin/personalization/ProductSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/personalization/usePersonalizationData.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/products/BulkImportDialog.tsx": { - "@typescript-eslint/no-explicit-any": 3, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/admin/products/CategoryCascadeSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/products/MaterialGroupTree.tsx": { - "eqeqeq": 1, - "no-undef": 1 - }, - "src/components/admin/products/NewSupplierDialog.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/ProductFormFullscreen.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/admin/products/ProductFormHelpers.tsx": { - "no-undef": 4 - }, - "src/components/admin/products/ProductFormStepContent.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/products/ProductMarketingSection.tsx": { - "no-undef": 1 - }, - "src/components/admin/products/ProductMaterialsSection.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "eqeqeq": 3 - }, - "src/components/admin/products/ProductVariationAxesConfig.tsx": { - "eqeqeq": 1, - "no-undef": 1 - }, - "src/components/admin/products/SupplierFiscalInfo.tsx": { - "eqeqeq": 9 - }, - "src/components/admin/products/bulk-import/StepComplete.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/bulk-import/StepMapping.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/admin/products/bulk-import/StepPreview.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/admin/products/bulk-import/StepUpload.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/admin/products/bulk-import/types.ts": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/admin/products/hooks/useSkuValidation.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/admin/products/image-gallery/ImageBulkToolbar.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/image-gallery/ImageFilterBar.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/image-gallery/ImageGrid.tsx": { - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/components/admin/products/image-gallery/ImageMetaEditor.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/image-gallery/ImagePreviewDialog.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/image-gallery/ImageStatsBar.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/image-gallery/ImageUploadArea.tsx": { - "no-duplicate-imports": 1, - "no-undef": 5 - }, - "src/components/admin/products/image-gallery/ProductImageGallery.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/admin/products/image-gallery/useProductImageGallery.ts": { - "eqeqeq": 1, - "no-duplicate-imports": 1, - "no-undef": 6 - }, - "src/components/admin/products/kit-components/ComponentMediaManager.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/admin/products/kit-components/PrintAreasManager.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/kit-components/ProductKitComponentsSection.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/new-supplier/tabs/AddressTab.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "no-undef": 14 - }, - "src/components/admin/products/new-supplier/tabs/BasicDataTab.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "no-undef": 7 - }, - "src/components/admin/products/new-supplier/useNewSupplierForm.ts": { - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/components/admin/products/sections/ProductClassificationSection.tsx": { - "no-undef": 2 - }, - "src/components/admin/products/sections/ProductDimensionsSection.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductEngravingSection.tsx": { - "eqeqeq": 4 - }, - "src/components/admin/products/sections/ProductFiscalSection.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductFlagsSection.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/products/sections/ProductInfoSection.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductMarketingTextsSection.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductPackagingSection.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductPriceSection.tsx": { - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/admin/products/sections/ProductSeoSection.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/ProductSupplierSection.tsx": { - "@typescript-eslint/no-unused-vars": 4, - "eqeqeq": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/engraving/EngravingAreaCard.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 2, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/sections/engraving/useEngravingWizard.ts": { - "eqeqeq": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/video-gallery/VideoGrid.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/components/admin/products/video-gallery/VideoMetaEditor.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/admin/products/video-gallery/VideoUploadArea.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1, - "no-undef": 5 - }, - "src/components/admin/products/video-gallery/useProductVideoGallery.ts": { - "@typescript-eslint/no-explicit-any": 2, - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1, - "no-duplicate-imports": 1, - "no-empty": 1, - "no-undef": 6 - }, - "src/components/admin/security/ActiveIpsList.tsx": { - "no-shadow-restricted-names": 1 - }, - "src/components/admin/security/RecentAuditTable.tsx": { - "no-undef": 1 - }, - "src/components/admin/security/SuspiciousTokensPanel.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/admin/security/keys/McpKeyRow.tsx": { - "no-redeclare": 1 - }, - "src/components/admin/security/keys/audit/McpAuditRow.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 2 - }, - "src/components/admin/security/keys/audit/useMcpAuditFeed.ts": { - "prefer-const": 1 - }, - "src/components/admin/security/role-migration/RoleMigrationPanel.tsx": { - "eqeqeq": 2 - }, - "src/components/admin/suppliers-manager/SupplierFormDialog.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1 - }, - "src/components/admin/suppliers-manager/SupplierTable.tsx": { - "eqeqeq": 1 - }, - "src/components/admin/suppliers-manager/useSuppliersManager.ts": { - "@typescript-eslint/no-unused-vars": 4, - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/admin/telemetry/AppHealthDashboard.tsx": { - "eqeqeq": 2, - "no-undef": 1 - }, - "src/components/admin/telemetry/BreakerStatusCard.tsx": { - "eqeqeq": 2 - }, - "src/components/admin/telemetry/ColdStartRetriesPanel.tsx": { - "eqeqeq": 2 - }, - "src/components/admin/telemetry/ColdVsWarmCrmCard.tsx": { - "eqeqeq": 2 - }, - "src/components/admin/users/EditUserDialog.tsx": { - "no-undef": 1 - }, - "src/components/admin/users/useUserManagement.ts": { - "@typescript-eslint/no-explicit-any": 4 - }, - "src/components/ai/AIMockupAssistant.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/components/audit/AuditHistory.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/auth/ForgotPasswordForm.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/auth/PasswordStrengthIndicator.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/auth/StepUpAuthDialog.tsx": { - "@typescript-eslint/consistent-type-imports": 1, - "no-undef": 2 - }, - "src/components/bi/BIAiCopilot.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/bi/BIBriefingMode.tsx": { - "eqeqeq": 4 - }, - "src/components/bi/BITourGuide.tsx": { - "no-undef": 2 - }, - "src/components/bi/ChurnRiskBanner.tsx": { - "eqeqeq": 1 - }, - "src/components/bi/ClientAffinityProducts.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/bi/ClientCategoryRadar.tsx": { - "eqeqeq": 3 - }, - "src/components/bi/ClientComparator.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/components/bi/ClientHealthHero.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/bi/ClientOverview360.tsx": { - "eqeqeq": 2 - }, - "src/components/bi/EnrichedOrdersTimeline.tsx": { - "eqeqeq": 2 - }, - "src/components/bi/ExecutiveSummaryButton.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/components/cart/CartCompanyPicker.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/cart/CartCompanyPickerDialog.tsx": { - "no-undef": 1 - }, - "src/components/cart/CartHeaderButton.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/cart/CartUtilComponents.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1 - }, - "src/components/catalog/CatalogContent.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1 - }, - "src/components/catalog/CatalogHeader.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/catalog/CatalogToolbar.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/categories/CategorySidebarPanel.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/collections/CollectionDetailHeader.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/collections/CollectionListItem.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/collections/CollectionTableView.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/components/common/BulkActionsBar.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/common/EnhancedSpotlight.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/common/GlassElements.tsx": { - "no-undef": 1 - }, - "src/components/common/ImageWithFallback.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 3 - }, - "src/components/common/IntelligenceBadges.tsx": { - "eqeqeq": 1 - }, - "src/components/common/MicroInteractions.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/common/SkipToContent.tsx": { - "no-redeclare": 1 - }, - "src/components/common/SocialProof.tsx": { - "eqeqeq": 2 - }, - "src/components/common/StatusTimeline.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/compare/AIComparisonAdvisor.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/compare/CompareEmptyStateSmart.tsx": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/components/compare/CompareTableView.tsx": { - "@typescript-eslint/no-explicit-any": 8, - "no-undef": 1 - }, - "src/components/compare/ComparisonDuelView.tsx": { - "@typescript-eslint/no-explicit-any": 4, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/compare/ComparisonHighlights.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/compare/ComparisonMobileView.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/compare/ComparisonPresentationLauncher.tsx": { - "@typescript-eslint/no-explicit-any": 3, - "no-undef": 1 - }, - "src/components/compare/ComparisonRadarChart.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/compare/ComparisonScoreCard.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/ComparisonShortcutsCheatsheet.tsx": { - "no-undef": 1 - }, - "src/components/compare/ExportComparisonButton.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/compare/FloatingCompareBar.tsx": { - "@typescript-eslint/no-explicit-any": 4, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/compare/HistoricalPriceOverlay.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "eqeqeq": 1 - }, - "src/components/compare/ImageZoomCell.tsx": { - "no-undef": 1 - }, - "src/components/compare/OtherSuppliersRow.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/PriceSparkline.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/RecentComparisonsSidebar.tsx": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/components/compare/ShareComparisonDialog.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/SimilarProductsRail.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/SortableColumnWrapper.tsx": { - "@typescript-eslint/consistent-type-imports": 2 - }, - "src/components/compare/StockRiskBadge.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/compare/SupplierComparisonModal.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/compare/SyncedZoomGallery.tsx": { - "no-undef": 3 - }, - "src/components/dashboard/MyPendingOrdersWidget.tsx": { - "eqeqeq": 1 - }, - "src/components/dashboard/MyRecentQuotesWidget.tsx": { - "eqeqeq": 1 - }, - "src/components/dashboard/QuickActionsPanel.tsx": { - "@typescript-eslint/no-unused-vars": 7 - }, - "src/components/dashboard/RecentKitsWidget.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/dev/BridgeMetricsOverlay.tsx": { - "react-hooks/rules-of-hooks": 9 - }, - "src/components/engraving/PricingPanel.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/expert/ExpertChatDialog.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/expert/FlowFilterPanel.tsx": { - "@typescript-eslint/no-unused-expressions": 1, - "no-undef": 2 - }, - "src/components/expert/FlowFilterPrimitives.tsx": { - "no-undef": 3 - }, - "src/components/expert/chat/ChatHistoryPanel.tsx": { - "no-undef": 1 - }, - "src/components/expert/chat/ChatInputBar.tsx": { - "no-undef": 2 - }, - "src/components/expert/chat/useExpertChat.ts": { - "@typescript-eslint/no-unused-expressions": 2, - "no-empty": 4, - "no-undef": 2 - }, - "src/components/favorites/QuickListPicker.tsx": { - "no-undef": 1 - }, - "src/components/filters/ColorGroupFilter.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/filters/CommemorativeDateFilter.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/filters/DebouncedPriceInput.tsx": { - "no-undef": 1 - }, - "src/components/filters/ExternalCategoryFilter.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/filters/FilterPanel.tsx": { - "@typescript-eslint/no-unused-vars": 6, - "no-duplicate-imports": 1 - }, - "src/components/filters/InlineColorGroupFilter.tsx": { - "@typescript-eslint/no-unused-expressions": 1 - }, - "src/components/filters/StickyFilterBar.tsx": { - "no-undef": 1 - }, - "src/components/filters/filter-panel/sections/MaterialsFilter.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/filters/filter-panel/sections/RamosFilter.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/components/filters/filter-panel/sections/SizeFilter.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/filters/filter-panel/types.ts": { - "@typescript-eslint/consistent-type-imports": 2 - }, - "src/components/goals/SalesGoalsCard.tsx": { - "no-undef": 1 - }, - "src/components/intelligence/CategoryRanking.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/intelligence/IntelligenceFilterBar.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/intelligence/MarketIntelligenceChart.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/components/intelligence/MarketIntelligenceInsightsCard.tsx": { - "no-undef": 1 - }, - "src/components/intelligence/RankingFilterToolbar.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/intelligence/RealtimeBadge.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/intelligence/SalesOverviewChart.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/intelligence/SegmentAnalysis.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/intelligence/TrendsInsightsCard.tsx": { - "no-undef": 1 - }, - "src/components/inventory/StockAlertDialogs.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/inventory/StockAlertsIndicator.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/inventory/StockCategoryTreeSelect.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/inventory/StockDashboard.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/inventory/StockFilterToolbar.tsx": { - "@typescript-eslint/no-unused-vars": 4, - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/inventory/VariantStockTable.tsx": { - "no-undef": 1 - }, - "src/components/inventory/risk/ProductRiskDetail.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 5, - "no-duplicate-imports": 1 - }, - "src/components/inventory/risk/RiskTooltip.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "eqeqeq": 5 - }, - "src/components/inventory/risk/types.ts": { - "eqeqeq": 2 - }, - "src/components/kit-builder/BoxSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/DiscontinuedItemsAlert.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/kit-builder/ItemCard.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/KitBuilderHeader.tsx": { - "no-undef": 1 - }, - "src/components/kit-builder/KitComparisonDialog.tsx": { - "@typescript-eslint/no-explicit-any": 5 - }, - "src/components/kit-builder/KitConflictAlerts.tsx": { - "no-undef": 1 - }, - "src/components/kit-builder/KitIdentityPicker.tsx": { - "no-undef": 2 - }, - "src/components/kit-builder/KitIsometricPreview.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/kit-builder/KitMobileSummaryBar.tsx": { - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/kit-builder/KitPresentablePreview.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/KitSmartSuggestions.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/kit-builder/KitSummary.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/KitVariantsManager.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/PersonalizationConfig.tsx": { - "eqeqeq": 1, - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/SelectedItemsBadges.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/kit-builder/SimilarKitsWidget.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/kit-builder/VariantSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/kit-builder/kit-summary/KitCompositionCard.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/kit-builder/kit-summary/KitPricingCard.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/kit-library/KitCard.tsx": { - "no-undef": 1 - }, - "src/components/kit-library/KitTemplatePreviewDialog.tsx": { - "no-undef": 1 - }, - "src/components/kit-library/RelatedTemplates.tsx": { - "no-undef": 1 - }, - "src/components/layout/Header.tsx": { - "@typescript-eslint/no-unused-vars": 6 - }, - "src/components/layout/MainLayout.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 1 - }, - "src/components/layout/SidebarReorganized.tsx": { - "@typescript-eslint/no-unused-vars": 5 - }, - "src/components/layout/sidebar/SidebarNavGroup.tsx": { - "eqeqeq": 2 - }, - "src/components/magic-up/AdImageResult.tsx": { - "@typescript-eslint/no-unused-vars": 4, - "no-duplicate-imports": 1, - "no-redeclare": 1 - }, - "src/components/magic-up/MagicUpCurationStatus.tsx": { - "no-redeclare": 1 - }, - "src/components/magic-up/MagicUpVariationComparator.tsx": { - "no-undef": 1 - }, - "src/components/magic-up/PromptBank.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/magic-up/PromptGenerator.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/materials/MaterialBadge.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/mobile/MobileProductActions.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/mobile/SmartMobileNav.tsx": { - "@typescript-eslint/no-unused-vars": 8, - "no-duplicate-imports": 1 - }, - "src/components/mockup/AreaCard.tsx": { - "no-undef": 1 - }, - "src/components/mockup/LogoColorAnalyzer.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "eqeqeq": 1 - }, - "src/components/mockup/LogoPositionEditor.tsx": { - "no-undef": 1 - }, - "src/components/mockup/MockupAnnotations.tsx": { - "no-undef": 1 - }, - "src/components/mockup/MockupBeforeAfter.tsx": { - "no-undef": 2 - }, - "src/components/mockup/MockupConfigPanel.tsx": { - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/components/mockup/MockupHistoryPanel.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/mockup/MockupProductSelector.tsx": { - "@typescript-eslint/no-unused-vars": 5 - }, - "src/components/mockup/MockupResultCard.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/mockup/MockupWizard.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/mockup/MultiAreaManager.tsx": { - "eqeqeq": 2 - }, - "src/components/mockup/ShareMenu.tsx": { - "no-undef": 1 - }, - "src/components/mockup/TechniqueColorConfigDialog.tsx": { - "no-duplicate-imports": 1, - "no-undef": 3 - }, - "src/components/mockup/TechniqueTooltip.tsx": { - "eqeqeq": 4, - "no-undef": 1 - }, - "src/components/mockup/TemplateSelector.tsx": { - "no-undef": 1 - }, - "src/components/mockup/logo-editor/LogoPreviewCanvas.tsx": { - "no-undef": 3 - }, - "src/components/mockup/logo-editor/useLogoDrag.ts": { - "no-undef": 2 - }, - "src/components/navigation/DynamicBreadcrumbs.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/notifications/NotificationDrawer.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/novelties/NoveltiesSection.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/novelties/NoveltyCards.tsx": { - "eqeqeq": 3 - }, - "src/components/novelties/NoveltyStatsCards.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/onboarding/OnboardingTour.tsx": { - "@typescript-eslint/no-unused-vars": 5 - }, - "src/components/orders/OrderCard.tsx": { - "eqeqeq": 1 - }, - "src/components/pdf/ProposalSections.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/pdf/PropostaComercialTailwind.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/pdf/proposal/LogoWithTransparentBg.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/pdf/proposal/ProposalFooter.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/pdf/proposal/ProposalNotes.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/pdf/proposal/ProposalTotals.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/personalization/TechniqueSLACard.tsx": { - "no-undef": 1 - }, - "src/components/personalization/TechniqueSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/pricing/ProductPriceSimulator.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/pricing/QuantityPriceCalculator.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "@typescript-eslint/no-unused-vars": 3, - "no-duplicate-imports": 2 - }, - "src/components/pricing/calculator/QuantityComparisonTable.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/pricing/calculator/TechniqueConfigCard.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/pricing/calculator/TechniqueMultiSelector.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/pricing/calculator/types.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/pricing/simulator/CustomizationOptions.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/pricing/simulator/EngravingList.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/pricing/simulator/MultiEngravingResult.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/pricing/simulator/PriceResultV51.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1 - }, - "src/components/pricing/simulator/QuantityAndResult.tsx": { - "@typescript-eslint/no-unused-vars": 5 - }, - "src/components/product-registration/BulkImportPanel.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/product-registration/BulkImportSteps.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/products/BulkActionBar.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/products/ColumnSelector.tsx": { - "no-empty": 2, - "no-undef": 1 - }, - "src/components/products/EnhancedProductCard.tsx": { - "no-undef": 5 - }, - "src/components/products/InlinePriceCalculator.tsx": { - "eqeqeq": 2 - }, - "src/components/products/KitVisualComposition.tsx": { - "eqeqeq": 1 - }, - "src/components/products/PackagingModal.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/products/PriceFreshnessBadge.tsx": { - "no-undef": 1 - }, - "src/components/products/ProductCard.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 3 - }, - "src/components/products/ProductCardActions.tsx": { - "no-undef": 4 - }, - "src/components/products/ProductCardImage.tsx": { - "eqeqeq": 1 - }, - "src/components/products/ProductCategoryBadges.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/products/ProductCustomizationOptions.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/ProductDimensions.tsx": { - "no-undef": 1 - }, - "src/components/products/ProductGallery.tsx": { - "@typescript-eslint/no-unused-expressions": 1, - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 4 - }, - "src/components/products/ProductGrid.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/products/ProductHoverPreview.tsx": { - "no-undef": 1 - }, - "src/components/products/ProductInfoBar.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/ProductIntelligence.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 1 - }, - "src/components/products/ProductList.tsx": { - "no-undef": 1 - }, - "src/components/products/ProductListItem.tsx": { - "@typescript-eslint/no-unused-vars": 11, - "no-undef": 3 - }, - "src/components/products/ProductPersonalizationRules.tsx": { - "@typescript-eslint/no-explicit-any": 10, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/ProductQuickView.tsx": { - "@typescript-eslint/no-unused-vars": 16 - }, - "src/components/products/ProductSectionNav.tsx": { - "no-undef": 1 - }, - "src/components/products/ProductSparkline.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 2 - }, - "src/components/products/ProductStickyHeader.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/ProductTableView.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 1 - }, - "src/components/products/QuickAddToQuote.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/RecentlyViewedBar.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/SalesHistoryChart.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/SimilarProducts.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/SingleVariantPicker.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/products/StatsPopover.tsx": { - "no-undef": 1 - }, - "src/components/products/StockHistoryChart.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "eqeqeq": 10 - }, - "src/components/products/SupplierComparisonCards.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "eqeqeq": 1 - }, - "src/components/products/VariantGridMatrix.tsx": { - "eqeqeq": 1 - }, - "src/components/products/VariantPickerDialog.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/VirtualizedProductGrid.tsx": { - "no-undef": 1 - }, - "src/components/products/customization/ConfigurationPanel.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/customization/ConfigurationPanelV6.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/customization/LocationCard.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/components/products/gallery/GalleryFullscreen.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 4 - }, - "src/components/products/kit-composition/KitComponentCard.tsx": { - "eqeqeq": 6, - "no-undef": 3 - }, - "src/components/products/list-item/ListItemActions.tsx": { - "no-undef": 4 - }, - "src/components/products/share/ShareAllColorsDialog.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/share/ShareContactSelector.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "@typescript-eslint/no-unused-vars": 2, - "no-empty": 2 - }, - "src/components/products/share/usePhotoDownload.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/products/useStockChartData.ts": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 3 - }, - "src/components/products/zoomable-gallery/useGalleryZoom.ts": { - "no-undef": 1 - }, - "src/components/quote/MarginInsightBadge.tsx": { - "eqeqeq": 2 - }, - "src/components/quote/NegotiationMarkupCard.tsx": { - "eqeqeq": 3 - }, - "src/components/quote/QuickQuoteFAB.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-duplicate-imports": 1 - }, - "src/components/quotes/AIRecommendationsPanel.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/AdminTemplatesManager.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/ClientPicker.tsx": { - "no-undef": 1 - }, - "src/components/quotes/DraggableQuoteItems.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 4 - }, - "src/components/quotes/PdfGenerationDialog.tsx": { - "@typescript-eslint/no-unused-vars": 11, - "no-undef": 2 - }, - "src/components/quotes/QuoteAutoSave.tsx": { - "@typescript-eslint/no-explicit-any": 4, - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 1 - }, - "src/components/quotes/QuoteBuilderProductSearch.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/QuoteBuilderSummaryColumn.tsx": { - "@typescript-eslint/no-unused-vars": 5, - "eqeqeq": 1 - }, - "src/components/quotes/QuoteConvertToOrder.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/quotes/QuoteHistoryPanel.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/components/quotes/QuoteItemDetailSheet.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/components/quotes/QuoteKanbanBoard.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/quotes/QuoteProductColorSelector.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/QuoteSignaturePad.tsx": { - "no-undef": 6 - }, - "src/components/quotes/QuoteStatusTimeline.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "eqeqeq": 1 - }, - "src/components/quotes/QuoteTemplateForm.tsx": { - "no-undef": 1 - }, - "src/components/quotes/QuoteTemplateSelector.tsx": { - "no-undef": 1 - }, - "src/components/quotes/QuoteTemplatesList.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/QuoteValidityBanner.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/quotes/QuoteVersionCompare.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/quotes/QuoteVersionHistory.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/quotes/SaveAsTemplateButton.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/ramo-atividade/RamoAtividadeBadge.tsx": { - "@typescript-eslint/no-unused-vars": 5 - }, - "src/components/ramo-atividade/RamoAtividadeGroupAccordion.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/ramo-atividade/SegmentoCheckbox.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/replenishments/ReplenishmentCards.tsx": { - "@typescript-eslint/no-unused-expressions": 1, - "eqeqeq": 2, - "no-undef": 1 - }, - "src/components/replenishments/ReplenishmentProductGrid.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/replenishments/ReplenishmentStatsCards.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/reports/ScheduledReportsManager.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/search/AdvancedSearch.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 8 - }, - "src/components/search/GlobalSearch.tsx": { - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/components/search/GlobalSearchHelpers.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/search/GlobalSearchIdleState.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/search/GlobalSearchPalette.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/search/SearchResultGroups.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/search/SearchWithSuggestions.tsx": { - "no-undef": 2 - }, - "src/components/search/SmartSearchInput.tsx": { - "no-undef": 1 - }, - "src/components/search/SmartSuggestions.tsx": { - "@typescript-eslint/no-unused-vars": 4 - }, - "src/components/search/VisualSearchButton.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "no-undef": 3 - }, - "src/components/search/VoiceSearchOverlay.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/search/search-types.ts": { - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/search/useGlobalSearch.ts": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 1 - }, - "src/components/search/voice/VoiceOverlaySections.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/search/voice/VoiceTranscriptPanel.tsx": { - "no-duplicate-imports": 1, - "no-undef": 1 - }, - "src/components/security/PushNotificationSettings.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/simulator/MockupPreview.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/NicheRecommendationBadge.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/ScenarioComparison.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/TechniqueCard.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/wizard/ComparisonCard.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/wizard/ConfirmedSummary.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/wizard/MobilePersonalizationSummary.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/wizard/PersonalizationSummary.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/wizard/PersonalizationTabs.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/wizard/RemovePersonalizationDialog.tsx": { - "no-undef": 1 - }, - "src/components/simulator/wizard/StepComparison.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/components/simulator/wizard/StepLocation.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/components/simulator/wizard/StepProduct.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/wizard/StepSpecs.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 4 - }, - "src/components/simulator/wizard/WizardContextBar.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/simulator/wizard/WizardStepIndicator.tsx": { - "no-undef": 1 - }, - "src/components/ui/DataCard.tsx": { - "no-undef": 2 - }, - "src/components/ui/LoadingButton.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/components/ui/LoadingState.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/components/ui/command.tsx": { - "@typescript-eslint/no-empty-object-type": 1 - }, - "src/components/ui/kpi-card.tsx": { - "no-duplicate-imports": 1 - }, - "src/components/ui/sidebar.tsx": { - "no-redeclare": 1 - }, - "src/components/ui/sonner.tsx": { - "no-undef": 1 - }, - "src/components/ui/textarea.tsx": { - "@typescript-eslint/no-empty-object-type": 1 - }, - "src/contexts/AuthContext.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/contexts/ProductsContext.tsx": { - "no-duplicate-imports": 1 - }, - "src/data/mock-match-products.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/bi/useChurnRisk.ts": { - "eqeqeq": 1 - }, - "src/hooks/bi/useClientCategoryAffinity.ts": { - "eqeqeq": 2 - }, - "src/hooks/bi/useClientHealthScore.ts": { - "eqeqeq": 1 - }, - "src/hooks/bi/useSeasonalPeakNotifications.ts": { - "eqeqeq": 2 - }, - "src/hooks/gravacao/gravacao-constants.ts": { - "eqeqeq": 1 - }, - "src/hooks/mockup/mockupGenerationService.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/simulator/useSimulatorWizard.ts": { - "no-duplicate-imports": 1 - }, - "src/hooks/simulator/useUndoRedo.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/simulator/useWizardPersistence.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/simulator/useWizardPricing.ts": { - "@typescript-eslint/no-unused-vars": 2, - "no-undef": 1 - }, - "src/hooks/tecnicas/usePrecoCalculation.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/use-toast.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useAuditLog.ts": { - "@typescript-eslint/no-explicit-any": 10 - }, - "src/hooks/useBulkImportFile.ts": { - "no-undef": 2 - }, - "src/hooks/useCatalogPrefetch.ts": { - "eqeqeq": 1 - }, - "src/hooks/useCatalogRealStats.ts": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/hooks/useCatalogState.ts": { - "@typescript-eslint/no-unused-vars": 7, - "no-duplicate-imports": 1, - "no-empty": 2 - }, - "src/hooks/useCollections.ts": { - "@typescript-eslint/no-explicit-any": 6 - }, - "src/hooks/useColorEnrichment.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useCommemorativeDates.ts": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/hooks/useComparisonScore.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/hooks/useComparisonSync.ts": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/hooks/useComparisonWeights.ts": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/hooks/useContextualSuggestions.ts": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/hooks/useCrmCompanies.ts": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/hooks/useCustomKitPersistence.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useCustomizationPrice.ts": { - "eqeqeq": 2 - }, - "src/hooks/useDebounce.ts": { - "no-undef": 1 - }, - "src/hooks/useFavoriteLists.ts": { - "prefer-const": 1 - }, - "src/hooks/useFavoriteQuickAdd.ts": { - "@typescript-eslint/no-unused-vars": 1, - "no-empty": 1 - }, - "src/hooks/useFavoriteReactions.ts": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/hooks/useKitAutoSave.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useKitBuilderQueries.ts": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/hooks/useMagicUpGeneration.ts": { - "no-duplicate-imports": 1 - }, - "src/hooks/useMagicUpState.ts": { - "@typescript-eslint/no-unused-vars": 1, - "no-undef": 1 - }, - "src/hooks/useMaterialTypes.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useMockupDraft.ts": { - "@typescript-eslint/no-unused-vars": 4, - "no-undef": 1 - }, - "src/hooks/useMockupGenerator.ts": { - "@typescript-eslint/no-explicit-any": 4, - "@typescript-eslint/no-unused-vars": 4 - }, - "src/hooks/useMockupTechniques.ts": { - "@typescript-eslint/no-unused-vars": 2, - "eqeqeq": 2 - }, - "src/hooks/useProductAnalytics.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useProductImages.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useProductIntelligenceBadges.ts": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 2 - }, - "src/hooks/useProductRecommendations.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/hooks/useProductRegistrationImport.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/hooks/useProductSupplierSources.ts": { - "@typescript-eslint/no-unused-vars": 2, - "no-empty": 1 - }, - "src/hooks/useProductsLightweight.ts": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/hooks/usePushNotifications.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useQuoteBuilderState.ts": { - "@typescript-eslint/no-unused-expressions": 1, - "@typescript-eslint/no-unused-vars": 2, - "eqeqeq": 2, - "no-undef": 2 - }, - "src/hooks/useQuoteFunnel.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useQuoteHistory.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/hooks/useQuoteVersions.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/hooks/useQuotes.ts": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/hooks/useRamoAtividade.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useScheduledReports.ts": { - "@typescript-eslint/no-explicit-any": 2, - "no-case-declarations": 1 - }, - "src/hooks/useScroll.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useSimulation.ts": { - "@typescript-eslint/no-unused-vars": 4, - "no-duplicate-imports": 1 - }, - "src/hooks/useSimulatorPreferences.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useStepUpAuth.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useStockHistory.ts": { - "eqeqeq": 2 - }, - "src/hooks/useSupplierNames.ts": { - "no-duplicate-imports": 1 - }, - "src/hooks/useSupplierTrust.ts": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/hooks/useTechniqueRecommendations.ts": { - "no-useless-escape": 1 - }, - "src/hooks/useTecnicasUnificadas.ts": { - "@typescript-eslint/consistent-type-imports": 3 - }, - "src/hooks/useVariantStock.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/hooks/useVoiceAgent.ts": { - "@typescript-eslint/no-unused-vars": 1, - "no-empty": 1 - }, - "src/hooks/useWebAuthn.ts": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/lib/bi/dossierPdfGenerator.ts": { - "eqeqeq": 1 - }, - "src/lib/bi/pptxGenerator.ts": { - "eqeqeq": 2 - }, - "src/lib/error-kind-inference.ts": { - "eqeqeq": 1 - }, - "src/lib/error-reporter.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/lib/external-db/products-detail.ts": { - "no-duplicate-imports": 1 - }, - "src/lib/external-db/products-lightweight.ts": { - "no-duplicate-imports": 1 - }, - "src/lib/external-db/products.ts": { - "no-duplicate-imports": 1, - "no-redeclare": 2 - }, - "src/lib/kit-builder/price-calculator.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/lib/kit-builder/types.ts": { - "eqeqeq": 1 - }, - "src/lib/lazyWithRetry.ts": { - "no-undef": 1 - }, - "src/lib/pdf/whitelabel-comparison.ts": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/lib/personalization/adapters/customization-options.adapter.ts": { - "eqeqeq": 2 - }, - "src/lib/personalization/adapters/price-response.adapter.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/lib/personalization/adapters/raw-row.adapter.ts": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 1 - }, - "src/lib/personalization/transformers.ts": { - "eqeqeq": 4 - }, - "src/lib/personalization/types.ts": { - "@typescript-eslint/consistent-type-imports": 1 - }, - "src/lib/quote-status-config.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/lib/roles.ts": { - "no-duplicate-imports": 1 - }, - "src/lib/sensitive-masking.ts": { - "eqeqeq": 1 - }, - "src/lib/stock-chart-utils.ts": { - "eqeqeq": 7 - }, - "src/lib/supabase-untyped.ts": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/lib/telemetry/bridgeCallMetrics.ts": { - "eqeqeq": 1 - }, - "src/lib/theme-presets.ts": { - "no-empty": 2 - }, - "src/pages/AdvancedPriceSearchPage.tsx": { - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1, - "no-undef": 2 - }, - "src/pages/Auth.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/pages/BusinessIntelligencePage.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/CollectionDetailPage.tsx": { - "@typescript-eslint/no-unused-expressions": 1, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/pages/CollectionsPage.tsx": { - "no-undef": 2 - }, - "src/pages/ComparePage.tsx": { - "@typescript-eslint/no-explicit-any": 3, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/CustomizableDashboard.tsx": { - "@typescript-eslint/no-unused-vars": 6, - "no-undef": 2 - }, - "src/pages/DropboxBrowserPage.tsx": { - "@typescript-eslint/no-unused-vars": 2 - }, - "src/pages/FavoritesPage.tsx": { - "no-empty": 8 - }, - "src/pages/KitBuilderPage.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 1, - "no-duplicate-imports": 1 - }, - "src/pages/KitLibraryPage.tsx": { - "@typescript-eslint/no-unused-vars": 3, - "no-undef": 2 - }, - "src/pages/MockupGenerator.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "no-duplicate-imports": 1 - }, - "src/pages/MockupHistoryPage.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/OrderDetailPage.tsx": { - "@typescript-eslint/no-explicit-any": 3, - "@typescript-eslint/no-unused-vars": 4, - "eqeqeq": 1 - }, - "src/pages/PermissionsPage.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/pages/PersonalizationSimulator.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/PriceSimulatorPage.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/ProductDetail.tsx": { - "@typescript-eslint/no-explicit-any": 7, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/pages/PublicFavoriteListPage.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/PublicKitViewPage.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/PublicQuoteApprovalPage.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/QuoteBuilderPage.tsx": { - "@typescript-eslint/no-unused-vars": 3 - }, - "src/pages/QuoteViewPage.tsx": { - "no-duplicate-imports": 1 - }, - "src/pages/QuotesDashboardPage.tsx": { - "@typescript-eslint/no-unused-vars": 4 - }, - "src/pages/QuotesKanbanPage.tsx": { - "no-duplicate-imports": 1 - }, - "src/pages/RateLimitDashboardPage.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/ResetPassword.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/RolePermissionsPage.tsx": { - "@typescript-eslint/no-explicit-any": 2, - "no-undef": 1 - }, - "src/pages/RolesPage.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/pages/SSOCallbackPage.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/SellerCartsPage.tsx": { - "@typescript-eslint/consistent-type-imports": 1, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/SimuladorWizard.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/SystemStatusPage.tsx": { - "no-undef": 1 - }, - "src/pages/TrendsPage.tsx": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 2 - }, - "src/pages/admin/AdminProductFormPage.tsx": { - "@typescript-eslint/no-explicit-any": 9, - "@typescript-eslint/no-unused-vars": 3 - }, - "src/pages/admin/AdminSegurancaAcessoPage.tsx": { - "no-undef": 1, - "no-useless-escape": 1 - }, - "src/pages/admin/AdminTelemetriaPage.tsx": { - "no-duplicate-imports": 1 - }, - "src/pages/admin/AdminTemasPage.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/admin/KitTemplatesAdminPage.tsx": { - "no-undef": 2 - }, - "src/pages/admin/SellerDiscountLimitsAdminPage.tsx": { - "no-undef": 1 - }, - "src/pages/admin/ai-usage/AiSummaryCard.tsx": { - "no-undef": 1 - }, - "src/pages/admin/ai-usage/MarketIntelInsightsUsagePanel.tsx": { - "no-undef": 1 - }, - "src/pages/admin/telemetry/exportHelpers.ts": { - "eqeqeq": 2 - }, - "src/pages/advanced-price-search/ResultViews.tsx": { - "no-duplicate-imports": 1 - }, - "src/pages/advanced-price-search/useAdvancedPriceSearch.ts": { - "no-duplicate-imports": 1 - }, - "src/pages/magic-up/MagicUpResultPanel.tsx": { - "no-undef": 2 - }, - "src/pages/mockup-generator/MockupTechniqueHandlers.tsx": { - "@typescript-eslint/no-explicit-any": 6 - }, - "src/pages/product-detail/ProductDetailHero.tsx": { - "@typescript-eslint/no-explicit-any": 6 - }, - "src/pages/product-match/MatchCards.tsx": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/product-match/MatchFiltersPanel.tsx": { - "no-undef": 2 - }, - "src/pages/public-approval/PublicQuoteItems.tsx": { - "@typescript-eslint/no-explicit-any": 5, - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/public-approval/PublicQuoteStatusScreens.tsx": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/public-approval/usePublicQuoteApproval.ts": { - "@typescript-eslint/no-explicit-any": 8 - }, - "src/pages/quote-view/QuoteActionHandlers.ts": { - "@typescript-eslint/no-explicit-any": 1, - "no-undef": 2 - }, - "src/pages/quote-view/QuoteBitrixSync.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/quote-view/useQuoteViewData.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/quotes-dashboard/useQuotesDashboard.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/pages/seller-carts/useSellerCartsPage.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/pages/trends/TrendsCharts.tsx": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/services/materialService.ts": { - "@typescript-eslint/no-explicit-any": 3 - }, - "src/types/browser.d.ts": { - "no-undef": 2 - }, - "src/types/jspdf-autotable.d.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, - "src/types/product-catalog.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/utils/color-image-resolver.ts": { - "@typescript-eslint/no-explicit-any": 4 - }, - "src/utils/excelExport.ts": { - "@typescript-eslint/no-explicit-any": 9, - "@typescript-eslint/no-unused-vars": 3 - }, - "src/utils/kitPdfGenerator.ts": { - "@typescript-eslint/no-explicit-any": 1, - "@typescript-eslint/no-unused-vars": 3 - }, - "src/utils/personalizationExport.ts": { - "@typescript-eslint/no-explicit-any": 1 - }, - "src/utils/product-colors.ts": { - "@typescript-eslint/no-explicit-any": 2 - }, - "src/utils/product-mapper.ts": { - "@typescript-eslint/no-explicit-any": 4, - "no-duplicate-imports": 1 - }, - "src/utils/product-search.ts": { - "no-duplicate-imports": 1 - }, - "src/utils/productPdfExport.ts": { - "@typescript-eslint/no-unused-vars": 1, - "eqeqeq": 9 - }, - "src/utils/proposalPdfReactGenerator.ts": { - "@typescript-eslint/no-unused-vars": 1 - } - } + "generatedAt": "2026-05-01T03:27:03.647Z", + "totalErrors": 0, + "counts": {} } diff --git a/.husky/pre-commit b/.husky/pre-commit index 2312dc587..9e47b8865 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ npx lint-staged +node scripts/check-eslint-baseline.mjs diff --git a/.husky/pre-push b/.husky/pre-push index aa5f63972..646a6a07f 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1 @@ -npm run typecheck && npm run lint:check && npm run test -- --run --reporter=dot +npm run typecheck && npm run lint:baseline && npm run test -- --run --reporter=dot diff --git a/e2e/flows/14-favorites-remove-persistence.spec.ts b/e2e/flows/14-favorites-remove-persistence.spec.ts index 366dc7c3f..4e35dffbf 100644 --- a/e2e/flows/14-favorites-remove-persistence.spec.ts +++ b/e2e/flows/14-favorites-remove-persistence.spec.ts @@ -194,7 +194,7 @@ async function resolveRemoveButton(card: Locator): Promise { .then(() => true) .catch(() => false); if (visible) { - // eslint-disable-next-line no-console + console.log(`[resolveRemoveButton] usando fallback: ${name}`); return locator; } diff --git a/e2e/flows/19-favorites-api-intercept.spec.ts b/e2e/flows/19-favorites-api-intercept.spec.ts index e37cba2fb..224f18b6f 100644 --- a/e2e/flows/19-favorites-api-intercept.spec.ts +++ b/e2e/flows/19-favorites-api-intercept.spec.ts @@ -362,7 +362,7 @@ test.describe("Fluxo: interceptação API favorite_items + validação pós-relo } // C4. DIAGNÓSTICO — log estruturado p/ debug futuro (não falha) - // eslint-disable-next-line no-console + console.log( `[favorites-api-spy] flow=${isRemoteFlow ? "remote" : "legacy"} ` + `total=${allCalls.length} add=${addCalls.length} nav=${navCalls.length} ` + diff --git a/e2e/flows/22-header-sticky.spec.ts b/e2e/flows/22-header-sticky.spec.ts index ee3b46be5..f9e42f4af 100644 --- a/e2e/flows/22-header-sticky.spec.ts +++ b/e2e/flows/22-header-sticky.spec.ts @@ -210,7 +210,7 @@ test.describe("Header sticky — pós-login e navegação entre módulos", () => // Pula rotas que exigem role que o user E2E não tem. if (/\/login(\?|$)/.test(page.url())) { - // eslint-disable-next-line no-console + console.warn(`[header-sticky] pulando ${route} — redirect para /login`); continue; } diff --git a/eslint.config.js b/eslint.config.js index 7e3b8ef7d..373332ef4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -5,6 +5,7 @@ import reactHooks from 'eslint-plugin-react-hooks'; import typescript from '@typescript-eslint/eslint-plugin'; import typescriptParser from '@typescript-eslint/parser'; import jsxA11y from 'eslint-plugin-jsx-a11y'; +import unusedImports from 'eslint-plugin-unused-imports'; // Parser options compartilhados — apontam para o tsconfig.eslint.json que // inclui src/, e2e/, tests/ e scripts/. Isso evita o erro @@ -51,6 +52,7 @@ export default [ 'react-hooks': reactHooks, '@typescript-eslint': typescript, 'jsx-a11y': jsxA11y, + 'unused-imports': unusedImports, }, rules: { ...js.configs.recommended.rules, @@ -60,8 +62,11 @@ export default [ // TypeScript strict rules '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-explicit-any': 'warn', + // Imports não utilizados são auto-removíveis via unused-imports/no-unused-imports + // (suporta --fix). A regra core fica para vars locais. '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + 'unused-imports/no-unused-imports': 'error', '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }], '@typescript-eslint/no-non-null-assertion': 'warn', '@typescript-eslint/naming-convention': [ @@ -71,18 +76,25 @@ export default [ { selector: 'enum', format: ['PascalCase'] }, { selector: 'enumMember', format: ['UPPER_CASE', 'PascalCase'] }, { selector: 'variable', modifiers: ['const', 'exported'], format: ['camelCase', 'PascalCase', 'UPPER_CASE'] }, - { selector: 'function', format: ['camelCase', 'PascalCase'] }, + { selector: 'function', format: ['camelCase', 'PascalCase'], leadingUnderscore: 'allow' }, { selector: 'parameter', format: ['camelCase'], leadingUnderscore: 'allow' }, - { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'typeLike', format: ['PascalCase'], leadingUnderscore: 'allow' }, ], // General strict rules 'no-console': ['warn', { allow: ['warn', 'error'] }], 'no-debugger': 'error', + 'no-empty': ['error', { allowEmptyCatch: true }], 'no-duplicate-imports': 'error', 'no-else-return': 'warn', 'prefer-const': 'error', - 'eqeqeq': ['error', 'always'], + // null:'ignore' permite o idiomático `value == null` (cobre null+undefined) + // sem perder o gate em comparações entre valores tipados. + 'eqeqeq': ['error', 'always', { null: 'ignore' }], + // TypeScript já valida símbolos não-definidos com mais precisão que + // o ESLint (que não conhece tipos como React.FormEvent). Desabilitar + // no-undef em TS evita falsos positivos sem perder cobertura. + 'no-undef': 'off', // React 'react-hooks/rules-of-hooks': 'error', @@ -114,6 +126,8 @@ export default [ '@typescript-eslint/no-non-null-assertion': 'off', 'no-console': 'off', 'no-empty-pattern': 'off', // Playwright fixtures: ({}, testInfo) => ... + // TypeScript checa símbolos com tipo, sem falsos positivos como ESLint. + 'no-undef': 'off', }, }, @@ -180,6 +194,8 @@ export default [ 'no-console': 'off', // Tests podem usar mocks/stubs com nomes não convencionais '@typescript-eslint/naming-convention': 'off', + // TS checa símbolos com tipos. + 'no-undef': 'off', }, settings: { react: { version: 'detect' }, diff --git a/package-lock.json b/package-lock.json index d1aba1218..824e16fe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "name": "@adm01-debug/gifts-store", "version": "2.0.0", "dependencies": { - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@e965/xlsx": "0.20.3", "@elevenlabs/react": "^1.0.2", @@ -50,6 +50,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/jest-axe": "^3.5.9", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", "@vitest/coverage-v8": "^3.2.4", @@ -69,6 +71,7 @@ "html2canvas": "^1.4.1", "husky": "^9.1.7", "input-otp": "^1.2.4", + "jest-axe": "^10.0.0", "jsdom": "^20.0.3", "jspdf": "4.2.1", "jspdf-autotable": "5.0.7", @@ -107,6 +110,7 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react-swc": "^3.11.0", "autoprefixer": "^10.5.0", + "eslint-plugin-unused-imports": "^4.4.1", "postcss": "^8.5.10", "tailwindcss": "^3.4.19", "tailwindcss-animate": "^1.0.7", @@ -257,16 +261,16 @@ } }, "node_modules/@dnd-kit/sortable": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-8.0.0.tgz", - "integrity": "sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", "license": "MIT", "dependencies": { "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { - "@dnd-kit/core": "^6.1.0", + "@dnd-kit/core": "^6.3.0", "react": ">=16.8.0" } }, @@ -1042,6 +1046,79 @@ "node": ">=8" } }, + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2955,6 +3032,12 @@ "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "license": "MIT" + }, "node_modules/@supabase/auth-js": { "version": "2.98.0", "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.98.0.tgz", @@ -3394,6 +3477,19 @@ } } }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3528,6 +3624,85 @@ "@types/unist": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jest-axe": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/@types/jest-axe/-/jest-axe-3.5.9.tgz", + "integrity": "sha512-z98CzR0yVDalCEuhGXXO4/zN4HHuSebAukXDjTLJyjEAgoUf1H1i+sr7SUB/mz8CRS/03/XChsx0dcLjHkndoQ==", + "license": "MIT", + "dependencies": { + "@types/jest": "*", + "axe-core": "^3.5.5" + } + }, + "node_modules/@types/jest-axe/node_modules/axe-core": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.6.tgz", + "integrity": "sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3603,6 +3778,12 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -3625,6 +3806,21 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.58.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", @@ -4824,6 +5020,21 @@ "node": ">= 6" } }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -5751,6 +5962,15 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -6356,6 +6576,22 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", + "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -6557,6 +6793,23 @@ "node": ">=0.8.x" } }, + "node_modules/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -7076,6 +7329,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -8056,6 +8315,281 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-axe": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jest-axe/-/jest-axe-10.0.0.tgz", + "integrity": "sha512-9QR0M7//o5UVRnEUUm68IsGapHrcKGakYy9dKWWMX79LmeUKguDI6DREyljC5I13j78OUmtKLF5My6ccffLFBg==", + "license": "MIT", + "dependencies": { + "axe-core": "4.10.2", + "chalk": "4.1.2", + "jest-matcher-utils": "29.2.2", + "lodash.merge": "4.6.2" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/jest-axe/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/jest-axe/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-axe/node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-axe/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/jest-matcher-utils": { + "version": "29.2.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz", + "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.2.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.2.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -11916,6 +12450,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/slice-ansi": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", @@ -11998,6 +12541,27 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", diff --git a/package.json b/package.json index c7163004f..80deeb643 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,7 @@ }, "lint-staged": { "src/**/*.{ts,tsx}": [ - "prettier --write", - "eslint --fix --max-warnings=0" + "prettier --write" ], "src/**/*.css": [ "prettier --write" @@ -165,6 +164,7 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react-swc": "^3.11.0", "autoprefixer": "^10.5.0", + "eslint-plugin-unused-imports": "^4.4.1", "postcss": "^8.5.10", "tailwindcss": "^3.4.19", "tailwindcss-animate": "^1.0.7", diff --git a/scripts/check-edge-authorization.mjs b/scripts/check-edge-authorization.mjs index 8100618a0..edee927aa 100644 --- a/scripts/check-edge-authorization.mjs +++ b/scripts/check-edge-authorization.mjs @@ -47,6 +47,7 @@ const entries = new Map(); // 2) Lista funções no FS const fns = readdirSync(FN_DIR).filter((d) => { if (d.startsWith("_")) return false; + if (d === "node_modules") return false; try { return statSync(join(FN_DIR, d)).isDirectory(); } catch { diff --git a/scripts/check-edge-structured-logging.mjs b/scripts/check-edge-structured-logging.mjs index 45641245e..d9c826ffa 100644 --- a/scripts/check-edge-structured-logging.mjs +++ b/scripts/check-edge-structured-logging.mjs @@ -56,6 +56,7 @@ const SNAPSHOT_SIZE = 84; function listEdgeFunctions() { return readdirSync(FN_ROOT) .filter((name) => !name.startsWith("_")) + .filter((name) => name !== "node_modules") .filter((name) => { const p = join(FN_ROOT, name); try { return statSync(p).isDirectory(); } catch { return false; } diff --git a/scripts/merge-duplicate-imports.mjs b/scripts/merge-duplicate-imports.mjs new file mode 100644 index 000000000..853a9568c --- /dev/null +++ b/scripts/merge-duplicate-imports.mjs @@ -0,0 +1,83 @@ +#!/usr/bin/env node +/** + * Merges duplicate imports from the same module into a single statement. + * Handles: import { A } + import type { B } → import { A, type B } + */ + +import { readFileSync, writeFileSync } from 'fs'; +import { glob } from 'glob'; + +const files = await glob('src/**/*.{ts,tsx}', { cwd: process.cwd() }); +let fixCount = 0; + +for (const file of files) { + const content = readFileSync(file, 'utf8'); + const lines = content.split('\n'); + + // Collect all import lines with their indices + const importGroups = new Map(); // moduleKey → [{ index, line, isTypeOnly, specifiers }] + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // Match: import { ... } from 'module' or import type { ... } from 'module' + const m = line.match(/^(import\s+)(type\s+)?\{([^}]*)\}\s+from\s+(['"][^'"]+['"])\s*;?\s*$/); + if (!m) continue; + + const isTypeOnly = !!m[2]; + const specifiers = m[3].trim(); + const moduleKey = m[4]; // includes quotes + + if (!importGroups.has(moduleKey)) { + importGroups.set(moduleKey, []); + } + importGroups.get(moduleKey).push({ index: i, line, isTypeOnly, specifiers }); + } + + let modified = false; + const toRemove = new Set(); + const toReplace = new Map(); // index → newLine + + for (const [moduleKey, group] of importGroups) { + if (group.length < 2) continue; + + // We have duplicates — merge them all into the first occurrence + // Separate value imports from type-only imports + const valueImports = group.filter(g => !g.isTypeOnly); + const typeImports = group.filter(g => g.isTypeOnly); + + // Build merged specifiers list + const allValueSpecs = valueImports.flatMap(g => + g.specifiers.split(',').map(s => s.trim()).filter(Boolean) + ); + const allTypeSpecs = typeImports.flatMap(g => + g.specifiers.split(',').map(s => s.trim()).filter(Boolean) + ).map(s => `type ${s}`); + + const allSpecs = [...allValueSpecs, ...allTypeSpecs]; + const merged = `import { ${allSpecs.join(', ')} } from ${moduleKey};`; + + // Replace the first import, remove the rest + const first = group[0]; + const rest = group.slice(1); + + toReplace.set(first.index, merged); + rest.forEach(g => toRemove.add(g.index)); + modified = true; + } + + if (!modified) continue; + + const newLines = lines + .map((line, i) => { + if (toRemove.has(i)) return null; + if (toReplace.has(i)) return toReplace.get(i); + return line; + }) + .filter(l => l !== null); + + writeFileSync(file, newLines.join('\n'), 'utf8'); + fixCount++; + console.log(`Fixed: ${file}`); +} + +console.log(`\nMerged imports in ${fixCount} files.`); diff --git a/scripts/typecheck-edge-functions.mjs b/scripts/typecheck-edge-functions.mjs index d1d40bc17..ec8fe6bd1 100644 --- a/scripts/typecheck-edge-functions.mjs +++ b/scripts/typecheck-edge-functions.mjs @@ -67,13 +67,16 @@ function checkFunction(fn) { // `deno cache` and doesn't execute code. We pass all files at once so // shared types within the function are resolved together. // - // If the function has a local deno.json (with import map for npm:/jsr: - // bare specifiers), pass it via --config so imports like - // `import { Hono } from "hono"` resolve. Without this, bare specifiers - // fail with: Relative import path "X" not prefixed with / or ./ or ../ + // Resolução de --config (do mais específico para o mais geral): + // 1. supabase/functions//deno.json (override por função) + // 2. supabase/functions/deno.json (config compartilhada — npm: imports) + // Sem nenhum dos dois, npm:* falha em Deno 2.x ("Could not find X in + // node_modules") porque o resolver exige nodeModulesDir + import map. const localConfig = join(fnDir, "deno.json"); + const sharedConfig = join(FUNCTIONS_DIR, "deno.json"); const args = ["check"]; if (existsSync(localConfig)) args.push("--config", localConfig); + else if (existsSync(sharedConfig)) args.push("--config", sharedConfig); args.push(...files); const result = spawnSync("deno", args, { diff --git a/src/components/admin/ImageUploadButton.tsx b/src/components/admin/ImageUploadButton.tsx index b4f06c9d7..40e150b80 100644 --- a/src/components/admin/ImageUploadButton.tsx +++ b/src/components/admin/ImageUploadButton.tsx @@ -1,14 +1,10 @@ -import React, { useState, useRef } from "react"; -import { supabase } from "@/integrations/supabase/client"; -import { toast } from "sonner"; -import { Button } from "@/components/ui/button"; -import { Loader2, Upload, X, Image as ImageIcon } from "lucide-react"; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { validateFile } from "@/lib/security/file-validation"; +import React, { useState, useRef } from 'react'; +import { supabase } from '@/integrations/supabase/client'; +import { toast } from 'sonner'; +import { Button } from '@/components/ui/button'; +import { Loader2, Upload, X, Image as ImageIcon } from 'lucide-react'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; +import { validateFile } from '@/lib/security/file-validation'; interface ImageUploadButtonProps { currentImageUrl: string | null; @@ -22,7 +18,7 @@ export function ImageUploadButton({ currentImageUrl, onUpload, onRemove, - folder = "locations", + folder = 'locations', className, }: ImageUploadButtonProps) { const [isUploading, setIsUploading] = useState(false); @@ -35,12 +31,12 @@ export function ImageUploadButton({ // 🛡️ Camada de Segurança V2.0: Validação de integridade e tipo real const validation = await validateFile(file, { maxSizeMb: 5, - allowedExtensions: [".jpg", ".jpeg", ".png", ".webp"], - allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"], + allowedExtensions: ['.jpg', '.jpeg', '.png', '.webp'], + allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'], }); if (!validation.valid) { - toast.error(validation.error || "Arquivo inválido"); + toast.error(validation.error || 'Arquivo inválido'); return; } @@ -48,12 +44,12 @@ export function ImageUploadButton({ try { // Generate unique filename - const fileExt = file.name.split(".").pop(); - const fileName = `${folder}/${Date.now()}-${Math.random().toString(36).substring(7)}.${fileExt}`; + const fileExt = file.name.split('.').pop(); + const _fileName = `${folder}/${Date.now()}-${Math.random().toString(36).substring(7)}.${fileExt}`; const formData = new FormData(); - formData.append("file", file); - formData.append("folder", folder); + formData.append('file', file); + formData.append('folder', folder); let retryCount = 0; const maxRetries = 3; @@ -62,7 +58,7 @@ export function ImageUploadButton({ while (retryCount < maxRetries && !uploadSuccess) { try { - const { data, error } = await supabase.functions.invoke("secure-upload", { + const { data, error } = await supabase.functions.invoke('secure-upload', { body: formData, }); @@ -75,21 +71,24 @@ export function ImageUploadButton({ } onUpload(data.url); - toast.success("Imagem enviada com segurança!"); + toast.success('Imagem enviada com segurança!'); uploadSuccess = true; } catch (error: any) { lastError = error; - + // Se for bloqueio de segurança (403), interrompe as tentativas - if (error.status === 403 || (error.context?.context?.status === 403)) { + if (error.status === 403 || error.context?.context?.status === 403) { break; } retryCount++; if (retryCount < maxRetries) { const delay = Math.pow(2, retryCount) * 1000; // Exponential backoff: 2s, 4s - console.warn(`Tentativa ${retryCount} falhou. Tentando novamente em ${delay}ms...`, error); - await new Promise(resolve => setTimeout(resolve, delay)); + console.warn( + `Tentativa ${retryCount} falhou. Tentando novamente em ${delay}ms...`, + error, + ); + await new Promise((resolve) => setTimeout(resolve, delay)); } } } @@ -98,12 +97,12 @@ export function ImageUploadButton({ throw lastError; } } catch (error) { - console.error("Upload error:", error); - toast.error("Erro ao enviar imagem"); + console.error('Upload error:', error); + toast.error('Erro ao enviar imagem'); } finally { setIsUploading(false); if (fileInputRef.current) { - fileInputRef.current.value = ""; + fileInputRef.current.value = ''; } } }; @@ -113,16 +112,16 @@ export function ImageUploadButton({ try { // Extract path from URL - const urlParts = currentImageUrl.split("/personalization-images/"); + const urlParts = currentImageUrl.split('/personalization-images/'); if (urlParts.length > 1) { const filePath = urlParts[1]; - await supabase.storage.from("personalization-images").remove([filePath]); + await supabase.storage.from('personalization-images').remove([filePath]); } onRemove(); - toast.success("Imagem removida!"); + toast.success('Imagem removida!'); } catch (error) { - console.error("Remove error:", error); - toast.error("Erro ao remover imagem"); + console.error('Remove error:', error); + toast.error('Erro ao remover imagem'); } }; @@ -140,12 +139,7 @@ export function ImageUploadButton({
- @@ -153,7 +147,9 @@ export function ImageUploadButton({ Área de gravação + className="max-h-48 max-w-64 rounded" + loading="lazy" + />
- - Aprove ou rejeite solicitações de recuperação de senha - + Aprove ou rejeite solicitações de recuperação de senha {pendingRequests.length === 0 ? ( -
- +
+

Nenhuma solicitação pendente

) : ( @@ -88,7 +105,7 @@ export function PasswordResetApproval() { {pendingRequests.map((request) => (
@@ -97,7 +114,10 @@ export function PasswordResetApproval() { {getStatusBadge(request.status)}

- Solicitado em {format(new Date(request.requested_at), "dd/MM/yyyy 'às' HH:mm", { locale: ptBR })} + Solicitado em{' '} + {format(new Date(request.requested_at), "dd/MM/yyyy 'às' HH:mm", { + locale: ptBR, + })}

@@ -110,7 +130,7 @@ export function PasswordResetApproval() { setAction('reject'); }} > - + Rejeitar
@@ -137,16 +157,14 @@ export function PasswordResetApproval() { Histórico - - Solicitações já processadas - + Solicitações já processadas
{processedRequests.slice(0, 10).map((request) => (
@@ -154,10 +172,16 @@ export function PasswordResetApproval() { {getStatusBadge(request.status)}

- Processado em {request.reviewed_at && format(new Date(request.reviewed_at), "dd/MM/yyyy 'às' HH:mm", { locale: ptBR })} + Processado em{' '} + {request.reviewed_at && + format(new Date(request.reviewed_at), "dd/MM/yyyy 'às' HH:mm", { + locale: ptBR, + })}

{request.reviewer_notes && ( -

"{request.reviewer_notes}"

+

+ "{request.reviewer_notes}" +

)}
@@ -168,20 +192,26 @@ export function PasswordResetApproval() { )} {/* Confirmation Dialog */} - { setSelectedRequest(null); setAction(null); setNotes(''); }}> + { + setSelectedRequest(null); + setAction(null); + setNotes(''); + }} + > {action === 'approve' ? 'Aprovar Solicitação' : 'Rejeitar Solicitação'} - {action === 'approve' + {action === 'approve' ? `Ao aprovar, um email de recuperação será enviado para ${selectedRequest?.email}` - : `A solicitação de ${selectedRequest?.email} será rejeitada` - } + : `A solicitação de ${selectedRequest?.email} será rejeitada`} - +
@@ -195,7 +225,14 @@ export function PasswordResetApproval() {
- + + + - Novo ComponenteAdicione um componente personalizável + + Novo Componente + Adicione um componente personalizável +
-
setNewComponent({ ...newComponent, code: e.target.value })} />
-
setNewComponent({ ...newComponent, name: e.target.value })} />
+
+ + setNewComponent({ ...newComponent, code: e.target.value })} + /> +
+
+ + setNewComponent({ ...newComponent, name: e.target.value })} + /> +
- - + +
@@ -147,114 +268,484 @@ export function ProductPersonalizationManager() { {componentsLoading ? ( -
+
+ +
) : !components?.length ? ( -

Nenhum componente configurado

+
+ +

Nenhum componente configurado

+
) : ( - - c.id)} strategy={verticalListSortingStrategy}> + + c.id)} + strategy={verticalListSortingStrategy} + > - {components.map(component => ( + {components.map((component) => ( - + -
- {component.component_code} +
+ + {component.component_code} + {component.component_name} -
- {component.is_personalizable && Personalizável} - {!component.is_active && Inativo} +
+ {component.is_personalizable && ( + + Personalizável + + )} + {!component.is_active && ( + + Inativo + + )}
- -
-
updateComponentMutation.mutate({ id: component.id, component_code: v.toUpperCase() })} className="font-mono" />
-
updateComponentMutation.mutate({ id: component.id, component_name: v })} />
-
updateComponentMutation.mutate({ id: component.id, is_personalizable: c })} />
-
updateComponentMutation.mutate({ id: component.id, is_active: c })} />
+ +
+
+ + + updateComponentMutation.mutate({ + id: component.id, + component_code: v.toUpperCase(), + }) + } + className="font-mono" + /> +
+
+ + + updateComponentMutation.mutate({ + id: component.id, + component_name: v, + }) + } + /> +
+
+ + updateComponentMutation.mutate({ + id: component.id, + is_personalizable: c, + }) + } + /> + +
+
+ + updateComponentMutation.mutate({ + id: component.id, + is_active: c, + }) + } + /> + +
{/* Locations */}
-

Localizações

- { setIsAddLocationOpen(open); if (open) setSelectedComponentId(component.id); }}> - +

+ + Localizações +

+ { + setIsAddLocationOpen(open); + if (open) setSelectedComponentId(component.id); + }} + > + + + - Nova LocalizaçãoDefina uma área para {component.component_name} + + Nova Localização + + Defina uma área para {component.component_name} + +
-
setNewLocation({ ...newLocation, code: e.target.value })} />
-
setNewLocation({ ...newLocation, name: e.target.value })} />
+
+ + + setNewLocation({ + ...newLocation, + code: e.target.value, + }) + } + /> +
+
+ + + setNewLocation({ + ...newLocation, + name: e.target.value, + }) + } + /> +
-
setNewLocation({ ...newLocation, maxWidth: e.target.value })} />
-
setNewLocation({ ...newLocation, maxHeight: e.target.value })} />
-
setNewLocation({ ...newLocation, maxArea: e.target.value })} />
+
+ + + setNewLocation({ + ...newLocation, + maxWidth: e.target.value, + }) + } + /> +
+
+ + + setNewLocation({ + ...newLocation, + maxHeight: e.target.value, + }) + } + /> +
+
+ + + setNewLocation({ + ...newLocation, + maxArea: e.target.value, + }) + } + /> +
- - + +
{getLocationsForComponent(component.id).length === 0 ? ( -

Nenhuma localização cadastrada

+

+ Nenhuma localização cadastrada +

) : (
- {getLocationsForComponent(component.id).map(location => ( -
-
-
updateLocationMutation.mutate({ id: location.id, location_code: v.toUpperCase() })} className="font-mono text-xs" />
-
updateLocationMutation.mutate({ id: location.id, location_name: v })} />
-
updateLocationMutation.mutate({ id: location.id, max_width_cm: v ? parseFloat(v) : null })} type="number" placeholder="—" />
-
updateLocationMutation.mutate({ id: location.id, max_height_cm: v ? parseFloat(v) : null })} type="number" placeholder="—" />
-
updateLocationMutation.mutate({ id: location.id, max_area_cm2: v ? parseFloat(v) : null })} type="number" placeholder="—" />
+ {getLocationsForComponent(component.id).map((location) => ( +
+
+
+ + + updateLocationMutation.mutate({ + id: location.id, + location_code: v.toUpperCase(), + }) + } + className="font-mono text-xs" + /> +
+
+ + + updateLocationMutation.mutate({ + id: location.id, + location_name: v, + }) + } + /> +
+
+ + + updateLocationMutation.mutate({ + id: location.id, + max_width_cm: v ? parseFloat(v) : null, + }) + } + type="number" + placeholder="—" + /> +
+
+ + + updateLocationMutation.mutate({ + id: location.id, + max_height_cm: v ? parseFloat(v) : null, + }) + } + type="number" + placeholder="—" + /> +
+
+ + + updateLocationMutation.mutate({ + id: location.id, + max_area_cm2: v ? parseFloat(v) : null, + }) + } + type="number" + placeholder="—" + /> +
-
updateLocationMutation.mutate({ id: location.id, is_active: c })} />
- updateLocationMutation.mutate({ id: location.id, area_image_url: url })} onRemove={() => updateLocationMutation.mutate({ id: location.id, area_image_url: null })} folder={`products/${selectedProduct}`} /> - +
+ + updateLocationMutation.mutate({ + id: location.id, + is_active: c, + }) + } + /> + +
+ + updateLocationMutation.mutate({ + id: location.id, + area_image_url: url, + }) + } + onRemove={() => + updateLocationMutation.mutate({ + id: location.id, + area_image_url: null, + }) + } + folder={`products/${selectedProduct}`} + /> +
{/* Techniques */} -
-
- Técnicas permitidas: - { setIsAddTechniqueOpen(open); if (open) setSelectedLocationId(location.id); }}> - +
+
+ + Técnicas permitidas: + + { + setIsAddTechniqueOpen(open); + if (open) setSelectedLocationId(location.id); + }} + > + + + - Associar TécnicaAdicione uma técnica para {location.location_name} + + Associar Técnica + + Adicione uma técnica para {location.location_name} + +
-
-
setNewMaxColors(e.target.value)} />

Deixe em branco para sem limite

+
+ + +
+
+ + + setNewMaxColors(e.target.value) + } + /> +

+ Deixe em branco para sem limite +

+
- - + +
- {getTechniquesForLocation(location.id).map(lt => ( + {getTechniquesForLocation(location.id).map((lt) => ( - updateTechniqueMutation.mutate({ id: lt.id, is_default: !lt.is_default })}> + + updateTechniqueMutation.mutate({ + id: lt.id, + is_default: !lt.is_default, + }) + } + > {lt.is_default && } {lt.technique?.name} - {lt.max_colors && ({lt.max_colors} cores)} - + {lt.max_colors && ( + + ({lt.max_colors} cores) + + )} + -

Clique para {lt.is_default ? "remover" : "definir"} como padrão

+ +

+ Clique para{' '} + {lt.is_default ? 'remover' : 'definir'} como + padrão +

+
))} - {getTechniquesForLocation(location.id).length === 0 && Nenhuma técnica associada} + {getTechniquesForLocation(location.id).length === 0 && ( + + Nenhuma técnica associada + + )}
@@ -263,8 +754,15 @@ export function ProductPersonalizationManager() { )}
-
- +
+
diff --git a/src/components/admin/connections/ConnectionsPulseBar.tsx b/src/components/admin/connections/ConnectionsPulseBar.tsx index 43a2df52d..ce7418386 100644 --- a/src/components/admin/connections/ConnectionsPulseBar.tsx +++ b/src/components/admin/connections/ConnectionsPulseBar.tsx @@ -9,7 +9,7 @@ * * Tom de voz: híbrido com tradução (termo técnico + explicação curta). */ -import { Activity, AlertTriangle, AlertOctagon, CheckCircle2, RefreshCw, Webhook, Clock, XCircle } from "lucide-react"; +import { type Activity, AlertTriangle, AlertOctagon, CheckCircle2, RefreshCw, Webhook, Clock, XCircle } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; import { ptBR } from "date-fns/locale"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; diff --git a/src/components/admin/connections/EventsMultiSelect.tsx b/src/components/admin/connections/EventsMultiSelect.tsx index 8b7d6df4f..0f4e79662 100644 --- a/src/components/admin/connections/EventsMultiSelect.tsx +++ b/src/components/admin/connections/EventsMultiSelect.tsx @@ -3,7 +3,6 @@ * Multi-select agrupado por categoria com busca, "todos do grupo" e suporte a eventos legacy. */ import { useMemo, useState } from "react"; -import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Check, Search, X } from "lucide-react"; diff --git a/src/components/admin/connections/InboundEventsPanel.tsx b/src/components/admin/connections/InboundEventsPanel.tsx index d4670ca1a..70e0deb9b 100644 --- a/src/components/admin/connections/InboundEventsPanel.tsx +++ b/src/components/admin/connections/InboundEventsPanel.tsx @@ -85,7 +85,7 @@ export function InboundEventsPanel() { setLoading(false); }; - useEffect(() => { load(); /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [period, endpointFilter, onlyInvalid, onlyUnprocessed]); + useEffect(() => { load(); }, [period, endpointFilter, onlyInvalid, onlyUnprocessed]); const epMap = useMemo(() => new Map(endpoints.map((e) => [e.id, e])), [endpoints]); diff --git a/src/components/admin/connections/IntegrationsHealthCard.tsx b/src/components/admin/connections/IntegrationsHealthCard.tsx index 433d84be6..0bd2ff781 100644 --- a/src/components/admin/connections/IntegrationsHealthCard.tsx +++ b/src/components/admin/connections/IntegrationsHealthCard.tsx @@ -30,7 +30,6 @@ import { toast } from "sonner"; import { useCredentialsSourceFilter, resolveSource, - type CredentialSource, } from "./CredentialsSourceFilterContext"; import type { SecretStatus } from "@/hooks/useSecretsManager"; diff --git a/src/components/admin/connections/KeysValidationTab.tsx b/src/components/admin/connections/KeysValidationTab.tsx index 618daba06..f4eec2877 100644 --- a/src/components/admin/connections/KeysValidationTab.tsx +++ b/src/components/admin/connections/KeysValidationTab.tsx @@ -13,7 +13,6 @@ import { Briefcase, Workflow, Plug, - Webhook, } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; diff --git a/src/components/admin/connections/KpiExplainTooltip.tsx b/src/components/admin/connections/KpiExplainTooltip.tsx index 6b08d38db..fde905e86 100644 --- a/src/components/admin/connections/KpiExplainTooltip.tsx +++ b/src/components/admin/connections/KpiExplainTooltip.tsx @@ -8,9 +8,8 @@ * - Fonte de dados (tabela/coluna) * - Threshold/alvo (opcional) */ -import { Calculator, Clock, Database, Target } from "lucide-react"; +import { Calculator, Clock, Database, Target, Info } from "lucide-react"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; -import { Info } from "lucide-react"; import { cn } from "@/lib/utils"; export interface KpiExplain { diff --git a/src/components/admin/connections/SecretField.tsx b/src/components/admin/connections/SecretField.tsx index 6b79af87f..19d2de2f2 100644 --- a/src/components/admin/connections/SecretField.tsx +++ b/src/components/admin/connections/SecretField.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; -import { AlertCircle, Check, CheckCircle2, Eye, EyeOff, Loader2, RefreshCw, RotateCw, Save, ShieldAlert, Sparkles } from "lucide-react"; +import { AlertCircle, Check, CheckCircle2, Eye, EyeOff, Loader2, RefreshCw, RotateCw, Save, ShieldAlert, Sparkles, ArrowDownToLine } from "lucide-react"; import { validateSecret, getMinLength, MIN_SUFFIX_LENGTH } from "./secretValidators"; -import { formatMaskedSuffix, normalizeMaskedSuffix, diagnoseMaskedSuffix } from "@/lib/masked-suffix"; +import { formatMaskedSuffix, normalizeMaskedSuffix } from "@/lib/masked-suffix"; import { MaskedSuffixBadge } from "./MaskedSuffixBadge"; import { normalizeSecret } from "./secretNormalizers"; import { validateSecretName } from "./secretWhitelist"; @@ -20,7 +20,6 @@ import { RotationHistoryRow } from "./RotationHistoryRow"; import { CredentialSourceBadge } from "./CredentialSourceBadge"; import { SecretImpactTooltip } from "./SecretImpactTooltip"; import { useCredentialsSourceFilter } from "./CredentialsSourceFilterContext"; -import { ArrowDownToLine } from "lucide-react"; import { RotateSecretConfirmDialog } from "./RotateSecretConfirmDialog"; import { SaveSecretConfirmDialog } from "./SaveSecretConfirmDialog"; import { withRetryBackoff, CancelledError } from "./secretRetry"; diff --git a/src/components/admin/connections/SmokeTestChecklist.tsx b/src/components/admin/connections/SmokeTestChecklist.tsx index 2337bee56..95e82b8ae 100644 --- a/src/components/admin/connections/SmokeTestChecklist.tsx +++ b/src/components/admin/connections/SmokeTestChecklist.tsx @@ -146,7 +146,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: rotateResult.error?.message ?? "Falha desconhecida", durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 1 FAILED", rotateResult.error); setRunning(false); updateStep("history", { status: "skipped", detail: "Pulado (rotação falhou)" }); @@ -163,7 +163,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: `Sufixo esperado ${formatMaskedSuffix(expectedSuffix)}, recebido ${formatMaskedSuffix(rotateResult.masked_suffix)}`, durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 1 mismatch", { expectedSuffix, got: rotateResult.masked_suffix }); } else { updateStep("rotate", { @@ -181,7 +181,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: err instanceof Error ? err.message : "Erro inesperado", durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 1 EXCEPTION", err); setRunning(false); // eslint-disable-next-line no-console @@ -204,7 +204,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: `Nenhum registro com sufixo ${formatMaskedSuffix(expectedSuffix)} encontrado (${entries.length} entradas vistas).`, durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 2 missing entry", { expectedSuffix, total: entries.length }); } else { const author = matching.rotated_by_email ?? matching.rotated_by ?? "desconhecido"; @@ -223,7 +223,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: err instanceof Error ? err.message : "Erro ao consultar histórico", durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 2 EXCEPTION", err); } @@ -240,7 +240,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: "Secret não retornou na listagem após reload.", durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 3 missing in list"); } else if (normalizeMaskedSuffix(target.masked_suffix) !== expectedSuffix) { updateStep("reload", { @@ -248,7 +248,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: `Sufixo divergente após reload: esperado ${formatMaskedSuffix(expectedSuffix)}, recebido ${formatMaskedSuffix(target.masked_suffix)}`, durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 3 suffix mismatch after reload", target); } else { const sourceTag = target.source ? ` • source=${target.source}` : ""; @@ -267,7 +267,7 @@ export function SmokeTestChecklist({ availableSecrets = [] }: Props) { detail: err instanceof Error ? err.message : "Erro ao recarregar", durationMs: took, }); - // eslint-disable-next-line no-console + console.error("[smoke-test] step 3 EXCEPTION", err); } diff --git a/src/components/admin/connections/TestProgressIndicator.tsx b/src/components/admin/connections/TestProgressIndicator.tsx index b6d2cb41a..70c42d0b4 100644 --- a/src/components/admin/connections/TestProgressIndicator.tsx +++ b/src/components/admin/connections/TestProgressIndicator.tsx @@ -1,8 +1,8 @@ -import { useEffect, useState } from "react"; -import { Loader2, CheckCircle2, XCircle } from "lucide-react"; -import { cn } from "@/lib/utils"; +import { useEffect, useState } from 'react'; +import { Loader2, CheckCircle2, XCircle } from 'lucide-react'; +import { cn } from '@/lib/utils'; -export type TestProgressPhase = "idle" | "running" | "completed" | "failed"; +export type TestProgressPhase = 'idle' | 'running' | 'completed' | 'failed'; interface Props { phase: TestProgressPhase; @@ -30,15 +30,15 @@ export function TestProgressIndicator({ onDismiss, className, }: Props) { - const [visible, setVisible] = useState(phase !== "idle"); + const [visible, setVisible] = useState(phase !== 'idle'); useEffect(() => { - if (phase === "idle") { + if (phase === 'idle') { setVisible(false); return; } setVisible(true); - if (phase === "running") return; // never auto-dismiss while running + if (phase === 'running') return; // never auto-dismiss while running const t = setTimeout(() => { setVisible(false); onDismiss?.(); @@ -46,47 +46,39 @@ export function TestProgressIndicator({ return () => clearTimeout(t); }, [phase, autoDismissMs, onDismiss]); - if (!visible || phase === "idle") return null; + if (!visible || phase === 'idle') return null; - const isRunning = phase === "running"; - const isOk = phase === "completed"; - const isFail = phase === "failed"; + const isRunning = phase === 'running'; + const isOk = phase === 'completed'; + const _isFail = phase === 'failed'; const Icon = isRunning ? Loader2 : isOk ? CheckCircle2 : XCircle; const tone = isRunning - ? "border-primary/30 bg-primary/5 text-primary" + ? 'border-primary/30 bg-primary/5 text-primary' : isOk - ? "border-green-500/30 bg-green-500/5 text-green-700 dark:text-green-400" - : "border-destructive/30 bg-destructive/5 text-destructive"; + ? 'border-green-500/30 bg-green-500/5 text-green-700 dark:text-green-400' + : 'border-destructive/30 bg-destructive/5 text-destructive'; - const label = isRunning - ? "Teste em andamento…" - : isOk - ? "Teste concluído" - : "Teste falhou"; + const label = isRunning ? 'Teste em andamento…' : isOk ? 'Teste concluído' : 'Teste falhou'; const detail = isRunning - ? "Adicionando ao histórico assim que terminar" - : [ - latencyMs != null ? `${latencyMs}ms` : null, - message, - ].filter(Boolean).join(" · ") || (isOk ? "Histórico atualizado" : "Veja detalhes no histórico"); + ? 'Adicionando ao histórico assim que terminar' + : [latencyMs != null ? `${latencyMs}ms` : null, message].filter(Boolean).join(' · ') || + (isOk ? 'Histórico atualizado' : 'Veja detalhes no histórico'); return (
- + {label} - {detail && ( - — {detail} - )} + {detail && — {detail}}
); } diff --git a/src/components/admin/connections/ZoneCommandPalette.tsx b/src/components/admin/connections/ZoneCommandPalette.tsx index 9e2812660..ee302279f 100644 --- a/src/components/admin/connections/ZoneCommandPalette.tsx +++ b/src/components/admin/connections/ZoneCommandPalette.tsx @@ -13,7 +13,7 @@ * âncora própria (ex: tabs do "Conexões"). */ import { useEffect, useMemo, useState } from "react"; -import { Activity, Settings2, Network, Search, Layers } from "lucide-react"; +import { Activity, Settings2, Network, Layers } from "lucide-react"; import { CommandDialog, CommandEmpty, diff --git a/src/components/admin/connections/ZoneSection.tsx b/src/components/admin/connections/ZoneSection.tsx index e08a77526..1480950f9 100644 --- a/src/components/admin/connections/ZoneSection.tsx +++ b/src/components/admin/connections/ZoneSection.tsx @@ -9,11 +9,10 @@ * - Espaçamento interno padronizado (space-y-4) * - Suporte opcional a actions à direita do header */ -import type { LucideIcon } from "lucide-react"; -import { ChevronDown } from "lucide-react"; -import { cn } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { ChevronDown, type LucideIcon } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; interface ZoneSectionProps { id: string; @@ -21,7 +20,7 @@ interface ZoneSectionProps { title: string; description?: string; /** Tom semântico do header — afeta apenas o ícone + barra lateral */ - tone?: "primary" | "info" | "neutral"; + tone?: 'primary' | 'info' | 'neutral'; actions?: React.ReactNode; /** Quando true, aplica anel + glow temporário (ex: deep-link de incidente) */ highlight?: boolean; @@ -34,9 +33,13 @@ interface ZoneSectionProps { } const TONE_CLS = { - primary: { iconBg: "bg-primary/10", iconColor: "text-primary", bar: "bg-primary/40" }, - info: { iconBg: "bg-sky-500/10", iconColor: "text-sky-600 dark:text-sky-400", bar: "bg-sky-500/40" }, - neutral: { iconBg: "bg-muted", iconColor: "text-muted-foreground", bar: "bg-border" }, + primary: { iconBg: 'bg-primary/10', iconColor: 'text-primary', bar: 'bg-primary/40' }, + info: { + iconBg: 'bg-sky-500/10', + iconColor: 'text-sky-600 dark:text-sky-400', + bar: 'bg-sky-500/40', + }, + neutral: { iconBg: 'bg-muted', iconColor: 'text-muted-foreground', bar: 'bg-border' }, } as const; export function ZoneSection({ @@ -44,7 +47,7 @@ export function ZoneSection({ icon: Icon, title, description, - tone = "primary", + tone = 'primary', actions, highlight = false, collapsed = false, @@ -61,40 +64,33 @@ export function ZoneSection({ id={id} aria-labelledby={headingId} className={cn( - "scroll-mt-24 space-y-4 rounded-xl transition-shadow duration-500 -mx-2 px-2 py-1", - highlight && "ring-2 ring-primary/60 ring-offset-2 ring-offset-background shadow-[0_0_0_4px_hsl(var(--primary)/0.15)]", + '-mx-2 scroll-mt-24 space-y-4 rounded-xl px-2 py-1 transition-shadow duration-500', + highlight && + 'shadow-[0_0_0_4px_hsl(var(--primary)/0.15)] ring-2 ring-primary/60 ring-offset-2 ring-offset-background', className, )} >
-
-