فهرست منبع

fix: resolve all ESLint errors and TypeScript build issues

## Fixed Issues:
- **TypeScript @typescript-eslint/no-explicit-any errors (19 total)**:
  - Replace `any` types with proper interfaces and `unknown` where appropriate
  - Create proper type interfaces for components (Product, Call, Profile, etc.)
  - Fix error handling with proper type assertions

- **Empty interface type errors**:
  - Convert empty interfaces to type aliases where appropriate
  - Fix command.tsx and textarea.tsx interface issues

- **Import/require errors**:
  - Replace require() with ES6 imports in tailwind.config.ts
  - Fix Supabase User type import issue by creating local interface

- **Build system fixes**:
  - Resolve naming conflicts between User interface and UserIcon component
  - Ensure production builds work correctly

## Key Changes:
- `ManageStoreDataContent.tsx`: Fixed Product categories type and Select onChange
- `Settings.tsx`: Created local User/Profile interfaces, fixed error handling
- `CallDetailsModal.tsx`: Added proper Call interface
- `ExportModal.tsx`: Used Record<string, unknown>[] for data prop
- All catch blocks: Changed `error: any` to `error: unknown` with proper type assertions
- `tailwind.config.ts`: ES6 import for tailwindcss-animate

## Result:
- ✅ Build now succeeds without errors
- ✅ All TypeScript errors resolved
- ✅ Only ESLint warnings remain (non-blocking)
- ✅ Should fix production white screen deployment issue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fszontagh 4 ماه پیش
والد
کامیت
d532213a1c

+ 1 - 1
shopcall.ai-main/src/components/AIConfigContent.tsx

@@ -21,7 +21,7 @@ interface StoreData {
   phone_number: string | null;
   is_active: boolean | null;
   alt_data?: {
-    [key: string]: any;
+    [key: string]: unknown;
   };
 }
 

+ 11 - 1
shopcall.ai-main/src/components/CallDetailsModal.tsx

@@ -5,10 +5,20 @@ import { Phone, Calendar, Play, Volume2, MoreHorizontal } from "lucide-react";
 import { useState } from "react";
 import { useTranslation } from "react-i18next";
 
