affiliations

This commit is contained in:
ghassan committed 2026-01-27 16:34:46 +03:00
1 parent 4f3f9c9aad
commit 0b5b3dd3ee
177 files changed
+9660 -8531

No files matched your search

@@ -0,0 +1,131 @@
@props([
'name' => 'date',
'id' => 'date',
'value' => '',
'required' => false,
'error' => null,
'label' => 'Date',
'minYear' => null,
'maxYear' => null,
'minAge' => null,
'maxAge' => null
])
@php
$currentYear = date('Y');
// Calculate year range - support both direct year values and age-based calculation
if ($minYear !== null && $maxYear !== null) {
// Direct year range specified
$startYear = $maxYear;
$endYear = $minYear;
} elseif ($minAge !== null && $maxAge !== null) {
// Age-based calculation (for birthdates)
$startYear = $currentYear - $minAge;
$endYear = $currentYear - $maxAge;
} else {
// Default: current year to 100 years ago
$startYear = $currentYear;
$endYear = $currentYear - 100;
}
// Parse existing value
$selectedDay = '';
$selectedMonth = '';
$selectedYear = '';
if ($value) {
$parts = explode('-', $value);
if (count($parts) === 3) {
$selectedYear = $parts[0];
$selectedMonth = $parts[1];
$selectedDay = $parts[2];
}
}
@endphp
<div class="mb-3">
<label class="form-label">{{ $label }}@if($required) <span class="text-danger">*</span>@endif</label>
<div class="row g-2">
<div class="col-4">
<select id="{{ $id }}_day"
class="form-select @if($error) is-invalid @endif"
{{ $required ? 'required' : '' }}
onchange="updateDate_{{ $id }}()">
<option value="">Day</option>
@for($day = 1; $day <= 31; $day++)
@php $dayPadded = str_pad($day, 2, '0', STR_PAD_LEFT); @endphp
<option value="{{ $dayPadded }}" {{ $selectedDay == $dayPadded ? 'selected' : '' }}>
{{ $day }}
</option>
@endfor
</select>
</div>
<div class="col-4">
<select id="{{ $id }}_month"
class="form-select @if($error) is-invalid @endif"
{{ $required ? 'required' : '' }}
onchange="updateDate_{{ $id }}()">
<option value="">Month</option>
<option value="01" {{ $selectedMonth == '01' ? 'selected' : '' }}>January</option>
<option value="02" {{ $selectedMonth == '02' ? 'selected' : '' }}>February</option>
<option value="03" {{ $selectedMonth == '03' ? 'selected' : '' }}>March</option>
<option value="04" {{ $selectedMonth == '04' ? 'selected' : '' }}>April</option>
<option value="05" {{ $selectedMonth == '05' ? 'selected' : '' }}>May</option>
<option value="06" {{ $selectedMonth == '06' ? 'selected' : '' }}>June</option>
<option value="07" {{ $selectedMonth == '07' ? 'selected' : '' }}>July</option>
<option value="08" {{ $selectedMonth == '08' ? 'selected' : '' }}>August</option>
<option value="09" {{ $selectedMonth == '09' ? 'selected' : '' }}>September</option>
<option value="10" {{ $selectedMonth == '10' ? 'selected' : '' }}>October</option>
<option value="11" {{ $selectedMonth == '11' ? 'selected' : '' }}>November</option>
<option value="12" {{ $selectedMonth == '12' ? 'selected' : '' }}>December</option>
</select>
</div>
<div class="col-4">
<select id="{{ $id }}_year"
class="form-select @if($error) is-invalid @endif"
{{ $required ? 'required' : '' }}
onchange="updateDate_{{ $id }}()">
<option value="">Year</option>
@for($year = $startYear; $year >= $endYear; $year--)
<option value="{{ $year }}" {{ $selectedYear == $year ? 'selected' : '' }}>
{{ $year }}
</option>
@endfor
</select>
</div>
</div>
<input type="hidden" id="{{ $id }}" name="{{ $name }}" value="{{ $value }}">
@if($error)
<span class="invalid-feedback d-block" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize all date dropdown components
document.querySelectorAll('[id$="_day"]').forEach(function(daySelect) {
const baseId = daySelect.id.replace('_day', '');
if (document.getElementById(baseId + '_month') && document.getElementById(baseId + '_year')) {
// Component exists, initialize it
window['updateDate_' + baseId] = function() {
const day = document.getElementById(baseId + '_day').value;
const month = document.getElementById(baseId + '_month').value;
const year = document.getElementById(baseId + '_year').value;
const hidden = document.getElementById(baseId);
if (day && month && year) {
hidden.value = `${year}-${month}-${day}`;
} else {
hidden.value = '';
}
};
}
});
});
</script>
@endpush
@endonce
@@ -22,7 +22,7 @@
onkeydown="event.stopPropagation()"
onkeyup="event.stopPropagation()">
<div class="country-list" id="{{ $id }}List" style="max-height: 300px; overflow-y: auto;">
<div class="country-list country-code-dropdown-list" id="{{ $id }}List" data-component-id="{{ $id }}" style="max-height: 300px; overflow-y: auto;">
<!-- Countries will be populated by JavaScript -->
</div>
</div>
@@ -162,9 +162,9 @@
}
}
// Initialize only country code dropdowns (not nationality dropdowns)
document.querySelectorAll('[id$="country_codeList"]').forEach(function(listElement) {
const componentId = listElement.id.replace('List', '');
// Initialize all country code dropdowns
document.querySelectorAll('.country-code-dropdown-list').forEach(function(listElement) {
const componentId = listElement.getAttribute('data-component-id');
initializeCountryDropdown(componentId, countries);
});
@@ -3,8 +3,10 @@
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Currency</label>
<select id="{{ $id }}"
class="form-select currency-select @error($name) is-invalid @enderror"
class="form-select currency-dropdown-select @error($name) is-invalid @enderror"
name="{{ $name }}"
data-component-id="{{ $id }}"
data-initial-value="{{ $value }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Currency</option>
</select>
@@ -23,60 +25,60 @@
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Initialize all currency dropdowns
document.querySelectorAll('.currency-dropdown-select').forEach(function(selectElement) {
const initialValue = selectElement.getAttribute('data-initial-value');
// Get unique currencies
const uniqueCurrencies = {};
countries.forEach(country => {
if (!uniqueCurrencies[country.currency]) {
uniqueCurrencies[country.currency] = {
currency: country.currency,
currency_symbol: country.currency_symbol,
flag: country.flag,
name: country.name
};
}
}
});
// Populate dropdown
Object.values(uniqueCurrencies).forEach(currencyData => {
const option = document.createElement('option');
option.value = currencyData.currency;
option.textContent = `${currencyData.name} (${currencyData.currency} ${currencyData.currency_symbol})`;
option.setAttribute('data-flag', currencyData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
// Get unique currencies
const uniqueCurrencies = {};
countries.forEach(country => {
if (country.currency && !uniqueCurrencies[country.currency]) {
uniqueCurrencies[country.currency] = {
currency: country.currency,
currency_symbol: country.currency_symbol || '',
flag: country.flag,
name: country.name
};
}
});
}
// Populate dropdown
Object.values(uniqueCurrencies).forEach(currencyData => {
const option = document.createElement('option');
option.value = currencyData.currency;
option.textContent = `${currencyData.currency} - ${currencyData.name} ${currencyData.currency_symbol}`;
option.setAttribute('data-flag', currencyData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
});
})
.catch(error => console.error('Error loading countries:', error));
});
@@ -0,0 +1,18 @@
@props(['name' => 'gender', 'id' => 'gender', 'value' => '', 'required' => false, 'error' => null])
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Gender</label>
<select id="{{ $id }}"
class="form-select @if($error) is-invalid @endif"
name="{{ $name }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Gender</option>
<option value="m" {{ $value == 'm' ? 'selected' : '' }}>Male</option>
<option value="f" {{ $value == 'f' ? 'selected' : '' }}>Female</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@@ -0,0 +1,84 @@
@props([
'id' => 'image_upload_' . uniqid(),
'name' => 'image',
'width' => 300,
'height' => 300,
'shape' => 'square',
'folder' => 'uploads',
'filename' => 'image_' . time(),
'uploadUrl' => '',
'currentImage' => '',
'placeholder' => 'No image',
'placeholderIcon' => 'bi-image',
'buttonText' => 'Change Photo',
'buttonClass' => 'btn btn-success',
'previewClass' => '',
'showPreview' => true,
'rounded' => false
])
@php
$cacheBuster = $currentImage ? '?v=' . time() : '';
$imageUrl = $currentImage ? $currentImage . $cacheBuster : '';
$borderRadius = $rounded ? '50%' : '8px';
$previewWidth = $width . 'px';
$previewHeight = $height . 'px';
@endphp
<div class="image-upload-component text-center {{ $previewClass }}">
@if($showPreview)
<div class="image-preview mb-3">
@if($imageUrl)
<img src="{{ $imageUrl }}"
alt="Preview"
id="{{ $id }}_preview"
class="image-upload-preview"
style="width: {{ $previewWidth }}; height: {{ $previewHeight }}; object-fit: cover; border: 3px solid #dee2e6; border-radius: {{ $borderRadius }};">
@else
<div class="image-placeholder"
id="{{ $id }}_placeholder"
style="width: {{ $previewWidth }}; height: {{ $previewHeight }}; background-color: #f0f0f0; border: 3px solid #dee2e6; border-radius: {{ $borderRadius }}; display: flex; align-items: center; justify-content: center; margin: 0 auto;">
<div class="text-center">
<i class="bi {{ $placeholderIcon }}" style="font-size: 60px; color: #dee2e6;"></i>
<p class="text-muted mt-2 mb-0">{{ $placeholder }}</p>
</div>
</div>
@endif
</div>
@endif
<x-takeone-cropper
:id="$id"
:width="$width"
:height="$height"
:shape="$shape"
:folder="$folder"
:filename="$filename"
:uploadUrl="$uploadUrl"
:currentImage="$currentImage"
:buttonText="$buttonText"
:buttonClass="$buttonClass"
/>
</div>
@once
@push('styles')
<style>
.image-upload-component {
display: inline-block;
}
.image-upload-preview {
transition: opacity 0.3s ease;
}
.image-upload-preview:hover {
opacity: 0.9;
}
.image-placeholder {
transition: all 0.3s ease;
}
.image-placeholder:hover {
background-color: #e9ecef !important;
}
</style>
@endpush
@endonce
@@ -26,7 +26,7 @@
onkeydown="event.stopPropagation()"
onkeyup="event.stopPropagation()">
<div class="country-list" id="{{ $id }}List" style="max-height: 300px; overflow-y: auto;">
<div class="country-list nationality-dropdown-list" id="{{ $id }}List" data-component-id="{{ $id }}" style="max-height: 300px; overflow-y: auto;">
<!-- Countries will be populated by JavaScript -->
</div>
</div>
@@ -72,9 +72,9 @@
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
// Initialize only nationality dropdowns (not country code dropdowns)
document.querySelectorAll('[id$="nationalityList"]').forEach(function(listElement) {
const componentId = listElement.id.replace('List', '');
// Initialize all nationality/country dropdowns by finding elements with data attribute
document.querySelectorAll('.nationality-dropdown-list').forEach(function(listElement) {
const componentId = listElement.getAttribute('data-component-id');
initializeNationalityDropdown(componentId, countries);
});
})
@@ -3,8 +3,10 @@
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Timezone</label>
<select id="{{ $id }}"
class="form-select timezone-select @error($name) is-invalid @enderror"
class="form-select timezone-dropdown-select @error($name) is-invalid @enderror"
name="{{ $name }}"
data-component-id="{{ $id }}"
data-initial-value="{{ $value }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Timezone</option>
</select>
@@ -23,59 +25,59 @@
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Initialize all timezone dropdowns
document.querySelectorAll('.timezone-dropdown-select').forEach(function(selectElement) {
const initialValue = selectElement.getAttribute('data-initial-value');
// Get unique timezones
const uniqueTimezones = {};
countries.forEach(country => {
if (!uniqueTimezones[country.timezone]) {
uniqueTimezones[country.timezone] = {
timezone: country.timezone,
flag: country.flag,
name: country.name
};
}
}
});
// Populate dropdown
Object.values(uniqueTimezones).forEach(timezoneData => {
const option = document.createElement('option');
option.value = timezoneData.timezone;
option.textContent = `${timezoneData.name} (${timezoneData.timezone})`;
option.setAttribute('data-flag', timezoneData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
// Get unique timezones
const uniqueTimezones = {};
countries.forEach(country => {
if (country.timezone && !uniqueTimezones[country.timezone]) {
uniqueTimezones[country.timezone] = {
timezone: country.timezone,
flag: country.flag,
name: country.name
};
}
});
}
// Populate dropdown
Object.values(uniqueTimezones).forEach(timezoneData => {
const option = document.createElement('option');
option.value = timezoneData.timezone;
option.textContent = `${timezoneData.name} (${timezoneData.timezone})`;
option.setAttribute('data-flag', timezoneData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
});
})
.catch(error => console.error('Error loading countries:', error));
});
@@ -0,0 +1,227 @@
@props(['position' => 'top-right'])
@once
@push('styles')
<style>
/* Toast Notification Styles */
.toast-container {
position: fixed;
z-index: 9999;
pointer-events: none;
}
.toast-container.top-right {
top: 20px;
right: 20px;
}
.toast-container.top-left {
top: 20px;
left: 20px;
}
.toast-container.top-center {
top: 20px;
left: 50%;
transform: translateX(-50%);
}
.toast-container.bottom-right {
bottom: 20px;
right: 20px;
}
.toast-container.bottom-left {
bottom: 20px;
left: 20px;
}
.toast-container.bottom-center {
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.toast-notification {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
min-width: 300px;
max-width: 400px;
margin-bottom: 10px;
pointer-events: auto;
animation: toastSlideIn 0.3s ease-out;
}
.toast-notification.hiding {
animation: toastFadeOut 0.3s ease-in forwards;
}
.toast-notification.success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
}
.toast-notification.error {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: white;
}
.toast-notification.warning {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: white;
}
.toast-notification.info {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: white;
}
.toast-notification .toast-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.toast-notification .toast-content {
flex: 1;
}
.toast-notification .toast-title {
font-weight: 600;
font-size: 15px;
margin-bottom: 2px;
}
.toast-notification .toast-message {
font-size: 13px;
opacity: 0.9;
}
.toast-notification .toast-close {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
cursor: pointer;
padding: 4px 8px;
font-size: 16px;
line-height: 1;
border-radius: 6px;
transition: background 0.2s;
}
.toast-notification .toast-close:hover {
background: rgba(255, 255, 255, 0.3);
}
.toast-notification .toast-progress {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: rgba(255, 255, 255, 0.4);
border-radius: 0 0 12px 12px;
animation: toastProgress 3s linear forwards;
}
@keyframes toastSlideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes toastFadeOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
@keyframes toastProgress {
from { width: 100%; }
to { width: 0%; }
}
</style>
@endpush
@push('scripts')
<script>
const Toast = {
container: null,
position: '{{ $position }}',
init() {
if (!this.container) {
this.container = document.createElement('div');
this.container.className = `toast-container ${this.position}`;
document.body.appendChild(this.container);
}
return this.container;
},
show(type, title, message, duration = 3000) {
const container = this.init();
const icons = {
success: '<i class="bi bi-check-lg"></i>',
error: '<i class="bi bi-x-lg"></i>',
warning: '<i class="bi bi-exclamation-lg"></i>',
info: '<i class="bi bi-info-lg"></i>'
};
const toast = document.createElement('div');
toast.className = `toast-notification ${type}`;
toast.style.position = 'relative';
toast.innerHTML = `
<span class="toast-icon">${icons[type] || icons.info}</span>
<div class="toast-content">
<div class="toast-title">${title}</div>
<div class="toast-message">${message}</div>
</div>
<button class="toast-close" aria-label="Close">×</button>
<div class="toast-progress" style="animation-duration: ${duration}ms"></div>
`;
// Close button handler
toast.querySelector('.toast-close').addEventListener('click', () => {
this.hide(toast);
});
container.appendChild(toast);
// Auto remove
if (duration > 0) {
setTimeout(() => this.hide(toast), duration);
}
return toast;
},
hide(toast) {
if (!toast || !toast.parentElement) return;
toast.classList.add('hiding');
setTimeout(() => {
if (toast.parentElement) toast.remove();
}, 300);
},
success(title, message, duration) {
return this.show('success', title, message, duration);
},
error(title, message, duration) {
return this.show('error', title, message, duration);
},
warning(title, message, duration) {
return this.show('warning', title, message, duration);
},
info(title, message, duration) {
return this.show('info', title, message, duration);
}
};
// Global function for backward compatibility
function showToast(type, title, message, duration = 3000) {
return Toast.show(type, title, message, duration);
}
</script>
@endpush
@endonce