|
|
@@ -1,29 +1,16 @@
|
|
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
-import { Input } from "@/components/ui/input";
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
|
import { Switch } from "@/components/ui/switch";
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
-import { Badge } from "@/components/ui/badge";
|
|
|
import { Mic, MessageSquare, Store, Loader2 } from "lucide-react";
|
|
|
import { useState, useEffect } from "react";
|
|
|
import { API_URL } from "@/lib/config";
|
|
|
import { useToast } from "@/hooks/use-toast";
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
-
|
|
|
-interface StoreData {
|
|
|
- id: string;
|
|
|
- store_name: string | null;
|
|
|
- platform_name: string;
|
|
|
- store_url: string | null;
|
|
|
- phone_number: string | null;
|
|
|
- is_active: boolean | null;
|
|
|
- alt_data?: {
|
|
|
- [key: string]: unknown;
|
|
|
- };
|
|
|
-}
|
|
|
+import { useShop } from "@/components/context/ShopContext";
|
|
|
|
|
|
interface AIConfig {
|
|
|
voice_type: string;
|
|
|
@@ -38,9 +25,7 @@ interface AIConfig {
|
|
|
export function AIConfigContent() {
|
|
|
const { t } = useTranslation();
|
|
|
const { toast } = useToast();
|
|
|
- const [stores, setStores] = useState<StoreData[]>([]);
|
|
|
- const [selectedStore, setSelectedStore] = useState<StoreData | null>(null);
|
|
|
- const [loading, setLoading] = useState(true);
|
|
|
+ const { selectedShop, stores, isLoading } = useShop();
|
|
|
const [saving, setSaving] = useState(false);
|
|
|
const [aiConfig, setAiConfig] = useState<AIConfig>({
|
|
|
voice_type: "sarah",
|
|
|
@@ -52,66 +37,14 @@ export function AIConfigContent() {
|
|
|
escalation_policy: "medium"
|
|
|
});
|
|
|
|
|
|
- // Get shop parameter from URL
|
|
|
+ // Load AI config when selected shop changes
|
|
|
useEffect(() => {
|
|
|
- const params = new URLSearchParams(window.location.search);
|
|
|
- const shopId = params.get('shop');
|
|
|
-
|
|
|
- const fetchStores = async () => {
|
|
|
- try {
|
|
|
- const sessionData = localStorage.getItem('session_data');
|
|
|
- if (!sessionData) {
|
|
|
- throw new Error('No session data found');
|
|
|
- }
|
|
|
-
|
|
|
- const session = JSON.parse(sessionData);
|
|
|
- const response = await fetch(`${API_URL}/api/stores`, {
|
|
|
- headers: {
|
|
|
- 'Authorization': `Bearer ${session.session.access_token}`,
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error('Failed to fetch stores');
|
|
|
- }
|
|
|
-
|
|
|
- const data = await response.json();
|
|
|
- if (data.success) {
|
|
|
- const storesList = data.stores || [];
|
|
|
- setStores(storesList);
|
|
|
-
|
|
|
- // Select the store from URL parameter or first store
|
|
|
- if (shopId) {
|
|
|
- const store = storesList.find((s: StoreData) => s.id === shopId);
|
|
|
- if (store) {
|
|
|
- setSelectedStore(store);
|
|
|
- loadAIConfig(store);
|
|
|
- } else if (storesList.length > 0) {
|
|
|
- setSelectedStore(storesList[0]);
|
|
|
- loadAIConfig(storesList[0]);
|
|
|
- }
|
|
|
- } else if (storesList.length > 0) {
|
|
|
- setSelectedStore(storesList[0]);
|
|
|
- loadAIConfig(storesList[0]);
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('Error fetching stores:', error);
|
|
|
- toast({
|
|
|
- title: t('common.error'),
|
|
|
- description: t('webshops.error'),
|
|
|
- variant: "destructive"
|
|
|
- });
|
|
|
- } finally {
|
|
|
- setLoading(false);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- fetchStores();
|
|
|
- }, [toast]);
|
|
|
+ if (selectedShop) {
|
|
|
+ loadAIConfig(selectedShop);
|
|
|
+ }
|
|
|
+ }, [selectedShop]);
|
|
|
|
|
|
- const loadAIConfig = (store: StoreData) => {
|
|
|
+ const loadAIConfig = (store: any) => {
|
|
|
// Load AI config from store's alt_data or use defaults
|
|
|
const config = store.alt_data?.ai_config || {};
|
|
|
|
|
|
@@ -126,18 +59,8 @@ export function AIConfigContent() {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
- const handleStoreChange = (storeId: string) => {
|
|
|
- const store = stores.find(s => s.id === storeId);
|
|
|
- if (store) {
|
|
|
- setSelectedStore(store);
|
|
|
- loadAIConfig(store);
|
|
|
- // Update URL
|
|
|
- window.history.replaceState({}, '', `/ai-config?shop=${storeId}`);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
const handleSaveConfig = async () => {
|
|
|
- if (!selectedStore) return;
|
|
|
+ if (!selectedShop) return;
|
|
|
|
|
|
setSaving(true);
|
|
|
try {
|
|
|
@@ -149,7 +72,7 @@ export function AIConfigContent() {
|
|
|
const session = JSON.parse(sessionData);
|
|
|
|
|
|
// Update store's alt_data with AI config
|
|
|
- const response = await fetch(`${API_URL}/api/stores/${selectedStore.id}/ai-config`, {
|
|
|
+ const response = await fetch(`${API_URL}/api/stores/${selectedShop.id}/ai-config`, {
|
|
|
method: 'PUT',
|
|
|
headers: {
|
|
|
'Authorization': `Bearer ${session.session.access_token}`,
|
|
|
@@ -178,7 +101,7 @@ export function AIConfigContent() {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- if (loading) {
|
|
|
+ if (isLoading) {
|
|
|
return (
|
|
|
<div className="flex-1 flex items-center justify-center min-h-screen bg-slate-900">
|
|
|
<Loader2 className="w-8 h-8 text-cyan-500 animate-spin" />
|
|
|
@@ -208,7 +131,7 @@ export function AIConfigContent() {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- if (!selectedStore) {
|
|
|
+ if (!selectedShop) {
|
|
|
return (
|
|
|
<div className="flex-1 flex items-center justify-center min-h-screen bg-slate-900">
|
|
|
<p className="text-slate-400">No store selected</p>
|
|
|
@@ -241,55 +164,6 @@ export function AIConfigContent() {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- {/* Webshop Selector */}
|
|
|
- <Card className="bg-slate-800 border-slate-700">
|
|
|
- <CardHeader>
|
|
|
- <div className="flex items-center gap-3">
|
|
|
- <Store className="w-6 h-6 text-cyan-500" />
|
|
|
- <CardTitle className="text-white">{t('aiConfig.selectWebshop.title')}</CardTitle>
|
|
|
- </div>
|
|
|
- <p className="text-slate-400">{t('aiConfig.selectWebshop.subtitle')}</p>
|
|
|
- </CardHeader>
|
|
|
- <CardContent>
|
|
|
- <Select value={selectedStore.id} onValueChange={handleStoreChange}>
|
|
|
- <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
|
|
|
- <SelectValue />
|
|
|
- </SelectTrigger>
|
|
|
- <SelectContent className="bg-slate-700 border-slate-600">
|
|
|
- {stores.map((store) => (
|
|
|
- <SelectItem key={store.id} value={store.id}>
|
|
|
- <div className="flex items-center gap-3">
|
|
|
- <div>
|
|
|
- <div className="text-white font-medium">{store.store_name || 'Unnamed Store'}</div>
|
|
|
- <div className="text-slate-400 text-sm capitalize">{store.platform_name}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </SelectItem>
|
|
|
- ))}
|
|
|
- </SelectContent>
|
|
|
- </Select>
|
|
|
-
|
|
|
- {/* Current Shop Info */}
|
|
|
- <div className="mt-4 p-4 bg-slate-700/50 rounded-lg">
|
|
|
- <div className="flex items-center justify-between">
|
|
|
- <div>
|
|
|
- <h4 className="text-white font-medium">{selectedStore.store_name || 'Unnamed Store'}</h4>
|
|
|
- <p className="text-slate-400 text-sm capitalize">{selectedStore.platform_name}</p>
|
|
|
- {selectedStore.store_url && (
|
|
|
- <p className="text-slate-500 text-xs mt-1">{selectedStore.store_url}</p>
|
|
|
- )}
|
|
|
- {selectedStore.phone_number && (
|
|
|
- <p className="text-slate-300 text-sm font-mono mt-1">{selectedStore.phone_number}</p>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- <Badge className={`${selectedStore.is_active ? "bg-green-500" : "bg-red-500"} text-white`}>
|
|
|
- {selectedStore.is_active ? t('webshops.active') : t('webshops.inactive')}
|
|
|
- </Badge>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
-
|
|
|
<div className="grid gap-6">
|
|
|
<Card className="bg-slate-800 border-slate-700">
|
|
|
<CardHeader>
|
|
|
@@ -297,7 +171,7 @@ export function AIConfigContent() {
|
|
|
<Mic className="w-6 h-6 text-cyan-500" />
|
|
|
<CardTitle className="text-white">{t('aiConfig.voiceSettings.title')}</CardTitle>
|
|
|
</div>
|
|
|
- <p className="text-slate-400">{t('aiConfig.voiceSettings.subtitle')} {selectedStore.store_name || 'your store'}</p>
|
|
|
+ <p className="text-slate-400">{t('aiConfig.voiceSettings.subtitle')} {selectedShop.store_name || 'your store'}</p>
|
|
|
</CardHeader>
|
|
|
<CardContent className="space-y-6">
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
|
@@ -350,7 +224,7 @@ export function AIConfigContent() {
|
|
|
<MessageSquare className="w-6 h-6 text-cyan-500" />
|
|
|
<CardTitle className="text-white">{t('aiConfig.conversationBehavior.title')}</CardTitle>
|
|
|
</div>
|
|
|
- <p className="text-slate-400">{t('aiConfig.conversationBehavior.subtitle')} {selectedStore.store_name || 'your store'} customers</p>
|
|
|
+ <p className="text-slate-400">{t('aiConfig.conversationBehavior.subtitle')} {selectedShop.store_name || 'your store'} customers</p>
|
|
|
</CardHeader>
|
|
|
<CardContent className="space-y-6">
|
|
|
<div className="space-y-2">
|