added club create and edit modal
This commit is contained in:
1 parent
a4bc983660
commit
da688375e3
38 files changed
+9194
-44
No files matched your search
@@ -0,0 +1,743 @@
|
||||
@props(['club' => null, 'mode' => 'create'])
|
||||
|
||||
@php
|
||||
$isEdit = $mode === 'edit' && $club;
|
||||
@endphp
|
||||
|
||||
<div class="container-fluid px-0">
|
||||
<h5 class="fw-bold mb-3">Identity & Branding</h5>
|
||||
<p class="text-muted mb-4">Define your club's public identity, URL, and visual branding</p>
|
||||
|
||||
<!-- Club Slug -->
|
||||
<div class="mb-4">
|
||||
<label for="slug" class="form-label">
|
||||
Club Slug <span class="text-danger">*</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white">
|
||||
<i class="bi bi-link-45deg"></i>
|
||||
</span>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="slug"
|
||||
name="slug"
|
||||
value="{{ $club->slug ?? old('slug') }}"
|
||||
required
|
||||
pattern="[a-z0-9-]+"
|
||||
data-error-message="Slug is required and must be URL-friendly"
|
||||
placeholder="e.g., bh-taekwondo">
|
||||
</div>
|
||||
<small class="text-muted">URL-friendly identifier (lowercase letters, numbers, and hyphens only)</small>
|
||||
<div class="invalid-feedback">Please enter a valid slug.</div>
|
||||
</div>
|
||||
|
||||
<!-- Club URL Preview -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Club Public URL</label>
|
||||
<div class="border rounded p-3" style="background-color: hsl(var(--muted) / 0.2);">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-globe text-primary"></i>
|
||||
<code id="clubUrlPreview" class="text-primary mb-0">{{ url('/club/') }}/{{ $club->slug ?? 'your-club-slug' }}</code>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-auto" onclick="copyClubUrl()">
|
||||
<i class="bi bi-clipboard"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">This is the public URL where members can view your club</small>
|
||||
</div>
|
||||
|
||||
<!-- QR Code -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Club QR Code</label>
|
||||
<div class="border rounded p-4 text-center" style="background-color: hsl(var(--muted) / 0.1);">
|
||||
<div id="qrCodeContainer" class="d-inline-block mb-3"></div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="downloadQRCode()">
|
||||
<i class="bi bi-download me-2"></i>Download QR Code
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="printQRCode()">
|
||||
<i class="bi bi-printer me-2"></i>Print
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">Share this QR code for easy access to your club's page</small>
|
||||
</div>
|
||||
|
||||
<!-- Logo and Cover Images -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label d-block">Club Logo <span class="text-danger">*</span></label>
|
||||
<div class="text-center">
|
||||
<!-- Logo Preview -->
|
||||
<div class="cropper-preview-container mb-2" id="logoPreviewContainer">
|
||||
@if($isEdit && $club->logo)
|
||||
<img src="{{ asset('storage/' . $club->logo) }}"
|
||||
id="logoPreview"
|
||||
class="cropper-preview-image"
|
||||
style="width: 150px; height: 150px; border-radius: 8px; border: 2px solid #dee2e6;">
|
||||
@else
|
||||
<div id="logoPreview"
|
||||
class="cropper-preview-placeholder"
|
||||
style="width: 150px; height: 150px; border-radius: 8px; border: 2px dashed #dee2e6; display: flex; align-items: center; justify-content: center; background-color: #f0f0f0; color: #6c757d;">
|
||||
<i class="bi bi-image" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<input type="hidden" name="logo" id="logoInput" value="{{ $isEdit && $club->logo ? $club->logo : '' }}">
|
||||
<input type="hidden" name="logo_folder" value="clubs/logos">
|
||||
<input type="hidden" name="logo_filename" value="logo_{{ time() }}">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="openLogoCropper()">
|
||||
<i class="bi bi-camera me-2"></i>Upload Logo
|
||||
</button>
|
||||
<small class="text-muted d-block mt-2">Square image recommended (400x400px)</small>
|
||||
<small class="text-muted d-block">Used as main logo and favicon</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label d-block">Cover Image</label>
|
||||
<div class="text-center">
|
||||
<!-- Cover Preview -->
|
||||
<div class="cropper-preview-container mb-2" id="coverPreviewContainer">
|
||||
@if($isEdit && $club->cover_image)
|
||||
<img src="{{ asset('storage/' . $club->cover_image) }}"
|
||||
id="coverPreview"
|
||||
class="cropper-preview-image"
|
||||
style="width: 250px; height: 83px; border-radius: 8px; border: 2px solid #dee2e6;">
|
||||
@else
|
||||
<div id="coverPreview"
|
||||
class="cropper-preview-placeholder"
|
||||
style="width: 250px; height: 83px; border-radius: 8px; border: 2px dashed #dee2e6; display: flex; align-items: center; justify-content: center; background-color: #f0f0f0; color: #6c757d;">
|
||||
<i class="bi bi-image" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<input type="hidden" name="cover_image" id="coverInput" value="{{ $isEdit && $club->cover_image ? $club->cover_image : '' }}">
|
||||
<input type="hidden" name="cover_image_folder" value="clubs/covers">
|
||||
<input type="hidden" name="cover_image_filename" value="cover_{{ time() }}">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="openCoverCropper()">
|
||||
<i class="bi bi-camera me-2"></i>Upload Cover
|
||||
</button>
|
||||
<small class="text-muted d-block mt-2">Wide banner image (1200x400px)</small>
|
||||
<small class="text-muted d-block">Used for club profile header</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PART 2: Internal Cropper Overlays (NOT separate modals) -->
|
||||
<!-- Logo Cropper Overlay -->
|
||||
<div id="logoCropperOverlay" class="cropper-overlay" style="display: none;">
|
||||
<div class="cropper-panel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0">Crop Logo</h5>
|
||||
<button type="button" class="btn-close" onclick="closeLogoCropper()"></button>
|
||||
</div>
|
||||
|
||||
<input type="file" id="logoFileInput" class="form-control form-control-sm mb-3" accept="image/*">
|
||||
|
||||
<div id="logoBox" class="takeone-canvas" style="height: 400px; background: #111; border-radius: 8px;"></div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Zoom</label>
|
||||
<input type="range" class="form-range" id="logoZoom" min="0" max="100" step="1" value="0">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Rotation</label>
|
||||
<input type="range" class="form-range" id="logoRotation" min="-180" max="180" step="1" value="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="button" class="btn btn-secondary flex-fill" onclick="closeLogoCropper()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary flex-fill" onclick="saveLogoCrop()">Save & Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cover Cropper Overlay -->
|
||||
<div id="coverCropperOverlay" class="cropper-overlay" style="display: none;">
|
||||
<div class="cropper-panel">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0">Crop Cover Image</h5>
|
||||
<button type="button" class="btn-close" onclick="closeCoverCropper()"></button>
|
||||
</div>
|
||||
|
||||
<input type="file" id="coverFileInput" class="form-control form-control-sm mb-3" accept="image/*">
|
||||
|
||||
<div id="coverBox" class="takeone-canvas" style="height: 400px; background: #111; border-radius: 8px;"></div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Zoom</label>
|
||||
<input type="range" class="form-range" id="coverZoom" min="0" max="100" step="1" value="0">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Rotation</label>
|
||||
<input type="range" class="form-range" id="coverRotation" min="-180" max="180" step="1" value="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="button" class="btn btn-secondary flex-fill" onclick="closeCoverCropper()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary flex-fill" onclick="saveCoverCrop()">Save & Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Media Links -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Social Media Links</label>
|
||||
<p class="text-muted small mb-3">Add links to your club's social media profiles</p>
|
||||
|
||||
<div id="socialLinksContainer">
|
||||
@if($isEdit && $club->socialLinks && $club->socialLinks->count() > 0)
|
||||
@foreach($club->socialLinks as $index => $link)
|
||||
<div class="social-link-row mb-3" data-index="{{ $index }}">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select class="form-select" name="social_links[{{ $index }}][platform]" required>
|
||||
<option value="">Select Platform</option>
|
||||
<option value="facebook" {{ $link->platform === 'facebook' ? 'selected' : '' }}>
|
||||
<i class="bi bi-facebook"></i> Facebook
|
||||
</option>
|
||||
<option value="instagram" {{ $link->platform === 'instagram' ? 'selected' : '' }}>
|
||||
Instagram
|
||||
</option>
|
||||
<option value="twitter" {{ $link->platform === 'twitter' ? 'selected' : '' }}>
|
||||
X (Twitter)
|
||||
</option>
|
||||
<option value="tiktok" {{ $link->platform === 'tiktok' ? 'selected' : '' }}>
|
||||
TikTok
|
||||
</option>
|
||||
<option value="youtube" {{ $link->platform === 'youtube' ? 'selected' : '' }}>
|
||||
YouTube
|
||||
</option>
|
||||
<option value="whatsapp" {{ $link->platform === 'whatsapp' ? 'selected' : '' }}>
|
||||
WhatsApp
|
||||
</option>
|
||||
<option value="website" {{ $link->platform === 'website' ? 'selected' : '' }}>
|
||||
Website
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
name="social_links[{{ $index }}][url]"
|
||||
value="{{ $link->url }}"
|
||||
placeholder="https://..."
|
||||
required>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="button" class="btn btn-outline-danger w-100" onclick="removeSocialLink(this)">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addSocialLink()">
|
||||
<i class="bi bi-plus-circle me-2"></i>Add Social Link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
/* PART 2: Cropper Overlay Styles */
|
||||
.cropper-overlay {
|
||||
position: fixed; /* PART 1 FIX: Fixed positioning to cover entire modal */
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
z-index: 1065; /* PART 1 FIX: Higher than modal content (1060) */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cropper-panel {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.takeone-canvas {
|
||||
position: relative;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
|
||||
.cropper-preview-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cropper-preview-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f0f0f0;
|
||||
border: 2px dashed #dee2e6;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.cropper-preview-image {
|
||||
object-fit: cover;
|
||||
border: 2px solid #dee2e6;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/cropme.min.css">
|
||||
<script src="https://unpkg.com/[email protected]/dist/cropme.min.js"></script>
|
||||
|
||||
<script>
|
||||
let socialLinkIndex = {{ $isEdit && $club->socialLinks ? $club->socialLinks->count() : 0 }};
|
||||
let qrCode = null;
|
||||
|
||||
// PART 2: Cropper instances
|
||||
let logoCropper = null;
|
||||
let coverCropper = null;
|
||||
const zoomMin = 0.01;
|
||||
const zoomMax = 3;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Update URL preview when slug changes
|
||||
const slugInput = document.getElementById('slug');
|
||||
const urlPreview = document.getElementById('clubUrlPreview');
|
||||
|
||||
if (slugInput && urlPreview) {
|
||||
slugInput.addEventListener('input', function() {
|
||||
const baseUrl = '{{ url("/club/") }}';
|
||||
const slug = this.value || 'your-club-slug';
|
||||
urlPreview.textContent = `${baseUrl}/${slug}`;
|
||||
|
||||
// Regenerate QR code
|
||||
generateQRCode(`${baseUrl}/${slug}`);
|
||||
});
|
||||
|
||||
// Mark slug as manually edited when user types
|
||||
slugInput.addEventListener('keydown', function() {
|
||||
this.dataset.manuallyEdited = 'true';
|
||||
});
|
||||
|
||||
// Initial QR code generation
|
||||
const initialUrl = urlPreview.textContent;
|
||||
generateQRCode(initialUrl);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate QR Code
|
||||
function generateQRCode(url) {
|
||||
const container = document.getElementById('qrCodeContainer');
|
||||
if (!container) return;
|
||||
|
||||
// Clear existing QR code
|
||||
container.innerHTML = '';
|
||||
|
||||
// Generate new QR code
|
||||
qrCode = new QRCode(container, {
|
||||
text: url,
|
||||
width: 200,
|
||||
height: 200,
|
||||
colorDark: "#000000",
|
||||
colorLight: "#ffffff",
|
||||
correctLevel: QRCode.CorrectLevel.H
|
||||
});
|
||||
}
|
||||
|
||||
// Copy club URL to clipboard
|
||||
function copyClubUrl() {
|
||||
const urlPreview = document.getElementById('clubUrlPreview');
|
||||
if (!urlPreview) return;
|
||||
|
||||
const url = urlPreview.textContent;
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
// Show success feedback
|
||||
if (typeof Toast !== 'undefined') {
|
||||
Toast.success('Copied!', 'Club URL copied to clipboard');
|
||||
} else {
|
||||
alert('URL copied to clipboard!');
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Download QR Code
|
||||
function downloadQRCode() {
|
||||
const container = document.getElementById('qrCodeContainer');
|
||||
if (!container) return;
|
||||
|
||||
const canvas = container.querySelector('canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const url = canvas.toDataURL('image/png');
|
||||
const link = document.createElement('a');
|
||||
link.download = 'club-qr-code.png';
|
||||
link.href = url;
|
||||
link.click();
|
||||
}
|
||||
|
||||
// Print QR Code
|
||||
function printQRCode() {
|
||||
const container = document.getElementById('qrCodeContainer');
|
||||
if (!container) return;
|
||||
|
||||
const canvas = container.querySelector('canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Club QR Code</title>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
.container {
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
max-width: 400px;
|
||||
height: auto;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src="${canvas.toDataURL('image/png')}" alt="Club QR Code">
|
||||
<h2>${document.getElementById('club_name')?.value || 'Club QR Code'}</h2>
|
||||
<p>${document.getElementById('clubUrlPreview')?.textContent || ''}</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
printWindow.document.close();
|
||||
printWindow.focus();
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
printWindow.close();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
// Add social link row
|
||||
function addSocialLink() {
|
||||
const container = document.getElementById('socialLinksContainer');
|
||||
if (!container) return;
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'social-link-row mb-3';
|
||||
row.dataset.index = socialLinkIndex;
|
||||
row.innerHTML = `
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select class="form-select" name="social_links[${socialLinkIndex}][platform]" required>
|
||||
<option value="">Select Platform</option>
|
||||
<option value="facebook">Facebook</option>
|
||||
<option value="instagram">Instagram</option>
|
||||
<option value="twitter">X (Twitter)</option>
|
||||
<option value="tiktok">TikTok</option>
|
||||
<option value="youtube">YouTube</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
<option value="website">Website</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
name="social_links[${socialLinkIndex}][url]"
|
||||
placeholder="https://..."
|
||||
required>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="button" class="btn btn-outline-danger w-100" onclick="removeSocialLink(this)">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.appendChild(row);
|
||||
socialLinkIndex++;
|
||||
}
|
||||
|
||||
// Remove social link row
|
||||
function removeSocialLink(button) {
|
||||
const row = button.closest('.social-link-row');
|
||||
if (row) {
|
||||
row.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PART 2: Cropper Overlay Functions
|
||||
// ============================================
|
||||
|
||||
// Helper function to apply transform
|
||||
function applyTransform(instance) {
|
||||
if (!instance.properties.image) return;
|
||||
const p = instance.properties;
|
||||
const t = `translate3d(${p.x}px, ${p.y}px, 0) scale(${p.scale}) rotate(${p.deg}deg)`;
|
||||
p.image.style.transform = t;
|
||||
}
|
||||
|
||||
// Initialize cropper with specific aspect ratio
|
||||
function initCropper(elementId, width, height, shape, aspectRatio) {
|
||||
const el = document.getElementById(elementId);
|
||||
if (!el) return null;
|
||||
|
||||
const cropper = new Cropme(el, {
|
||||
container: { width: '100%', height: 400 },
|
||||
viewport: {
|
||||
width: width,
|
||||
height: height,
|
||||
type: shape,
|
||||
border: { enable: true, width: 2, color: '#fff' }
|
||||
},
|
||||
transformOrigin: 'viewport',
|
||||
zoom: { min: zoomMin, max: zoomMax, enable: true, mouseWheel: true, slider: false },
|
||||
rotation: { enable: true, slider: false }
|
||||
});
|
||||
|
||||
return cropper;
|
||||
}
|
||||
|
||||
// ===== LOGO CROPPER =====
|
||||
function openLogoCropper() {
|
||||
const overlay = document.getElementById('logoCropperOverlay');
|
||||
overlay.style.display = 'flex';
|
||||
|
||||
// Prevent main modal body from scrolling
|
||||
const modalBody = document.querySelector('#clubModal .modal-body');
|
||||
if (modalBody) modalBody.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeLogoCropper() {
|
||||
const overlay = document.getElementById('logoCropperOverlay');
|
||||
overlay.style.display = 'none';
|
||||
|
||||
// Restore main modal body scrolling
|
||||
const modalBody = document.querySelector('#clubModal .modal-body');
|
||||
if (modalBody) modalBody.style.overflow = 'auto';
|
||||
|
||||
// Destroy cropper
|
||||
if (logoCropper) {
|
||||
logoCropper.destroy();
|
||||
logoCropper = null;
|
||||
}
|
||||
|
||||
// Reset file input
|
||||
document.getElementById('logoFileInput').value = '';
|
||||
}
|
||||
|
||||
function saveLogoCrop() {
|
||||
if (!logoCropper) return;
|
||||
|
||||
// Logo: Crop to 400x400 square
|
||||
logoCropper.crop({
|
||||
type: 'base64',
|
||||
width: 400,
|
||||
height: 400
|
||||
}).then(base64 => {
|
||||
// Store in hidden input
|
||||
document.getElementById('logoInput').value = base64;
|
||||
|
||||
// Update preview
|
||||
const preview = document.getElementById('logoPreview');
|
||||
if (preview && preview.tagName === 'IMG') {
|
||||
preview.src = base64;
|
||||
} else {
|
||||
const container = document.getElementById('logoPreviewContainer');
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<img src="${base64}"
|
||||
id="logoPreview"
|
||||
class="cropper-preview-image"
|
||||
style="width: 150px; height: 150px; border-radius: 8px; border: 2px solid #dee2e6;">
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Close overlay
|
||||
closeLogoCropper();
|
||||
});
|
||||
}
|
||||
|
||||
// Logo file input handler
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const logoFileInput = document.getElementById('logoFileInput');
|
||||
if (logoFileInput) {
|
||||
logoFileInput.addEventListener('change', function() {
|
||||
if (this.files && this.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
if (logoCropper) logoCropper.destroy();
|
||||
// Logo: Square aspect ratio (400x400)
|
||||
logoCropper = initCropper('logoBox', 400, 400, 'square', 1);
|
||||
if (logoCropper) {
|
||||
logoCropper.bind({ url: event.target.result }).then(() => {
|
||||
document.getElementById('logoZoom').value = 0;
|
||||
document.getElementById('logoRotation').value = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Logo zoom handler
|
||||
const logoZoom = document.getElementById('logoZoom');
|
||||
if (logoZoom) {
|
||||
logoZoom.addEventListener('input', function() {
|
||||
if (!logoCropper || !logoCropper.properties.image) return;
|
||||
const p = parseFloat(this.value);
|
||||
const scale = zoomMin + (zoomMax - zoomMin) * (p / 100);
|
||||
logoCropper.properties.scale = Math.min(Math.max(scale, zoomMin), zoomMax);
|
||||
applyTransform(logoCropper);
|
||||
});
|
||||
}
|
||||
|
||||
// Logo rotation handler
|
||||
const logoRotation = document.getElementById('logoRotation');
|
||||
if (logoRotation) {
|
||||
logoRotation.addEventListener('input', function() {
|
||||
if (logoCropper) {
|
||||
logoCropper.rotate(parseInt(this.value, 10));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ===== COVER CROPPER =====
|
||||
function openCoverCropper() {
|
||||
const overlay = document.getElementById('coverCropperOverlay');
|
||||
overlay.style.display = 'flex';
|
||||
|
||||
// Prevent main modal body from scrolling
|
||||
const modalBody = document.querySelector('#clubModal .modal-body');
|
||||
if (modalBody) modalBody.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeCoverCropper() {
|
||||
const overlay = document.getElementById('coverCropperOverlay');
|
||||
overlay.style.display = 'none';
|
||||
|
||||
// Restore main modal body scrolling
|
||||
const modalBody = document.querySelector('#clubModal .modal-body');
|
||||
if (modalBody) modalBody.style.overflow = 'auto';
|
||||
|
||||
// Destroy cropper
|
||||
if (coverCropper) {
|
||||
coverCropper.destroy();
|
||||
coverCropper = null;
|
||||
}
|
||||
|
||||
// Reset file input
|
||||
document.getElementById('coverFileInput').value = '';
|
||||
}
|
||||
|
||||
function saveCoverCrop() {
|
||||
if (!coverCropper) return;
|
||||
|
||||
// Cover: Crop to 1200x400 wide banner (3:1 aspect ratio)
|
||||
coverCropper.crop({
|
||||
type: 'base64',
|
||||
width: 1200,
|
||||
height: 400
|
||||
}).then(base64 => {
|
||||
// Store in hidden input for COVER (not logo!)
|
||||
document.getElementById('coverInput').value = base64;
|
||||
|
||||
// Update COVER preview (not logo!)
|
||||
const preview = document.getElementById('coverPreview');
|
||||
if (preview && preview.tagName === 'IMG') {
|
||||
preview.src = base64;
|
||||
} else {
|
||||
const container = document.getElementById('coverPreviewContainer');
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<img src="${base64}"
|
||||
id="coverPreview"
|
||||
class="cropper-preview-image"
|
||||
style="width: 250px; height: 83px; border-radius: 8px; border: 2px solid #dee2e6;">
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Close overlay
|
||||
closeCoverCropper();
|
||||
});
|
||||
}
|
||||
|
||||
// Cover file input handler
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const coverFileInput = document.getElementById('coverFileInput');
|
||||
if (coverFileInput) {
|
||||
coverFileInput.addEventListener('change', function() {
|
||||
if (this.files && this.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
if (coverCropper) coverCropper.destroy();
|
||||
// Cover: Wide banner aspect ratio (600x200 = 3:1) - fits in container
|
||||
// Will be scaled up to 1200x400 on save
|
||||
coverCropper = initCropper('coverBox', 600, 200, 'square', 3);
|
||||
if (coverCropper) {
|
||||
coverCropper.bind({ url: event.target.result }).then(() => {
|
||||
document.getElementById('coverZoom').value = 0;
|
||||
document.getElementById('coverRotation').value = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cover zoom handler
|
||||
const coverZoom = document.getElementById('coverZoom');
|
||||
if (coverZoom) {
|
||||
coverZoom.addEventListener('input', function() {
|
||||
if (!coverCropper || !coverCropper.properties.image) return;
|
||||
const p = parseFloat(this.value);
|
||||
const scale = zoomMin + (zoomMax - zoomMin) * (p / 100);
|
||||
coverCropper.properties.scale = Math.min(Math.max(scale, zoomMin), zoomMax);
|
||||
applyTransform(coverCropper);
|
||||
});
|
||||
}
|
||||
|
||||
// Cover rotation handler
|
||||
const coverRotation = document.getElementById('coverRotation');
|
||||
if (coverRotation) {
|
||||
coverRotation.addEventListener('input', function() {
|
||||
if (coverCropper) {
|
||||
coverCropper.rotate(parseInt(this.value, 10));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
Reference in new issue
Block a user