leLab / src /components /landing /NgrokConfigModal.tsx
jurmy24's picture
mega big updates pre-demo
9a9d18a
raw
history blame
7.29 kB
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import { Globe, Wifi, WifiOff, ExternalLink } from "lucide-react";
import { useApi } from "@/contexts/ApiContext";
import { useToast } from "@/hooks/use-toast";
interface NgrokConfigModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
const NgrokConfigModal: React.FC<NgrokConfigModalProps> = ({
open,
onOpenChange,
}) => {
const {
ngrokUrl,
isNgrokEnabled,
setNgrokUrl,
resetToLocalhost,
fetchWithHeaders,
} = useApi();
const [inputUrl, setInputUrl] = useState(ngrokUrl);
const [isTestingConnection, setIsTestingConnection] = useState(false);
const { toast } = useToast();
const handleSave = async () => {
if (!inputUrl.trim()) {
resetToLocalhost();
toast({
title: "Ngrok Disabled",
description: "Switched back to localhost mode.",
});
onOpenChange(false);
return;
}
setIsTestingConnection(true);
try {
// Clean the URL
let cleanUrl = inputUrl.trim();
if (!cleanUrl.startsWith("http")) {
cleanUrl = `https://${cleanUrl}`;
}
cleanUrl = cleanUrl.replace(/\/$/, "");
// Test the connection
const testResponse = await fetchWithHeaders(`${cleanUrl}/health`, {
method: "GET",
headers: {
Accept: "application/json",
},
});
if (testResponse.ok) {
setNgrokUrl(cleanUrl);
toast({
title: "Ngrok Configured Successfully",
description: `Connected to ${cleanUrl}. All API calls will now use this URL.`,
});
onOpenChange(false);
} else {
throw new Error(`Server responded with status ${testResponse.status}`);
}
} catch (error) {
console.error("Failed to connect to ngrok URL:", error);
toast({
title: "Connection Failed",
description: `Could not connect to ${inputUrl}. Please check the URL and ensure your ngrok tunnel is running.`,
variant: "destructive",
});
} finally {
setIsTestingConnection(false);
}
};
const handleReset = () => {
resetToLocalhost();
setInputUrl("");
toast({
title: "Reset to Localhost",
description: "All API calls will now use localhost:8000.",
});
onOpenChange(false);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="bg-gray-900 border-gray-800 text-white w-[95vw] max-w-[600px] max-h-[90vh] overflow-y-auto p-4 sm:p-6 md:p-8">
<DialogHeader>
<div className="flex justify-center items-center mb-4">
<Globe className="w-8 h-8 text-blue-500" />
</div>
<DialogTitle className="text-white text-center text-xl sm:text-2xl font-bold">
Ngrok Configuration
</DialogTitle>
<DialogDescription className="text-gray-400 text-center text-sm sm:text-base">
Configure ngrok tunnel for external access and phone camera
features.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 sm:space-y-6 py-4">
{/* Current Status */}
<div className="bg-gray-800 rounded-lg p-3 sm:p-4 border border-gray-700">
<div className="flex items-center gap-3 mb-2">
{isNgrokEnabled ? (
<Wifi className="w-4 h-4 sm:w-5 sm:h-5 text-green-500" />
) : (
<WifiOff className="w-4 h-4 sm:w-5 sm:h-5 text-gray-500" />
)}
<span className="font-semibold text-sm sm:text-base">
Current Mode: {isNgrokEnabled ? "Ngrok" : "Localhost"}
</span>
</div>
<p className="text-xs sm:text-sm text-gray-400 break-all">
{isNgrokEnabled
? `Using: ${ngrokUrl}`
: "Using: http://localhost:8000"}
</p>
</div>
{/* URL Input */}
<div className="space-y-2">
<Label
htmlFor="ngrokUrl"
className="text-sm font-medium text-gray-300"
>
Ngrok URL
</Label>
<Input
id="ngrokUrl"
value={inputUrl}
onChange={(e) => setInputUrl(e.target.value)}
placeholder="https://abc123.ngrok.io"
className="bg-gray-800 border-gray-700 text-white text-sm sm:text-base"
/>
<p className="text-xs text-gray-500">
Enter your ngrok tunnel URL. Leave empty to use localhost.
</p>
</div>
{/* Benefits */}
<div className="bg-green-900/20 border border-green-800 rounded-lg p-3 sm:p-4">
<h3 className="font-semibold text-green-400 mb-2 text-sm sm:text-base">
Why use Ngrok?
</h3>
<ul className="text-xs sm:text-sm text-gray-300 space-y-1">
<li>• Access your robot from anywhere on the internet</li>
<li>• Use phone cameras as secondary recording angles</li>
<li>• Share your robot session with remote collaborators</li>
<li>• Test your setup from different devices</li>
</ul>
</div>
{/* Action Buttons */}
<div className="flex flex-col gap-3 sm:gap-4 pt-4">
<Button
onClick={handleSave}
disabled={isTestingConnection}
className="w-full bg-blue-500 hover:bg-blue-600 text-white px-6 sm:px-8 py-3 text-sm sm:text-lg transition-all shadow-md shadow-blue-500/30 hover:shadow-lg hover:shadow-blue-500/40"
>
{isTestingConnection ? (
<>
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" />
Testing Connection...
</>
) : (
<>
<ExternalLink className="w-4 h-4 sm:w-5 sm:h-5 mr-2" />
Save Configuration
</>
)}
</Button>
<div className="flex flex-col sm:flex-row gap-3">
{isNgrokEnabled && (
<Button
onClick={handleReset}
variant="outline"
className="w-full border-orange-500 hover:border-orange-400 text-orange-400 hover:text-orange-300 px-6 sm:px-8 py-3 text-sm sm:text-lg"
>
Reset to Localhost
</Button>
)}
<Button
onClick={() => onOpenChange(false)}
variant="outline"
className="w-full border-gray-500 hover:border-gray-200 px-6 sm:px-8 py-3 text-sm sm:text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
>
Cancel
</Button>
</div>
</div>
</div>
</DialogContent>
</Dialog>
);
};
export default NgrokConfigModal;