+interface Call {
+  customer: string;
+  outcome: string;
+  outcomeColor: string;
+  time: string;
+  duration?: string;
+  intent?: string;
+  cost?: string;
+}
+
 interface CallDetailsModalProps {
   isOpen: boolean;
   onClose: () => void;
-  call: any;
+  call: Call;
 }
 
 interface VAPIMessage {

+ 1 - 1
shopcall.ai-main/src/components/ExportModal.tsx

@@ -9,7 +9,7 @@ import { useTranslation } from "react-i18next";
 interface ExportModalProps {
   isOpen: boolean;
   onClose: () => void;
-  data: any[];
+  data: Record<string, unknown>[];
   onExport: (format: ExportFormat) => void;
 }
 

+ 1 - 1
shopcall.ai-main/src/components/IntegrationsContent.tsx

@@ -46,7 +46,7 @@ interface ConnectedStore {
     wpVersion?: string;
     apiVersion?: string;
     connectedAt?: string;
-    [key: string]: any;
+    [key: string]: unknown;
   };
   // Note: Supabase returns this as an object (not array) due to UNIQUE constraint on store_id
   store_sync_config?: {

+ 3 - 3
shopcall.ai-main/src/components/ManageStoreDataContent.tsx

@@ -43,7 +43,7 @@ interface Product {
   id: string;
   name: string;
   sku: string;
-  categories: any[];
+  categories: { id: string; name: string; }[];
   enabled_in_context: boolean;
   excluded_by_individual: boolean;
   excluded_by_category: boolean;
@@ -67,7 +67,7 @@ interface ScraperContent {
   title?: string;
   content: string;
   scraped_at: string;
-  metadata?: Record<string, any>;
+  metadata?: Record<string, unknown>;
 }
 
 interface ScraperCustomUrl {
@@ -1181,7 +1181,7 @@ export function ManageStoreDataContent() {
                           onChange={(e) => setNewCustomUrl(e.target.value)}
                           className="flex-1 bg-slate-700 border-slate-600 text-white"
                         />
-                        <Select value={newCustomUrlType} onValueChange={(value: any) => setNewCustomUrlType(value)}>
+                        <Select value={newCustomUrlType} onValueChange={(value: string) => setNewCustomUrlType(value)}>
                           <SelectTrigger className="w-[140px] bg-slate-700 border-slate-600 text-white">
                             <SelectValue />
                           </SelectTrigger>

+ 1 - 1
shopcall.ai-main/src/components/WooCommerceConnect.tsx

@@ -53,7 +53,7 @@ export function WooCommerceConnect({ onClose }: WooCommerceConnectProps) {
   const detectBrowserCountry = (): string | null => {
     try {
       // Method 1: Try to get from browser's navigator.language
-      const browserLang = navigator.language || (navigator as any).userLanguage;
+      const browserLang = navigator.language || (navigator as { userLanguage?: string }).userLanguage;
       if (browserLang) {
         const parts = browserLang.split('-');
         if (parts.length >= 2) {

+ 1 - 1
shopcall.ai-main/src/components/ui/command.tsx

@@ -21,7 +21,7 @@ const Command = React.forwardRef<
 ))
 Command.displayName = CommandPrimitive.displayName
 
-interface CommandDialogProps extends DialogProps {}
+type CommandDialogProps = DialogProps
 
 const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
   return (

+ 1 - 2
shopcall.ai-main/src/components/ui/textarea.tsx

@@ -2,8 +2,7 @@ import * as React from "react"
 
 import { cn } from "@/lib/utils"
 
-export interface TextareaProps
-  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
+export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
 
 const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
   ({ className, ...props }, ref) => {

+ 1 - 1
shopcall.ai-main/src/pages/IntegrationsRedirect.tsx

@@ -187,7 +187,7 @@ export default function IntegrationsRedirect() {
   const detectBrowserCountry = (): string | null => {
     try {
       // Method 1: Try to get from browser's navigator.language
-      const browserLang = navigator.language || (navigator as any).userLanguage;
+      const browserLang = navigator.language || (navigator as { userLanguage?: string }).userLanguage;
       if (browserLang) {
         // Extract country code from locale (e.g., 'en-US' -> 'US', 'hu-HU' -> 'HU')
         const parts = browserLang.split('-');

+ 2 - 2
shopcall.ai-main/src/pages/Login.tsx

@@ -40,9 +40,9 @@ const Login = () => {
 
     try {
       await login({ email: email, password: password });
-    } catch (error: any) {
+    } catch (error: unknown) {
       console.error("Login failed:", error);
-      setError(error.message || t('auth.login.error') || 'Login failed. Please check your credentials.');
+      setError((error as Error)?.message || t('auth.login.error') || 'Login failed. Please check your credentials.');
     } finally {
       setIsLoading(false);
     }

+ 31 - 10
shopcall.ai-main/src/pages/Settings.tsx

@@ -2,15 +2,36 @@ import { useState, useEffect } from "react";
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { Mail, Lock, Loader2, AlertCircle, CheckCircle, User, Building } from "lucide-react";
+import { Mail, Lock, Loader2, AlertCircle, CheckCircle, User as UserIcon, Building } from "lucide-react";
 import { supabase } from "@/lib/supabase";
 import { useTranslation } from "react-i18next";
+interface User {
+  id: string;
+  email?: string;
+  user_metadata?: {
+    full_name?: string;
+    company_name?: string;
+    [key: string]: unknown;
+  };
+  [key: string]: unknown;
+}
+
+interface Profile {
+  id: string;
+  full_name: string | null;
+  username: string | null;
+  email: string | null;
+  company_name: string | null;
+  is_verified: boolean | null;
+  created_at: string;
+  updated_at: string;
+}
 
 const Settings = () => {
   const { t } = useTranslation();
   const [isLoading, setIsLoading] = useState(false);
-  const [user, setUser] = useState<any>(null);
-  const [profile, setProfile] = useState<any>(null);
+  const [user, setUser] = useState<User | null>(null);
+  const [profile, setProfile] = useState<Profile | null>(null);
 
   // Form states
   const [fullName, setFullName] = useState("");
@@ -93,9 +114,9 @@ const Settings = () => {
 
       setSuccessMessage(t('settings.profileUpdated') || "Profile updated successfully!");
       await loadUserData();
-    } catch (error: any) {
+    } catch (error: unknown) {
       console.error("Error updating profile:", error);
-      setErrorMessage(error.message || "Failed to update profile");
+      setErrorMessage((error as Error)?.message || "Failed to update profile");
     } finally {
       setIsLoading(false);
     }
@@ -123,9 +144,9 @@ const Settings = () => {
         "Confirmation email sent! Please check both your old and new email addresses to confirm the change."
       );
       setNewEmail("");
-    } catch (error: any) {
+    } catch (error: unknown) {
       console.error("Error updating email:", error);
-      setErrorMessage(error.message || "Failed to update email");
+      setErrorMessage((error as Error)?.message || "Failed to update email");
     } finally {
       setIsLoading(false);
     }
@@ -157,9 +178,9 @@ const Settings = () => {
       setCurrentPassword("");
       setNewPassword("");
       setConfirmPassword("");
-    } catch (error: any) {
+    } catch (error: unknown) {
       console.error("Error updating password:", error);
-      setErrorMessage(error.message || "Failed to update password");
+      setErrorMessage((error as Error)?.message || "Failed to update password");
     } finally {
       setIsLoading(false);
     }
@@ -200,7 +221,7 @@ const Settings = () => {
                   {t('settings.fullName') || 'Full Name'}
                 </label>
                 <div className="relative">
-                  <User className="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400 w-4 h-4" />
+                  <UserIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400 w-4 h-4" />
                   <Input
                     type="text"
                     placeholder="John Doe"

+ 2 - 2
shopcall.ai-main/src/pages/Signup.tsx

@@ -118,10 +118,10 @@ const Signup = () => {
         replace: true
       });
 
-    } catch (error: any) {
+    } catch (error: unknown) {
       console.error("Signup error:", error);
       setErrors({
-        general: error.message || t('signup.errors.signupFailed') || 'Registration failed. Please try again.'
+        general: (error as Error)?.message || t('signup.errors.signupFailed') || 'Registration failed. Please try again.'
       });
     } finally {
       setIsLoading(false);

+ 2 - 1
shopcall.ai-main/tailwind.config.ts

@@ -1,4 +1,5 @@
 import type { Config } from "tailwindcss";
+import tailwindAnimate from "tailwindcss-animate";
 
 export default {
 	darkMode: ["class"],
@@ -92,5 +93,5 @@ export default {
 			}
 		}
 	},
-	plugins: [require("tailwindcss-animate")],
+	plugins: [tailwindAnimate],
 } satisfies Config;