Files
athenahr/resources/views/dashboard/administration/invites.blade.php
Miguel Nogueira d48b35e845 fix: make sure approved invites are not deleted daily
even though people would have time to use approved invites (24 hrs at least), it would be better to delete them when they expire instead.

Signed-off-by: Miguel Nogueira <me@nogueira.codes>
2025-08-10 11:01:24 +01:00

181 lines
10 KiB
PHP

@extends('adminlte::page')
@section('title', config('app.name') . ' | ' . __('Invitation management'))
@section('content_header')
<h4>{{__('Administration')}} / {{__('Invitation management')}}</h4>
@stop
@section('js')
<x-global-errors></x-global-errors>
@stop
@section('content')
@if(\App\Facades\Options::getOption('enable_registrations'))
<x-alert alert-type="danger" title="{{ __('Invitation system disabled') }}" icon="fas fa-ban">
<p>{!! __('The invitation system is currently disabled because sign ups are open to everyone. If you\'d like to change this, head over to App Settings > <a href=":globalSettingsLink">Global Settings</a> and disable registrations.', ['globalSettingsLink' => route('showSettings')]) !!}</p>
</x-alert>
@endif
<div class="row mt-5">
<div class="col">
<x-card id="inviteManagement" card-title="{{ __('Invitation management system') }}" footer-style="text-muted">
<x-slot name="cardHeader">
@if(\App\Facades\Options::getOption('enable_registrations'))
<div class="d-flex justify-content-end">
<button type="button" id="inviteUserBtn" class="btn btn-success btn-sm ml-3" disabled>
<i class="fas fa-user-plus"></i> {{ __('Invite user') }}
</button>
</div>
@else
<div class="d-flex justify-content-end">
<button type="button" id="inviteUserBtn" class="btn btn-success btn-sm ml-3" data-toggle="modal" data-target="#inviteUserModal">
<i class="fas fa-user-plus"></i> {{ __('Invite user') }}
</button>
</div>
@endif
</x-slot>
@if(!empty($invites) && count($invites) > 0)
<table class="table table-striped table-hover align-middle shadow-sm rounded">
<thead class="thead-dark">
<tr>
<th scope="col"><i class="fas fa-envelope"></i> {{ __('Email') }}</th>
<th scope="col"><i class="fas fa-network-wired"></i> {{ __('IP Address') }}</th>
<th scope="col"><i class="fas fa-clipboard-check"></i> {{ __('Status') }}</th>
<th scope="col"><i class="fas fa-bell"></i> {{ __('Notified?') }}</th>
<th scope="col"><i class="fas fa-calendar-alt"></i> {{ __('Received at') }}</th>
<th scope="col"><i class="fas fa-hourglass-end"></i> {{ __('Expiration') }}</th>
<th scope="col"><i class="fas fa-tasks"></i> {{ __('Actions') }}</th>
</tr>
</thead>
<tbody>
@foreach($invites as $invite)
<tr>
<td>
<span class="font-weight-bold text-primary">{{ $invite->requestor_email }}</span>
</td>
<td>
<span class="badge badge-info">{{ $invite->requestor_ip_address }}</span>
</td>
<td>
@php
$statusColors = [
'approved' => 'success',
'denied' => 'danger',
'pending' => 'warning',
'completed' => 'primary'
];
$status = strtolower($invite->status);
$color = $statusColors[$status] ?? 'secondary';
@endphp
<span class="badge badge-{{ $color }} text-uppercase px-3 py-2">
<i class="fas fa-circle"></i> {{ __(ucfirst($status)) }}
</span>
</td>
<td>
@if($invite->notified)
<span class="badge badge-success">{{ __('Yes') }}</span>
@else
<span class="badge badge-secondary">{{ __('No') }}</span>
@endif
</td>
<td>
<span class="text-muted">{{ $invite->created_at->format('Y-m-d H:i') }}</span>
</td>
<td>
@php
$now = \Carbon\Carbon::now();
$expiration = $invite->expiration;
$isExpired = $expiration < $now;
$expirationDate = $expiration->format('Y-m-d H:i');
$relative = $isExpired ? __('EXPIRED') : $expiration->diffForHumans($now, ['parts' => 2, 'short' => true, 'syntax' => \Carbon\CarbonInterface::DIFF_RELATIVE_TO_NOW]);
$color = $isExpired ? 'danger' : 'info';
@endphp
<span class="badge badge-{{ $color }} text-uppercase px-3 py-2" title="{{ $expirationDate }}">
<i class="fas fa-hourglass-end"></i> {{ $relative }}
</span>
</td>
<td>
@if(strtolower($invite->status) === 'pending')
<form method="POST" action="{{ route('invitations.approve-invite', $invite->id) }}" style="display:inline;">
@csrf
@method('PATCH')
<button type="submit" class="btn btn-success btn-sm" title="{{ __('Approve') }}">
<i class="fas fa-check"></i>
</button>
</form>
<form method="POST" action="{{ route('invitations.deny-invite', $invite->id) }}" style="display:inline;">
@csrf
@method('PATCH')
<button type="submit" class="btn btn-danger btn-sm" title="{{ __('Deny') }}">
<i class="fas fa-times"></i>
</button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@else
<x-alert alert-type="warning" title="{{ __('Nothing here') }}" icon="fas fa-exclamation-triangle">
{{ __('There are currently no outgoing invites or any user requests. Feel free to send out any invites if necessary.') }}
</x-alert>
@endif
<x-slot name="cardFooter">
<p><i class="fas fa-info-circle"></i> {{ __('Here, you can manage the invitation system, and approve any invite requests that might have come through. You can also send invites from here if necessary.') }}</p>
<p><i class="fas fa-info-circle"></i> {!! __('Remember: invitations are unique on an email/IP address basis. The same user won\'t be able to request an invite twice. This restriction also applies to you. Check if there\'s already an invite before sending one out.') !!}</p>
<p><i class="fas fa-exclamation-triangle"></i> {!! __('<b>Invite lifecycle: </b><i>Completed</i> invites are wiped from the system every 12 hours. <i>Expired</i> invites are wiped 1 day after expiration. <i>Denied</i> invites are <b>never</b> wiped ') !!}</p>
</x-slot>
</x-card>
</div>
</div>
@if(!\App\Facades\Options::getOption('enable_registrations'))
<x-modal id="inviteUserModal" :modalTitle="__('Invite a User')" :modalLabel="'inviteUserModalLabel'" :includeCloseButton="true">
<form method="POST" action="{{ route('invitations.request') }}">
@csrf
<div class="input-group mt-3">
<div class="input-group-prepend">
<span class="input-group-text">
<i class="fas fa-envelope"></i>
</span>
</div>
<label for="inviteEmail" class="sr-only">{{ __('Email address') }}</label>
<input type="email" class="form-control" id="inviteEmail" name="email" required placeholder="{{ __('Enter an email address to send the invite to') }}">
</div>
<p class="text-muted mt-2 mb-2"><i class="fas fa-info-circle"></i> {{ __('Sending an invite here will immediately create an approved invite request which will in turn send this user an email message with a link. Be aware that this will allow them to register for a new account.') }}</p>
</form>
<x-slot name="modalFooter">
<button id="sendInviteBtn" class="btn btn-success" type="button">
<i class="fas fa-paper-plane"></i> {{ __('Invite') }}
</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('sendInviteBtn').addEventListener('click', function () {
document.querySelector('#inviteUserModal form').submit();
});
});
</script>
</x-slot>
</x-modal>
@endif
@stop
@section('footer')
@include('breadcrumbs.dashboard.footer')
@stop