Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
<div class="user-presence-container border rounded p-4 my-3 bg-white">
<div class="mb-4">
<h4 class="font-bold text-lg">Online Users ({{ $guard }})</h4>
@if($showCount)
<div class="flex items-center space-x-2 text-sm text-theme-secondary mt-2">
<div class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span>{{ count($onlineUsers) }} {{__('online') }}</span>
</div>
@endif
</div>
<div class="space-y-2 mb-4">
@forelse($onlineUsers as $user)
<div class="flex items-center space-x-2 bg-gray-50 rounded">
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
<span class="text-sm font-medium">{{ $user['name'] ?? 'Unknown' }}</span>
<small class="text-theme-muted">
@if(isset($user['last_seen']))
{{ \Carbon\Carbon::parse($user['last_seen'])->diffForHumans() }}
@else
{{__('Now')}}
@endif
</small>
</div>
@empty
<div class="text-theme-muted text-sm italic">{{__('No users online') }}</div>
@endforelse
</div>
</div>
{{-- Enhanced JavaScript with offline detection --}}
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
let lastActivity = Date.now();
let offlineTimeout;
function dispatchUserActivity() {
let success = false;
try {
// Method 1: Direct component method call (most reliable)
const presenceContainers = document.querySelectorAll('.user-presence-container');
presenceContainers.forEach(container => {
const wireId = container.getAttribute('wire:id');
if (wireId) {
try {
const component = window.Livewire.find(wireId);
if (component && typeof component.call === 'function') {
component.call('handleUserActivity');
success = true;
}
} catch (e) {
// component not available
}
}
});
// Method 2: Global dispatch (as backup)
if (!success && window.Livewire && typeof window.Livewire.dispatch === 'function') {
window.Livewire.dispatch('user-activity');
}
} catch (e) {
// dispatch failed
}
}
function dispatchUserOffline() {
try {
// Direct component method call (most reliable)
const presenceContainers = document.querySelectorAll('.user-presence-container');
presenceContainers.forEach(container => {
const wireId = container.getAttribute('wire:id');
if (wireId) {
try {
const component = window.Livewire.find(wireId);
if (component && typeof component.call === 'function') {
component.call('handleUserOffline');
}
} catch (e) {
// component not available
}
}
});
} catch (e) {
// dispatch failed
}
}
function updateActivity() {
const now = Date.now();
// Clear existing offline timeout
if (offlineTimeout) {
clearTimeout(offlineTimeout);
}
// Only dispatch if 15 seconds have passed
if (now - lastActivity > 15000) {
lastActivity = now;
dispatchUserActivity();
}
// Set offline timeout for 2 minutes of inactivity
offlineTimeout = setTimeout(() => {
dispatchUserOffline();
}, 120000);
}
// Activity tracking
['click', 'keypress', 'mousemove', 'scroll'].forEach(event => {
document.addEventListener(event, updateActivity, { passive: true });
});
// Page visibility handling
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// User switched tabs - set shorter offline timeout
if (offlineTimeout) clearTimeout(offlineTimeout);
offlineTimeout = setTimeout(() => {
dispatchUserOffline();
}, 30000);
} else {
// User came back - immediate activity
updateActivity();
}
});
// Handle page unload
window.addEventListener('beforeunload', function() {
dispatchUserOffline();
});
// Initial activity setup
updateActivity();
// Manual triggers for testing
window.triggerPresenceActivity = function() {
dispatchUserActivity();
};
window.triggerPresenceOffline = function() {
dispatchUserOffline();
};
});
</script>
@endpush