wulf-pulse/app/configuration-items/[id]/page.tsx

196 lines
6.5 KiB
TypeScript
Raw Normal View History

'use client';
import { useState, useEffect } from 'react';
import { useParams, useRouter, useSearchParams } from 'next/navigation';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { ThemeToggle } from '@/components/theme-toggle';
import { PSATab } from '@/components/configuration-items/psa-tab';
import { RMMTab } from '@/components/configuration-items/rmm-tab';
import { StatusCards } from '@/components/configuration-items/status-cards';
import {
Server,
Monitor,
ArrowLeft,
AlertCircle,
RefreshCw
} from 'lucide-react';
import { ConfigurationItem } from '@/lib/types/autotask';
import { DattoRMMDevice } from '@/lib/types/datto-rmm';
interface ConfigItemDetail {
autotaskDevice?: ConfigurationItem;
rmmDevice?: DattoRMMDevice;
companyName?: string;
}
export default function ConfigurationItemDetailPage() {
const params = useParams();
const router = useRouter();
const searchParams = useSearchParams();
const [data, setData] = useState<ConfigItemDetail>({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Get company context from URL params
const companyId = searchParams.get('companyId');
const companyName = searchParams.get('companyName');
// Parse the ID parameter - it might be "at-123" or "rmm-abc" or "123"
const parseItemId = () => {
const id = params.id as string;
if (id.startsWith('at-')) {
return { type: 'autotask', id: id.substring(3) };
} else if (id.startsWith('rmm-')) {
return { type: 'rmm', id: id.substring(4) };
} else {
return { type: 'autotask', id }; // Default to Autotask
}
};
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const { type, id } = parseItemId();
// Fetch the configuration item details
const response = await fetch(`/api/configuration-items/${id}?type=${type}`);
if (!response.ok) {
throw new Error('Failed to fetch configuration item');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
fetchData();
}, [params.id]);
const handleUpdate = (updatedDevice: ConfigurationItem) => {
setData({ ...data, autotaskDevice: updatedDevice });
};
if (loading) {
return (
<div className="min-h-screen bg-background">
<div className="container mx-auto px-4 py-8">
<div className="space-y-4">
<Skeleton className="h-12 w-64" />
<Skeleton className="h-96 w-full" />
</div>
</div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen bg-background">
<div className="container mx-auto px-4 py-8">
<Card className="border-red-500">
<CardContent className="pt-6">
<div className="flex items-center gap-2 text-red-500">
<AlertCircle className="w-5 h-5" />
<p>Error: {error}</p>
</div>
</CardContent>
</Card>
</div>
</div>
);
}
const device = data.autotaskDevice;
const rmmDevice = data.rmmDevice;
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-16 items-center">
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center space-x-4">
<Button
variant="ghost"
size="sm"
onClick={() => {
// Navigate back to configuration items with company context
if (companyId) {
const params = new URLSearchParams({
companyId: companyId,
companyName: companyName || ''
});
router.push(`/configuration-items?${params.toString()}`);
} else {
router.push('/configuration-items');
}
}}
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to List
</Button>
<div className="flex items-center space-x-3">
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-purple-600 to-purple-700 text-white shadow-lg">
<Server className="h-5 w-5" />
</div>
<div>
<h1 className="text-xl font-semibold tracking-tight">
{device?.referenceTitle || rmmDevice?.hostname || 'Configuration Item'}
</h1>
<p className="text-xs text-muted-foreground">
{data.companyName || 'Configuration Item Details'}
</p>
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<Button variant="ghost" size="icon" onClick={() => window.location.reload()}>
<RefreshCw className="h-4 w-4" />
</Button>
<ThemeToggle />
</div>
</div>
</div>
</header>
{/* Main Content */}
<main className="container mx-auto px-4 py-8">
{/* Status Cards */}
<StatusCards device={device} rmmDevice={rmmDevice} />
{/* Tabs */}
<Tabs defaultValue="psa" className="space-y-4 mt-6">
<TabsList className="grid w-full grid-cols-2 max-w-md">
<TabsTrigger value="psa" className="flex items-center gap-2">
<Server className="w-4 h-4" />
PSA Data
</TabsTrigger>
<TabsTrigger value="rmm" className="flex items-center gap-2">
<Monitor className="w-4 h-4" />
RMM Data
</TabsTrigger>
</TabsList>
<TabsContent value="psa">
<PSATab device={device} onUpdate={handleUpdate} />
</TabsContent>
<TabsContent value="rmm">
<RMMTab device={rmmDevice} />
</TabsContent>
</Tabs>
</main>
</div>
);
}