Browse Source

feat: add i18n translations to AIConfigContent and start IntegrationsContent #69

Claude 5 months ago
parent
commit
e162546f64

+ 58 - 56
shopcall.ai-main/src/components/AIConfigContent.tsx

@@ -11,6 +11,7 @@ import { Mic, MessageSquare, Brain, Zap, Store, ChevronDown, Loader2 } from "luc
 import { useState, useEffect } from "react";
 import { useState, useEffect } from "react";
 import { API_URL } from "@/lib/config";
 import { API_URL } from "@/lib/config";
 import { useToast } from "@/hooks/use-toast";
 import { useToast } from "@/hooks/use-toast";
+import { useTranslation } from "react-i18next";
 
 
 interface StoreData {
 interface StoreData {
   id: string;
   id: string;
@@ -35,6 +36,7 @@ interface AIConfig {
 }
 }
 
 
 export function AIConfigContent() {
 export function AIConfigContent() {
+  const { t } = useTranslation();
   const { toast } = useToast();
   const { toast } = useToast();
   const [stores, setStores] = useState<StoreData[]>([]);
   const [stores, setStores] = useState<StoreData[]>([]);
   const [selectedStore, setSelectedStore] = useState<StoreData | null>(null);
   const [selectedStore, setSelectedStore] = useState<StoreData | null>(null);
@@ -97,8 +99,8 @@ export function AIConfigContent() {
       } catch (error) {
       } catch (error) {
         console.error('Error fetching stores:', error);
         console.error('Error fetching stores:', error);
         toast({
         toast({
-          title: "Error",
-          description: "Failed to load stores. Please try again.",
+          title: t('common.error'),
+          description: t('webshops.error'),
           variant: "destructive"
           variant: "destructive"
         });
         });
       } finally {
       } finally {
@@ -161,14 +163,14 @@ export function AIConfigContent() {
       }
       }
 
 
       toast({
       toast({
-        title: "Success",
-        description: "AI configuration saved successfully.",
+        title: t('common.success'),
+        description: t('aiConfig.saveConfiguration'),
       });
       });
     } catch (error) {
     } catch (error) {
       console.error('Error saving config:', error);
       console.error('Error saving config:', error);
       toast({
       toast({
-        title: "Error",
-        description: "Failed to save configuration. Please try again.",
+        title: t('common.error'),
+        description: t('common.tryAgain'),
         variant: "destructive"
         variant: "destructive"
       });
       });
     } finally {
     } finally {
@@ -190,15 +192,15 @@ export function AIConfigContent() {
         <div className="flex items-center justify-center min-h-[60vh]">
         <div className="flex items-center justify-center min-h-[60vh]">
           <div className="text-center">
           <div className="text-center">
             <Store className="w-16 h-16 text-slate-600 mx-auto mb-4" />
             <Store className="w-16 h-16 text-slate-600 mx-auto mb-4" />
-            <h3 className="text-lg font-semibold text-white mb-2">No Stores Connected</h3>
+            <h3 className="text-lg font-semibold text-white mb-2">{t('aiConfig.noStoresConnected.title')}</h3>
             <p className="text-slate-400 mb-6">
             <p className="text-slate-400 mb-6">
-              Please connect a store first before configuring AI settings.
+              {t('aiConfig.noStoresConnected.description')}
             </p>
             </p>
             <Button
             <Button
               className="bg-cyan-500 hover:bg-cyan-600 text-white"
               className="bg-cyan-500 hover:bg-cyan-600 text-white"
               onClick={() => window.location.href = '/webshops'}
               onClick={() => window.location.href = '/webshops'}
             >
             >
-              Go to Webshops
+              {t('aiConfig.noStoresConnected.goToWebshops')}
             </Button>
             </Button>
           </div>
           </div>
         </div>
         </div>
@@ -218,8 +220,8 @@ export function AIConfigContent() {
     <div className="flex-1 space-y-6 p-8 bg-slate-900">
     <div className="flex-1 space-y-6 p-8 bg-slate-900">
       <div className="flex items-center justify-between">
       <div className="flex items-center justify-between">
         <div>
         <div>
-          <h2 className="text-3xl font-bold tracking-tight text-white">AI Configuration</h2>
-          <p className="text-slate-400">Configure AI behavior and responses per webshop</p>
+          <h2 className="text-3xl font-bold tracking-tight text-white">{t('aiConfig.title')}</h2>
+          <p className="text-slate-400">{t('aiConfig.subtitle')}</p>
         </div>
         </div>
         <div className="flex gap-3">
         <div className="flex gap-3">
           <Button
           <Button
@@ -230,10 +232,10 @@ export function AIConfigContent() {
             {saving ? (
             {saving ? (
               <>
               <>
                 <Loader2 className="w-4 h-4 mr-2 animate-spin" />
                 <Loader2 className="w-4 h-4 mr-2 animate-spin" />
-                Saving...
+                {t('aiConfig.saving')}
               </>
               </>
             ) : (
             ) : (
-              "Save Configuration"
+              t('aiConfig.saveConfiguration')
             )}
             )}
           </Button>
           </Button>
         </div>
         </div>
@@ -244,9 +246,9 @@ export function AIConfigContent() {
         <CardHeader>
         <CardHeader>
           <div className="flex items-center gap-3">
           <div className="flex items-center gap-3">
             <Store className="w-6 h-6 text-cyan-500" />
             <Store className="w-6 h-6 text-cyan-500" />
-            <CardTitle className="text-white">Select Webshop</CardTitle>
+            <CardTitle className="text-white">{t('aiConfig.selectWebshop.title')}</CardTitle>
           </div>
           </div>
-          <p className="text-slate-400">Choose which webshop to configure</p>
+          <p className="text-slate-400">{t('aiConfig.selectWebshop.subtitle')}</p>
         </CardHeader>
         </CardHeader>
         <CardContent>
         <CardContent>
           <Select value={selectedStore.id} onValueChange={handleStoreChange}>
           <Select value={selectedStore.id} onValueChange={handleStoreChange}>
@@ -281,7 +283,7 @@ export function AIConfigContent() {
                 )}
                 )}
               </div>
               </div>
               <Badge className={`${selectedStore.is_active ? "bg-green-500" : "bg-red-500"} text-white`}>
               <Badge className={`${selectedStore.is_active ? "bg-green-500" : "bg-red-500"} text-white`}>
-                {selectedStore.is_active ? "Active" : "Inactive"}
+                {selectedStore.is_active ? t('webshops.active') : t('webshops.inactive')}
               </Badge>
               </Badge>
             </div>
             </div>
           </div>
           </div>
@@ -293,49 +295,49 @@ export function AIConfigContent() {
           <CardHeader>
           <CardHeader>
             <div className="flex items-center gap-3">
             <div className="flex items-center gap-3">
               <Mic className="w-6 h-6 text-cyan-500" />
               <Mic className="w-6 h-6 text-cyan-500" />
-              <CardTitle className="text-white">Voice & Speech Settings</CardTitle>
+              <CardTitle className="text-white">{t('aiConfig.voiceSettings.title')}</CardTitle>
             </div>
             </div>
-            <p className="text-slate-400">Configure voice characteristics for {selectedStore.store_name || 'your store'}</p>
+            <p className="text-slate-400">{t('aiConfig.voiceSettings.subtitle')} {selectedStore.store_name || 'your store'}</p>
           </CardHeader>
           </CardHeader>
           <CardContent className="space-y-6">
           <CardContent className="space-y-6">
             <div className="grid gap-6 md:grid-cols-2">
             <div className="grid gap-6 md:grid-cols-2">
               <div className="space-y-2">
               <div className="space-y-2">
-                <Label className="text-slate-300">Voice Type</Label>
+                <Label className="text-slate-300">{t('aiConfig.voiceSettings.voiceType')}</Label>
                 <Select value={aiConfig.voice_type} onValueChange={(value) => setAiConfig({ ...aiConfig, voice_type: value })}>
                 <Select value={aiConfig.voice_type} onValueChange={(value) => setAiConfig({ ...aiConfig, voice_type: value })}>
                   <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                   <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                     <SelectValue />
                     <SelectValue />
                   </SelectTrigger>
                   </SelectTrigger>
                   <SelectContent className="bg-slate-700 border-slate-600">
                   <SelectContent className="bg-slate-700 border-slate-600">
-                    <SelectItem value="sarah">Sarah (Professional)</SelectItem>
-                    <SelectItem value="james">James (Friendly)</SelectItem>
-                    <SelectItem value="emma">Emma (Warm)</SelectItem>
+                    <SelectItem value="sarah">{t('aiConfig.voiceSettings.voices.sarah')}</SelectItem>
+                    <SelectItem value="james">{t('aiConfig.voiceSettings.voices.james')}</SelectItem>
+                    <SelectItem value="emma">{t('aiConfig.voiceSettings.voices.emma')}</SelectItem>
                   </SelectContent>
                   </SelectContent>
                 </Select>
                 </Select>
               </div>
               </div>
               <div className="space-y-2">
               <div className="space-y-2">
-                <Label className="text-slate-300">Speaking Speed</Label>
+                <Label className="text-slate-300">{t('aiConfig.voiceSettings.speakingSpeed')}</Label>
                 <Select value={aiConfig.speaking_speed} onValueChange={(value) => setAiConfig({ ...aiConfig, speaking_speed: value })}>
                 <Select value={aiConfig.speaking_speed} onValueChange={(value) => setAiConfig({ ...aiConfig, speaking_speed: value })}>
                   <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                   <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                     <SelectValue />
                     <SelectValue />
                   </SelectTrigger>
                   </SelectTrigger>
                   <SelectContent className="bg-slate-700 border-slate-600">
                   <SelectContent className="bg-slate-700 border-slate-600">
-                    <SelectItem value="slow">Slow</SelectItem>
-                    <SelectItem value="normal">Normal</SelectItem>
-                    <SelectItem value="fast">Fast</SelectItem>
+                    <SelectItem value="slow">{t('aiConfig.voiceSettings.speeds.slow')}</SelectItem>
+                    <SelectItem value="normal">{t('aiConfig.voiceSettings.speeds.normal')}</SelectItem>
+                    <SelectItem value="fast">{t('aiConfig.voiceSettings.speeds.fast')}</SelectItem>
                   </SelectContent>
                   </SelectContent>
                 </Select>
                 </Select>
               </div>
               </div>
             </div>
             </div>
             <div className="space-y-2">
             <div className="space-y-2">
-              <Label className="text-slate-300">Accent & Language</Label>
+              <Label className="text-slate-300">{t('aiConfig.voiceSettings.accentLanguage')}</Label>
               <Select value={aiConfig.accent_language} onValueChange={(value) => setAiConfig({ ...aiConfig, accent_language: value })}>
               <Select value={aiConfig.accent_language} onValueChange={(value) => setAiConfig({ ...aiConfig, accent_language: value })}>
                 <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                 <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                   <SelectValue />
                   <SelectValue />
                 </SelectTrigger>
                 </SelectTrigger>
                 <SelectContent className="bg-slate-700 border-slate-600">
                 <SelectContent className="bg-slate-700 border-slate-600">
-                  <SelectItem value="us-english">US English</SelectItem>
-                  <SelectItem value="uk-english">UK English</SelectItem>
-                  <SelectItem value="australian">Australian</SelectItem>
+                  <SelectItem value="us-english">{t('aiConfig.voiceSettings.accents.usEnglish')}</SelectItem>
+                  <SelectItem value="uk-english">{t('aiConfig.voiceSettings.accents.ukEnglish')}</SelectItem>
+                  <SelectItem value="australian">{t('aiConfig.voiceSettings.accents.australian')}</SelectItem>
                 </SelectContent>
                 </SelectContent>
               </Select>
               </Select>
             </div>
             </div>
@@ -346,13 +348,13 @@ export function AIConfigContent() {
           <CardHeader>
           <CardHeader>
             <div className="flex items-center gap-3">
             <div className="flex items-center gap-3">
               <MessageSquare className="w-6 h-6 text-cyan-500" />
               <MessageSquare className="w-6 h-6 text-cyan-500" />
-              <CardTitle className="text-white">Conversation Behavior</CardTitle>
+              <CardTitle className="text-white">{t('aiConfig.conversationBehavior.title')}</CardTitle>
             </div>
             </div>
-            <p className="text-slate-400">Define how the AI interacts with {selectedStore.store_name || 'your store'} customers</p>
+            <p className="text-slate-400">{t('aiConfig.conversationBehavior.subtitle')} {selectedStore.store_name || 'your store'} customers</p>
           </CardHeader>
           </CardHeader>
           <CardContent className="space-y-6">
           <CardContent className="space-y-6">
             <div className="space-y-2">
             <div className="space-y-2">
-              <Label className="text-slate-300">Greeting Message</Label>
+              <Label className="text-slate-300">{t('aiConfig.conversationBehavior.greetingMessage')}</Label>
               <Textarea
               <Textarea
                 className="bg-slate-700 border-slate-600 text-white min-h-[100px]"
                 className="bg-slate-700 border-slate-600 text-white min-h-[100px]"
                 value={aiConfig.greeting_message}
                 value={aiConfig.greeting_message}
@@ -363,8 +365,8 @@ export function AIConfigContent() {
             <div className="grid gap-6 md:grid-cols-2">
             <div className="grid gap-6 md:grid-cols-2">
               <div className="flex items-center justify-between">
               <div className="flex items-center justify-between">
                 <div className="space-y-1">
                 <div className="space-y-1">
-                  <Label className="text-slate-300">Business Hours Mode</Label>
-                  <p className="text-sm text-slate-400">Adjust behavior based on store hours</p>
+                  <Label className="text-slate-300">{t('aiConfig.conversationBehavior.businessHoursMode')}</Label>
+                  <p className="text-sm text-slate-400">{t('aiConfig.conversationBehavior.businessHoursModeDesc')}</p>
                 </div>
                 </div>
                 <Switch
                 <Switch
                   checked={aiConfig.business_hours_mode}
                   checked={aiConfig.business_hours_mode}
@@ -374,8 +376,8 @@ export function AIConfigContent() {
               </div>
               </div>
               <div className="flex items-center justify-between">
               <div className="flex items-center justify-between">
                 <div className="space-y-1">
                 <div className="space-y-1">
-                  <Label className="text-slate-300">Local Currency Support</Label>
-                  <p className="text-sm text-slate-400">Auto-detect customer location</p>
+                  <Label className="text-slate-300">{t('aiConfig.conversationBehavior.localCurrencySupport')}</Label>
+                  <p className="text-sm text-slate-400">{t('aiConfig.conversationBehavior.localCurrencySupportDesc')}</p>
                 </div>
                 </div>
                 <Switch
                 <Switch
                   checked={aiConfig.local_currency_support}
                   checked={aiConfig.local_currency_support}
@@ -386,15 +388,15 @@ export function AIConfigContent() {
             </div>
             </div>
 
 
             <div className="space-y-2">
             <div className="space-y-2">
-              <Label className="text-slate-300">Escalation Policy</Label>
+              <Label className="text-slate-300">{t('aiConfig.conversationBehavior.escalationPolicy')}</Label>
               <Select value={aiConfig.escalation_policy} onValueChange={(value) => setAiConfig({ ...aiConfig, escalation_policy: value })}>
               <Select value={aiConfig.escalation_policy} onValueChange={(value) => setAiConfig({ ...aiConfig, escalation_policy: value })}>
                 <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                 <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
                   <SelectValue />
                   <SelectValue />
                 </SelectTrigger>
                 </SelectTrigger>
                 <SelectContent className="bg-slate-700 border-slate-600">
                 <SelectContent className="bg-slate-700 border-slate-600">
-                  <SelectItem value="low">Quick Escalation - Transfer to human quickly</SelectItem>
-                  <SelectItem value="medium">Balanced - Try to resolve then escalate</SelectItem>
-                  <SelectItem value="high">AI First - Extensive AI resolution attempts</SelectItem>
+                  <SelectItem value="low">{t('aiConfig.conversationBehavior.escalationPolicies.low')}</SelectItem>
+                  <SelectItem value="medium">{t('aiConfig.conversationBehavior.escalationPolicies.medium')}</SelectItem>
+                  <SelectItem value="high">{t('aiConfig.conversationBehavior.escalationPolicies.high')}</SelectItem>
                 </SelectContent>
                 </SelectContent>
               </Select>
               </Select>
             </div>
             </div>
@@ -405,47 +407,47 @@ export function AIConfigContent() {
           <CardHeader>
           <CardHeader>
             <div className="flex items-center gap-3">
             <div className="flex items-center gap-3">
               <Brain className="w-6 h-6 text-cyan-500" />
               <Brain className="w-6 h-6 text-cyan-500" />
-              <CardTitle className="text-white">Knowledge Base</CardTitle>
+              <CardTitle className="text-white">{t('aiConfig.knowledgeBase.title')}</CardTitle>
             </div>
             </div>
-            <p className="text-slate-400">Training data specific to {selectedStore.store_name || 'your store'}</p>
+            <p className="text-slate-400">{t('aiConfig.knowledgeBase.subtitle')} {selectedStore.store_name || 'your store'}</p>
           </CardHeader>
           </CardHeader>
           <CardContent className="space-y-6">
           <CardContent className="space-y-6">
             <div className="grid gap-4 md:grid-cols-3">
             <div className="grid gap-4 md:grid-cols-3">
               <div className="p-4 bg-slate-700/50 rounded-lg">
               <div className="p-4 bg-slate-700/50 rounded-lg">
                 <div className="flex items-center justify-between mb-2">
                 <div className="flex items-center justify-between mb-2">
-                  <h4 className="text-white font-medium">Product Catalog</h4>
+                  <h4 className="text-white font-medium">{t('aiConfig.knowledgeBase.productCatalog')}</h4>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
-                    {selectedStore.sync_status === 'completed' ? 'Synced' : 'Not Synced'}
+                    {selectedStore.sync_status === 'completed' ? t('aiConfig.knowledgeBase.synced') : t('aiConfig.knowledgeBase.notSynced')}
                   </Badge>
                   </Badge>
                 </div>
                 </div>
                 <p className="text-slate-400 text-sm">
                 <p className="text-slate-400 text-sm">
-                  {selectedStore.alt_data?.last_sync_stats?.products?.synced || 0} products
+                  {selectedStore.alt_data?.last_sync_stats?.products?.synced || 0} {t('aiConfig.knowledgeBase.products')}
                 </p>
                 </p>
-                <p className="text-slate-500 text-xs capitalize">Auto-sync from {selectedStore.platform_name}</p>
+                <p className="text-slate-500 text-xs capitalize">{t('aiConfig.knowledgeBase.autoSync')} {selectedStore.platform_name}</p>
               </div>
               </div>
               <div className="p-4 bg-slate-700/50 rounded-lg">
               <div className="p-4 bg-slate-700/50 rounded-lg">
                 <div className="flex items-center justify-between mb-2">
                 <div className="flex items-center justify-between mb-2">
-                  <h4 className="text-white font-medium">Orders</h4>
+                  <h4 className="text-white font-medium">{t('aiConfig.knowledgeBase.orders')}</h4>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
-                    {selectedStore.sync_status === 'completed' ? 'Synced' : 'Not Synced'}
+                    {selectedStore.sync_status === 'completed' ? t('aiConfig.knowledgeBase.synced') : t('aiConfig.knowledgeBase.notSynced')}
                   </Badge>
                   </Badge>
                 </div>
                 </div>
                 <p className="text-slate-400 text-sm">
                 <p className="text-slate-400 text-sm">
-                  {selectedStore.alt_data?.last_sync_stats?.orders?.synced || 0} orders
+                  {selectedStore.alt_data?.last_sync_stats?.orders?.synced || 0} {t('aiConfig.knowledgeBase.ordersCount')}
                 </p>
                 </p>
-                <p className="text-slate-500 text-xs">Order history</p>
+                <p className="text-slate-500 text-xs">{t('aiConfig.knowledgeBase.orderHistory')}</p>
               </div>
               </div>
               <div className="p-4 bg-slate-700/50 rounded-lg">
               <div className="p-4 bg-slate-700/50 rounded-lg">
                 <div className="flex items-center justify-between mb-2">
                 <div className="flex items-center justify-between mb-2">
-                  <h4 className="text-white font-medium">Customers</h4>
+                  <h4 className="text-white font-medium">{t('aiConfig.knowledgeBase.customers')}</h4>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
                   <Badge className={`${selectedStore.sync_status === 'completed' ? 'bg-green-500' : 'bg-slate-500'} text-white`}>
-                    {selectedStore.sync_status === 'completed' ? 'Synced' : 'Not Synced'}
+                    {selectedStore.sync_status === 'completed' ? t('aiConfig.knowledgeBase.synced') : t('aiConfig.knowledgeBase.notSynced')}
                   </Badge>
                   </Badge>
                 </div>
                 </div>
                 <p className="text-slate-400 text-sm">
                 <p className="text-slate-400 text-sm">
-                  {selectedStore.alt_data?.last_sync_stats?.customers?.synced || 0} customers
+                  {selectedStore.alt_data?.last_sync_stats?.customers?.synced || 0} {t('aiConfig.knowledgeBase.customersCount')}
                 </p>
                 </p>
-                <p className="text-slate-500 text-xs">Customer database</p>
+                <p className="text-slate-500 text-xs">{t('aiConfig.knowledgeBase.customerDatabase')}</p>
               </div>
               </div>
             </div>
             </div>
 
 
@@ -454,7 +456,7 @@ export function AIConfigContent() {
                 className="bg-cyan-500 hover:bg-cyan-600 text-white"
                 className="bg-cyan-500 hover:bg-cyan-600 text-white"
                 onClick={() => window.location.href = `/manage-store-data?shop=${selectedStore.id}`}
                 onClick={() => window.location.href = `/manage-store-data?shop=${selectedStore.id}`}
               >
               >
-                Manage Store Data
+                {t('aiConfig.knowledgeBase.manageStoreData')}
               </Button>
               </Button>
             </div>
             </div>
           </CardContent>
           </CardContent>

+ 2 - 0
shopcall.ai-main/src/components/IntegrationsContent.tsx

@@ -13,6 +13,7 @@ import { ShopifyConnect } from "./ShopifyConnect";
 import { DataAccessSettings } from "./DataAccessSettings";
 import { DataAccessSettings } from "./DataAccessSettings";
 import { API_URL } from "@/lib/config";
 import { API_URL } from "@/lib/config";
 import { useToast } from "@/hooks/use-toast";
 import { useToast } from "@/hooks/use-toast";
+import { useTranslation } from "react-i18next";
 
 
 interface DataAccessPermissions {
 interface DataAccessPermissions {
   allow_customer_access: boolean;
   allow_customer_access: boolean;
@@ -50,6 +51,7 @@ interface ConnectedStore {
 }
 }
 
 
 export function IntegrationsContent() {
 export function IntegrationsContent() {
+  const { t } = useTranslation();
   const [connectedShops, setConnectedShops] = useState<ConnectedStore[]>([]);
   const [connectedShops, setConnectedShops] = useState<ConnectedStore[]>([]);
   const [loading, setLoading] = useState(true);
   const [loading, setLoading] = useState(true);
   const [showConnectDialog, setShowConnectDialog] = useState(false);
   const [showConnectDialog, setShowConnectDialog] = useState(false);