diff --git a/client/src/components/layouts/mobile-sidebar.tsx b/client/src/components/layouts/mobile-sidebar.tsx index 5b37989..0c4420b 100644 --- a/client/src/components/layouts/mobile-sidebar.tsx +++ b/client/src/components/layouts/mobile-sidebar.tsx @@ -33,6 +33,11 @@ export function MobileSidebar({ isOpen, onClose }: MobileSidebarProps) { href: "/", icon: , }, + { + title: "Organizations", + href: "/organizations", + icon: , + }, { title: "Domains", href: "/domains", @@ -127,9 +132,9 @@ export function MobileSidebar({ isOpen, onClose }: MobileSidebarProps) { {navItems.map((item) => ( - {item.icon} {item.title} - + ))} @@ -149,9 +154,9 @@ export function MobileSidebar({ isOpen, onClose }: MobileSidebarProps) { {filteredAdminItems.map((item) => ( - {item.icon} {item.title} - + ))} diff --git a/client/src/components/shared/back-button.tsx b/client/src/components/shared/back-button.tsx index 3557f97..4c2d415 100644 --- a/client/src/components/shared/back-button.tsx +++ b/client/src/components/shared/back-button.tsx @@ -1,35 +1,23 @@ -import { Button } from "@/components/ui/button"; -import { ChevronLeft } from "lucide-react"; -import { useLocation } from "wouter"; +import { Link } from 'wouter'; +import { Button } from '@/components/ui/button'; +import { ArrowLeft } from 'lucide-react'; interface BackButtonProps { - to?: string; - fallbackPath?: string; - className?: string; + href: string; + label?: string; } -export function BackButton({ to, fallbackPath = "/", className = "" }: BackButtonProps) { - const [_, setLocation] = useLocation(); - - const handleBack = () => { - if (to) { - setLocation(to); - } else if (window.history.length > 2) { - window.history.back(); - } else { - setLocation(fallbackPath); - } - }; - +export function BackButton({ href, label = 'Back' }: BackButtonProps) { return ( - - - Back - + + + + {label} + + ); } \ No newline at end of file diff --git a/client/src/components/shared/public-ip-card.tsx b/client/src/components/shared/public-ip-card.tsx new file mode 100644 index 0000000..77018f0 --- /dev/null +++ b/client/src/components/shared/public-ip-card.tsx @@ -0,0 +1,122 @@ +import { useState, useEffect } from 'react'; +import { Card, CardContent, CardTitle } from '@/components/ui/card'; +import { Separator } from '@/components/ui/separator'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { Network, Info, Copy } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { toast } from '@/hooks/use-toast'; + +export function PublicIpCard() { + const [ipv4, setIpv4] = useState(null); + const [ipv6, setIpv6] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + const fetchPublicIp = async () => { + try { + setLoading(true); + + // Fetch IPv4 + const ipv4Response = await fetch('https://api.ipify.org?format=json') + .then(res => res.json()) + .catch(() => ({ ip: null })); + + // Fetch IPv6 (might not be available for all users) + const ipv6Response = await fetch('https://api64.ipify.org?format=json') + .then(res => res.json()) + .catch(() => ({ ip: null })); + + setIpv4(ipv4Response.ip); + setIpv6(ipv6Response.ip); + setError(false); + } catch (err) { + console.error('Error fetching public IP:', err); + setError(true); + } finally { + setLoading(false); + } + }; + + fetchPublicIp(); + + // Refresh every 5 minutes + const interval = setInterval(fetchPublicIp, 300000); + return () => clearInterval(interval); + }, []); + + const copyToClipboard = (text: string | null) => { + if (text) { + navigator.clipboard.writeText(text); + toast({ + title: 'Copied to clipboard', + description: text, + }); + } + }; + + return ( + + + + + Public IP Addresses + + + + + + + + + Your current public IP addresses as seen from the internet. + + + + + + + + + IPv4 Address + {ipv4 && ( + copyToClipboard(ipv4)} + > + + + )} + + + {loading ? 'Loading...' : error ? 'Error fetching IP' : ipv4 || 'N/A'} + + + + + + + + IPv6 Address + {ipv6 && ( + copyToClipboard(ipv6)} + > + + + )} + + + {loading ? 'Loading...' : error ? 'Error fetching IP' : ipv6 || 'N/A'} + + + + + + ); +} \ No newline at end of file diff --git a/client/src/pages/dashboard.tsx b/client/src/pages/dashboard.tsx index 3e68cf6..04f345a 100644 --- a/client/src/pages/dashboard.tsx +++ b/client/src/pages/dashboard.tsx @@ -4,6 +4,7 @@ import { MainLayout } from "@/components/layouts/main-layout"; import { DomainStatusCard } from "@/components/domain/domain-status-card"; import { DomainTable } from "@/components/domain/domain-table"; import { RecentActivity } from "@/components/activity/recent-activity"; +import { PublicIpCard } from "@/components/shared/public-ip-card"; import { DnsUpdateChart } from "@/components/charts/dns-update-chart"; import { ProviderDistributionChart } from "@/components/charts/provider-distribution-chart"; import { RecordTypeChart } from "@/components/charts/record-type-chart"; @@ -77,6 +78,11 @@ export default function DashboardPage() { /> + {/* Public IP Information */} + + + + {/* Recent Activity and DNS Update Chart */}
Your current public IP addresses as seen from the internet.