Files
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

416 lines
23 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div>
<section>
<!-- Card -->
<div class="bg-theme-background p-8 md:12 lg:p-16 shadow-xl sm:rounded-lg">
@php
// Don't blur if viewer is Admin/Bank viewing an incomplete-only profile
$isIncompleteOnly = $isIncomplete && !$inactive && !$emailUnverifiedLabel;
$canViewIncomplete = in_array(get_class(getActiveProfile()), ['App\Models\Admin', 'App\Models\Bank']);
// Never blur when viewing your own profile
$isViewingOwnProfile = getActiveProfile() &&
get_class(getActiveProfile()) === get_class($profile) &&
getActiveProfile()->id === $profile->id;
$shouldBlur = $hidden && !($isIncompleteOnly && $canViewIncomplete) && !$isViewingOwnProfile;
@endphp
<div class="{{ $shouldBlur ? 'blur-md pointer-events-none select-none' : '' }}">
<!-- Top section -->
<div class="flex-start flex flex-col justify-center gap-4 sm:flex-row sm:justify-between">
<div class="flex justify-between sm:contents">
<!-- Profile photo -->
<div class="flex-shrink-0">
<img alt="{{ $profile->name }}"
class="h-36 w-36 min-h-36 min-w-36 lg:h-56 lg:w-56 lg:min-h-56 lg:min-w-56 aspect-square rounded-full profile-photo object-cover outline outline-1 outline-offset-1 outline-theme-secondary"
src="{{ Storage::url($profile->profile_photo_path) }}">
</div>
<!-- Reaction buttons visible only on extra small screens (top right next to photo) -->
<div class="flex sm:hidden">
<!-- Star button -->
<span>
@livewire('reaction-button', [
'typeName' => 'star',
'showCounter' => true,
'reactionCounter' => $profile->reactionCounter,
'modelInstance' => $profile,
])
</span>
<!-- Bookmark button -->
<span>
@livewire('reaction-button', [
'typeName' => 'bookmark',
'showCounter' => false,
'reactionCounter' => null,
'modelInstance' => $profile,
])
</span>
</div>
</div>
<div class="flex flex-col mx-6 justify-between">
<div class="">
<!-- Name details aligned to the left -->
<div class="">
<h1
class="mb-2 text-2xl lg:text-4xl font-extrabold text-black group-hover:text-white dark:text-white">
{{ $profile->name }}
</h1>
<div class="text-base lg:text-lg text-black">
{{ $profile->full_name }} {{ $age }}
</div>
@if ($inactiveLabel || $removedSince || $incompleteLabel || $noExchangesYetLabel)
@if ($removedSince)
<div class="text-base lg:text-lg text-red-700">
{{ __('Removed') }}
</div>
@elseif ($inactiveLabel)
<div class="text-base lg:text-lg text-red-700">
{{ __('Inactive') }}
</div>
@endif
@if ($incompleteLabel && !$removedSince)
<div class="text-base lg:text-lg text-red-700">
{{ __('Incomplete profile') }}
</div>
@endif
@if ($noExchangesYetLabel && !$removedSince)
<div class="text-base lg:text-lg text-red-700">
{{ __('No exchanges yet, but ready to help') }}
</div>
@endif
@endif
</div>
<div class="my-4 lg:my-6 text-base lg:text-lg leading-relaxed text-theme-primary font-semibold ">
{{ Illuminate\Support\Str::ucfirst($profile->about_short) }}
</div>
</div>
<div class="flex flex-col lg:flex-row lg:items-end lg:justify-between text-theme-primary gap-2">
<div class="flex flex-row items-end justify-between lg:gap-4">
<!-- Online status -->
@php
// Map profile class to guard name
$guardName = match(get_class($profile)) {
'App\Models\Admin' => 'admin',
'App\Models\Bank' => 'bank',
'App\Models\Organization' => 'organization',
default => 'web'
};
@endphp
<livewire:profile-status-badge :guard="$guardName" :profileId="$profile->id" :showIcon="true"
:showText="true" size="lg" />
<!-- Phone -->
@if (!empty($phone))
<div class="flex items-center gap-2">
<svg class="h-6 w-6 flex-shrink-0" fill="none" stroke-width="1.5"
stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
<a class="text-base lg:text-lg underline hover:text-theme-secondary" href="{{ $location['url'] }}" >
{{ $phone }}
</a>
</div>
@endif
</div>
<!-- Location -->
@if (!empty($location['url']) && !empty($location['name']))
<div class="flex items-start gap-2 min-w-0">
<svg class="h-6 w-6 flex-shrink-0" fill="none" stroke-width="1.5"
stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
<a class="text-base lg:text-lg underline hover:text-theme-secondary break-words" target="_blank" href="{{ $location['url'] }}">
{{ $location['name'] }}
</a>
</div>
@elseif (!empty($location['name']))
<div class="flex items-start gap-2 min-w-0">
<svg class="h-6 w-6 flex-shrink-0" fill="none" stroke-width="1.5"
stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
<span class="text-base lg:text-lg break-words">{{ $location['name'] }}</span>
</div>
@endif
</div>
</div>
<!-- Reaction buttons on all screens 640px and larger (aligned to the right) -->
<div class="hidden sm:flex lg:flex">
<!-- Star button -->
<span>
@livewire('reaction-button', [
'typeName' => 'star',
'showCounter' => true,
'reactionCounter' => $profile->reactionCounter,
'modelInstance' => $profile,
])
</span>
<!-- Bookmark button -->
<span>
@livewire('reaction-button', [
'typeName' => 'bookmark',
'showCounter' => false,
'reactionCounter' => null,
'modelInstance' => $profile,
])
</span>
</div>
</div>
</div>
<!-- Bottom section -->
<!-- About -->
@php
if (empty($profile->about) === true && !empty($profile->cyclos_skills) === true) {
$about = $profile->cyclos_skills;
} else {
$about = $profile->about;
}
@endphp
@if ($about && strlen(strip_tags(html_entity_decode($about))) > 350)
<div class="text-sm lg:text-base leading-relaxed" x-data="{ showAboutFullText: false }">
<p class="my-10 text-theme-primary font-semibold">
<span x-show="!showAboutFullText">{!! nl2br(e(Illuminate\Support\Str::limit(strip_tags(html_entity_decode($about)), 350))) !!}</span>
&nbsp;&nbsp;
<span x-show="showAboutFullText">{!! nl2br(e(strip_tags(html_entity_decode($about)))) !!}</span> &nbsp;&nbsp;<a @click.prevent="showAboutFullText = !showAboutFullText"
class="text-sm lg:text-base text-theme-primary underline transition-colors duration-100 hover:text-theme-primary hover:underline font-normal whitespace-nowrap"
href="#"><span x-show="!showAboutFullText">{{ __('Read more') }}<svg class="inline h-4 w-4 rtl:-scale-x-100" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
fill-rule="evenodd"></path>
</svg></span><span x-show="showAboutFullText"><svg class="inline h-4 w-4 scale-x-[-1] transform" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
fill-rule="evenodd"></path>
</svg> {{ __('Show less') }}</span></a>
</p>
</div>
@else
<div class="">
<p class="my-10 text-sm lg:text-base text-theme-primary font-semibold">
{!! nl2br(e(strip_tags(html_entity_decode($about)))) !!}
</p>
</div>
@endif
<!--- Languages -->
@if ($profile->languages->count() > 0)
<div class="mt-10 flex flex-wrap gap-x-4 gap-y-1 text-sm lg:text-base text-theme-primary">
@foreach ($profile->languages as $language)
<div class="flex items-center gap-2">
<span>{{ $language['flag'] }}</span>
<span>{{ trans($language['name']) }}, {{ trans($language->competence_name) }}</span>
</div>
@endforeach
</div>
@if ($profile->lang_preference)
<div class="mb-10 mt-2 flex items-center text-sm lg:text-base text-theme-primary">
{{ trans($profile->lang_preference) }} {{ __('is preferred') }}
</div>
@endif
@endif
<!--- Skills -->
@if (($skills->count() > 0))
<div class="my-10 flex flex-wrap gap-2">
@foreach ($skills->sortBy('category_color') as $skill)
<span class="bg-{{ $skill['category_color'] . '-400' }} rounded px-2 py-1 text-sm lg:text-base text-black lg:block max-w-full truncate"
style="cursor: default;" title="{{ $skill['category_path'] }}">{{ $skill['name'] }}</span>
@endforeach
</div>
@endif
<!-- Motivation --->
@if (!empty($profile->motivation))
<div class="mt-10 flex flex-col item-start text-sm lg:text-base leading-relaxed text-theme-primary">
<div class="font-semibold">
{{ __('Why Timebank?') }}
</div>
{{ $profile->motivation }}
</div>
@endif
<!-- Acivity info -->
<div class="my-8 grid grid-cols-1 gap-y-8 gap-x-4 md:grid-cols-3 text-sm lg:text-base text-theme-primary">
<!--- Last login, last exchange, registered since -->
<div>
@if($lastLoginAt || $lastLoginAt || $registeredSince)
<p class="font-semibold">{{ __('Activity') }} </p>
@endif
@if ($lastLoginAt)
<div>
{{ __('Last login') . ' ' . $lastLoginAt }}
</div>
@endif
@if ($lastExchangeAt)
<div>
{{ __('Last exchange') . ' ' . $lastExchangeAt }}
</div>
@endif
<div>
{{ __('Registered') . ' ' . $registeredSince }}
</div>
@if ($inactiveSince || $removedSince)
@if ($removedSince)
<div class="text-red-700">
{{ __('Removed') . ' ' . $removedSince }}
</div>
@else
<div class="text-red-700">
{{ __('Inactive') . ' ' . $inactiveSince }}
</div>
@endif
@endif
@if ($emailUnverifiedLabel)
<div class="text-red-700">
{{ __('Email not verified') }}
</div>
@endif
</div>
<!--- Transaction info-->
@if (
(($accountsTotals['transfersReceived'] ?? false) && ($accountsTotals['transfersGiven'] ?? false)) ||
($accountsTotals['transfersReceivedOrGiven'] ?? false))
<div>
<p class="font-semibold">{{ __('Transactions') }} </p>
@if ($accountsTotals['transfersReceived'] && $accountsTotals['transfersGiven'])
@if ($accountsTotals['transfersReceived'])
<p>{{ $accountsTotals['transfersReceived'] }}
{{ __('× received') . ' ' . trans(timebank_config('account_info.' . $type . '.countTransfersSince_humanReadable')) }}
</p>
@endif
@if ($accountsTotals['transfersGiven'])
<p>{{ $accountsTotals['transfersGiven'] }}
{{ __('× given') . ' ' . trans(timebank_config('account_info.' . $type . '.countTransfersSince_humanReadable')) }}
</p>
@endif
@elseif ($accountsTotals['transfersReceivedOrGiven'])
<p>{{ $accountsTotals['transfersReceivedOrGiven'] }}
{{ __('×') . ' ' . trans(timebank_config('account_info.' . $type . '.countTransfersSince_humanReadable')) }}
</p>
@endif
</div>
@endif
<!-- Account info -->
@if ($accountsTotals['sumBalances'])
<div>
<p class="font-semibold">{{ __('Balance') }} </p>
<p>{{ tbFormat($accountsTotals['sumBalances']) }} {{ __('available') }}</p>
</div>
@endif
</div>
<!-- Website -->
@if ($profile->website)
<div class="my-6 text-sm lg:text-base">
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-4 w-4 lg:h-6 lg:w-6 flex-shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
</svg>
<a class="underline hover:text-theme-secondary" href="{{ $profile->website }}" target="_blank">
{{ Illuminate\Support\Str::after($profile->website, '://') }}
</a>
</div>
</div>
@endif
<!-- Social Media -->
@if ($socials && count($socials) > 0)
<div class="mt-6 mb-12 md:mb-8 lg:mb-0">
<div class="flex flex-wrap items-center gap-2 g:gap-4">
@foreach ($socials as $value)
<a aria-label="{{ $value->name }}"
class="text-theme-light hover:text-theme-light dark:text-theme-light dark:hover:text-theme-light"
href="{{ str_starts_with($value->pivot->user_on_social, 'https://')
? $value->pivot->user_on_social
: str_replace('#', $value->pivot->server_of_social, $value->url_structure) . $value->pivot->user_on_social }}"
target="_blank">
<img alt="{{ $value->name }}" class="h-6 w-6 lg:h-8 lg:w-8 opacity-100 hover:opacity-75"
src="{{ Storage::url($value->icon) }}"
title="{{ $value->pivot->user_on_social . ' on ' . $value->name }}" />
</a>
@endforeach
</div>
</div>
@endif
<!-- Action buttons -->
<div class="col-span-6 row-start-4">
<div class="">
<div class="bg-theme-background flex items-center justify-end gap-8 text-right">
@if (
(get_class(getActiveProfile()) === get_class($profile) && getActiveProfile()->id === $profile->id) ||
$profile->isRemoved())
<!--- Disabled buttons -->
<x-jetstream.button disabled wire:click="payButton">
{{ __('Pay') }}
</x-jetstream.button>
<x-jetstream.button disabled wire:click="createConversation">
{{ __('Send Message') }}
</x-jetstream.button>
@else
<!-- Enabled buttons -->
<x-jetstream.button wire:click="payButton" :disabled="!canActiveProfileCreatePayments()">
{{ __('Pay') }}
</x-jetstream.button>
<x-jetstream.button wire:click="createConversation">
{{ __('Send Message') }}
</x-jetstream.button>
@endif
</div>
</div>
</div>
</div>
@php
$showPrivateCalls = $isViewingOwnProfile || $canViewIncomplete;
$profileCalls = \App\Http\Livewire\Calls\ProfileCalls::getCallsForProfile($profile, $showPrivateCalls);
@endphp
@if ($profileCalls->isNotEmpty())
<div class="mt-8 mx-2 sm:mx-0">
<h3 class="mb-4 text-lg font-medium text-theme-primary">{{ __('Active calls') }}</h3>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
@foreach ($profileCalls as $index => $result)
<x-call-card :result="$result" :index="$index" :show-callable="false" height-class="h-[380px] md:h-[500px] lg:h-[380px]" />
@endforeach
</div>
</div>
@endif
</div>
</section>
</div>