|
@@ -1,32 +1,35 @@
|
|
|
|
|
|
|
|
-import { useState } from "react";
|
|
|
|
|
|
|
+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 { Input } from "@/components/ui/input";
|
|
import { Input } from "@/components/ui/input";
|
|
|
import { Label } from "@/components/ui/label";
|
|
import { Label } from "@/components/ui/label";
|
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
-import {
|
|
|
|
|
- Store,
|
|
|
|
|
- PhoneCall,
|
|
|
|
|
- CreditCard,
|
|
|
|
|
- Check,
|
|
|
|
|
- ArrowRight,
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ Store,
|
|
|
|
|
+ PhoneCall,
|
|
|
|
|
+ CreditCard,
|
|
|
|
|
+ Check,
|
|
|
|
|
+ ArrowRight,
|
|
|
ArrowLeft,
|
|
ArrowLeft,
|
|
|
Zap,
|
|
Zap,
|
|
|
Crown,
|
|
Crown,
|
|
|
Star,
|
|
Star,
|
|
|
Shield,
|
|
Shield,
|
|
|
- Globe
|
|
|
|
|
|
|
+ Globe,
|
|
|
|
|
+ Loader2
|
|
|
} from "lucide-react";
|
|
} from "lucide-react";
|
|
|
|
|
|
|
|
-const phoneNumbers = [
|
|
|
|
|
- { number: "+1 (555) 123-4567", location: "New York, US", type: "Local" },
|
|
|
|
|
- { number: "+1 (555) 987-6543", location: "Los Angeles, US", type: "Local" },
|
|
|
|
|
- { number: "+1 (800) 555-0123", location: "Toll-free US", type: "Toll-free" },
|
|
|
|
|
- { number: "+44 20 7946 0958", location: "London, UK", type: "Local" },
|
|
|
|
|
- { number: "+1 (416) 555-7890", location: "Toronto, CA", type: "Local" },
|
|
|
|
|
-];
|
|
|
|
|
|
|
+interface PhoneNumber {
|
|
|
|
|
+ id: string;
|
|
|
|
|
+ phone_number: string;
|
|
|
|
|
+ country_code: string;
|
|
|
|
|
+ country_name: string;
|
|
|
|
|
+ location: string | null;
|
|
|
|
|
+ phone_type: string;
|
|
|
|
|
+ is_available: boolean;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const packages = [
|
|
const packages = [
|
|
|
{
|
|
{
|
|
@@ -84,6 +87,56 @@ export function OnboardingContent() {
|
|
|
const [shopifyUrl, setShopifyUrl] = useState("");
|
|
const [shopifyUrl, setShopifyUrl] = useState("");
|
|
|
const [selectedPhone, setSelectedPhone] = useState("");
|
|
const [selectedPhone, setSelectedPhone] = useState("");
|
|
|
const [selectedPackage, setSelectedPackage] = useState("free-trial");
|
|
const [selectedPackage, setSelectedPackage] = useState("free-trial");
|
|
|
|
|
+ const [phoneNumbers, setPhoneNumbers] = useState<PhoneNumber[]>([]);
|
|
|
|
|
+ const [loadingPhoneNumbers, setLoadingPhoneNumbers] = useState(false);
|
|
|
|
|
+ const [phoneNumbersError, setPhoneNumbersError] = useState<string | null>(null);
|
|
|
|
|
+
|
|
|
|
|
+ // Fetch phone numbers when component mounts or step changes to 2
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (currentStep === 2 && phoneNumbers.length === 0) {
|
|
|
|
|
+ fetchPhoneNumbers();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [currentStep]);
|
|
|
|
|
+
|
|
|
|
|
+ const fetchPhoneNumbers = async () => {
|
|
|
|
|
+ setLoadingPhoneNumbers(true);
|
|
|
|
|
+ setPhoneNumbersError(null);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const sessionData = localStorage.getItem('session_data');
|
|
|
|
|
+ if (!sessionData) {
|
|
|
|
|
+ setPhoneNumbersError('Not authenticated');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const session = JSON.parse(sessionData);
|
|
|
|
|
+ const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:54321/functions/v1';
|
|
|
|
|
+
|
|
|
|
|
+ const response = await fetch(`${apiUrl}/api/phone-numbers`, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${session.access_token}`,
|
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) {
|
|
|
|
|
+ throw new Error('Failed to fetch phone numbers');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+
|
|
|
|
|
+ if (data.success && data.phone_numbers) {
|
|
|
|
|
+ setPhoneNumbers(data.phone_numbers);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setPhoneNumbersError('No phone numbers available');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('Error fetching phone numbers:', error);
|
|
|
|
|
+ setPhoneNumbersError('Failed to load phone numbers');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setLoadingPhoneNumbers(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
const nextStep = () => {
|
|
const nextStep = () => {
|
|
|
if (currentStep < 3) {
|
|
if (currentStep < 3) {
|
|
@@ -198,45 +251,68 @@ export function OnboardingContent() {
|
|
|
{/* Step 2: Phone Number Selection */}
|
|
{/* Step 2: Phone Number Selection */}
|
|
|
{currentStep === 2 && (
|
|
{currentStep === 2 && (
|
|
|
<div className="space-y-6">
|
|
<div className="space-y-6">
|
|
|
- <RadioGroup value={selectedPhone} onValueChange={setSelectedPhone}>
|
|
|
|
|
- <div className="grid gap-4">
|
|
|
|
|
- {phoneNumbers.map((phone) => (
|
|
|
|
|
- <div key={phone.number} className="flex items-center space-x-3">
|
|
|
|
|
- <RadioGroupItem value={phone.number} id={phone.number} />
|
|
|
|
|
- <Label
|
|
|
|
|
- htmlFor={phone.number}
|
|
|
|
|
- className="flex-1 cursor-pointer"
|
|
|
|
|
- >
|
|
|
|
|
- <Card className="bg-slate-700 border-slate-600 hover:border-cyan-500 transition-colors">
|
|
|
|
|
- <CardContent className="p-4">
|
|
|
|
|
- <div className="flex items-center justify-between">
|
|
|
|
|
- <div>
|
|
|
|
|
- <div className="text-white font-mono text-lg">{phone.number}</div>
|
|
|
|
|
- <div className="text-slate-400 text-sm flex items-center gap-2">
|
|
|
|
|
- <Globe className="w-4 h-4" />
|
|
|
|
|
- {phone.location}
|
|
|
|
|
|
|
+ {loadingPhoneNumbers ? (
|
|
|
|
|
+ <div className="flex items-center justify-center py-8">
|
|
|
|
|
+ <Loader2 className="w-8 h-8 text-cyan-500 animate-spin" />
|
|
|
|
|
+ <span className="ml-3 text-slate-400">Loading phone numbers...</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) : phoneNumbersError ? (
|
|
|
|
|
+ <div className="bg-red-500/10 border border-red-500 rounded-lg p-4">
|
|
|
|
|
+ <p className="text-red-500">{phoneNumbersError}</p>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ onClick={fetchPhoneNumbers}
|
|
|
|
|
+ className="mt-4 bg-cyan-500 hover:bg-cyan-600"
|
|
|
|
|
+ >
|
|
|
|
|
+ Retry
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) : phoneNumbers.length === 0 ? (
|
|
|
|
|
+ <div className="bg-yellow-500/10 border border-yellow-500 rounded-lg p-4">
|
|
|
|
|
+ <p className="text-yellow-500">No phone numbers available for your country yet. Please contact support.</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <RadioGroup value={selectedPhone} onValueChange={setSelectedPhone}>
|
|
|
|
|
+ <div className="grid gap-4">
|
|
|
|
|
+ {phoneNumbers.map((phone) => (
|
|
|
|
|
+ <div key={phone.id} className="flex items-center space-x-3">
|
|
|
|
|
+ <RadioGroupItem value={phone.phone_number} id={phone.id} />
|
|
|
|
|
+ <Label
|
|
|
|
|
+ htmlFor={phone.id}
|
|
|
|
|
+ className="flex-1 cursor-pointer"
|
|
|
|
|
+ >
|
|
|
|
|
+ <Card className="bg-slate-700 border-slate-600 hover:border-cyan-500 transition-colors">
|
|
|
|
|
+ <CardContent className="p-4">
|
|
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div className="text-white font-mono text-lg">{phone.phone_number}</div>
|
|
|
|
|
+ <div className="text-slate-400 text-sm flex items-center gap-2">
|
|
|
|
|
+ <Globe className="w-4 h-4" />
|
|
|
|
|
+ {phone.location || phone.country_name}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Badge
|
|
|
|
|
+ className={phone.phone_type === "toll-free" ? "bg-purple-500" : "bg-green-500"}
|
|
|
|
|
+ >
|
|
|
|
|
+ {phone.phone_type}
|
|
|
|
|
+ </Badge>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- <Badge
|
|
|
|
|
- className={phone.type === "Toll-free" ? "bg-purple-500" : "bg-green-500"}
|
|
|
|
|
- >
|
|
|
|
|
- {phone.type}
|
|
|
|
|
- </Badge>
|
|
|
|
|
- </div>
|
|
|
|
|
- </CardContent>
|
|
|
|
|
- </Card>
|
|
|
|
|
- </Label>
|
|
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
</div>
|
|
</div>
|
|
|
- ))}
|
|
|
|
|
- </div>
|
|
|
|
|
- </RadioGroup>
|
|
|
|
|
|
|
+ </RadioGroup>
|
|
|
|
|
|
|
|
- <div className="bg-slate-700/50 p-4 rounded-lg">
|
|
|
|
|
- <p className="text-slate-300 text-sm">
|
|
|
|
|
- <strong>Note:</strong> Your selected phone number will be instantly activated and ready to receive calls.
|
|
|
|
|
- You can always add more numbers later from your dashboard.
|
|
|
|
|
- </p>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div className="bg-slate-700/50 p-4 rounded-lg">
|
|
|
|
|
+ <p className="text-slate-300 text-sm">
|
|
|
|
|
+ <strong>Note:</strong> Your selected phone number will be instantly activated and ready to receive calls.
|
|
|
|
|
+ You can always add more numbers later from your dashboard.
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|