feat: initialize Infra Manager with React, Tailwind CSS, and Inertia.js
- Added main application entry point (app.jsx) and bootstrap file for React setup. - Created Wiki index page (Index.jsx) for documentation and file management. - Implemented utility functions for formatting and status handling in utils.js. - Set up Blade template (app.blade.php) for Inertia.js integration. - Defined API routes for metrics and terminal access in api.php. - Established console commands for scheduled tasks in console.php. - Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php. - Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments. - Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js. - Set up Vite for asset bundling and development server in vite.config.js.
This commit is contained in:
120
resources/js/Pages/Acl/Index.jsx
Normal file
120
resources/js/Pages/Acl/Index.jsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import AppLayout from '@/Layouts/AppLayout';
|
||||
import { Head, router } from '@inertiajs/react';
|
||||
import { Users, Shield, Plus, Edit, Trash2, UserCheck } from 'lucide-react';
|
||||
import { GlassCard, StatCard } from '@/Components/ui/GlassCard';
|
||||
import { Button } from '@/Components/ui/Button';
|
||||
import { DataTable } from '@/Components/ui/DataTable';
|
||||
import { StatusBadge } from '@/Components/ui/StatusBadge';
|
||||
import { timeAgo } from '@/lib/utils';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@radix-ui/react-tabs';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function AclIndex({ users, roles }) {
|
||||
const [tab, setTab] = useState('users');
|
||||
|
||||
const userColumns = [
|
||||
{ header: 'User', accessor: 'name', render: row => (
|
||||
<div className="flex items-center gap-3">
|
||||
<img src={row.avatar_url} className="w-8 h-8 rounded-full ring-1 ring-white/20" alt={row.name} />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">{row.name}</p>
|
||||
<p className="text-xs text-white/40">{row.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
)},
|
||||
{ header: 'Auth', accessor: 'auth_source', render: row => (
|
||||
<span className={cn('text-xs px-2 py-0.5 rounded capitalize',
|
||||
row.auth_source === 'ldap' ? 'bg-purple-500/15 text-purple-400' : 'bg-white/5 text-white/50'
|
||||
)}>{row.auth_source}</span>
|
||||
)},
|
||||
{ header: 'Roles', key: 'roles', render: row => (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(row.roles ?? []).map(r => (
|
||||
<span key={r} className="text-xs px-2 py-0.5 rounded bg-cyan-500/15 text-cyan-400">{r}</span>
|
||||
))}
|
||||
</div>
|
||||
)},
|
||||
{ header: 'Status', accessor: 'is_active', render: row => (
|
||||
<StatusBadge status={row.is_active ? 'active' : 'offline'} />
|
||||
)},
|
||||
{ header: 'Last Login', accessor: 'last_login_at', render: row => (
|
||||
<span className="text-xs text-white/40">{timeAgo(row.last_login_at)}</span>
|
||||
)},
|
||||
{ header: '', key: 'actions', render: row => (
|
||||
<div className="flex gap-1" onClick={e => e.stopPropagation()}>
|
||||
<a href={`/acl/users/${row.id}/edit`}><Button size="sm" variant="ghost" icon={Edit} /></a>
|
||||
<Button size="sm" variant="ghost" icon={Trash2}
|
||||
onClick={() => confirm('Delete this user?') && router.delete(`/acl/users/${row.id}`)} />
|
||||
</div>
|
||||
)},
|
||||
];
|
||||
|
||||
const roleColumns = [
|
||||
{ header: 'Role', accessor: 'name', render: row => (
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white capitalize">{row.name}</p>
|
||||
<p className="text-xs text-white/30">{row.permissions_count ?? 0} permissions</p>
|
||||
</div>
|
||||
)},
|
||||
{ header: 'Users', key: 'users', render: row => (
|
||||
<span className="text-sm text-white/60">{row.users_count ?? 0}</span>
|
||||
)},
|
||||
{ header: '', key: 'actions', render: row => (
|
||||
<div className="flex gap-1">
|
||||
<a href={`/acl/roles/${row.id}/edit`}><Button size="sm" variant="ghost" icon={Edit} /></a>
|
||||
</div>
|
||||
)},
|
||||
];
|
||||
|
||||
return (
|
||||
<AppLayout title="ACL">
|
||||
<Head title="Access Control" />
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-white">Access Control</h2>
|
||||
<p className="text-sm text-white/40">Users, roles, and permissions management</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{tab === 'users' && <a href="/acl/users/create"><Button icon={Plus}>Add User</Button></a>}
|
||||
{tab === 'roles' && <a href="/acl/roles/create"><Button icon={Plus}>Add Role</Button></a>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
<StatCard title="Total Users" value={users?.total ?? 0} icon={Users} color="cyan" />
|
||||
<StatCard title="Active" value={users?.data?.filter(u => u.is_active).length ?? 0} icon={UserCheck} color="green" />
|
||||
<StatCard title="LDAP Users" value={users?.data?.filter(u => u.auth_source === 'ldap').length ?? 0} icon={Shield} color="purple" />
|
||||
<StatCard title="Roles" value={roles?.length ?? 0} icon={Shield} color="blue" />
|
||||
</div>
|
||||
|
||||
<Tabs value={tab} onValueChange={setTab}>
|
||||
<TabsList className="flex gap-1 p-1 bg-white/5 rounded-xl w-fit border border-white/10 mb-4">
|
||||
{[{ value: 'users', label: 'Users' }, { value: 'roles', label: 'Roles & Permissions' }].map(t => (
|
||||
<TabsTrigger key={t.value} value={t.value}
|
||||
className={cn('px-5 py-2 rounded-lg text-sm font-medium transition-all',
|
||||
tab === t.value ? 'bg-white/10 text-white' : 'text-white/50 hover:text-white'
|
||||
)}>{t.label}</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="users">
|
||||
<GlassCard className="p-5">
|
||||
<DataTable columns={userColumns} data={users?.data ?? []}
|
||||
onRowClick={row => router.visit(`/acl/users/${row.id}/edit`)}
|
||||
emptyMessage="No users found." />
|
||||
</GlassCard>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="roles">
|
||||
<GlassCard className="p-5">
|
||||
<DataTable columns={roleColumns} data={roles ?? []}
|
||||
emptyMessage="No roles defined." />
|
||||
</GlassCard>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user