@@ -0,0 +1,317 @@
|
||||
import { Form, Head, Link, router, usePage } from '@inertiajs/react';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
type Credential = {
|
||||
id: number;
|
||||
name: string;
|
||||
username: string;
|
||||
connection_type: string;
|
||||
privilege_type: string;
|
||||
is_master: boolean;
|
||||
is_active: boolean;
|
||||
};
|
||||
type Props = {
|
||||
device: {
|
||||
id: number;
|
||||
name: string;
|
||||
hostname?: string;
|
||||
management_address: string;
|
||||
management_port: number;
|
||||
connection_type: string;
|
||||
connection_ports?: Record<string, number>;
|
||||
status: string;
|
||||
location?: string;
|
||||
serial_number?: string;
|
||||
firmware_version?: string;
|
||||
software_version?: string;
|
||||
device_facts?: Record<string, string>;
|
||||
notes?: string;
|
||||
vendor: { name: string; slug: string };
|
||||
activation_status: string;
|
||||
activation_message?: string;
|
||||
type: { name: string };
|
||||
model?: { name: string };
|
||||
creator?: { name: string };
|
||||
};
|
||||
credentials: Credential[];
|
||||
};
|
||||
|
||||
export default function DeviceShow({ device, credentials }: Props) {
|
||||
const { auth, errors } = usePage().props;
|
||||
const rows = [
|
||||
['Vendor', device.vendor.name],
|
||||
['Tipe', device.type.name],
|
||||
['Model', device.model?.name ?? '-'],
|
||||
['Alamat Management', device.management_address],
|
||||
['Koneksi', Object.entries(device.connection_ports ?? { [device.connection_type]: device.management_port }).map(([protocol, port]) => `${protocol}: ${port}`).join(' · ')],
|
||||
['Hostname', device.hostname ?? '-'],
|
||||
['Serial Number', device.serial_number ?? '-'],
|
||||
['Versi RouterOS', device.software_version ?? '-'],
|
||||
['Firmware', device.firmware_version ?? '-'],
|
||||
[
|
||||
'Board / Model Terdeteksi',
|
||||
device.device_facts?.['board-name'] ??
|
||||
device.device_facts?.model ??
|
||||
'-',
|
||||
],
|
||||
['Arsitektur', device.device_facts?.['architecture-name'] ?? '-'],
|
||||
['CPU', device.device_facts?.cpu ?? '-'],
|
||||
['Jumlah CPU', device.device_facts?.['cpu-count'] ?? '-'],
|
||||
['Total Memori', device.device_facts?.['total-memory'] ?? '-'],
|
||||
['Uptime', device.device_facts?.uptime ?? '-'],
|
||||
['Lokasi', device.location ?? '-'],
|
||||
['Status', device.status],
|
||||
['Dibuat Oleh', device.creator?.name ?? '-'],
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title={device.name} />
|
||||
<div className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-6 p-4 md:p-6">
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">
|
||||
{device.name}
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Detail inventaris perangkat
|
||||
</p>
|
||||
</div>
|
||||
{auth.permissions.includes('device.update') && (
|
||||
<Button asChild>
|
||||
<Link href={`/devices/${device.id}/edit`}>
|
||||
Edit Perangkat
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{['mikrotik', 'zte', 'hsgq', 'hisfocus'].includes(device.vendor.slug) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
Aktivasi {device.vendor.name}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col justify-between gap-4 md:flex-row md:items-center">
|
||||
<div className="text-sm">
|
||||
<p>
|
||||
Status:{' '}
|
||||
<strong className="uppercase">
|
||||
{device.activation_status}
|
||||
</strong>
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
{device.activation_message ??
|
||||
'Aktivasi akan menguji koneksi dan membuat Base User management dengan aman.'}
|
||||
</p>
|
||||
{errors.activation && (
|
||||
<p className="mt-2 text-destructive">
|
||||
{errors.activation}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{auth.permissions.includes(
|
||||
'device.connect',
|
||||
) && (
|
||||
<Form
|
||||
action={`/devices/${device.id}/activate`}
|
||||
method="post"
|
||||
>
|
||||
{({ processing }) => (
|
||||
<Button disabled={processing}>
|
||||
{processing
|
||||
? 'Mengaktifkan...'
|
||||
: 'Cek & Aktifkan Perangkat'}
|
||||
</Button>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Informasi</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 md:grid-cols-2">
|
||||
{rows.map(([label, value]) => (
|
||||
<div key={label} className="border-b pb-3">
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{label}
|
||||
</div>
|
||||
<div className="font-medium">{value}</div>
|
||||
</div>
|
||||
))}
|
||||
{device.notes && (
|
||||
<div className="md:col-span-2">
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Catatan
|
||||
</div>
|
||||
<p>{device.notes}</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{auth.permissions.includes('device.credential.view') && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Credential Perangkat</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-5">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left">
|
||||
<th className="py-2">Nama</th>
|
||||
<th>Username</th>
|
||||
<th>Password</th>
|
||||
<th>Protokol</th>
|
||||
<th>Privilege</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{credentials.map((item) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className="border-b"
|
||||
>
|
||||
<td className="py-3">
|
||||
{item.name}
|
||||
{item.is_master && (
|
||||
<span className="ml-2 rounded bg-amber-100 px-2 py-1 text-xs text-amber-800">
|
||||
MASTER
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td>{item.username}</td>
|
||||
<td>••••••••••••</td>
|
||||
<td>{item.connection_type}</td>
|
||||
<td>{item.privilege_type}</td>
|
||||
<td className="text-right">
|
||||
{auth.permissions.includes(
|
||||
'device.credential.delete',
|
||||
) && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
confirm(
|
||||
'Hapus credential ini?',
|
||||
) &&
|
||||
router.delete(
|
||||
`/devices/${device.id}/credentials/${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{credentials.length === 0 && (
|
||||
<p className="py-5 text-center text-muted-foreground">
|
||||
Belum ada credential.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{auth.permissions.includes(
|
||||
'device.credential.create',
|
||||
) && (
|
||||
<Form
|
||||
action={`/devices/${device.id}/credentials`}
|
||||
method="post"
|
||||
resetOnSuccess
|
||||
className="grid gap-4 border-t pt-5 md:grid-cols-3"
|
||||
>
|
||||
<div>
|
||||
<Label>Nama Credential</Label>
|
||||
<Input name="name" required />
|
||||
</div>
|
||||
<div>
|
||||
<Label>Username</Label>
|
||||
<Input name="username" required />
|
||||
</div>
|
||||
<div>
|
||||
<Label>Password</Label>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Enable Password (ZTE/CLI)</Label>
|
||||
<Input
|
||||
name="enable_password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Protokol</Label>
|
||||
<select
|
||||
name="connection_type"
|
||||
defaultValue={
|
||||
device.connection_type
|
||||
}
|
||||
className="h-10 w-full rounded-md border bg-background px-3"
|
||||
>
|
||||
<option value="routeros_api">
|
||||
RouterOS API
|
||||
</option>
|
||||
<option value="ssh">SSH</option>
|
||||
<option value="telnet">
|
||||
Telnet
|
||||
</option>
|
||||
<option value="snmp">SNMP</option>
|
||||
<option value="vendor_api">
|
||||
Vendor API
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Privilege</Label>
|
||||
<select
|
||||
name="privilege_type"
|
||||
defaultValue="custom"
|
||||
className="h-10 w-full rounded-md border bg-background px-3"
|
||||
>
|
||||
<option value="master">
|
||||
Master
|
||||
</option>
|
||||
<option value="noc">NOC</option>
|
||||
<option value="technician">
|
||||
Technician
|
||||
</option>
|
||||
<option value="monitoring">
|
||||
Monitoring
|
||||
</option>
|
||||
<option value="custom">
|
||||
Custom
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-end">
|
||||
<Button>Simpan Credential</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
<Button variant="outline" asChild>
|
||||
<Link href="/devices">Kembali ke Daftar</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user