feat: implement Alpine.js modal for content details and migrate to SortableJS for kanban board functionality
This commit is contained in:
@@ -70,7 +70,15 @@ class EventController extends Controller
|
||||
],
|
||||
]);
|
||||
|
||||
return view('schedule.index', compact('events', 'clients', 'calendarEvents', 'categoryTree'));
|
||||
$holidays = \Illuminate\Support\Facades\Cache::remember('indonesian_holidays', 86400, function() {
|
||||
$url = "https://libur.deno.dev/api";
|
||||
$response = @file_get_contents($url);
|
||||
if ($response === false) return [];
|
||||
$data = json_decode($response, true);
|
||||
return is_array($data) ? $data : [];
|
||||
});
|
||||
|
||||
return view('schedule.index', compact('events', 'clients', 'calendarEvents', 'categoryTree', 'holidays'));
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
|
||||
@@ -50,16 +50,28 @@
|
||||
</div>
|
||||
|
||||
{{-- Sortable items container --}}
|
||||
<div class="flex flex-col gap-2 flex-1 min-h-12" x-sort:group="kanban"
|
||||
@sort="moved($event, '{{ $status->value }}')" data-col-status="{{ $status->value }}">
|
||||
<div class="flex flex-col gap-2 flex-1 min-h-12 kanban-column"
|
||||
data-col-status="{{ $status->value }}">
|
||||
|
||||
@foreach($col['items'] as $brief)
|
||||
<div class="bg-white rounded-2xl p-3 shadow-sm border border-pink-50 select-none
|
||||
cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow"
|
||||
x-sort:item="{{ $brief->id }}" data-id="{{ $brief->id }}">
|
||||
cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow kanban-item"
|
||||
data-id="{{ $brief->id }}">
|
||||
<div class="flex items-start gap-2 mb-1.5">
|
||||
<span class="text-sm shrink-0">{{ $brief->platform_icon }}</span>
|
||||
<a href="{{ route('content.show', $brief) }}"
|
||||
<a href="#"
|
||||
data-brief="{{ json_encode([
|
||||
'id' => $brief->id,
|
||||
'title' => $brief->title,
|
||||
'platform' => $brief->platform_icon . ' ' . ucfirst($brief->platform),
|
||||
'status' => $brief->status->label(),
|
||||
'client' => $brief->client?->name,
|
||||
'target_date' => $brief->target_date?->format('d M Y'),
|
||||
'hook' => $brief->hook,
|
||||
'body' => $brief->body,
|
||||
'notes' => $brief->notes
|
||||
]) }}"
|
||||
@click.prevent="openModal(JSON.parse($el.dataset.brief))"
|
||||
class="font-semibold text-gray-800 text-xs leading-tight hover:text-pink-500 line-clamp-2 flex-1">
|
||||
{{ $brief->title }}
|
||||
</a>
|
||||
@@ -89,24 +101,106 @@
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- Content Detail Request Modal --}}
|
||||
<div x-show="modalOpen" class="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm" style="display: none;" x-transition>
|
||||
<div @click.outside="modalOpen = false" class="bg-white p-5 w-full max-w-lg rounded-3xl shadow-xl mx-4 max-h-[90vh] flex flex-col relative"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 translate-y-4">
|
||||
<div class="flex items-center justify-between mb-4 border-b border-pink-50 pb-3 shrink-0">
|
||||
<h3 class="font-extrabold text-gray-800 text-xl truncate pr-4" x-text="activeBrief?.title"></h3>
|
||||
<button @click="modalOpen = false" class="text-gray-400 hover:text-pink-500 hover:bg-pink-50 w-8 h-8 rounded-full flex items-center justify-center transition-colors">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-600 space-y-4 flex-1 overflow-y-auto pr-2 custom-scrollbar">
|
||||
<div class="flex gap-3"><span class="text-gray-400 w-20 shrink-0 font-medium">Platform</span><span class="font-bold text-gray-800" x-text="activeBrief?.platform"></span></div>
|
||||
<div class="flex gap-3"><span class="text-gray-400 w-20 shrink-0 font-medium">Status</span><span class="font-bold badge bg-pink-100 text-pink-700 rounded-full px-2 py-0.5" x-text="activeBrief?.status"></span></div>
|
||||
<template x-if="activeBrief?.client">
|
||||
<div class="flex gap-3 items-center"><span class="text-gray-400 w-20 shrink-0 font-medium">Client</span><span class="font-semibold text-pink-500" x-text="activeBrief?.client"></span></div>
|
||||
</template>
|
||||
<template x-if="activeBrief?.target_date">
|
||||
<div class="flex gap-3"><span class="text-gray-400 w-20 shrink-0 font-medium">Target</span><span class="font-medium" x-text="activeBrief?.target_date"></span></div>
|
||||
</template>
|
||||
|
||||
<template x-if="activeBrief?.hook">
|
||||
<div class="flex flex-col gap-1 mt-4">
|
||||
<span class="text-gray-400 font-medium">🎣 Hook</span>
|
||||
<div class="whitespace-pre-wrap bg-pink-50/50 border border-pink-100 p-3 rounded-2xl text-gray-700 leading-relaxed" x-text="activeBrief?.hook"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="activeBrief?.body">
|
||||
<div class="flex flex-col gap-1 mt-4">
|
||||
<span class="text-gray-400 font-medium">📖 Body</span>
|
||||
<div class="whitespace-pre-wrap bg-gray-50 border border-gray-100 p-3 rounded-2xl text-gray-700 leading-relaxed" x-text="activeBrief?.body"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="activeBrief?.notes">
|
||||
<div class="flex flex-col gap-1 mt-4">
|
||||
<span class="text-gray-400 font-medium">📝 Notes</span>
|
||||
<div class="whitespace-pre-wrap bg-yellow-50/50 border border-yellow-100 p-3 rounded-2xl text-gray-700 leading-relaxed" x-text="activeBrief?.notes"></div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 mt-5 pt-4 border-t border-pink-50 shrink-0">
|
||||
<a :href="`/content/${activeBrief?.id}/edit`" class="btn-sakura text-sm flex-1 text-center py-2.5">Edit Brief</a>
|
||||
<a :href="`/content/${activeBrief?.id}`" class="py-2.5 rounded-full border border-pink-200 text-pink-500 text-sm hover:bg-pink-50 flex-1 text-center font-bold bg-white transition-colors">Details Page</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
|
||||
<script>
|
||||
function kanban() {
|
||||
return {
|
||||
moved(event, targetStatus) {
|
||||
const id = event.item?.dataset?.id;
|
||||
if (!id) return;
|
||||
modalOpen: false,
|
||||
activeBrief: null,
|
||||
init() {
|
||||
// Start Sortable once script loads
|
||||
const initSort = () => {
|
||||
if (typeof Sortable !== 'undefined') {
|
||||
document.querySelectorAll('.kanban-column').forEach(el => {
|
||||
Sortable.create(el, {
|
||||
group: 'kanban-board',
|
||||
animation: 150,
|
||||
onEnd: (evt) => {
|
||||
const id = evt.item.dataset.id;
|
||||
const targetCol = evt.to.dataset.colStatus;
|
||||
if (!id || !targetCol) return;
|
||||
|
||||
// If dropped in same column identically, skip fetch
|
||||
if (evt.from === evt.to && evt.oldIndex === evt.newIndex) return;
|
||||
|
||||
fetch(`/content/${id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||
},
|
||||
body: JSON.stringify({ status: targetStatus }),
|
||||
}).then(r => { if (r.ok) window.KolManager.notify('Status updated! 🎬'); });
|
||||
body: JSON.stringify({ status: targetCol }),
|
||||
}).then(r => {
|
||||
if (r.ok) window.KolManager.notify('Status updated! 🎬');
|
||||
else window.KolManager.notify('❌ Failed to update status');
|
||||
}).catch(() => window.KolManager.notify('❌ Network error!'));
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setTimeout(initSort, 100);
|
||||
}
|
||||
};
|
||||
initSort();
|
||||
},
|
||||
openModal(brief) {
|
||||
this.activeBrief = brief;
|
||||
this.modalOpen = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
@section('page-subtitle', 'Schedule a new task, meeting, or posting')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="flex items-center gap-3 mb-5">
|
||||
<a href="{{ route('schedule.index') }}"
|
||||
class="text-xs px-3 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
||||
<a href="{{ route('schedule.index') }}" class="btn-sakura text-sm px-5 py-2">
|
||||
← Back to Schedule
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="cat-card p-4 lg:p-8">
|
||||
<form action="{{ route('schedule.store') }}" method="POST" id="create-event-form"
|
||||
x-data="contractCheck('{{ route('contracts.check', [], false) }}')"
|
||||
class="space-y-5">
|
||||
x-data="contractCheck('{{ route('contracts.check', [], false) }}')" class="space-y-5">
|
||||
@csrf
|
||||
<input type="hidden" name="start_time" id="start_time_hidden">
|
||||
<input type="hidden" name="end_time" id="end_time_hidden">
|
||||
@@ -26,8 +24,8 @@
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">
|
||||
Title <span class="text-pink-400">*</span>
|
||||
</label>
|
||||
<input type="text" name="title" value="{{ old('title') }}"
|
||||
class="input-soft" placeholder="Event title" required>
|
||||
<input type="text" name="title" value="{{ old('title') }}" class="input-soft" placeholder="Event title"
|
||||
required>
|
||||
@error('title') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
@@ -140,8 +138,7 @@
|
||||
{{-- Client --}}
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
|
||||
<select name="client_id" id="client-select" class="input-soft"
|
||||
x-model="clientId"
|
||||
<select name="client_id" id="client-select" class="input-soft" x-model="clientId"
|
||||
@change="onClientChange($event); if($event.target.value !== '__new__') checkContract()">
|
||||
<option value="">— None —</option>
|
||||
<option value="__new__" class="font-semibold text-pink-600">+ Create New Client…</option>
|
||||
@@ -159,16 +156,20 @@
|
||||
<p class="text-xs font-bold text-purple-700">🐱 New Client Basic Info</p>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Name <span class="text-pink-400">*</span></label>
|
||||
<input type="text" x-model="newName" class="input-soft text-sm" placeholder="Client / brand name">
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Name <span
|
||||
class="text-pink-400">*</span></label>
|
||||
<input type="text" x-model="newName" class="input-soft text-sm"
|
||||
placeholder="Client / brand name">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Company</label>
|
||||
<input type="text" x-model="newCompany" class="input-soft text-sm" placeholder="Company name">
|
||||
<input type="text" x-model="newCompany" class="input-soft text-sm"
|
||||
placeholder="Company name">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Email</label>
|
||||
<input type="email" x-model="newEmail" class="input-soft text-sm" placeholder="contact@brand.com">
|
||||
<input type="email" x-model="newEmail" class="input-soft text-sm"
|
||||
placeholder="contact@brand.com">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Phone</label>
|
||||
@@ -178,8 +179,10 @@
|
||||
<p x-show="errMsg" x-text="errMsg" class="text-xs text-red-500"></p>
|
||||
<div class="flex gap-2 pt-1">
|
||||
<button type="button" @click="saveNewClient()" :disabled="saving"
|
||||
class="btn-sakura text-sm px-4 py-2" x-text="saving ? 'Saving…' : '✓ Add & Select'"></button>
|
||||
<button type="button" @click="isNew = false; document.getElementById('client-select').value = ''"
|
||||
class="btn-sakura text-sm px-4 py-2"
|
||||
x-text="saving ? 'Saving…' : '✓ Add & Select'"></button>
|
||||
<button type="button"
|
||||
@click="isNew = false; document.getElementById('client-select').value = ''"
|
||||
class="text-sm px-4 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
||||
Cancel
|
||||
</button>
|
||||
@@ -190,7 +193,8 @@
|
||||
{{-- Category --}}
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Category</label>
|
||||
<select name="category_id" id="category-select" class="input-soft" x-model="categoryId" @change="onCategoryChange($event)">
|
||||
<select name="category_id" id="category-select" class="input-soft" x-model="categoryId"
|
||||
@change="onCategoryChange($event)">
|
||||
<option value="">— None —</option>
|
||||
<option value="__new__" class="font-semibold text-pink-600">+ Create New Category…</option>
|
||||
@foreach($categoryTree as $parent)
|
||||
@@ -211,14 +215,18 @@
|
||||
class="sm:col-span-2 bg-pink-50 rounded-2xl p-4 space-y-3 border border-pink-100">
|
||||
<p class="text-xs font-bold text-pink-700">🏷️ New Category</p>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Category Name <span class="text-pink-400">*</span></label>
|
||||
<input type="text" x-model="newCatName" class="input-soft text-sm" placeholder="e.g. Sponsored Post">
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Category Name <span
|
||||
class="text-pink-400">*</span></label>
|
||||
<input type="text" x-model="newCatName" class="input-soft text-sm"
|
||||
placeholder="e.g. Sponsored Post">
|
||||
</div>
|
||||
<p x-show="catErrMsg" x-text="catErrMsg" class="text-xs text-red-500"></p>
|
||||
<div class="flex gap-2 pt-1">
|
||||
<button type="button" @click="saveNewCategory()" :disabled="savingCat"
|
||||
class="btn-sakura text-sm px-4 py-2" x-text="savingCat ? 'Saving…' : '✓ Add & Select'"></button>
|
||||
<button type="button" @click="isNewCat = false; document.getElementById('category-select').value = ''"
|
||||
class="btn-sakura text-sm px-4 py-2"
|
||||
x-text="savingCat ? 'Saving…' : '✓ Add & Select'"></button>
|
||||
<button type="button"
|
||||
@click="isNewCat = false; document.getElementById('category-select').value = ''"
|
||||
class="text-sm px-4 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
||||
Cancel
|
||||
</button>
|
||||
@@ -238,8 +246,8 @@
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">
|
||||
Start Date <span class="text-pink-400">*</span>
|
||||
</label>
|
||||
<input type="date" id="start_date" class="input-soft"
|
||||
value="{{ old('_start_date') }}" required @change="checkContract()">
|
||||
<input type="date" id="start_date" class="input-soft" value="{{ old('_start_date') }}" required
|
||||
@change="checkContract()">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Start Time</label>
|
||||
@@ -259,8 +267,8 @@
|
||||
{{-- Location --}}
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Location</label>
|
||||
<input type="text" name="location" value="{{ old('location') }}"
|
||||
class="input-soft" placeholder="Online / venue address">
|
||||
<input type="text" name="location" value="{{ old('location') }}" class="input-soft"
|
||||
placeholder="Online / venue address">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -284,8 +292,7 @@
|
||||
|
||||
{{-- Priority --}}
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="checkbox" name="is_priority" value="1" id="priority"
|
||||
class="w-4 h-4 accent-pink-400" {{ old('is_priority') ? 'checked' : '' }}>
|
||||
<input type="checkbox" name="is_priority" value="1" id="priority" class="w-4 h-4 accent-pink-400" {{ old('is_priority') ? 'checked' : '' }}>
|
||||
<label for="priority" class="text-sm text-gray-600 cursor-pointer">⭐ Mark as Priority</label>
|
||||
</div>
|
||||
|
||||
@@ -310,19 +317,26 @@
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Platform</label>
|
||||
<select name="content_platform" class="input-soft text-sm">
|
||||
<option value="instagram" {{ old('content_platform') === 'instagram' ? 'selected' : '' }}>📸 Instagram</option>
|
||||
<option value="tiktok" {{ old('content_platform') === 'tiktok' ? 'selected' : '' }}>🎵 TikTok</option>
|
||||
<option value="youtube" {{ old('content_platform') === 'youtube' ? 'selected' : '' }}>▶️ YouTube</option>
|
||||
<option value="other" {{ old('content_platform') === 'other' ? 'selected' : '' }}>📱 Other</option>
|
||||
<option value="instagram" {{ old('content_platform') === 'instagram' ? 'selected' : '' }}>📸
|
||||
Instagram</option>
|
||||
<option value="tiktok" {{ old('content_platform') === 'tiktok' ? 'selected' : '' }}>🎵 TikTok
|
||||
</option>
|
||||
<option value="youtube" {{ old('content_platform') === 'youtube' ? 'selected' : '' }}>▶️
|
||||
YouTube</option>
|
||||
<option value="other" {{ old('content_platform') === 'other' ? 'selected' : '' }}>📱 Other
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Content Type</label>
|
||||
<select name="content_type" class="input-soft text-sm">
|
||||
<option value="post" {{ old('content_type') === 'post' ? 'selected' : '' }}>Post / Feed</option>
|
||||
<option value="reel" {{ old('content_type') === 'reel' ? 'selected' : '' }}>Reel / Short</option>
|
||||
<option value="post" {{ old('content_type') === 'post' ? 'selected' : '' }}>Post / Feed
|
||||
</option>
|
||||
<option value="reel" {{ old('content_type') === 'reel' ? 'selected' : '' }}>Reel / Short
|
||||
</option>
|
||||
<option value="story" {{ old('content_type') === 'story' ? 'selected' : '' }}>Story</option>
|
||||
<option value="video" {{ old('content_type') === 'video' ? 'selected' : '' }}>Long Video</option>
|
||||
<option value="video" {{ old('content_type') === 'video' ? 'selected' : '' }}>Long Video
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="sm:col-span-2 text-xs text-purple-500">
|
||||
@@ -345,8 +359,7 @@
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Contract Title</label>
|
||||
<input type="text" name="contract_title" class="input-soft text-sm"
|
||||
placeholder="e.g. Exclusive Posting Contract — BrandX"
|
||||
value="{{ old('contract_title') }}">
|
||||
placeholder="e.g. Exclusive Posting Contract — BrandX" value="{{ old('contract_title') }}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Contract Start</label>
|
||||
@@ -363,8 +376,7 @@
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3 pt-1">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': contractWarning }"
|
||||
:disabled="contractWarning">
|
||||
:class="{ 'opacity-50 cursor-not-allowed': contractWarning }" :disabled="contractWarning">
|
||||
🐾 Schedule It!
|
||||
</button>
|
||||
<a href="{{ route('schedule.index') }}"
|
||||
@@ -374,11 +386,11 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function contractCheck(checkUrl) {
|
||||
@push('scripts')
|
||||
<script>
|
||||
function contractCheck(checkUrl) {
|
||||
return {
|
||||
categoryId: '',
|
||||
clientId: '',
|
||||
@@ -417,9 +429,9 @@ function contractCheck(checkUrl) {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('create-event-form').addEventListener('submit', function (e) {
|
||||
document.getElementById('create-event-form').addEventListener('submit', function (e) {
|
||||
const startDate = document.getElementById('start_date').value;
|
||||
const startTime = document.getElementById('start_time_input').value || '00:00';
|
||||
const endDate = document.getElementById('end_date').value;
|
||||
@@ -435,7 +447,7 @@ document.getElementById('create-event-form').addEventListener('submit', function
|
||||
if (endDate) {
|
||||
document.getElementById('end_time_hidden').value = endDate + 'T' + endTime;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
@@ -104,6 +104,43 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
|
||||
dayCellDidMount(info) {
|
||||
const holidays = @json($holidays ?? []);
|
||||
const holidayMap = {};
|
||||
holidays.forEach(h => holidayMap[h.date] = h.name);
|
||||
|
||||
const year = info.date.getFullYear();
|
||||
const month = String(info.date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(info.date.getDate()).padStart(2, '0');
|
||||
const dateStr = `${year}-${month}-${day}`;
|
||||
|
||||
if (holidayMap[dateStr]) {
|
||||
const frame = info.el.querySelector('.fc-daygrid-day-frame');
|
||||
if (frame) {
|
||||
// Update background color for the highlighted box
|
||||
frame.style.backgroundColor = '#fff0f0'; // Light red highlighted box
|
||||
frame.style.position = 'relative'; // Required for absolute positioning of text
|
||||
|
||||
// Add the holiday text to the top-left corner
|
||||
const holidayEl = document.createElement('div');
|
||||
holidayEl.textContent = holidayMap[dateStr];
|
||||
holidayEl.style.position = 'absolute';
|
||||
holidayEl.style.top = '4px';
|
||||
holidayEl.style.left = '4px';
|
||||
holidayEl.style.color = '#ef4444'; // Red text
|
||||
holidayEl.style.fontSize = '0.70rem'; // Small text
|
||||
holidayEl.style.fontWeight = '600';
|
||||
holidayEl.style.whiteSpace = 'nowrap';
|
||||
holidayEl.style.overflow = 'hidden';
|
||||
holidayEl.style.textOverflow = 'ellipsis';
|
||||
holidayEl.style.maxWidth = '75%'; // Prevent it from overlapping with the date number (usually on right)
|
||||
holidayEl.style.zIndex = '1';
|
||||
|
||||
frame.appendChild(holidayEl);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
eventDrop(info) {
|
||||
fetch(`/schedule/${info.event.id}/dates`, {
|
||||
method: 'PATCH',
|
||||
|
||||
Reference in New Issue
Block a user