Initial commit

This commit is contained in:
2025-12-22 12:03:01 +07:00
commit 10dc345147
367 changed files with 31188 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
@extends('layouts.app')
@section('content')
<div class="sm:flex sm:items-center sm:justify-between mb-6">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Root CA Management</h1>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Manage your Root and Intermediate Certificates.</p>
</div>
</div>
<div class="bg-white dark:bg-gray-800 shadow-theme-xs rounded-xl border border-gray-100 dark:border-gray-700 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">Type</th>
<th scope="col" class="px-6 py-3">Common Name</th>
<th scope="col" class="px-6 py-3">Serial Number</th>
<th scope="col" class="px-6 py-3">Valid From</th>
<th scope="col" class="px-6 py-3">Valid To</th>
<th scope="col" class="px-6 py-3">Status</th>
<th scope="col" class="px-6 py-3">Actions</th>
</tr>
</thead>
<tbody>
@forelse($certificates as $cert)
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700">
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{{ ucfirst(str_replace('_', ' ', $cert->ca_type)) }}
</td>
<td class="px-6 py-4">
{{ $cert->common_name }}
</td>
<td class="px-6 py-4 font-mono text-xs">
{{ $cert->serial_number }}
</td>
<td class="px-6 py-4">
{{ \Carbon\Carbon::parse($cert->valid_from)->format('Y-m-d H:i') }}
</td>
<td class="px-6 py-4">
{{ \Carbon\Carbon::parse($cert->valid_to)->format('Y-m-d H:i') }}
</td>
<td class="px-6 py-4">
@if($cert->status === 'valid')
<span class="bg-green-100 text-green-800 text-xs font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-green-900 dark:text-green-300">Valid</span>
@else
<span class="bg-red-100 text-red-800 text-xs font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300">Expired</span>
@endif
</td>
<td class="px-6 py-4">
<form action="{{ route('admin.root-ca.renew', $cert->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to renew this certificate?');">
@csrf
<input type="hidden" name="days" value="3650">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-xs px-3 py-1.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
Renew (10 Years)
</button>
</form>
</td>
</tr>
@empty
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td colspan="7" class="px-6 py-4 text-center">
No Root CA certificates found.
<span class="text-gray-400">(Run Setup first)</span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection

View File

