Browse Source

feat: implement /phone-numbers page with real data #67

- Modified PhoneNumbersContent to fetch real store data via API
- Display stores with their assigned phone numbers from database
- Show phone number details (country, location, type, price)
- Added loading state while fetching data
- Added empty state when no stores are connected
- Added error handling for API failures
- Disabled "Add Phone Number" button (feature not yet available)
- Disabled "Assign" button on all stores (read-only mode)
- Disabled settings button for existing phone numbers (read-only mode)
- Maintained existing UI style and layout
- Display status badges based on store active state and phone assignment

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

Co-Authored-By: Claude <noreply@anthropic.com>
Claude 5 months ago
parent
commit
a956bbcccd
1 changed files with 214 additions and 116 deletions
  1. 214 116
      shopcall.ai-main/src/components/PhoneNumbersContent.tsx

+ 214 - 116
shopcall.ai-main/src/components/PhoneNumbersContent.tsx

@@ -1,58 +1,31 @@
+import { useState, useEffect } from "react";
 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 import { Button } from "@/components/ui/button";
 import { Button } from "@/components/ui/button";
 import { Badge } from "@/components/ui/badge";
 import { Badge } from "@/components/ui/badge";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
-import { PhoneCall, Plus, Settings, AlertTriangle, CheckCircle, Clock, Link, Shield } from "lucide-react";
+import { PhoneCall, Plus, Settings, AlertTriangle, CheckCircle, Clock, Link, Shield, Loader2 } from "lucide-react";
+import { API_URL } from "@/lib/config";
+import { Alert, AlertDescription } from "@/components/ui/alert";
 
 
-const connectedShops = [
-  {
-    name: "Fashion Forward",
-    platform: "Shopify",
-    phoneNumber: "+1 (555) 123-4567",
-    status: "Active",
-    type: "Free",
-    country: "United States",
-    statusColor: "bg-green-500"
-  },
-  {
-    name: "Tech Gadgets Pro",
-    platform: "WooCommerce", 
-    phoneNumber: "+1 (555) 987-6543",
-    status: "Active",
-    type: "Premium",
-    country: "United States",
-    statusColor: "bg-green-500"
-  },
-  {
-    name: "Home & Garden",
-    platform: "Shopify",
-    phoneNumber: "+44 20 7946 0958",
-    status: "Pending KYC",
-    type: "Premium",
-    country: "United Kingdom",
-    statusColor: "bg-yellow-500"
-  },
-  {
-    name: "Sports Equipment",
-    platform: "WooCommerce",
-    phoneNumber: "+1 (555) 456-7890",
-    status: "Active", 
-    type: "Free",
-    country: "United States",
-    statusColor: "bg-green-500"
-  },
-  {
-    name: "Beauty Essentials",
-    platform: "Shopify",
-    phoneNumber: "Not assigned",
-    status: "Setup Required",
-    type: "-",
-    country: "-",
-    statusColor: "bg-red-500"
-  }
-];
+interface PhoneNumber {
+  id: string;
+  phone_number: string;
+  country_code: string;
+  country_name: string;
+  location: string;
+  phone_type: string;
+  price: number;
+  is_available: boolean;
+}
+
+interface Store {
+  id: string;
+  store_name: string;
+  platform_name: string;
+  store_url: string;
+  is_active: boolean;
+  connected_at: string;
+  phone_numbers?: PhoneNumber;
+}
 
 
 const availableCountries = [
 const availableCountries = [
   {
   {
@@ -66,7 +39,7 @@ const availableCountries = [
     availability: "Instant"
     availability: "Instant"
   },
   },
   {
   {
-    name: "United Kingdom", 
+    name: "United Kingdom",
     flag: "🇬🇧",
     flag: "🇬🇧",
     pricing: {
     pricing: {
       setup: "$0",
       setup: "$0",
@@ -129,6 +102,70 @@ const carriers = [
 ];
 ];
 
 
 export function PhoneNumbersContent() {
 export function PhoneNumbersContent() {
+  const [stores, setStores] = useState<Store[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [error, setError] = useState<string>("");
+
+  useEffect(() => {
+    fetchStores();
+  }, []);
+
+  const fetchStores = async () => {
+    try {
+      const sessionData = localStorage.getItem('session_data');
+      if (!sessionData) {
+        setError('Authentication required. Please log in again.');
+        setLoading(false);
+        return;
+      }
+
+      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) {
+        setStores(data.stores || []);
+      } else {
+        setError('Failed to load stores');
+      }
+    } catch (err) {
+      console.error('Error fetching stores:', err);
+      setError('Failed to load stores. Please try again.');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const getPlatformDisplayName = (platform: string) => {
+    const platformMap: { [key: string]: string } = {
+      'shopify': 'Shopify',
+      'woocommerce': 'WooCommerce',
+      'shoprenter': 'ShopRenter'
+    };
+    return platformMap[platform.toLowerCase()] || platform;
+  };
+
+  const getStatusBadge = (store: Store) => {
+    if (!store.is_active) {
+      return { text: "Inactive", color: "bg-red-500" };
+    }
+    if (!store.phone_numbers) {
+      return { text: "No Phone Number", color: "bg-yellow-500" };
+    }
+    return { text: "Active", color: "bg-green-500" };
+  };
+
+  const activeStoresCount = stores.filter(s => s.is_active && s.phone_numbers).length;
+
   return (
   return (
     <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">
@@ -136,7 +173,10 @@ export function PhoneNumbersContent() {
           <h2 className="text-3xl font-bold tracking-tight text-white">Phone Numbers</h2>
           <h2 className="text-3xl font-bold tracking-tight text-white">Phone Numbers</h2>
           <p className="text-slate-400">Manage phone numbers for your webshops</p>
           <p className="text-slate-400">Manage phone numbers for your webshops</p>
         </div>
         </div>
-        <Button className="bg-cyan-500 hover:bg-cyan-600 text-white">
+        <Button
+          className="bg-slate-600 hover:bg-slate-600 text-slate-400 cursor-not-allowed"
+          disabled
+        >
           <Plus className="w-4 h-4 mr-2" />
           <Plus className="w-4 h-4 mr-2" />
           Add Phone Number
           Add Phone Number
         </Button>
         </Button>
@@ -145,72 +185,130 @@ export function PhoneNumbersContent() {
       <div className="space-y-6">
       <div className="space-y-6">
         <div className="flex items-center justify-between">
         <div className="flex items-center justify-between">
           <h3 className="text-xl font-semibold text-white">Webshop Phone Numbers</h3>
           <h3 className="text-xl font-semibold text-white">Webshop Phone Numbers</h3>
-          <p className="text-slate-400">5 webshops • 4 active numbers</p>
+          <p className="text-slate-400">
+            {loading ? "Loading..." : `${stores.length} webshop${stores.length !== 1 ? 's' : ''} • ${activeStoresCount} active number${activeStoresCount !== 1 ? 's' : ''}`}
+          </p>
         </div>
         </div>
 
 
-        <Card className="bg-slate-800 border-slate-700">
-          <CardContent className="p-0">
-            <div className="overflow-x-auto">
-              <table className="w-full">
-                <thead className="border-b border-slate-700">
-                  <tr className="text-left">
-                    <th className="p-4 text-slate-300 font-medium">Webshop</th>
-                    <th className="p-4 text-slate-300 font-medium">Phone Number</th>
-                    <th className="p-4 text-slate-300 font-medium">Country</th>
-                    <th className="p-4 text-slate-300 font-medium">Type</th>
-                    <th className="p-4 text-slate-300 font-medium">Status</th>
-                    <th className="p-4 text-slate-300 font-medium">Actions</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  {connectedShops.map((shop, index) => (
-                    <tr key={index} className="border-b border-slate-700/50 hover:bg-slate-700/30">
-                      <td className="p-4">
-                        <div>
-                          <div className="text-white font-medium">{shop.name}</div>
-                          <div className="text-slate-400 text-sm">{shop.platform}</div>
-                        </div>
-                      </td>
-                      <td className="p-4">
-                        <div className="flex items-center gap-2">
-                          <PhoneCall className="w-4 h-4 text-slate-400" />
-                          <span className={`text-sm ${shop.phoneNumber === "Not assigned" ? "text-slate-500 italic" : "text-white font-mono"}`}>
-                            {shop.phoneNumber}
-                          </span>
-                        </div>
-                      </td>
-                      <td className="p-4 text-slate-300">{shop.country}</td>
-                      <td className="p-4">
-                        <Badge variant={shop.type === "Free" ? "default" : "secondary"} className={shop.type === "Free" ? "bg-green-600 text-white" : "bg-purple-600 text-white"}>
-                          {shop.type}
-                        </Badge>
-                      </td>
-                      <td className="p-4">
-                        <Badge className={`${shop.statusColor} text-white`}>
-                          {shop.status}
-                        </Badge>
-                      </td>
-                      <td className="p-4">
-                        <div className="flex items-center gap-2">
-                          {shop.phoneNumber === "Not assigned" ? (
-                            <Button size="sm" className="bg-cyan-500 hover:bg-cyan-600 text-white">
-                              <Plus className="w-4 h-4 mr-1" />
-                              Assign
-                            </Button>
-                          ) : (
-                            <Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
-                              <Settings className="w-4 h-4" />
-                            </Button>
-                          )}
-                        </div>
-                      </td>
+        {error && (
+          <Alert className="bg-red-500/10 border-red-500/50">
+            <AlertTriangle className="h-4 w-4 text-red-500" />
+            <AlertDescription className="text-red-500">
+              {error}
+            </AlertDescription>
+          </Alert>
+        )}
+
+        {loading ? (
+          <Card className="bg-slate-800 border-slate-700">
+            <CardContent className="p-8">
+              <div className="flex items-center justify-center gap-3">
+                <Loader2 className="w-6 h-6 text-cyan-500 animate-spin" />
+                <span className="text-slate-400">Loading stores and phone numbers...</span>
+              </div>
+            </CardContent>
+          </Card>
+        ) : stores.length === 0 ? (
+          <Card className="bg-slate-800 border-slate-700">
+            <CardContent className="p-8">
+              <div className="text-center">
+                <PhoneCall className="w-12 h-12 text-slate-600 mx-auto mb-4" />
+                <h3 className="text-white text-lg font-semibold mb-2">No webshops connected</h3>
+                <p className="text-slate-400 mb-4">Connect a webshop first to see phone number assignments</p>
+                <Button className="bg-cyan-500 hover:bg-cyan-600 text-white" onClick={() => window.location.href = '/webshops'}>
+                  Go to Webshops
+                </Button>
+              </div>
+            </CardContent>
+          </Card>
+        ) : (
+          <Card className="bg-slate-800 border-slate-700">
+            <CardContent className="p-0">
+              <div className="overflow-x-auto">
+                <table className="w-full">
+                  <thead className="border-b border-slate-700">
+                    <tr className="text-left">
+                      <th className="p-4 text-slate-300 font-medium">Webshop</th>
+                      <th className="p-4 text-slate-300 font-medium">Phone Number</th>
+                      <th className="p-4 text-slate-300 font-medium">Country</th>
+                      <th className="p-4 text-slate-300 font-medium">Type</th>
+                      <th className="p-4 text-slate-300 font-medium">Status</th>
+                      <th className="p-4 text-slate-300 font-medium">Actions</th>
                     </tr>
                     </tr>
-                  ))}
-                </tbody>
-              </table>
-            </div>
-          </CardContent>
-        </Card>
+                  </thead>
+                  <tbody>
+                    {stores.map((store) => {
+                      const statusBadge = getStatusBadge(store);
+                      const phoneNumber = store.phone_numbers;
+
+                      return (
+                        <tr key={store.id} className="border-b border-slate-700/50 hover:bg-slate-700/30">
+                          <td className="p-4">
+                            <div>
+                              <div className="text-white font-medium">{store.store_name || store.store_url}</div>
+                              <div className="text-slate-400 text-sm">{getPlatformDisplayName(store.platform_name)}</div>
+                            </div>
+                          </td>
+                          <td className="p-4">
+                            <div className="flex items-center gap-2">
+                              <PhoneCall className="w-4 h-4 text-slate-400" />
+                              <span className={`text-sm ${phoneNumber ? "text-white font-mono" : "text-slate-500 italic"}`}>
+                                {phoneNumber ? phoneNumber.phone_number : "Not assigned"}
+                              </span>
+                            </div>
+                          </td>
+                          <td className="p-4 text-slate-300">
+                            {phoneNumber ? `${phoneNumber.country_name} (${phoneNumber.location})` : "-"}
+                          </td>
+                          <td className="p-4">
+                            {phoneNumber ? (
+                              <Badge
+                                variant={phoneNumber.price === 0 ? "default" : "secondary"}
+                                className={phoneNumber.price === 0 ? "bg-green-600 text-white" : "bg-purple-600 text-white"}
+                              >
+                                {phoneNumber.price === 0 ? "Free" : "Premium"}
+                              </Badge>
+                            ) : (
+                              <span className="text-slate-500">-</span>
+                            )}
+                          </td>
+                          <td className="p-4">
+                            <Badge className={`${statusBadge.color} text-white`}>
+                              {statusBadge.text}
+                            </Badge>
+                          </td>
+                          <td className="p-4">
+                            <div className="flex items-center gap-2">
+                              {phoneNumber ? (
+                                <Button
+                                  variant="ghost"
+                                  size="sm"
+                                  className="text-slate-400 hover:text-slate-400 cursor-not-allowed"
+                                  disabled
+                                >
+                                  <Settings className="w-4 h-4" />
+                                </Button>
+                              ) : (
+                                <Button
+                                  size="sm"
+                                  className="bg-slate-600 hover:bg-slate-600 text-slate-400 cursor-not-allowed"
+                                  disabled
+                                >
+                                  <Plus className="w-4 h-4 mr-1" />
+                                  Assign
+                                </Button>
+                              )}
+                            </div>
+                          </td>
+                        </tr>
+                      );
+                    })}
+                  </tbody>
+                </table>
+              </div>
+            </CardContent>
+          </Card>
+        )}
 
 
         <div className="space-y-4 opacity-60">
         <div className="space-y-4 opacity-60">
           <div className="flex items-center justify-between">
           <div className="flex items-center justify-between">