1290 lines
69 KiB
PHP
1290 lines
69 KiB
PHP
<div class="mt-12">
|
|
|
|
<!-- Action buttons -->
|
|
<div class="mb-6">
|
|
<livewire:profiles.create>
|
|
</div>
|
|
|
|
<!-- Search box -->
|
|
<div class="mb-4 flex items-center">
|
|
<div class="relative w-2/3">
|
|
<input class="w-full rounded-md border border-theme-primary px-3 py-1 pr-10 text-theme-primary shadow-sm focus:border-theme-accent focus:outline-none focus:ring-1 focus:ring-theme-accent sm:text-sm"
|
|
placeholder="{{ __('Search keywords') . '...' }}" type="text"
|
|
wire:keydown.enter="handleSearchEnter" wire:model="search">
|
|
|
|
@if ($search)
|
|
<button class="absolute inset-y-0 right-0 flex items-center pr-3 text-theme-secondary hover:text-theme-primary focus:outline-none"
|
|
wire:click.prevent="resetSearch">
|
|
<x-icon mini name="backspace" solid />
|
|
</button>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Search Button -->
|
|
<x-jetstream.secondary-button class="ml-3 w-32 justify-center" wire:click.prevent="searchProfiles">
|
|
{{ __('Search') }}
|
|
</x-jetstream.secondary-button>
|
|
</div>
|
|
|
|
<!-- Filter dropdowns -->
|
|
<div class="mb-4 flex flex-wrap items-center gap-3">
|
|
<!-- Type Filter Dropdown -->
|
|
<div>
|
|
<x-select :clearable="true" :searchable="false" class="!w-40" style="width: 10rem !important; min-width: 10rem !important;"
|
|
placeholder="{{ __('Type') }}" wire:model.live="typeFilter">
|
|
@foreach ($this->typeOptions as $type)
|
|
<x-select.option label="{{ $type['name'] }}" value="{{ $type['id'] }}" />
|
|
@endforeach
|
|
</x-select>
|
|
</div>
|
|
|
|
<!-- Active Status Filter Dropdown -->
|
|
<div>
|
|
<x-select :clearable="true" :searchable="false" class="!w-40" style="width: 10rem !important; min-width: 10rem !important;"
|
|
placeholder="{{ __('Status') }}" wire:model.live="activeFilter">
|
|
@foreach ($this->activeOptions as $status)
|
|
<x-select.option label="{{ $status['name'] }}" value="{{ $status['id'] }}" />
|
|
@endforeach
|
|
</x-select>
|
|
</div>
|
|
|
|
<!-- Email Verified Filter Dropdown -->
|
|
<div>
|
|
<x-select :clearable="true" :searchable="false" class="!w-40" style="width: 10rem !important; min-width: 10rem !important;"
|
|
placeholder="{{ __('Email') }}" wire:model.live="emailVerifiedFilter">
|
|
@foreach ($this->emailVerifiedOptions as $status)
|
|
<x-select.option label="{{ $status['name'] }}" value="{{ $status['id'] }}" />
|
|
@endforeach
|
|
</x-select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="bg-white shadow-sm rounded-lg" x-data="{
|
|
initScrollSync() {
|
|
if (this.$refs.topScroll && this.$refs.bottomScroll && this.$refs.table && this.$refs.topScrollContent) {
|
|
const topScroll = this.$refs.topScroll;
|
|
const bottomScroll = this.$refs.bottomScroll;
|
|
const table = this.$refs.table;
|
|
|
|
// Set the width of the dummy div to match table width
|
|
this.$refs.topScrollContent.style.width = table.scrollWidth + 'px';
|
|
}
|
|
}
|
|
}" x-init="setTimeout(() => initScrollSync(), 100)">
|
|
|
|
<!-- Top scrollbar -->
|
|
<div class="overflow-x-auto border-b border-gray-200" x-ref="topScroll" @scroll="$refs.bottomScroll.scrollLeft = $event.target.scrollLeft">
|
|
<div x-ref="topScrollContent" style="height: 20px;"></div>
|
|
</div>
|
|
|
|
<!-- Table with bottom scrollbar -->
|
|
<div class="overflow-x-auto" x-ref="bottomScroll" @scroll="$refs.topScroll.scrollLeft = $event.target.scrollLeft">
|
|
<table class="min-w-full divide-y divide-gray-200" x-ref="table">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
{{-- Sortable ID column --}}
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('id')">
|
|
<div class="flex items-center">
|
|
{{ __('Id') }}
|
|
@if ($sortField === 'id')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
{{-- Sortable Language column --}}
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('locale')">
|
|
<div class="flex items-center">
|
|
|
|
</div>
|
|
</th>
|
|
|
|
{{-- Sortable User Name column --}}
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('name')">
|
|
<div class="flex items-center">
|
|
{{ __('Name') }}
|
|
@if ($sortField === 'name')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
{{-- Sortable Type column --}}
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('type')">
|
|
<div class="flex items-center">
|
|
{{ __('Type') }}
|
|
@if ($sortField === 'type')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
{{-- Sortable Comment column --}}
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('comment')">
|
|
<div class="flex items-center">
|
|
{{ __('Comment') }}
|
|
@if ($sortField === 'comment')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('inactive')">
|
|
<div class="flex items-center">
|
|
{{ __('Active') }}
|
|
@if ($sortField === 'inactive')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('email_verif')">
|
|
<div class="flex items-center">
|
|
{{ __('Conf.') }}
|
|
@if ($sortField === 'email_verif')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('last_login_at')">
|
|
<div class="flex items-center">
|
|
{{ __('Last login') }}
|
|
@if ($sortField === 'last_login_at')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
|
wire:click="sortBy('updated_at')">
|
|
<div class="flex items-center">
|
|
{{ __('Created') }}
|
|
@if ($sortField === 'created_at')
|
|
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
|
@endif
|
|
</div>
|
|
</th>
|
|
|
|
{{-- Non-sortable Action columns --}}
|
|
<th class="px-3 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-32">{{ __('Actions') }}</th>
|
|
|
|
</tr>
|
|
</thead>
|
|
|
|
<!-- Table body -->
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
@forelse ($profiles as $profile)
|
|
<!-- Profiles --->
|
|
<tr>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{{ $profile->id }}
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap">
|
|
@if ($profile->profile_photo_path)
|
|
<div class="flex justify-center">
|
|
<div class="relative cursor-pointer h-7 w-7 flex-shrink-0"
|
|
onclick="window.location='{{ route('profile.show_by_type_and_id', ['type' => strtolower(__(strtolower($profile->type))), 'id' => $profile->id]) }}'">
|
|
<img alt="profile"
|
|
class="h-full w-full rounded-full object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
|
src="{{ $profile->profile_photo_path ? Storage::url($profile->profile_photo_path) : Storage::url(timebank_config('profiles.' . strtolower(class_basename($profile->model)) . '.profile_photo_path_default')) }}" />
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-4">
|
|
<div class="text-sm font-medium text-gray-900">
|
|
{{ $profile->name }}
|
|
</div>
|
|
<div class="text-sm text-gray-500">
|
|
{{ $profile->full_name }}
|
|
</div>
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
{{ $profile->type }}
|
|
</td>
|
|
<td class="px-3 py-4 text-sm text-gray-500">
|
|
<div class="line-clamp-3">
|
|
{{ $profile->comment }}
|
|
</div>
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
@if ($profile->deleted === __('yes'))
|
|
<div class="text-red-500">
|
|
{{ __('removed') }}
|
|
</div>
|
|
@elseif ($profile->deleted === __('planned'))
|
|
<div class="text-red-500">
|
|
{{ __('removing') }}
|
|
</div>
|
|
@elseif ($profile->inactive == __('no'))
|
|
{{ __('yes') }}
|
|
@elseif ($profile->inactive == __('yes'))
|
|
<div class="text-red-500">
|
|
{{ __('no') }}
|
|
</div>
|
|
@else
|
|
<div class="text-red-500">
|
|
{{ __('pausing') }}
|
|
</div>
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
@if ($profile->email_suppressed)
|
|
<div class="text-red-500 font-medium">
|
|
{{ __('Blocked') }}
|
|
</div>
|
|
@elseif ($profile->email_verif == __('yes'))
|
|
{{ __('yes') }}
|
|
@else
|
|
<div class="text-red-500">
|
|
{{ $profile->email_verif }}
|
|
</div>
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
@if ($profile->last_login_at)
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($profile->last_login_at))->diffForHumans() }}
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
@if ($profile->created_at)
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($profile->created_at))->diffForHumans() }}
|
|
@endif
|
|
</td>
|
|
|
|
<!-- Row buttons -->
|
|
<td class="px-3 py-4 whitespace-nowrap text-center text-sm font-medium">
|
|
<div class="flex items-center justify-center space-x-1">
|
|
<x-jetstream.secondary-button
|
|
:disabled="$profile->type === 'Admin'"
|
|
title="{{ __('Accounts') }}"
|
|
wire:click="openEditAccountsModal({{ $profile->id }}, '{{ addslashes(get_class($profile)) }}')">
|
|
<x-icon class="h-5 w-5" name="wallet" />
|
|
</x-jetstream.secondary-button>
|
|
|
|
<x-jetstream.secondary-button
|
|
:disabled="$profile->deleted === __('yes')"
|
|
title="{{ $profile->deleted === __('yes') ? __('Profile is deleted') : __('Profile') }}"
|
|
wire:click="openEditProfileModal({{ $profile->id }}, '{{ addslashes(get_class($profile)) }}')">
|
|
<x-icon class="h-5 w-5" name="user" />
|
|
</x-jetstream.secondary-button>
|
|
|
|
<x-jetstream.secondary-button
|
|
:disabled="$profile->deleted === __('yes')"
|
|
title="{{ $profile->deleted === __('yes') ? __('Profile is deleted') : __('Profile links') }}"
|
|
wire:click="openAttachProfilesModal({{ $profile->id }}, '{{ addslashes(get_class($profile)) }}')">
|
|
<x-icon class="h-5 w-5" name="link" />
|
|
</x-jetstream.secondary-button>
|
|
|
|
@if ($profile->deleted === __('yes'))
|
|
{{-- Show restore button for deleted profiles --}}
|
|
<x-jetstream.danger-button
|
|
:disabled="!$profile->is_restorable"
|
|
title="{{ $profile->is_restorable ? __('Restore profile') : __('Cannot restore: The grace period has passed.') }}"
|
|
wire:click="openRestoreProfileModal({{ $profile->id }}, '{{ addslashes(get_class($profile)) }}')">
|
|
<x-icon class="h-5 w-5" name="arrow-path" />
|
|
</x-jetstream.danger-button>
|
|
@else
|
|
{{-- Show delete button for active profiles --}}
|
|
<x-jetstream.danger-button
|
|
title="{{ __('Delete profile') }}"
|
|
wire:click="openDeleteProfileModal({{ $profile->id }}, '{{ addslashes(get_class($profile)) }}')">
|
|
<x-icon class="h-5 w-5" name="trash" />
|
|
</x-jetstream.danger-button>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="10" class="px-6 py-12 text-center text-gray-500">
|
|
{{ __('No results found') }}
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
@if($profiles->hasPages())
|
|
<div class="bg-white shadow-sm rounded-lg mt-4">
|
|
<div class="py-3 border-t border-gray-200 flex items-center justify-between">
|
|
<!-- Left Side: perPage Dropdown -->
|
|
<div class="flex items-center">
|
|
<select class="w-20 rounded-md border border-theme-primary bg-theme-background px-3 py-2 text-theme-primary shadow-sm focus:border-theme-accent focus:outline-none focus:ring-1 focus:ring-theme-accent sm:text-sm"
|
|
wire:model.live="perPage">
|
|
<option value="10">10</option>
|
|
<option value="20">20</option>
|
|
<option value="50">50</option>
|
|
</select>
|
|
<span class="ml-2 text-sm text-theme-secondary">{{ __('per page') }}</span>
|
|
</div>
|
|
|
|
<!-- Right Side: Paginator -->
|
|
{{ $profiles->links('livewire.long-paginator') }}
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Edit Accounts modal -->
|
|
@if ($editAccounts)
|
|
<x-jetstream.dialog-modal wire:key="modalEditAccounts" wire:model.live="modalEditAccounts" maxWidth="4xl" closeButton="true">
|
|
|
|
<x-slot name="title">
|
|
{{ __('Accounts') . ' ' . class_basename($editAccounts['model']) . ' ' . $editAccounts['id'] }}
|
|
</x-slot>
|
|
|
|
<x-slot name="content">
|
|
<div class="mb-4 flex flex-wrap w-full items-center gap-4">
|
|
{{-- Photo on the left --}}
|
|
<div class="whitespace-no-wrap pr-4 text-sm leading-5 flex-shrink-0">
|
|
@if ($editAccounts['profile_photo_path'])
|
|
<div class="relative block cursor-pointer"
|
|
class="mx-auto h-16 w-16 rounded-full object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
|
onclick="window.location='{{ route('profile.show_by_type_and_id', ['type' => strtolower(__(strtolower($profile->type))), 'id' => $profile->id]) }}'"
|
|
src="{{ $editAccounts['profile_photo_path'] ? Storage::url($editAccounts['profile_photo_path']) : Storage::url(timebank_config('profiles.' . strtolower(class_basename($editAccounts)) . '.profile_photo_path_default')) }}" />
|
|
</div>
|
|
@else
|
|
{{-- Optional: Placeholder if no photo --}}
|
|
<div class="flex h-16 w-16 items-center justify-center rounded-full bg-theme-surface text-theme-light">
|
|
<x-icon class="h-8 w-8" name="photograph" />
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='text-xl'>
|
|
<div class="text-lg font-semibold">
|
|
{{ $initAccounts['name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initAccounts['full_name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initAccounts['location'] }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6 flex flex-wrap flex-1 min-w-[200px] justify-end gap-3">
|
|
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ App\Models\Language::where('lang_code', $editAccounts['lang_preference'])->value('flag') }}
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
</div>
|
|
|
|
<div class='text-sm'>
|
|
<div class="">
|
|
{{ __('Last login') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Last update') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Registered') . ': ' }}
|
|
</div>
|
|
@if ($initAccounts['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Inactive') . ': ' }}
|
|
</div>
|
|
@endif
|
|
@if ($initAccounts['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Deleted') . ': ' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ $initAccounts['last_login_at'] ? \Carbon\Carbon::parse($initAccounts['last_login_at'])->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initAccounts['last_login_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initAccounts['updated_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initAccounts['created_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initAccounts['created_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
@if ($initAccounts['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initAccounts['inactive_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
@if ($initAccounts['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initAccounts['deleted_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@if (!empty($initAccounts['deleted_at']) && \Carbon\Carbon::parse($editAccounts['deleted_at'])->isPast())
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3 text-red-500">{{ __('Re-activate') }}</div>
|
|
<div class="flex-1">
|
|
<x-toggle id="re-activate" name="re-activate" negative disabled="true"
|
|
label="_('profile has been deleted') wire:model.live="reActivate" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@elseif (!empty($initAccounts['inactive_at']) && \Carbon\Carbon::parse($initProfile['inactive_at'])->isPast())
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3 text-red-500">{{ __('Re-activate') }}</div>
|
|
<div class="flex-1">
|
|
<x-toggle id="re-activate" name="re-activate" negative wire:model.live="reActivate" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex-1">
|
|
<div class="flex">
|
|
<div class="w-1/5 font-semibold">{{ __('Account id') }}</div>
|
|
<div class="w-2/5 font-semibold">{{ __('Name') }}</div>
|
|
<div class="w-1/5 font-semibold">{{ __('Balance') }}</div>
|
|
<div class="w-1/5 font-semibold">{{ __('Min. limit') }}</div>
|
|
<div class="w-1/5 font-semibold">{{ __('Max. limit') }}</div>
|
|
<div class="w-1/5 font-semibold">{{ __('Inactive') }}</div>
|
|
</div>
|
|
@foreach ($editAccounts['accounts'] as $account)
|
|
<div class="flex">
|
|
<div class="w-1/5">{{ $account['id'] }}</div>
|
|
<div class="w-2/5">{{ ucfirst($account['name']) }}</div>
|
|
<div class="w-1/5">{{ tbFormat($account['balance']) }}</div>
|
|
<div class="w-1/5">{{ tbFormat($account['limitMin']) }}</div>
|
|
<div class="w-1/5">{{ tbFormat($account['limitMax']) }}</div>
|
|
@if ($account['inactiveAt'] && \Illuminate\Support\Carbon::parse($account['inactiveAt'])->isPast())
|
|
<div class="w-1/5 text-red-500">
|
|
{{ \Illuminate\Support\Carbon::parse($account['inactiveAt'])->format('Y-m-d') }}
|
|
</div>
|
|
@else
|
|
<div class="w-1/5">
|
|
{{ $account['inactiveAt'] ? \Illuminate\Support\Carbon::parse($account['inactiveAt'])->format('Y-m-d') : '' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Sum of all accounts') }}</div>
|
|
<div class="flex-1">
|
|
{{ tbFormat($editAccounts['totals']['sumBalances']) }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Receivable limit') }}</div>
|
|
<div class="flex-1">
|
|
{{ !empty($editAccounts['accounts'][0]) ? tbFormat($editAccounts['accounts'][0]['limitReceivable']) : '-' }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Total transfers (ever)') }}</div>
|
|
<div class="flex-1">
|
|
{{ $editAccounts['totals']['transfers'] }}
|
|
@if ($editAccounts['totals']['transfers'] > 0)
|
|
({{ $editAccounts['totals']['transfersGiven'] }} {{ __('paid') }},
|
|
{{ $editAccounts['totals']['transfersReceived'] }} {{ __('received') }})
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Transfers') }}
|
|
{{ __(timebank_config('account_info.account_totals.countTransfersSince_humanReadable')) }}</div>
|
|
<div class="flex-1">
|
|
{{ $editAccounts['totalsPastYear']['transfers'] }}
|
|
@if ($editAccounts['totalsPastYear']['transfers'] > 0)
|
|
({{ $editAccounts['totalsPastYear']['transfersGiven'] }} {{ __('paid') }},
|
|
{{ $editAccounts['totalsPastYear']['transfersReceived'] }} {{ __('received') }})
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Last transfer') }}</div>
|
|
<div class="flex-1">
|
|
@if (empty($editAccounts['totals']['lastTransferDate']))
|
|
{{ __('No transfers yet') }}
|
|
@else
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($editAccounts['totals']['lastTransferDate']))->diffForHumans() }}
|
|
({{ $editAccounts['totals']['lastTransferDate'] }})
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<x-errors />
|
|
</x-slot>
|
|
<x-slot name="footer">
|
|
<x-jetstream.secondary-button wire:click="resetForm" wire:loading.attr="disabled">
|
|
{{ __('Close') }}
|
|
</x-jetstream.secondary-button>
|
|
</x-slot>
|
|
</x-jetstream.dialog-modal>
|
|
@endif
|
|
|
|
<!-- Edit Profile modal -->
|
|
@if ($editProfile)
|
|
<x-jetstream.dialog-modal wire:key="modalEditProfile" wire:model.live="modalEditProfile" maxWidth="4xl" closeButton="true">
|
|
|
|
<x-slot name="title">
|
|
{{ __('Profile info') . ': ' . class_basename($editProfile['model']) . ' ' . $editProfile['id'] }}
|
|
</x-slot>
|
|
|
|
<x-slot name="content">
|
|
<div class="mb-4 flex flex-wrap w-full items-center gap-4">
|
|
{{-- Photo on the left --}}
|
|
<div class="whitespace-no-wrap pr-4 text-sm leading-5 flex-shrink-0">
|
|
@if ($editProfile['profile_photo_path'])
|
|
<div class="relative block cursor-pointer"
|
|
onclick="window.location='{{ route('profile.show_by_type_and_id', ['type' => strtolower(__(strtolower(class_basename($editProfile['model'])))), 'id' => $editProfile['id']]) }}'">
|
|
<img alt="profile"
|
|
class="mx-auto h-16 w-16 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
|
src="{{ $editProfile['profile_photo_path'] ? Storage::url($editProfile['profile_photo_path']) : Storage::url(timebank_config('profiles.' . strtolower(class_basename($editProfile['model'])) . '.profile_photo_path_default')) }}" />
|
|
</div>
|
|
@else
|
|
{{-- Optional: Placeholder if no photo --}}
|
|
<div
|
|
class="flex h-16 w-16 items-center justify-center rounded-full bg-theme-surface text-theme-light">
|
|
<x-icon class="h-8 w-8" name="photograph" />
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='text-xl'>
|
|
<div class="text-lg font-semibold">
|
|
{{ $initProfile['name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initProfile['full_name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initProfile['location'] }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6 flex flex-wrap flex-1 min-w-[200px] justify-end gap-3">
|
|
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ App\Models\Language::where('lang_code', $editProfile['lang_preference'])->value('flag') }}
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
</div>
|
|
|
|
<div class='text-sm'>
|
|
<div class="">
|
|
{{ __('Last login') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Last update') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Registered') . ': ' }}
|
|
</div>
|
|
@if ($initProfile['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Inactive') . ': ' }}
|
|
</div>
|
|
@endif
|
|
@if ($initProfile['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Deleted') . ': ' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ $initProfile['last_login_at'] ? \Carbon\Carbon::parse($initProfile['last_login_at'])->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initProfile['last_login_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initProfile['updated_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initProfile['created_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initProfile['created_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
@if ($initProfile['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initProfile['inactive_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
@if ($initProfile['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initProfile['deleted_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (!empty($initProfile['deleted_at']) && \Illuminate\Support\Carbon::parse($editProfile['deleted_at'])->isPast())
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Re-activate') }}</div>
|
|
<div class="flex-1">
|
|
<x-toggle disabled id="re-activate"
|
|
label="{{ _('Disabled because profile has been deleted') }}"
|
|
name="re-activate" negative wire:model.live="reActivate" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@elseif (!empty($initProfile['inactive_at']) && \Illuminate\Support\Carbon::parse($initProfile['inactive_at'])->isPast())
|
|
<div class="mb-6 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3 text-red-500">{{ __('Re-activate') }}</div>
|
|
<div class="flex-1">
|
|
<x-toggle id="re-activate" name="re-activate" negative
|
|
wire:model.live="reActivate" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mb-2 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Username') }}</div>
|
|
<div class="flex-1">
|
|
<x-input placeholder="{{ __('Login or username') }}"
|
|
wire:model.lazy="editProfile.name" />
|
|
</div>
|
|
</div>
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Full name') }}</div>
|
|
<div class="flex-1">
|
|
<x-input placeholder="{{ __('Full name') }}" wire:model.lazy="editProfile.full_name" />
|
|
</div>
|
|
</div>
|
|
@if (isset($editProfile['modelClass']) && $editProfile['modelClass'] === 'App\Models\Bank')
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Bank level') }}</div>
|
|
<div class="flex-1">
|
|
<x-select placeholder="{{ __('Select bank level') }}" wire:model.lazy="editProfile.level">
|
|
<x-select.option label="{{ __('Non public system bank') }}" value="1" />
|
|
<x-select.option label="{{ __('Public bank') }}" value="2" />
|
|
</x-select>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
<div class="flex">
|
|
@if ($editProfile['email_verified_at'])
|
|
<div class="w-1/3">{{ __('Email') }} </div>
|
|
@else
|
|
<div class="w-1/3 text-red-500">{{ __('Email (not verified)') }} </div>
|
|
@endif
|
|
<div class="flex-1 flex items-center gap-2">
|
|
<div class="flex-1">
|
|
<x-input placeholder="{{ __('Email address') }}" wire:model.lazy="editProfile.email" />
|
|
</div>
|
|
@php
|
|
$emailPending = $editProfile['email_suppression_pending'] ?? null;
|
|
$emailIsSuppressed = $emailPending !== null
|
|
? $emailPending
|
|
: ($editProfile['email'] ? \App\Models\MailingBounce::isSuppressed($editProfile['email']) : false);
|
|
@endphp
|
|
@if ($emailIsSuppressed)
|
|
<x-jetstream.danger-button
|
|
title="{{ __('Unsuppress email — click to allow emails to this address again') }}"
|
|
wire:click="toggleEmailSuppression"
|
|
wire:target="toggleEmailSuppression"
|
|
wire:loading.attr="disabled">
|
|
<x-icon class="h-5 w-5" name="exclamation-triangle" solid />
|
|
</x-jetstream.danger-button>
|
|
@else
|
|
<x-jetstream.secondary-button
|
|
title="{{ __('Suppress email — block all emails to this address') }}"
|
|
wire:click="toggleEmailSuppression"
|
|
wire:target="toggleEmailSuppression"
|
|
wire:loading.attr="disabled">
|
|
<x-icon class="h-5 w-5" name="exclamation-triangle" />
|
|
</x-jetstream.secondary-button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Short introduction') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="3" wire:model.lazy="editProfile.about_short" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Long introduction') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="3" wire:model.lazy="editProfile.about" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Motivation to Timebank') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="3" wire:model.lazy="editProfile.motivation" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Website') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="3" wire:model.lazy="editProfile.website" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
@if ($editProfile['phone_public'])
|
|
@if ($editProfile['model'] == 'user')
|
|
<div class="w-1/3">{{ __('Phone (only public for friends!)') }}</div>
|
|
@else
|
|
<div class="w-1/3">{{ __('Phone (public for ' . platform_users() . ')') }}</div>
|
|
@endif
|
|
@else
|
|
<div class="w-1/3">{{ __('Phone (not public!)') }}</div>
|
|
@endif
|
|
<div class="flex-1">
|
|
<x-input placeholder="{{ __('+31612345678') }}" wire:model.lazy="editProfile.phone" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Admin comments') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="5" wire:model.lazy="editProfile.comment" />
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
@if ($editProfileChanged)
|
|
<div class="mt-3 text-red-500">
|
|
{{ __('The profile owner will be notified about this update.') }}
|
|
|
|
@if (count($editProfileMessages) > 0)
|
|
@foreach ($editProfileMessages as $message)
|
|
<div class="">
|
|
{{ $message }}
|
|
</div>
|
|
@endforeach
|
|
@endif
|
|
</div>
|
|
|
|
<div class="mt-3 text-red-500">
|
|
{{ __('This update can not be undone!') }}
|
|
</div>
|
|
@endif
|
|
<div class="mt-3 w-1/3">
|
|
<x-input label="{!! __('messages.confirm_input') !!}" placeholder="{{ __('Confirmation keyword') }}"
|
|
wire:model.live="confirmString" />
|
|
</div>
|
|
<x-errors />
|
|
</x-slot>
|
|
<x-slot name="footer">
|
|
<x-jetstream.secondary-button wire:click="resetForm" wire:loading.attr="disabled">
|
|
{{ __('Cancel') }}
|
|
</x-jetstream.secondary-button>
|
|
<x-jetstream.button :disabled="$buttonDisabled" class="ml-3"
|
|
wire:click.prevent="updateProfile({{ $editProfile['id'] }})"
|
|
wire:loading.attr="disabled">
|
|
{{ __('Update') }}
|
|
</x-jetstream.button>
|
|
</x-slot>
|
|
</x-jetstream.dialog-modal>
|
|
@endif
|
|
|
|
<!-- Edit Attach Profiles modal -->
|
|
@if ($editAttachProfile)
|
|
<x-jetstream.dialog-modal wire:key="modalAttachProfile" wire:model.live="modalAttachProfile" maxWidth="4xl" closeButton="true">
|
|
|
|
<x-slot name="title">
|
|
{{ __('Profile links') . ': ' . class_basename($editAttachProfile['model']) . ' ' . $editAttachProfile['id'] }}
|
|
</x-slot>
|
|
|
|
<x-slot name="content">
|
|
<div class="mb-4 flex flex-wrap w-full items-center gap-4">
|
|
{{-- Photo on the left --}}
|
|
<div class="whitespace-no-wrap pr-4 text-sm leading-5 flex-shrink-0">
|
|
@if ($editAttachProfile['profile_photo_path'])
|
|
<div class="relative block cursor-pointer"
|
|
onclick="window.location='{{ route('profile.show_by_type_and_id', ['type' => strtolower(__(strtolower(class_basename($editAttachProfile['model'])))), 'id' => $editAttachProfile['id']]) }}'">
|
|
<img alt="profile"
|
|
class="mx-auto h-16 w-16 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
|
src="{{ $editAttachProfile['profile_photo_path'] ? Storage::url($editAttachProfile['profile_photo_path']) : Storage::url(timebank_config('profiles.' . strtolower(class_basename($editAttachProfile['model'])) . '.profile_photo_path_default')) }}" />
|
|
</div>
|
|
@else
|
|
{{-- Optional: Placeholder if no photo --}}
|
|
<div
|
|
class="flex h-16 w-16 items-center justify-center rounded-full bg-theme-surface text-theme-light">
|
|
<x-icon class="h-8 w-8" name="photograph" />
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='text-xl'>
|
|
<div class="text-lg font-semibold">
|
|
{{ $initAttachProfile['name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initAttachProfile['full_name'] }}
|
|
</div>
|
|
<div class="text-sm font-normal text-theme-secondary">
|
|
{{ $initAttachProfile['location'] }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6 flex flex-wrap flex-1 min-w-[200px] justify-end gap-3">
|
|
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ App\Models\Language::where('lang_code', $initAttachProfile['lang_preference'])->value('flag') }}
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
<div class="">
|
|
</div>
|
|
</div>
|
|
|
|
<div class='text-sm'>
|
|
<div class="">
|
|
{{ __('Last login') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Last update') . ': ' }}
|
|
</div>
|
|
<div class="">
|
|
{{ __('Registered') . ': ' }}
|
|
</div>
|
|
@if ($initAttachProfile['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Inactive') . ': ' }}
|
|
</div>
|
|
@endif
|
|
@if ($initAttachProfile['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ __('Deleted') . ': ' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class='pr-3 text-sm'>
|
|
<div class="">
|
|
{{ $initAttachProfile['last_login_at'] ? \Carbon\Carbon::parse($initAttachProfile['last_login_at'])->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initAttachProfile['last_login_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initAttachProfile['updated_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
<div class="">
|
|
{{ $initAttachProfile['created_at'] ? \Carbon\Carbon::createFromTimeStamp(strtotime($initAttachProfile['created_at']))->diffForHumans() : '-' }}
|
|
</div>
|
|
@if ($initAttachProfile['inactive_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initAttachProfile['inactive_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
@if ($initAttachProfile['deleted_at'])
|
|
<div class="text-red-500">
|
|
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($initAttachProfile['deleted_at']))->diffForHumans() }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6 flex">
|
|
<div class="mt-2 w-1/3">{{ __('Linked profiles') }}</div>
|
|
<div class="flex-1">
|
|
@foreach ($editAttachProfile['profiles'] as $profileIndex => $profile)
|
|
@php
|
|
$isRemoved = !empty($profile['removed']);
|
|
// For Org/Bank modals the role type is the parent; for User modals it's the linked profile's own type.
|
|
$roleType = ucfirst($editAttachProfile['model']) === 'User'
|
|
? ($profile['type'] ?? '')
|
|
: ucfirst($editAttachProfile['model']);
|
|
$roleOptions = match($roleType) {
|
|
'Organization' => [
|
|
['id' => 'organization-coordinator','name' => __('Coordinator (full access except payments)')],
|
|
['id' => 'organization-manager', 'name' => __('Manager (full access including payments)')],
|
|
],
|
|
'Bank' => [
|
|
['id' => 'bank-coordinator','name' => __('Coordinator (full access except payments)')],
|
|
['id' => 'bank-manager', 'name' => __('Manager (full access including payments)')],
|
|
],
|
|
'Admin' => [['id' => 'administrator', 'name' => __('Administrator')]],
|
|
default => [],
|
|
};
|
|
$showRoleSelect = !$isRemoved && count($roleOptions) > 0;
|
|
@endphp
|
|
<div class="{{ $isRemoved ? 'opacity-50 text-theme-secondary' : '' }} mb-3 flex items-stretch gap-6">
|
|
{{-- Photo spanning both rows --}}
|
|
<div class="flex-shrink-0">
|
|
@if ($profile['profile_photo_path'] ?? false)
|
|
<img alt="profile"
|
|
class="{{ $isRemoved ? 'opacity-50 grayscale' : '' }} h-full w-16 rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
|
|
src="{{ Storage::url($profile['profile_photo_path']) }}" />
|
|
@else
|
|
<div class="h-full w-16"></div>
|
|
@endif
|
|
</div>
|
|
{{-- Name + role stacked --}}
|
|
<div class="flex flex-1 flex-col justify-between">
|
|
<div class="flex items-center">
|
|
<div class="flex-1 text-sm">{{ ($profile['username'] ?? '') . ' - ' . ucfirst($profile['name'] ?? '') }}</div>
|
|
<div class="text-right">
|
|
@if (!$isRemoved)
|
|
<button class="text-theme-primary hover:text-red-600" type="button"
|
|
wire:click="removeAttachedProfile({{ $profile['id'] }}, '{{ $profile['type'] }}')">
|
|
<x-icon mini name="x-circle" />
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@if ($showRoleSelect)
|
|
@php $fixedRole = in_array($roleType, ['User', 'Admin']); @endphp
|
|
<div>
|
|
<x-select :searchable="false" :disabled="$fixedRole" wire:model.live="editAttachProfile.profiles.{{ $profileIndex }}.role">
|
|
@foreach ($roleOptions as $opt)
|
|
<x-select.option label="{{ $opt['name'] }}" value="{{ $opt['id'] }}" />
|
|
@endforeach
|
|
</x-select>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="mt-2 w-1/3">{{ __('Attach new profile') }}</div>
|
|
<div class="flex-1">
|
|
<livewire:profile.select-profile :typesAvailable="$newTypesAvailable">
|
|
@if (!empty($editAttachProfile['newProfile']))
|
|
@php
|
|
$newProfileModel = class_basename($editAttachProfile['newProfile']['type'] ?? '');
|
|
$parentModel = ucfirst($editAttachProfile['model'] ?? '');
|
|
$newRoleType = $newProfileModel !== 'User' ? $newProfileModel : $parentModel;
|
|
$newRoleOptions = match($newRoleType) {
|
|
'Organization' => [
|
|
['id' => 'organization-coordinator','name' => __('Coordinator (full access except payments)')],
|
|
['id' => 'organization-manager', 'name' => __('Manager (full access including payments)')],
|
|
],
|
|
'Bank' => [
|
|
['id' => 'bank-coordinator','name' => __('Coordinator (full access except payments)')],
|
|
['id' => 'bank-manager', 'name' => __('Manager (full access including payments)')],
|
|
],
|
|
'Admin' => [['id' => 'administrator', 'name' => __('Administrator')]],
|
|
default => [],
|
|
};
|
|
@endphp
|
|
@if (count($newRoleOptions) > 0)
|
|
@php $newRoleFixed = in_array($newRoleType, ['User', 'Admin']); @endphp
|
|
<div class="mt-2 mb-6 max-w-md">
|
|
<x-select class="w-full" :searchable="false" :disabled="$newRoleFixed" wire:model.live="editAttachProfile.newProfile.role" placeholder="{{ __('Select a role') }}">
|
|
@foreach ($newRoleOptions as $opt)
|
|
<x-select.option label="{{ $opt['name'] }}" value="{{ $opt['id'] }}" />
|
|
@endforeach
|
|
</x-select>
|
|
@error('editAttachProfile.newProfile.role')
|
|
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
@endif
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div class="mt-2 w-1/3">{{ __('Admin comments') }}</div>
|
|
<div class="flex-1">
|
|
<x-textarea rows="5" wire:model.lazy="editAttachProfile.comment" />
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
@if ($editAttachProfileChanged)
|
|
<div class="mt-3 text-red-500">
|
|
{{ __('The profile owner will be notified about this update.') }}
|
|
</div>
|
|
|
|
<div class="mt-3 text-red-500">
|
|
{{ __('This update can not be undone!') }}
|
|
</div>
|
|
@endif
|
|
<div class="mt-3 w-1/3">
|
|
<x-input label="{!! __('messages.confirm_input') !!}" placeholder="{{ __('Confirmation keyword') }}"
|
|
wire:model.live="confirmString" />
|
|
</div>
|
|
<x-errors />
|
|
</x-slot>
|
|
<x-slot name="footer">
|
|
<x-jetstream.secondary-button wire:click="resetForm" wire:loading.attr="disabled">
|
|
{{ __('Cancel') }}
|
|
</x-jetstream.secondary-button>
|
|
<x-jetstream.button :disabled="$buttonDisabled" class="ml-3"
|
|
wire:click.prevent="attachProfile({{ $editAttachProfile['id'] }})"
|
|
wire:loading.attr="disabled">
|
|
{{ __('Update') }}
|
|
</x-jetstream.button>
|
|
</x-slot>
|
|
</x-jetstream.dialog-modal>
|
|
@endif
|
|
|
|
<!----Restore Profile modal ---->
|
|
@if ($modalRestoreProfile && $restoreProfileData)
|
|
<x-jetstream.dialog-modal wire:model.live="modalRestoreProfile" maxWidth="2xl">
|
|
<x-slot name="title">
|
|
{{ __('Are you sure?') }}
|
|
</x-slot>
|
|
|
|
<x-slot name="content">
|
|
<div class="font-semibold text-red-600">
|
|
{{ __('Do you want to restore this profile?') }}
|
|
</div>
|
|
|
|
{{-- Profile Info --}}
|
|
<div class="my-4 p-4 bg-gray-50 rounded-lg">
|
|
<div class="font-semibold text-gray-900">{{ $restoreProfileData['name'] }}</div>
|
|
<div class="text-sm text-gray-600">{{ $restoreProfileData['full_name'] }}</div>
|
|
<div class="text-sm text-gray-500">{{ $restoreProfileData['email'] }}</div>
|
|
<div class="text-sm text-gray-500">{{ __('Type') }}: {{ $restoreProfileData['type'] }}</div>
|
|
</div>
|
|
|
|
<div class="mb-2 flex flex-col space-y-2">
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Deleted') }}:</div>
|
|
<div class="flex-1">{{ $restoreProfileData['deletedAt'] }}</div>
|
|
</div>
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Time remaining') }}:</div>
|
|
<div class="flex-1">{{ $restoreProfileData['timeRemaining'] }}</div>
|
|
</div>
|
|
<div class="flex">
|
|
<div class="w-1/3">{{ __('Grace period expires') }}:</div>
|
|
<div class="flex-1">{{ $restoreProfileData['gracePeriodExpiry'] }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="my-3">
|
|
{{ __('This profile will be fully restored and the user will be able to log in again. All balance handling preferences will be cleared.') }}
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<x-jetstream.label value="{{ __('Admin password confirmation') }}" />
|
|
<x-jetstream.input type="password" class="mt-1 block w-3/4"
|
|
placeholder="{{ __('Your admin password') }}"
|
|
wire:model="adminPassword" />
|
|
<x-jetstream.input-error for="adminPassword" class="mt-2" />
|
|
</div>
|
|
</x-slot>
|
|
|
|
<x-slot name="footer">
|
|
<x-jetstream.secondary-button wire:click="resetForm" wire:loading.attr="disabled">
|
|
{{ __('Cancel') }}
|
|
</x-jetstream.secondary-button>
|
|
|
|
<x-jetstream.danger-button
|
|
class="ml-3"
|
|
wire:click="restoreProfile"
|
|
wire:loading.attr="disabled">
|
|
{{ __('Restore profile') }}
|
|
</x-jetstream.danger-button>
|
|
</x-slot>
|
|
</x-jetstream.dialog-modal>
|
|
@endif
|
|
|
|
<!----Delete Profile modal ---->
|
|
@if ($modalDeleteProfile && $deleteProfileData)
|
|
<x-jetstream.dialog-modal wire:model.live="modalDeleteProfile" maxWidth="4xl">
|
|
<x-slot name="title">
|
|
{{ __('Delete this profile?') }}
|
|
</x-slot>
|
|
|
|
<x-slot name="content">
|
|
<div class="font-semibold text-red-600">
|
|
{{ __('Are you sure you want to delete this profile? This step is irreversible.') }}
|
|
</div>
|
|
|
|
{{-- Profile Info --}}
|
|
<div class="my-4 p-4 bg-gray-50 rounded-lg">
|
|
<div class="font-semibold text-gray-900">{{ $deleteProfileData['name'] }}</div>
|
|
<div class="text-sm text-gray-600">{{ $deleteProfileData['full_name'] }}</div>
|
|
<div class="text-sm text-gray-500">{{ $deleteProfileData['email'] }}</div>
|
|
<div class="text-sm text-gray-500">{{ __('Type') }}: {{ $deleteProfileData['type'] }}</div>
|
|
</div>
|
|
|
|
<div class="my-3">
|
|
{{ __('Once your profile is deleted, all of its balance totals and data will be permanently deleted. All your transactions will be anonymized, also in the online transaction overviews and statements of other ' . platform_name() . ' users.')}}
|
|
</div>
|
|
|
|
{{-- Account Balances Overview --}}
|
|
@if($deleteProfileData['accounts'] && count($deleteProfileData['accounts']) > 0)
|
|
<div class="mt-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
<h4 class="text-sm font-semibold text-gray-700 mb-3">{{ __('Your accounts') }}</h4>
|
|
|
|
<div class="space-y-2">
|
|
@foreach($deleteProfileData['accounts'] as $account)
|
|
<div class="flex justify-between items-center text-sm">
|
|
<span class="text-gray-600">{{ $account['name'] }}</span>
|
|
<span class="font-medium {{ $account['balance'] < 0 ? 'text-red-600' : 'text-gray-900' }}">
|
|
{{ $account['balanceFormatted'] }}
|
|
</span>
|
|
</div>
|
|
@endforeach
|
|
|
|
<div class="pt-2 mt-2 border-t border-gray-300 flex justify-between items-center">
|
|
<span class="text-sm font-semibold text-gray-700">{{ __('Total balance') }}</span>
|
|
<span class="text-base font-bold {{ $deleteProfileData['totalBalance'] < 0 ? 'text-red-600' : 'text-gray-900' }}">
|
|
{{ tbFormat($deleteProfileData['totalBalance']) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Central Bank Warning --}}
|
|
@if($deleteProfileData['isCentralBank'])
|
|
<div class="mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<div class="flex items-start">
|
|
<svg class="w-5 h-5 text-red-600 mt-0.5 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-sm font-semibold text-red-800">{{ __('Central bank cannot be deleted') }}</div>
|
|
<p class="mt-1 text-sm text-red-700">{{ __('This is the central bank (level 0) and cannot be removed from the system. Central banks are essential for currency creation and management.') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Final Admin Warning --}}
|
|
@if($deleteProfileData['isFinalAdmin'])
|
|
<div class="mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<div class="flex items-start">
|
|
<svg class="w-5 h-5 text-red-600 mt-0.5 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-sm font-semibold text-red-800">{{ __('Final administrator cannot be deleted') }}</div>
|
|
<p class="mt-1 text-sm text-red-700">{{ __('At least one administrator account must remain active in the system.') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Negative Balance Warning --}}
|
|
@if($deleteProfileData['hasNegativeBalance'])
|
|
<div class="mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<div class="flex items-start">
|
|
<svg class="w-5 h-5 text-red-600 mt-0.5 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-sm font-semibold text-red-800">{{ __('Cannot delete profile with negative balance') }}</div>
|
|
<p class="mt-1 text-sm text-red-700">{{ __('You must settle all debts before you can delete your profile. Please ensure all your account balances are zero or positive.') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Balance Handling Options --}}
|
|
@if($deleteProfileData['showBalanceOptions'] && $deleteProfileData['totalBalance'] != 0 && !$deleteProfileData['hasNegativeBalance'] && !$deleteProfileData['isCentralBank'] && !$deleteProfileData['isFinalAdmin'])
|
|
<div class="mt-6">
|
|
<x-jetstream.label for="balanceHandlingOption" value="{{ __('What would you like to do with your remaining balance?') }}" />
|
|
|
|
<div class="mt-3 space-y-3">
|
|
<label class="flex items-start">
|
|
<input type="radio" wire:model.live="balanceHandlingOption" value="donate" class="mt-1 text-theme-brand focus:ring-theme-brand">
|
|
<span class="ml-3">
|
|
<span class="block text-sm font-medium text-theme-primary">{{ __('Donate to an organization') }}</span>
|
|
<span class="block text-sm text-theme-muted">{{ __('Transfer your remaining balance to an organization of your choice') }}</span>
|
|
</span>
|
|
</label>
|
|
|
|
<label class="flex items-start">
|
|
<input type="radio" wire:model.live="balanceHandlingOption" value="delete" class="mt-1 text-theme-brand focus:ring-theme-brand">
|
|
<span class="ml-3">
|
|
<span class="block text-sm font-medium text-theme-primary">{{ __('Delete balance') }}</span>
|
|
<span class="block text-sm text-theme-muted">{{ __('Permanently delete your balance total') }}</span>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
@if($balanceHandlingOption === 'donate')
|
|
<div class="mt-4">
|
|
<livewire:to-account-organization-only :label="__('Select organization account')" />
|
|
<x-jetstream.input-error for="donationAccountId" class="mt-2" />
|
|
|
|
@if($donationExceedsLimit && $donationLimitError)
|
|
<div class="mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<div class="flex items-start">
|
|
<svg class="w-5 h-5 text-red-600 mt-0.5 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
</svg>
|
|
<div>
|
|
<div class="text-sm font-semibold text-red-800">{{ __('Donation exceeds account limits') }}</div>
|
|
<p class="mt-1 text-sm text-red-700">{{ $donationLimitError }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
@if(!$deleteProfileData['isCentralBank'] && !$deleteProfileData['hasNegativeBalance'] && !$deleteProfileData['isFinalAdmin'])
|
|
<div class="mt-4">
|
|
<x-jetstream.label value="{{ __('Admin password confirmation') }}" />
|
|
<x-jetstream.input type="password" class="mt-1 block w-3/4"
|
|
placeholder="{{ __('Your admin password') }}"
|
|
wire:model="adminPassword" />
|
|
<x-jetstream.input-error for="adminPassword" class="mt-2" />
|
|
</div>
|
|
@endif
|
|
</x-slot>
|
|
|
|
<x-slot name="footer">
|
|
<x-jetstream.secondary-button wire:click="resetForm" wire:loading.attr="disabled">
|
|
{{ __('Cancel') }}
|
|
</x-jetstream.secondary-button>
|
|
|
|
<x-jetstream.danger-button
|
|
class="ml-3"
|
|
wire:click="deleteProfile"
|
|
wire:loading.attr="disabled"
|
|
:disabled="$deleteProfileData['hasNegativeBalance'] || $deleteProfileData['isCentralBank'] || $deleteProfileData['isFinalAdmin'] || $donationExceedsLimit">
|
|
{{ __('Delete profile') }}
|
|
</x-jetstream.danger-button>
|
|
</x-slot>
|
|
</x-jetstream.dialog-modal>
|
|
@endif
|
|
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('scroll-to-top', event => {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
});
|
|
</script>
|
|
@endpush
|
|
|
|
</div>
|