- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1418 lines
49 KiB
PHP
1418 lines
49 KiB
PHP
<!-- Edit Video Modal - Cute Staged Pop-up -->
|
||
<div class="modal fade" id="editVideoModal" tabindex="-1" aria-labelledby="editVideoModalLabel" aria-hidden="true"
|
||
data-bs-backdrop="static">
|
||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||
<div class="modal-content edit-modal-content">
|
||
<!-- Header -->
|
||
<div class="modal-header edit-modal-header">
|
||
<div class="d-flex align-items-center gap-3">
|
||
<div class="edit-icon-wrapper">
|
||
<i class="bi bi-pencil-fill"></i>
|
||
</div>
|
||
<div>
|
||
<h5 class="modal-title" id="editVideoModalLabel">Edit Video</h5>
|
||
<span class="edit-subtitle">Update your video</span>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn-close btn-close-white" onclick="closeEditVideoModal()"
|
||
aria-label="Close"></button>
|
||
</div>
|
||
|
||
<!-- Body -->
|
||
<div class="modal-body edit-modal-body">
|
||
<!-- Progress Steps -->
|
||
<div class="edit-steps">
|
||
<div class="edit-step active" data-step="1">
|
||
<div class="step-circle">
|
||
<span>1</span>
|
||
<i class="bi bi-check-lg"></i>
|
||
</div>
|
||
<span class="step-label">Details</span>
|
||
</div>
|
||
<div class="step-line"></div>
|
||
<div class="edit-step" data-step="2">
|
||
<div class="step-circle">
|
||
<span>2</span>
|
||
<i class="bi bi-check-lg"></i>
|
||
</div>
|
||
<span class="step-label">Thumbnail</span>
|
||
</div>
|
||
<div class="step-line"></div>
|
||
<div class="edit-step" data-step="3">
|
||
<div class="step-circle">
|
||
<span>3</span>
|
||
<i class="bi bi-check-lg"></i>
|
||
</div>
|
||
<span class="step-label">Privacy</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Form -->
|
||
<form id="edit-video-form-modal" enctype="multipart/form-data">
|
||
@csrf
|
||
@method('PUT')
|
||
|
||
<!-- Step 1: Details -->
|
||
<div class="edit-step-content active" data-step="1">
|
||
<!-- Current Thumbnail Preview -->
|
||
<div class="current-thumbnail-section">
|
||
<label class="form-label">
|
||
<i class="bi bi-image"></i> Current Thumbnail
|
||
</label>
|
||
<div class="current-thumbnail-wrapper" id="current-thumbnail-wrapper">
|
||
<img id="current-thumbnail-preview" src="" alt="Current thumbnail"
|
||
class="current-thumbnail-img">
|
||
<div class="thumbnail-placeholder" id="thumbnail-placeholder">
|
||
<i class="bi bi-card-image"></i>
|
||
<span>No thumbnail</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="form-label">
|
||
<i class="bi bi-card-heading"></i> Title *
|
||
</label>
|
||
<input type="text" name="title" id="edit-video-title" required class="form-input"
|
||
placeholder="Give your video a catchy title">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="form-label">
|
||
<i class="bi bi-text-paragraph"></i> Description
|
||
</label>
|
||
<textarea name="description" id="edit-video-description" rows="3" class="form-textarea"
|
||
placeholder="Tell viewers about your video"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label class="form-label">
|
||
<i class="bi bi-collection-play"></i> Video Type
|
||
</label>
|
||
<div class="video-type-options">
|
||
<label class="video-type-option" id="type-generic">
|
||
<input type="radio" name="type" value="generic" checked>
|
||
<div class="video-type-content">
|
||
<i class="bi bi-film"></i>
|
||
<span>Generic</span>
|
||
</div>
|
||
</label>
|
||
<label class="video-type-option" id="type-music">
|
||
<input type="radio" name="type" value="music">
|
||
<div class="video-type-content">
|
||
<i class="bi bi-music-note"></i>
|
||
<span>Music</span>
|
||
</div>
|
||
</label>
|
||
<label class="video-type-option" id="type-match">
|
||
<input type="radio" name="type" value="match">
|
||
<div class="video-type-content">
|
||
<i class="bi bi-trophy"></i>
|
||
<span>Match</span>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step-navigation">
|
||
<div></div>
|
||
<button type="button" class="action-btn action-btn-primary btn-next" onclick="editNextStep(2)">
|
||
<span>Next Step</span> <i class="bi bi-arrow-right"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2: Thumbnail / Slides -->
|
||
<div class="edit-step-content" data-step="2">
|
||
|
||
<!-- Standard thumbnail (non-audio) -->
|
||
<div id="edit-thumb-section" class="form-group">
|
||
<label class="form-label">
|
||
<i class="bi bi-camera"></i> Change Thumbnail
|
||
</label>
|
||
<div id="edit-thumbnail-dropzone" class="dropzone-modal">
|
||
<input type="file" name="thumbnail" id="edit-thumbnail-input" accept="image/*">
|
||
<div id="edit-thumbnail-default">
|
||
<div class="dropzone-icon">
|
||
<i class="bi bi-card-image"></i>
|
||
</div>
|
||
<p class="dropzone-title">Click to select or drag thumbnail</p>
|
||
<p class="dropzone-hint">JPG, PNG, WebP up to 20MB</p>
|
||
</div>
|
||
<div id="edit-thumbnail-info" class="file-info-modal">
|
||
<div class="file-preview thumbnail-preview">
|
||
<img id="edit-thumbnail-preview-img" src="" alt="Thumbnail preview">
|
||
</div>
|
||
<div class="file-details">
|
||
<p class="filename" id="edit-thumbnail-filename"></p>
|
||
<p id="edit-thumbnail-filesize"></p>
|
||
</div>
|
||
<button type="button" class="btn-remove-file" onclick="removeEditThumbnail(event)">
|
||
<i class="bi bi-x-lg"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slides manager (audio tracks) -->
|
||
<div id="edit-slides-section" style="display:none;">
|
||
<label class="form-label"><i class="bi bi-images"></i> Cover Slides
|
||
<span style="font-weight:400;font-size:12px;color:var(--text-secondary);margin-left:6px;">Drag to reorder · click × to remove</span>
|
||
</label>
|
||
|
||
<!-- Hidden inputs carrying order + new files -->
|
||
<input type="hidden" name="slides_order" id="edit-slides-order" value="[]">
|
||
<input type="file" name="slides_add[]" id="edit-slides-add-input" accept="image/*" multiple style="display:none;">
|
||
|
||
<div id="edit-slides-strip" style="display:flex;flex-wrap:wrap;gap:8px;min-height:72px;padding:10px;background:rgba(255,255,255,.03);border:2px dashed rgba(255,255,255,.1);border-radius:10px;margin-bottom:8px;">
|
||
<!-- Slide items injected by JS -->
|
||
<div id="edit-slides-empty" style="display:flex;align-items:center;justify-content:center;width:100%;color:var(--text-secondary);font-size:13px;">No slides yet</div>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<button type="button" class="action-btn" onclick="document.getElementById('edit-slides-add-input').click()">
|
||
<i class="bi bi-plus-lg"></i> <span>Add Images</span>
|
||
</button>
|
||
<span id="edit-slides-hint" style="font-size:12px;color:var(--text-secondary);"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step-navigation">
|
||
<button type="button" class="action-btn btn-prev" onclick="editPrevStep(1)">
|
||
<i class="bi bi-arrow-left"></i> <span>Back</span>
|
||
</button>
|
||
<button type="button" class="action-btn action-btn-primary btn-next" onclick="editNextStep(3)">
|
||
<span>Next Step</span> <i class="bi bi-arrow-right"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 3: Privacy -->
|
||
<div class="edit-step-content" data-step="3">
|
||
<div class="form-group">
|
||
<label class="form-label">
|
||
<i class="bi bi-shield-lock"></i> Privacy Setting
|
||
</label>
|
||
<div class="visibility-options-modal">
|
||
<label class="visibility-option-modal active" id="visibility-public">
|
||
<input type="radio" name="visibility" value="public" checked>
|
||
<div class="visibility-content-modal">
|
||
<i class="bi bi-globe"></i>
|
||
<div class="visibility-text">
|
||
<span class="visibility-title">Public</span>
|
||
<span class="visibility-desc">Everyone can see this video</span>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
<label class="visibility-option-modal" id="visibility-unlisted">
|
||
<input type="radio" name="visibility" value="unlisted">
|
||
<div class="visibility-content-modal">
|
||
<i class="bi bi-link-45deg"></i>
|
||
<div class="visibility-text">
|
||
<span class="visibility-title">Unlisted</span>
|
||
<span class="visibility-desc">Only people with the link</span>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
<label class="visibility-option-modal" id="visibility-private">
|
||
<input type="radio" name="visibility" value="private">
|
||
<div class="visibility-content-modal">
|
||
<i class="bi bi-lock"></i>
|
||
<div class="visibility-text">
|
||
<span class="visibility-title">Private</span>
|
||
<span class="visibility-desc">Only you can see</span>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Download Access -->
|
||
<div class="form-group" style="margin: 16px 0 0;">
|
||
<label class="form-label">
|
||
<i class="bi bi-download"></i> Who can download?
|
||
</label>
|
||
<div class="options-grid" id="edit-download-options-modal">
|
||
<label class="option-item" data-download="disabled">
|
||
<input type="radio" name="download_access" value="disabled">
|
||
<div class="option-content">
|
||
<i class="bi bi-slash-circle"></i>
|
||
<span>Disabled</span>
|
||
</div>
|
||
</label>
|
||
<label class="option-item" data-download="everyone">
|
||
<input type="radio" name="download_access" value="everyone">
|
||
<div class="option-content">
|
||
<i class="bi bi-globe"></i>
|
||
<span>Everyone</span>
|
||
</div>
|
||
</label>
|
||
<label class="option-item" data-download="registered">
|
||
<input type="radio" name="download_access" value="registered">
|
||
<div class="option-content">
|
||
<i class="bi bi-person-check"></i>
|
||
<span>Members</span>
|
||
</div>
|
||
</label>
|
||
<label class="option-item" data-download="subscribers">
|
||
<input type="radio" name="download_access" value="subscribers">
|
||
<div class="option-content">
|
||
<i class="bi bi-star"></i>
|
||
<span>Subscribers</span>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Status Message -->
|
||
<div id="edit-status-message" class="status-message-modal"></div>
|
||
|
||
<div class="step-navigation">
|
||
<button type="button" class="action-btn btn-prev" onclick="editPrevStep(2)">
|
||
<i class="bi bi-arrow-left"></i> <span>Back</span>
|
||
</button>
|
||
<button type="submit" class="action-btn action-btn-primary btn-save-changes" id="edit-submit-btn">
|
||
<i class="bi bi-check-lg"></i> <span>Save Changes</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
/* Modal Base Styles */
|
||
.edit-modal-content {
|
||
background: linear-gradient(145deg, #1e1e1e 0%, #252525 100%);
|
||
border: 1px solid #3a3a3a;
|
||
border-radius: 24px;
|
||
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 40px rgba(230, 30, 30, 0.1);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Cute Staged Animation */
|
||
#editVideoModal .modal-dialog {
|
||
max-width: 600px;
|
||
transform: scale(0.8) translateY(20px);
|
||
opacity: 0;
|
||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
|
||
#editVideoModal.show .modal-dialog {
|
||
transform: scale(1) translateY(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
#editVideoModal.show .modal-dialog {
|
||
animation: editModalBounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s both;
|
||
}
|
||
|
||
@keyframes editModalBounce {
|
||
0% {
|
||
transform: scale(0.9) translateY(10px);
|
||
opacity: 0;
|
||
}
|
||
|
||
60% {
|
||
transform: scale(1.02) translateY(-5px);
|
||
}
|
||
|
||
100% {
|
||
transform: scale(1) translateY(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Header */
|
||
.edit-modal-header {
|
||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||
border-bottom: none;
|
||
padding: 20px 24px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.edit-modal-header::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
right: -50%;
|
||
width: 100%;
|
||
height: 200%;
|
||
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.edit-modal-header .btn-close {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.edit-icon-wrapper {
|
||
width: 48px;
|
||
height: 48px;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22px;
|
||
color: white;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.edit-modal-header .modal-title {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: white;
|
||
margin: 0;
|
||
}
|
||
|
||
.edit-subtitle {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
/* Body */
|
||
.edit-modal-body {
|
||
padding: 24px;
|
||
background: #1a1a1a;
|
||
}
|
||
|
||
/* Progress Steps */
|
||
.edit-steps {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 28px;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.edit-step {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.edit-step .step-circle {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: #2a2a2a;
|
||
border: 2px solid #404040;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
color: #888;
|
||
transition: all 0.3s;
|
||
position: relative;
|
||
}
|
||
|
||
.edit-step .step-circle i {
|
||
display: none;
|
||
font-size: 18px;
|
||
color: #4ade80;
|
||
}
|
||
|
||
.edit-step.active .step-circle {
|
||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||
border-color: #e61e1e;
|
||
color: white;
|
||
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.4);
|
||
}
|
||
|
||
.edit-step.completed .step-circle {
|
||
background: #22c55e;
|
||
border-color: #22c55e;
|
||
color: white;
|
||
}
|
||
|
||
.edit-step.completed .step-circle span {
|
||
display: none;
|
||
}
|
||
|
||
.edit-step.completed .step-circle i {
|
||
display: block;
|
||
}
|
||
|
||
.step-label {
|
||
font-size: 12px;
|
||
color: #888;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.edit-step.active .step-label {
|
||
color: #e61e1e;
|
||
}
|
||
|
||
.step-line {
|
||
width: 60px;
|
||
height: 2px;
|
||
background: #333;
|
||
margin: 0 8px;
|
||
margin-bottom: 24px;
|
||
transition: background 0.3s;
|
||
}
|
||
|
||
.step-line.active {
|
||
background: linear-gradient(90deg, #e61e1e, #22c55e);
|
||
}
|
||
|
||
/* Step Content */
|
||
.edit-step-content {
|
||
display: none;
|
||
animation: fadeSlideIn 0.4s ease;
|
||
}
|
||
|
||
.edit-step-content.active {
|
||
display: block;
|
||
}
|
||
|
||
@keyframes fadeSlideIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(20px);
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
/* Current Thumbnail */
|
||
.current-thumbnail-section {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.current-thumbnail-wrapper {
|
||
width: 100%;
|
||
height: 140px;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
background: #151515;
|
||
border: 2px dashed #333;
|
||
position: relative;
|
||
}
|
||
|
||
.current-thumbnail-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.thumbnail-placeholder {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #555;
|
||
}
|
||
|
||
.thumbnail-placeholder i {
|
||
font-size: 32px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.thumbnail-placeholder span {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.current-thumbnail-wrapper.has-thumbnail .thumbnail-placeholder {
|
||
display: none;
|
||
}
|
||
|
||
.current-thumbnail-wrapper.has-thumbnail .current-thumbnail-img {
|
||
display: block;
|
||
}
|
||
|
||
.current-thumbnail-wrapper:not(.has-thumbnail) .current-thumbnail-img {
|
||
display: none;
|
||
}
|
||
|
||
/* Form Elements */
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 10px;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
color: #e5e5e5;
|
||
}
|
||
|
||
.form-label i {
|
||
color: #e61e1e;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.form-input,
|
||
.form-textarea {
|
||
width: 100%;
|
||
background: #121212;
|
||
border: 1px solid #333;
|
||
border-radius: 12px;
|
||
padding: 14px 16px;
|
||
color: #f1f1f1;
|
||
font-size: 14px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.form-input:focus,
|
||
.form-textarea:focus {
|
||
outline: none;
|
||
border-color: #e61e1e;
|
||
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
|
||
}
|
||
|
||
.form-input::placeholder,
|
||
.form-textarea::placeholder {
|
||
color: #666;
|
||
}
|
||
|
||
/* Video Type Options */
|
||
.video-type-options {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.video-type-option {
|
||
flex: 1;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.video-type-option input {
|
||
display: none;
|
||
}
|
||
|
||
.video-type-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 16px 12px;
|
||
background: #1a1a1a;
|
||
border: 2px solid #333;
|
||
border-radius: 12px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.video-type-option:hover .video-type-content {
|
||
border-color: #555;
|
||
background: #1f1f1f;
|
||
}
|
||
|
||
.video-type-option.active .video-type-content {
|
||
border-color: #e61e1e;
|
||
background: rgba(230, 30, 30, 0.1);
|
||
}
|
||
|
||
.video-type-content i {
|
||
font-size: 24px;
|
||
color: #666;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.video-type-option.active .video-type-content i {
|
||
color: #e61e1e;
|
||
}
|
||
|
||
.video-type-content span {
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #aaa;
|
||
}
|
||
|
||
.video-type-option.active .video-type-content span {
|
||
color: #e5e5e5;
|
||
}
|
||
|
||
/* Dropzone */
|
||
.dropzone-modal {
|
||
border: 2px dashed #404040;
|
||
border-radius: 16px;
|
||
padding: 32px 20px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
position: relative;
|
||
background: #151515;
|
||
}
|
||
|
||
.dropzone-modal:hover {
|
||
border-color: #e61e1e;
|
||
background: rgba(230, 30, 30, 0.05);
|
||
}
|
||
|
||
.dropzone-modal.dragover {
|
||
border-color: #e61e1e;
|
||
background: rgba(230, 30, 30, 0.1);
|
||
transform: scale(1.01);
|
||
}
|
||
|
||
.dropzone-modal input[type="file"] {
|
||
display: none;
|
||
}
|
||
|
||
.dropzone-icon {
|
||
font-size: 48px;
|
||
color: #e61e1e;
|
||
margin-bottom: 12px;
|
||
animation: float 3s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes float {
|
||
|
||
0%,
|
||
100% {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
50% {
|
||
transform: translateY(-8px);
|
||
}
|
||
}
|
||
|
||
.dropzone-title {
|
||
color: #e5e5e5;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
margin: 8px 0;
|
||
}
|
||
|
||
.dropzone-hint {
|
||
color: #666;
|
||
font-size: 13px;
|
||
margin: 0;
|
||
}
|
||
|
||
/* File Info */
|
||
.file-info-modal {
|
||
display: none;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
background: #1f1f1f;
|
||
border-radius: 12px;
|
||
border: 1px solid #333;
|
||
}
|
||
|
||
.file-info-modal.active {
|
||
display: flex;
|
||
}
|
||
|
||
.file-preview {
|
||
width: 56px;
|
||
height: 56px;
|
||
background: linear-gradient(135deg, #e61e1e 0%, #ff6b6b 100%);
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
color: white;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.file-preview.thumbnail-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.file-details {
|
||
flex: 1;
|
||
text-align: left;
|
||
}
|
||
|
||
.file-details .filename {
|
||
color: #e5e5e5;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
margin: 0 0 4px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 250px;
|
||
}
|
||
|
||
.file-details p {
|
||
color: #888;
|
||
font-size: 13px;
|
||
margin: 0;
|
||
}
|
||
|
||
.btn-remove-file {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
background: #333;
|
||
border: none;
|
||
color: #888;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-remove-file:hover {
|
||
background: #e61e1e;
|
||
color: white;
|
||
}
|
||
|
||
/* Visibility Options */
|
||
.visibility-options-modal {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.visibility-option-modal {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.visibility-option-modal input {
|
||
display: none;
|
||
}
|
||
|
||
.visibility-content-modal {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
background: #1a1a1a;
|
||
border: 2px solid #333;
|
||
border-radius: 14px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.visibility-option-modal:hover .visibility-content-modal {
|
||
border-color: #555;
|
||
background: #1f1f1f;
|
||
}
|
||
|
||
.visibility-option-modal.active .visibility-content-modal {
|
||
border-color: #e61e1e;
|
||
background: rgba(230, 30, 30, 0.1);
|
||
}
|
||
|
||
.visibility-content-modal i {
|
||
font-size: 22px;
|
||
color: #666;
|
||
width: 32px;
|
||
text-align: center;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.visibility-option-modal.active .visibility-content-modal i {
|
||
color: #e61e1e;
|
||
}
|
||
|
||
.visibility-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.visibility-title {
|
||
font-weight: 500;
|
||
font-size: 15px;
|
||
color: #e5e5e5;
|
||
}
|
||
|
||
.visibility-desc {
|
||
color: #777;
|
||
font-size: 13px;
|
||
}
|
||
|
||
/* Step Navigation */
|
||
.step-navigation {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 24px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #2a2a2a;
|
||
}
|
||
|
||
.btn-next:disabled,
|
||
.btn-save-changes:disabled {
|
||
background: #444 !important;
|
||
border-color: #444 !important;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
box-shadow: none;
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Status Message */
|
||
.status-message-modal {
|
||
display: none;
|
||
padding: 14px 18px;
|
||
border-radius: 10px;
|
||
margin-top: 16px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.status-message-modal.success {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background: rgba(34, 197, 94, 0.15);
|
||
color: #4ade80;
|
||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||
}
|
||
|
||
.status-message-modal.error {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background: rgba(239, 68, 68, 0.15);
|
||
color: #f87171;
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
}
|
||
|
||
/* Slide items in edit modal */
|
||
.es-item {
|
||
position: relative;
|
||
width: 72px;
|
||
height: 72px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 2px solid rgba(255,255,255,.12);
|
||
cursor: grab;
|
||
flex-shrink: 0;
|
||
transition: border-color .2s, transform .15s;
|
||
}
|
||
.es-item:hover { border-color: #e61e1e; }
|
||
.es-item img {
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
pointer-events: none;
|
||
}
|
||
.es-drag-handle {
|
||
position: absolute;
|
||
top: 2px; left: 2px;
|
||
width: 20px; height: 20px;
|
||
background: rgba(0,0,0,.55);
|
||
border-radius: 4px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 11px; color: #ccc;
|
||
cursor: grab;
|
||
}
|
||
.es-remove {
|
||
position: absolute;
|
||
top: 2px; right: 2px;
|
||
width: 20px; height: 20px;
|
||
background: rgba(0,0,0,.55);
|
||
border: none;
|
||
border-radius: 4px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 10px; color: #ccc;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
transition: background .15s, color .15s;
|
||
}
|
||
.es-remove:hover { background: #e61e1e; color: #fff; }
|
||
.es-item.es-dragging { opacity: .35; outline: 2px dashed #e61e1e; }
|
||
|
||
/* Responsive */
|
||
@media (max-width: 576px) {
|
||
.edit-modal-body {
|
||
padding: 20px 16px;
|
||
}
|
||
|
||
.edit-steps {
|
||
transform: scale(0.85);
|
||
}
|
||
|
||
.step-line {
|
||
width: 30px;
|
||
}
|
||
|
||
.video-type-options {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.step-navigation {
|
||
flex-direction: column-reverse;
|
||
gap: 12px;
|
||
}
|
||
|
||
.btn-prev,
|
||
.btn-next,
|
||
.btn-save-changes {
|
||
width: 100%;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
window.currentEditStep = window.currentEditStep ?? 1;
|
||
window.totalEditSteps = window.totalEditSteps ?? 3;
|
||
window.currentVideoId = window.currentVideoId ?? null;
|
||
|
||
// Open modal and load video data
|
||
function openEditVideoModal(videoId) {
|
||
// On mobile redirect to the full edit page
|
||
if (window.innerWidth < 992) {
|
||
window.location.href = `/videos/${videoId}/edit`;
|
||
return;
|
||
}
|
||
|
||
window.currentVideoId = videoId;
|
||
|
||
// Fetch video data
|
||
fetch(`/videos/${videoId}/edit`, {
|
||
headers: {
|
||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||
'Accept': 'application/json',
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const video = data.video;
|
||
|
||
// Set form values
|
||
document.getElementById('edit-video-title').value = video.title || '';
|
||
document.getElementById('edit-video-description').value = video.description || '';
|
||
|
||
// Set thumbnail preview
|
||
const thumbnailWrapper = document.getElementById('current-thumbnail-wrapper');
|
||
const thumbnailImg = document.getElementById('current-thumbnail-preview');
|
||
|
||
if (video.thumbnail) {
|
||
thumbnailImg.src = video.thumbnail_url;
|
||
thumbnailWrapper.classList.add('has-thumbnail');
|
||
} else {
|
||
thumbnailWrapper.classList.remove('has-thumbnail');
|
||
}
|
||
|
||
// Set video type
|
||
const videoType = video.type || 'generic';
|
||
document.querySelectorAll('.video-type-option').forEach(opt => {
|
||
opt.classList.remove('active');
|
||
const radio = opt.querySelector('input[type="radio"]');
|
||
if (radio.value === videoType) {
|
||
radio.checked = true;
|
||
opt.classList.add('active');
|
||
}
|
||
});
|
||
|
||
// Set visibility
|
||
const visibility = video.visibility || 'public';
|
||
document.querySelectorAll('.visibility-option-modal').forEach(opt => {
|
||
opt.classList.remove('active');
|
||
const radio = opt.querySelector('input[type="radio"]');
|
||
if (radio.value === visibility) {
|
||
radio.checked = true;
|
||
opt.classList.add('active');
|
||
}
|
||
});
|
||
|
||
// Set download_access radio cards
|
||
const dlValue = video.download_access || 'disabled';
|
||
document.querySelectorAll('#edit-download-options-modal .option-item').forEach(opt => {
|
||
opt.classList.remove('active');
|
||
const radio = opt.querySelector('input[type="radio"]');
|
||
if (radio.value === dlValue) {
|
||
radio.checked = true;
|
||
opt.classList.add('active');
|
||
} else {
|
||
radio.checked = false;
|
||
}
|
||
});
|
||
|
||
// Reset file inputs
|
||
document.getElementById('edit-thumbnail-default').style.display = 'block';
|
||
document.getElementById('edit-thumbnail-info').classList.remove('active');
|
||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||
|
||
// Populate slides or thumbnail section
|
||
populateEditSlides(video.slides || [], video.is_audio || false);
|
||
|
||
// Update step 2 label dynamically
|
||
const step2Label = document.querySelector('.edit-step[data-step="2"] .step-label');
|
||
if (step2Label) step2Label.textContent = video.is_audio ? 'Slides' : 'Thumbnail';
|
||
|
||
// Reset step to 1
|
||
window.currentEditStep = 1;
|
||
updateEditStepDisplay();
|
||
|
||
// Update form action
|
||
document.getElementById('edit-video-form-modal').action = `/videos/${videoId}`;
|
||
|
||
// Show modal
|
||
const modal = new bootstrap.Modal(document.getElementById('editVideoModal'));
|
||
modal.show();
|
||
|
||
setTimeout(() => {
|
||
document.getElementById('editVideoModal').classList.add('show');
|
||
}, 10);
|
||
} else {
|
||
showToast('Failed to load video data', 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showToast('Failed to load video data', 'error');
|
||
});
|
||
}
|
||
|
||
function closeEditVideoModal() {
|
||
const modalEl = document.getElementById('editVideoModal');
|
||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||
|
||
if (modal) {
|
||
modal.hide();
|
||
}
|
||
|
||
modalEl.addEventListener('hidden.bs.modal', function() {
|
||
modalEl.classList.remove('show');
|
||
window.currentVideoId = null;
|
||
}, {
|
||
once: true
|
||
});
|
||
}
|
||
|
||
// Step Navigation
|
||
function editNextStep(step) {
|
||
// Validate current step before moving
|
||
if (step === 2) {
|
||
const title = document.getElementById('edit-video-title').value.trim();
|
||
if (!title) {
|
||
showToast('Please enter a title for your video', 'error');
|
||
return;
|
||
}
|
||
}
|
||
|
||
window.currentEditStep = step;
|
||
updateEditStepDisplay();
|
||
}
|
||
|
||
function editPrevStep(step) {
|
||
window.currentEditStep = step;
|
||
updateEditStepDisplay();
|
||
}
|
||
|
||
function updateEditStepDisplay() {
|
||
// Update step indicators
|
||
document.querySelectorAll('.edit-step').forEach((el, index) => {
|
||
const stepNum = index + 1;
|
||
el.classList.remove('active', 'completed');
|
||
|
||
if (stepNum === window.currentEditStep) {
|
||
el.classList.add('active');
|
||
} else if (stepNum < window.currentEditStep) {
|
||
el.classList.add('completed');
|
||
}
|
||
});
|
||
|
||
// Update step lines
|
||
document.querySelectorAll('.step-line').forEach((el, index) => {
|
||
if (index < window.currentEditStep - 1) {
|
||
el.classList.add('active');
|
||
} else {
|
||
el.classList.remove('active');
|
||
}
|
||
});
|
||
|
||
// Update step content
|
||
document.querySelectorAll('.edit-step-content').forEach(el => {
|
||
el.classList.remove('active');
|
||
if (parseInt(el.dataset.step) === window.currentEditStep) {
|
||
el.classList.add('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Thumbnail dropzone handling
|
||
const editThumbnailDropzone = document.getElementById('edit-thumbnail-dropzone');
|
||
const editThumbnailInput = document.getElementById('edit-thumbnail-input');
|
||
|
||
if (editThumbnailInput) {
|
||
editThumbnailInput.addEventListener('change', function() {
|
||
handleEditThumbnailSelect(this);
|
||
});
|
||
|
||
if (editThumbnailDropzone) {
|
||
editThumbnailDropzone.addEventListener('click', function(e) {
|
||
if (e.target.closest('.btn-remove-file')) return;
|
||
if (typeof window.openCropperModal_thumb_edit === 'function') {
|
||
window.openCropperModal_thumb_edit();
|
||
} else {
|
||
editThumbnailInput.click();
|
||
}
|
||
});
|
||
|
||
editThumbnailDropzone.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
editThumbnailDropzone.classList.add('dragover');
|
||
});
|
||
|
||
editThumbnailDropzone.addEventListener('dragleave', () => {
|
||
editThumbnailDropzone.classList.remove('dragover');
|
||
});
|
||
|
||
editThumbnailDropzone.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
editThumbnailDropzone.classList.remove('dragover');
|
||
if (e.dataTransfer.files.length) {
|
||
const droppedFile = e.dataTransfer.files[0];
|
||
if (typeof window.tcPreload_thumb_edit === 'function') {
|
||
window.tcPreload_thumb_edit(droppedFile);
|
||
window.openCropperModal_thumb_edit();
|
||
} else {
|
||
editThumbnailInput.files = e.dataTransfer.files;
|
||
handleEditThumbnailSelect(editThumbnailInput);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
function handleEditThumbnailSelect(input) {
|
||
if (input.files && input.files[0]) {
|
||
const file = input.files[0];
|
||
document.getElementById('edit-thumbnail-filename').textContent = file.name;
|
||
document.getElementById('edit-thumbnail-filesize').textContent = (file.size / 1024 / 1024).toFixed(2) +
|
||
' MB';
|
||
|
||
// Show thumbnail preview
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
document.getElementById('edit-thumbnail-preview-img').src = e.target.result;
|
||
};
|
||
reader.readAsDataURL(file);
|
||
|
||
document.getElementById('edit-thumbnail-default').style.display = 'none';
|
||
document.getElementById('edit-thumbnail-info').classList.add('active');
|
||
}
|
||
}
|
||
|
||
function removeEditThumbnail(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
editThumbnailInput.value = '';
|
||
document.getElementById('edit-thumbnail-default').style.display = 'block';
|
||
document.getElementById('edit-thumbnail-info').classList.remove('active');
|
||
}
|
||
|
||
// ── Slides manager (audio tracks) ────────────────────────────
|
||
let editSlidesData = []; // [{id, url}] for existing; {file, url} for new additions
|
||
let editDragSrc = null;
|
||
|
||
function _esSyncOrder() {
|
||
const strip = document.getElementById('edit-slides-strip');
|
||
const keptIds = editSlidesData.filter(s => s.id).map(s => s.id);
|
||
document.getElementById('edit-slides-order').value = JSON.stringify(keptIds);
|
||
const count = editSlidesData.length;
|
||
document.getElementById('edit-slides-hint').textContent =
|
||
count === 1 ? '1 image — static cover' : count + ' images — will crossfade';
|
||
}
|
||
|
||
function editSlidesRefresh() {
|
||
const strip = document.getElementById('edit-slides-strip');
|
||
const empty = document.getElementById('edit-slides-empty');
|
||
|
||
strip.querySelectorAll('.es-item').forEach(el => el.remove());
|
||
empty.style.display = editSlidesData.length === 0 ? 'flex' : 'none';
|
||
|
||
editSlidesData.forEach((item, idx) => {
|
||
const div = document.createElement('div');
|
||
div.className = 'es-item';
|
||
div.draggable = true;
|
||
div._esData = item; // reference stored on element — avoids stale-index issues during drag
|
||
|
||
div.innerHTML = `
|
||
<div class="es-drag-handle" title="Drag to reorder"><i class="bi bi-grip-vertical"></i></div>
|
||
<img src="${item.url}" alt="">
|
||
<button type="button" class="es-remove" title="Remove"><i class="bi bi-x-lg"></i></button>
|
||
`;
|
||
|
||
div.querySelector('.es-remove').addEventListener('click', () => {
|
||
editSlidesData.splice(editSlidesData.indexOf(div._esData), 1);
|
||
editSlidesRefresh();
|
||
});
|
||
|
||
div.addEventListener('dragstart', e => {
|
||
editDragSrc = div;
|
||
e.dataTransfer.effectAllowed = 'move';
|
||
setTimeout(() => div.classList.add('es-dragging'), 0);
|
||
});
|
||
|
||
// On dragend: sync editSlidesData from current DOM order, then update inputs
|
||
div.addEventListener('dragend', () => {
|
||
div.classList.remove('es-dragging');
|
||
editDragSrc = null;
|
||
const newData = [];
|
||
strip.querySelectorAll('.es-item').forEach(el => newData.push(el._esData));
|
||
editSlidesData = newData;
|
||
_esSyncOrder();
|
||
});
|
||
|
||
// On dragover: just move the element in the DOM — never re-render
|
||
div.addEventListener('dragover', e => {
|
||
e.preventDefault();
|
||
e.dataTransfer.dropEffect = 'move';
|
||
if (!editDragSrc || editDragSrc === div) return;
|
||
const items = [...strip.querySelectorAll('.es-item')];
|
||
const srcPos = items.indexOf(editDragSrc);
|
||
const tgtPos = items.indexOf(div);
|
||
if (srcPos < tgtPos) div.after(editDragSrc);
|
||
else div.before(editDragSrc);
|
||
});
|
||
|
||
strip.appendChild(div);
|
||
});
|
||
|
||
_esSyncOrder();
|
||
}
|
||
|
||
// "Add Images" picker
|
||
document.getElementById('edit-slides-add-input').addEventListener('change', function() {
|
||
Array.from(this.files).forEach(file => {
|
||
const reader = new FileReader();
|
||
reader.onload = e => {
|
||
editSlidesData.push({ file, url: e.target.result });
|
||
editSlidesRefresh();
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
// Reset input so same files can be re-added after removal
|
||
this.value = '';
|
||
});
|
||
|
||
function populateEditSlides(slides, isAudio) {
|
||
document.getElementById('edit-thumb-section').style.display = isAudio ? 'none' : '';
|
||
document.getElementById('edit-slides-section').style.display = isAudio ? '' : 'none';
|
||
if (! isAudio) return;
|
||
|
||
editSlidesData = slides.map(s => ({ id: s.id, url: s.url }));
|
||
editSlidesRefresh();
|
||
}
|
||
|
||
// Inject new file inputs into the form on submit (FormData can't handle dynamic files otherwise)
|
||
function buildEditSlidesFormData(formData) {
|
||
// Remove existing slides_add from formData (the file input may have stale data)
|
||
// Instead append each new file individually
|
||
const newFiles = editSlidesData.filter(s => s.file);
|
||
newFiles.forEach(s => formData.append('slides_add[]', s.file, s.file.name));
|
||
return formData;
|
||
}
|
||
|
||
// Video type options
|
||
const videoTypeOptions = document.querySelectorAll('.video-type-option');
|
||
videoTypeOptions.forEach(option => {
|
||
const radio = option.querySelector('input[type="radio"]');
|
||
radio.addEventListener('change', function() {
|
||
videoTypeOptions.forEach(opt => opt.classList.remove('active'));
|
||
option.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// Visibility options
|
||
const editVisibilityOptions = document.querySelectorAll('.visibility-option-modal');
|
||
editVisibilityOptions.forEach(option => {
|
||
const radio = option.querySelector('input[type="radio"]');
|
||
radio.addEventListener('change', function() {
|
||
editVisibilityOptions.forEach(opt => opt.classList.remove('active'));
|
||
option.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// Download access options
|
||
document.querySelectorAll('#edit-download-options-modal .option-item').forEach(item => {
|
||
item.querySelector('input[type="radio"]').addEventListener('change', function() {
|
||
document.querySelectorAll('#edit-download-options-modal .option-item').forEach(o => o.classList.remove('active'));
|
||
item.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// Form submission
|
||
document.getElementById('edit-video-form-modal').addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
let formData = new FormData(this);
|
||
// Append any newly added slide files (not captured by FormData from hidden input)
|
||
formData = buildEditSlidesFormData(formData);
|
||
const submitBtn = document.getElementById('edit-submit-btn');
|
||
|
||
// Disable button and show loading
|
||
submitBtn.disabled = true;
|
||
submitBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Saving...';
|
||
document.getElementById('edit-status-message').className = 'status-message-modal';
|
||
|
||
fetch(`/videos/${window.currentVideoId}`, {
|
||
method: 'POST',
|
||
body: formData,
|
||
headers: {
|
||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||
'Accept': 'application/json',
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
}
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error('Network response was not ok');
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
if (data.success) {
|
||
document.getElementById('edit-status-message').innerHTML =
|
||
'<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
||
document.getElementById('edit-status-message').className =
|
||
'status-message-modal success';
|
||
|
||
// Close modal and reload page after short delay
|
||
setTimeout(() => {
|
||
closeEditVideoModal();
|
||
window.location.reload();
|
||
}, 1000);
|
||
} else {
|
||
throw new Error(data.message || 'Update failed');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
document.getElementById('edit-status-message').innerHTML =
|
||
'<i class="bi bi-exclamation-circle-fill"></i> ' + error.message;
|
||
document.getElementById('edit-status-message').className = 'status-message-modal error';
|
||
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = '<i class="bi bi-check-lg"></i> Save Changes';
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<x-image-cropper
|
||
id="thumb_edit"
|
||
:width="448"
|
||
:height="252"
|
||
shape="square"
|
||
target-input="edit-thumbnail-input"
|
||
preview-img="edit-thumbnail-preview-img"
|
||
output-width="1280"
|
||
title="Crop Thumbnail"
|
||
/>
|