chore: cleanup project structure and update readme for beta release

This commit is contained in:
2025-12-23 04:59:21 +07:00
parent 1640ced748
commit 10a00bac0e
122 changed files with 8320 additions and 661 deletions

View File

@@ -1,21 +1,48 @@
{{-- Notification Dropdown Component --}}
<div class="relative" x-data="{
dropdownOpen: false,
notifying: true,
notifying: false,
notifications: [],
init() {
this.fetchNotifications();
// Listen for realtime notifications
if (window.Echo) {
window.Echo.private('App.Models.User.{{ auth()->id() }}')
.notification((notification) => {
this.notifications.unshift({
id: notification.id,
data: {
title: notification.data?.title || notification.title || 'Notification',
body: notification.data?.body || notification.body || '',
icon: notification.data?.icon || notification.icon,
action_url: notification.data?.action_url || notification.action_url
},
created_at_human: 'Just now',
read_url: notification.read_url || '#'
});
this.notifying = true;
// Dispatch Global Event for Toast Alerts
window.dispatchEvent(new CustomEvent('reverb-notification', {
detail: notification.data?.title ? notification.data : notification
}));
});
}
},
fetchNotifications() {
fetch('{{ route('notifications.unread') }}')
.then(response => response.json())
.then(data => {
this.notifications = data.notifications;
this.notifying = data.count > 0;
});
},
toggleDropdown() {
this.dropdownOpen = !this.dropdownOpen;
this.notifying = false;
},
closeDropdown() {
this.dropdownOpen = false;
},
handleItemClick() {
console.log('Notification item clicked');
this.closeDropdown();
},
handleViewAllClick() {
console.log('View All Notifications clicked');
this.closeDropdown();
}
}" @click.away="closeDropdown()">
<!-- Notification Button -->
@@ -28,6 +55,7 @@
<span
x-show="notifying"
class="absolute right-0 top-0.5 z-1 h-2 w-2 rounded-full bg-orange-400"
style="display: none;"
>
<span
class="absolute inline-flex w-full h-full bg-orange-400 rounded-full opacity-75 -z-1 animate-ping"
@@ -61,7 +89,7 @@
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute -right-[240px] mt-[17px] flex h-[480px] w-[350px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark sm:w-[361px] lg:right-0"
class="absolute -right-[240px] mt-[17px] flex h-auto max-h-[480px] w-[350px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark sm:w-[361px] lg:right-0"
style="display: none;"
>
<!-- Dropdown Header -->
@@ -80,7 +108,7 @@
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51356 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
fill=""
/>
</svg>
@@ -88,135 +116,55 @@
</div>
<!-- Notification List -->
<ul class="flex flex-col h-auto overflow-y-auto custom-scrollbar">
@php
$notifications = [
[
'id' => 1,
'userName' => 'Terry Franci',
'userImage' => '/images/user/user-02.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Nganter App',
'type' => 'Project',
'time' => '5 min ago',
'status' => 'online',
],
[
'id' => 2,
'userName' => 'Alex Johnson',
'userImage' => '/images/user/user-03.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Nganter App',
'type' => 'Project',
'time' => '10 min ago',
'status' => 'offline',
],
[
'id' => 3,
'userName' => 'Sarah Williams',
'userImage' => '/images/user/user-04.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Dashboard UI',
'type' => 'Project',
'time' => '15 min ago',
'status' => 'online',
],
[
'id' => 4,
'userName' => 'Mike Brown',
'userImage' => '/images/user/user-05.jpg',
'action' => 'requests permission to change',
'project' => 'Project - E-commerce',
'type' => 'Project',
'time' => '20 min ago',
'status' => 'online',
],
[
'id' => 5,
'userName' => 'Emma Davis',
'userImage' => '/images/user/user-06.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Mobile App',
'type' => 'Project',
'time' => '25 min ago',
'status' => 'offline',
],
[
'id' => 6,
'userName' => 'John Smith',
'userImage' => '/images/user/user-07.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Landing Page',
'type' => 'Project',
'time' => '30 min ago',
'status' => 'online',
],
[
'id' => 7,
'userName' => 'Lisa Anderson',
'userImage' => '/images/user/user-08.jpg',
'action' => 'requests permission to change',
'project' => 'Project - Blog System',
'type' => 'Project',
'time' => '35 min ago',
'status' => 'online',
],
[
'id' => 8,
'userName' => 'David Wilson',
'userImage' => '/images/user/user-09.jpg',
'action' => 'requests permission to change',
'project' => 'Project - CRM Dashboard',
'type' => 'Project',
'time' => '40 min ago',
'status' => 'online',
],
];
@endphp
@foreach ($notifications as $notification)
<li @click="handleItemClick()">
<ul class="flex flex-col h-auto overflow-y-auto custom-scrollbar max-h-[300px]">
<template x-for="notification in notifications" :key="notification.id">
<li>
<a
class="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
href="#"
:href="notification.read_url"
>
<span class="relative block w-full h-10 rounded-full z-1 max-w-10">
<img src="{{ $notification['userImage'] }}" alt="User" class="overflow-hidden rounded-full" />
<span
class="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white dark:border-gray-900 {{ $notification['status'] === 'online' ? 'bg-success-500' : 'bg-error-500' }}"
></span>
</span>
<div class="flex-shrink-0">
<div class="w-10 h-10 rounded-full bg-brand-100 text-brand-500 flex items-center justify-center">
<template x-if="notification.data.icon">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
</template>
<template x-if="!notification.data.icon">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</template>
</div>
</div>
<span class="block">
<span class="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400">
<span class="font-medium text-gray-800 dark:text-white/90">
{{ $notification['userName'] }}
</span>
{{ $notification['action'] }}
<span class="font-medium text-gray-800 dark:text-white/90">
{{ $notification['project'] }}
</span>
<span class="font-medium text-gray-800 dark:text-white/90" x-text="notification.data.title"></span>
<span class="block text-xs mt-1" x-text="notification.data.body"></span>
</span>
<span class="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
<span>{{ $notification['type'] }}</span>
<span class="w-1 h-1 bg-gray-400 rounded-full"></span>
<span>{{ $notification['time'] }}</span>
<span x-text="notification.created_at_human"></span>
</span>
</span>
</a>
</li>
@endforeach
</template>
<li x-show="notifications.length === 0" class="p-6 text-center text-gray-500 dark:text-gray-400">
<p>No new notifications.</p>
</li>
</ul>
<!-- View All Button -->
<a
href="#"
class="mt-3 flex justify-center rounded-lg border border-gray-300 bg-white p-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200"
@click.prevent="handleViewAllClick()"
>
View All Notification
</a>
<div x-show="notifications.length > 0">
<!-- Mark All Read Button -->
<form action="{{ route('notifications.readAll') }}" method="POST" class="mt-3">
@csrf
<button
type="submit"
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white p-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200"
>
Mark All as Read
</button>
</form>
</div>
</div>
<!-- Dropdown End -->
</div>