feat: inisialisasi project kit v2
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
import React from 'react';
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, Link } from '@inertiajs/react';
|
||||
import { PageProps, User } from '@/types';
|
||||
|
||||
interface UserShowProps extends PageProps {
|
||||
viewUser: User & {
|
||||
roles: any[];
|
||||
permissions: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
}
|
||||
|
||||
function InfoRow({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row sm:items-start gap-1 sm:gap-0 py-3.5 border-b border-gray-50 last:border-0">
|
||||
<dt className="text-xs font-semibold text-gray-500 sm:w-44 shrink-0 mt-0.5">{label}</dt>
|
||||
<dd className="text-sm font-semibold text-[#3D4E4B] tracking-tight">{children}</dd>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UserShow({ viewUser }: UserShowProps) {
|
||||
const initials = `${viewUser.first_name?.charAt(0) || ''}${viewUser.last_name?.charAt(0) || ''}`.toUpperCase();
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout>
|
||||
<Head title={`${viewUser.first_name} ${viewUser.last_name} — Users`} />
|
||||
|
||||
<div className="flex items-center gap-2 text-sm mb-6 anim-fade">
|
||||
<Link href="/users" className="text-gray-400 hover:text-[#3D4E4B] transition-colors font-semibold">Users</Link>
|
||||
<svg className="w-3.5 h-3.5 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
<span className="text-[#3D4E4B] font-bold">{viewUser.first_name} {viewUser.last_name}</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Left: profile card */}
|
||||
<div className="lg:col-span-1 space-y-4 anim-up">
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="w-20 h-20 rounded-2xl bg-[#3D4E4B] flex items-center justify-center text-white text-2xl font-bold overflow-hidden mb-4">
|
||||
{viewUser.avatar_url
|
||||
? <img src={viewUser.avatar_url} className="w-full h-full object-cover" alt="" />
|
||||
: initials}
|
||||
</div>
|
||||
<h2 className="text-base font-bold text-[#3D4E4B] tracking-tight">{viewUser.first_name} {viewUser.last_name}</h2>
|
||||
<p className="text-xs text-gray-400 font-medium mt-0.5">{viewUser.email}</p>
|
||||
<div className="flex flex-wrap justify-center gap-1.5 mt-3">
|
||||
{viewUser.roles?.map((role: any) => (
|
||||
<span key={role.name || role} className="px-2.5 py-1 text-xs font-bold bg-white text-gray-500 border border-gray-100 rounded-md">
|
||||
{role.name || role}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<span className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold border ${
|
||||
viewUser.status === 'active'
|
||||
? 'text-green-600 bg-white border-green-100'
|
||||
: 'text-gray-400 bg-white border-gray-100'
|
||||
}`}>
|
||||
<span className={`w-1 h-1 rounded-full ${viewUser.status === 'active' ? 'bg-green-500' : 'bg-gray-300'}`} />
|
||||
{viewUser.status === 'active' ? 'Active' : 'Inactive'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 space-y-1">
|
||||
<Link href="/users"
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-semibold text-gray-500 hover:bg-gray-50 hover:text-[#3D4E4B] transition-colors w-full">
|
||||
<svg className="w-4 h-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
Back to Users
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: details */}
|
||||
<div className="lg:col-span-2 space-y-4 anim-up" style={{ animationDelay: '0.08s' }}>
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-50 bg-gray-50/30">
|
||||
<h3 className="text-sm font-bold text-[#3D4E4B] tracking-tight">Personal Information</h3>
|
||||
</div>
|
||||
<div className="px-6">
|
||||
<dl>
|
||||
<InfoRow label="First name">{viewUser.first_name}</InfoRow>
|
||||
<InfoRow label="Last name">{viewUser.last_name}</InfoRow>
|
||||
<InfoRow label="Email">{viewUser.email}</InfoRow>
|
||||
<InfoRow label="User ID"><span className="font-mono text-gray-400 text-xs">#{viewUser.id}</span></InfoRow>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-50 bg-gray-50/30">
|
||||
<h3 className="text-sm font-bold text-[#3D4E4B] tracking-tight">Roles & Permissions</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-5">
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-gray-400 tracking-tight mb-2">Assigned Roles</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{viewUser.roles?.length ? viewUser.roles.map((role: any) => (
|
||||
<span key={role.name || role} className="px-3 py-1.5 text-xs font-bold bg-[#3D4E4B] text-white rounded-lg">
|
||||
{role.name || role}
|
||||
</span>
|
||||
)) : <span className="text-sm text-gray-400 font-semibold">No roles assigned</span>}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-gray-400 tracking-tight mb-2">Granted Permissions</p>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-1.5">
|
||||
{viewUser.permissions?.length ? viewUser.permissions.map((p: any) => (
|
||||
<div key={p.name || p} className="flex items-center gap-2 text-xs text-gray-600 font-semibold bg-gray-50 border border-gray-100 px-2.5 py-1.5 rounded-lg">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-[#21A59F] shrink-0" />
|
||||
{p.name || p}
|
||||
</div>
|
||||
)) : <span className="text-sm text-gray-400 font-semibold col-span-full">Inherited from roles</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-50 bg-gray-50/30">
|
||||
<h3 className="text-sm font-bold text-[#3D4E4B] tracking-tight">Activity Timeline</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-xl bg-[#E3EBE8] flex items-center justify-center shrink-0">
|
||||
<svg className="w-4 h-4 text-[#3D4E4B]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" /></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-[#3D4E4B] tracking-tight">Account Created</p>
|
||||
<p className="text-xs text-gray-400 font-medium mt-0.5">
|
||||
{new Date(viewUser.created_at).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-xl bg-[#E3EBE8] flex items-center justify-center shrink-0">
|
||||
<svg className="w-4 h-4 text-[#3D4E4B]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-[#3D4E4B] tracking-tight">Last Updated</p>
|
||||
<p className="text-xs text-gray-400 font-medium mt-0.5">
|
||||
{new Date(viewUser.updated_at).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user