@@ -0,0 +1,213 @@
@extends('layouts.app')
@section('content')
<div class="mx-auto max-w-(--breakpoint-2xl) p-4 md:p-6" x-data="{
showEditEmailModal: false,
selectedUserId: null,
selectedUserEmail: '',
editEmailUrl: '',
openEditEmailModal(userId, userEmail) {
this.selectedUserId = userId;
this.selectedUserEmail = userEmail;
this.editEmailUrl = '{{ route('admin.users.update-email', ':id') }}'.replace(':id', userId);
this.showEditEmailModal = true;
$nextTick(() => $refs.emailInput.focus());
}
}">
<!-- Breadcrumb -->
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<h2 class="text-title-md2 font-semibold text-black dark:text-white">
User Management
</h2>
<nav>
<ol class="flex items-center gap-2">
<li>
<a class="font-medium text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500"
href="{{ route('dashboard') }}">
Dashboard /
</a>
</li>
<li class="font-medium text-brand-500">Users</li>
</ol>
</nav>
</div>
<!-- Table Section -->
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
<div class="max-w-full overflow-x-auto custom-scrollbar">
<table class="w-full min-w-[1102px]">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr class="border-b border-gray-100 dark:border-gray-800">
<th class="px-5 py-3 text-left sm:px-6">
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
User
</p>
</th>
<th class="px-5 py-3 text-left sm:px-6">
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
Role
</p>
</th>
<th class="px-5 py-3 text-left sm:px-6">
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
Joined Date
</p>
</th>
<th class="px-5 py-3 text-left sm:px-6">
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
Status
</p>
</th>
<th class="px-5 py-3 text-left sm:px-6">
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
Actions
</p>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
@foreach ($users as $user)
<tr class="hover:bg-gray-50 dark:hover:bg-white/[0.02]">
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center gap-3">
<div class="w-10 h-10 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center text-gray-500">
@if($user->avatar)
<img src="{{ asset('storage/' . $user->avatar) }}" alt="{{ $user->name }}" class="w-full h-full object-cover">
@else
<span class="text-xs font-bold">{{ substr($user->first_name ?? $user->name, 0, 2) }}</span>
@endif
</div>
<div>
<span class="block font-medium text-gray-800 text-theme-sm dark:text-white/90">
{{ $user->name ?? $user->first_name . ' ' . $user->last_name }}
</span>
<span class="block text-gray-500 text-theme-xs dark:text-gray-400">
{{ $user->email }}
</span>
</div>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ $user->isAdmin() ? 'bg-brand-50 text-brand-700 dark:bg-brand-500/15 dark:text-brand-500' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300' }}">
{{ $user->role ? ucfirst($user->role->name) : 'User' }}
</span>
</td>
<td class="px-5 py-4 sm:px-6">
<p class="text-gray-500 text-theme-sm dark:text-gray-400">
{{ $user->created_at->format('M d, Y') }}
<span class="block text-theme-xs text-gray-400">{{ $user->created_at->diffForHumans() }}</span>
</p>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex flex-col gap-2">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium w-fit {{ $user->status === 'active' ? 'bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500' : 'bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500' }}">
{{ ucfirst($user->status) }}
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium w-fit {{ $user->hasVerifiedEmail() ? 'bg-blue-50 text-blue-700 dark:bg-blue-500/15 dark:text-blue-500' : 'bg-yellow-50 text-yellow-700 dark:bg-yellow-500/15 dark:text-yellow-500' }}">
{{ $user->hasVerifiedEmail() ? 'Verified' : 'Unverified' }}
</span>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center space-x-2">
@if($user->id !== auth()->id())
<form action="{{ route('admin.users.toggle-status', $user->id) }}" method="POST" class="inline-block">
@csrf
@method('PATCH')
<button type="submit" class="text-gray-500 hover:text-{{ $user->status === 'active' ? 'red' : 'green' }}-500 dark:text-gray-400 dark:hover:text-{{ $user->status === 'active' ? 'red' : 'green' }}-500" title="{{ $user->status === 'active' ? 'Suspend' : 'Activate' }} User">
@if($user->status === 'active')
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"></path></svg>
@else
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
@endif
</button>
</form>
<button @click="openEditEmailModal('{{ $user->id }}', '{{ $user->email }}')" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Edit Email">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg>
</button>
@endif
@if(!$user->hasVerifiedEmail())
<form action="{{ route('admin.users.send-verification', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Send verification email to {{ $user->email }}?');">
@csrf
<button type="submit" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Send Verification Email">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
</button>
</form>
@endif
<form action="{{ route('admin.users.send-reset-link', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Send password reset link to {{ $user->email }}?');">
@csrf
<button type="submit" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Send Password Reset Link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11.536 11 9 13.536 7.464 12 4.929 14.536V17h2.472l4.243-4.243a6 6 0 018.828-5.743zM16.5 13.5V18h6v-4.5h-6z"></path></svg>
</button>
</form>
@if($user->id !== auth()->id())
<form action="{{ route('admin.users.destroy', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this user? This action cannot be undone.');">
@csrf
@method('DELETE')
<button type="submit" class="text-gray-500 hover:text-red-500 dark:text-gray-400 dark:hover:text-red-500" title="Delete User">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
</button>
</form>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination -->
@if($users->hasPages())
<div class="border-t border-gray-200 px-5 py-4 dark:border-gray-800">
{{ $users->links() }}
</div>
@endif
</div>
<!-- Edit Email Modal -->
<div x-show="showEditEmailModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-xs"
x-transition.opacity style="display: none;">
<div @click.outside="showEditEmailModal = false"
class="w-full max-w-lg rounded-2xl bg-white p-6 shadow-xl dark:bg-gray-900 mx-4"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95">
<h3 class="mb-4 text-lg font-semibold text-gray-800 dark:text-white/90">Edit User Email</h3>
<form :action="editEmailUrl" method="POST">
@csrf
@method('PATCH')
<div class="mb-5">
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
Email Address
</label>
<input type="email" name="email" x-model="selectedUserEmail" x-ref="emailInput" required
class="w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
</div>
<div class="flex justify-end gap-3">
<button type="button" @click="showEditEmailModal = false"
class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-white/[0.03]">
Cancel
</button>
<button type="submit"
class="rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-brand-600 focus:outline-hidden focus:ring-2 focus:ring-brand-500/50">
Update Email
</button>
</div>
</form>
</div>
</div>
</div>
@endsection