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
|
public function store(Request $request): RedirectResponse
|
||||||
|
|||||||
@@ -50,16 +50,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Sortable items container --}}
|
{{-- Sortable items container --}}
|
||||||
<div class="flex flex-col gap-2 flex-1 min-h-12" x-sort:group="kanban"
|
<div class="flex flex-col gap-2 flex-1 min-h-12 kanban-column"
|
||||||
@sort="moved($event, '{{ $status->value }}')" data-col-status="{{ $status->value }}">
|
data-col-status="{{ $status->value }}">
|
||||||
|
|
||||||
@foreach($col['items'] as $brief)
|
@foreach($col['items'] as $brief)
|
||||||
<div class="bg-white rounded-2xl p-3 shadow-sm border border-pink-50 select-none
|
<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"
|
cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow kanban-item"
|
||||||
x-sort:item="{{ $brief->id }}" data-id="{{ $brief->id }}">
|
data-id="{{ $brief->id }}">
|
||||||
<div class="flex items-start gap-2 mb-1.5">
|
<div class="flex items-start gap-2 mb-1.5">
|
||||||
<span class="text-sm shrink-0">{{ $brief->platform_icon }}</span>
|
<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">
|
class="font-semibold text-gray-800 text-xs leading-tight hover:text-pink-500 line-clamp-2 flex-1">
|
||||||
{{ $brief->title }}
|
{{ $brief->title }}
|
||||||
</a>
|
</a>
|
||||||
@@ -89,24 +101,106 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@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>
|
</div>
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function kanban() {
|
function kanban() {
|
||||||
return {
|
return {
|
||||||
moved(event, targetStatus) {
|
modalOpen: false,
|
||||||
const id = event.item?.dataset?.id;
|
activeBrief: null,
|
||||||
if (!id) return;
|
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`, {
|
fetch(`/content/${id}/status`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: targetStatus }),
|
body: JSON.stringify({ status: targetCol }),
|
||||||
}).then(r => { if (r.ok) window.KolManager.notify('Status updated! 🎬'); });
|
}).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>
|
</script>
|
||||||
|
|||||||
@@ -7,16 +7,14 @@
|
|||||||
@section('content')
|
@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">
|
<div class="flex items-center gap-3 mb-5">
|
||||||
<a href="{{ route('schedule.index') }}"
|
<a href="{{ route('schedule.index') }}" class="btn-sakura text-sm px-5 py-2">
|
||||||
class="text-xs px-3 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
|
||||||
← Back to Schedule
|
← Back to Schedule
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cat-card p-4 lg:p-8">
|
<div class="cat-card p-4 lg:p-8">
|
||||||
<form action="{{ route('schedule.store') }}" method="POST" id="create-event-form"
|
<form action="{{ route('schedule.store') }}" method="POST" id="create-event-form"
|
||||||
x-data="contractCheck('{{ route('contracts.check', [], false) }}')"
|
x-data="contractCheck('{{ route('contracts.check', [], false) }}')" class="space-y-5">
|
||||||
class="space-y-5">
|
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="start_time" id="start_time_hidden">
|
<input type="hidden" name="start_time" id="start_time_hidden">
|
||||||
<input type="hidden" name="end_time" id="end_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">
|
<label class="block text-sm font-semibold text-gray-700 mb-1">
|
||||||
Title <span class="text-pink-400">*</span>
|
Title <span class="text-pink-400">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input type="text" name="title" value="{{ old('title') }}"
|
<input type="text" name="title" value="{{ old('title') }}" class="input-soft" placeholder="Event title"
|
||||||
class="input-soft" placeholder="Event title" required>
|
required>
|
||||||
@error('title') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
|
@error('title') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -140,8 +138,7 @@
|
|||||||
{{-- Client --}}
|
{{-- Client --}}
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
|
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
|
||||||
<select name="client_id" id="client-select" class="input-soft"
|
<select name="client_id" id="client-select" class="input-soft" x-model="clientId"
|
||||||
x-model="clientId"
|
|
||||||
@change="onClientChange($event); if($event.target.value !== '__new__') checkContract()">
|
@change="onClientChange($event); if($event.target.value !== '__new__') checkContract()">
|
||||||
<option value="">— None —</option>
|
<option value="">— None —</option>
|
||||||
<option value="__new__" class="font-semibold text-pink-600">+ Create New Client…</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>
|
<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 class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Name <span class="text-pink-400">*</span></label>
|
<label class="block text-xs font-semibold text-gray-600 mb-1">Name <span
|
||||||
<input type="text" x-model="newName" class="input-soft text-sm" placeholder="Client / brand name">
|
class="text-pink-400">*</span></label>
|
||||||
|
<input type="text" x-model="newName" class="input-soft text-sm"
|
||||||
|
placeholder="Client / brand name">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Company</label>
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Email</label>
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Phone</label>
|
<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>
|
<p x-show="errMsg" x-text="errMsg" class="text-xs text-red-500"></p>
|
||||||
<div class="flex gap-2 pt-1">
|
<div class="flex gap-2 pt-1">
|
||||||
<button type="button" @click="saveNewClient()" :disabled="saving"
|
<button type="button" @click="saveNewClient()" :disabled="saving"
|
||||||
class="btn-sakura text-sm px-4 py-2" x-text="saving ? 'Saving…' : '✓ Add & Select'"></button>
|
class="btn-sakura text-sm px-4 py-2"
|
||||||
<button type="button" @click="isNew = false; document.getElementById('client-select').value = ''"
|
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">
|
class="text-sm px-4 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
@@ -190,7 +193,8 @@
|
|||||||
{{-- Category --}}
|
{{-- Category --}}
|
||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Category</label>
|
<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="">— None —</option>
|
||||||
<option value="__new__" class="font-semibold text-pink-600">+ Create New Category…</option>
|
<option value="__new__" class="font-semibold text-pink-600">+ Create New Category…</option>
|
||||||
@foreach($categoryTree as $parent)
|
@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">
|
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>
|
<p class="text-xs font-bold text-pink-700">🏷️ New Category</p>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Category Name <span class="text-pink-400">*</span></label>
|
<label class="block text-xs font-semibold text-gray-600 mb-1">Category Name <span
|
||||||
<input type="text" x-model="newCatName" class="input-soft text-sm" placeholder="e.g. Sponsored Post">
|
class="text-pink-400">*</span></label>
|
||||||
|
<input type="text" x-model="newCatName" class="input-soft text-sm"
|
||||||
|
placeholder="e.g. Sponsored Post">
|
||||||
</div>
|
</div>
|
||||||
<p x-show="catErrMsg" x-text="catErrMsg" class="text-xs text-red-500"></p>
|
<p x-show="catErrMsg" x-text="catErrMsg" class="text-xs text-red-500"></p>
|
||||||
<div class="flex gap-2 pt-1">
|
<div class="flex gap-2 pt-1">
|
||||||
<button type="button" @click="saveNewCategory()" :disabled="savingCat"
|
<button type="button" @click="saveNewCategory()" :disabled="savingCat"
|
||||||
class="btn-sakura text-sm px-4 py-2" x-text="savingCat ? 'Saving…' : '✓ Add & Select'"></button>
|
class="btn-sakura text-sm px-4 py-2"
|
||||||
<button type="button" @click="isNewCat = false; document.getElementById('category-select').value = ''"
|
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">
|
class="text-sm px-4 py-2 rounded-full border border-pink-200 text-gray-500 hover:bg-pink-50">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
@@ -238,8 +246,8 @@
|
|||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">
|
<label class="block text-sm font-semibold text-gray-700 mb-1">
|
||||||
Start Date <span class="text-pink-400">*</span>
|
Start Date <span class="text-pink-400">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input type="date" id="start_date" class="input-soft"
|
<input type="date" id="start_date" class="input-soft" value="{{ old('_start_date') }}" required
|
||||||
value="{{ old('_start_date') }}" required @change="checkContract()">
|
@change="checkContract()">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Start Time</label>
|
<label class="block text-sm font-semibold text-gray-700 mb-1">Start Time</label>
|
||||||
@@ -259,8 +267,8 @@
|
|||||||
{{-- Location --}}
|
{{-- Location --}}
|
||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Location</label>
|
<label class="block text-sm font-semibold text-gray-700 mb-1">Location</label>
|
||||||
<input type="text" name="location" value="{{ old('location') }}"
|
<input type="text" name="location" value="{{ old('location') }}" class="input-soft"
|
||||||
class="input-soft" placeholder="Online / venue address">
|
placeholder="Online / venue address">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -284,8 +292,7 @@
|
|||||||
|
|
||||||
{{-- Priority --}}
|
{{-- Priority --}}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<input type="checkbox" name="is_priority" value="1" id="priority"
|
<input type="checkbox" name="is_priority" value="1" id="priority" class="w-4 h-4 accent-pink-400" {{ old('is_priority') ? 'checked' : '' }}>
|
||||||
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>
|
<label for="priority" class="text-sm text-gray-600 cursor-pointer">⭐ Mark as Priority</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -310,19 +317,26 @@
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Platform</label>
|
<label class="block text-xs font-semibold text-gray-600 mb-1">Platform</label>
|
||||||
<select name="content_platform" class="input-soft text-sm">
|
<select name="content_platform" class="input-soft text-sm">
|
||||||
<option value="instagram" {{ old('content_platform') === 'instagram' ? 'selected' : '' }}>📸 Instagram</option>
|
<option value="instagram" {{ old('content_platform') === 'instagram' ? 'selected' : '' }}>📸
|
||||||
<option value="tiktok" {{ old('content_platform') === 'tiktok' ? 'selected' : '' }}>🎵 TikTok</option>
|
Instagram</option>
|
||||||
<option value="youtube" {{ old('content_platform') === 'youtube' ? 'selected' : '' }}>▶️ YouTube</option>
|
<option value="tiktok" {{ old('content_platform') === 'tiktok' ? 'selected' : '' }}>🎵 TikTok
|
||||||
<option value="other" {{ old('content_platform') === 'other' ? 'selected' : '' }}>📱 Other</option>
|
</option>
|
||||||
|
<option value="youtube" {{ old('content_platform') === 'youtube' ? 'selected' : '' }}>▶️
|
||||||
|
YouTube</option>
|
||||||
|
<option value="other" {{ old('content_platform') === 'other' ? 'selected' : '' }}>📱 Other
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Content Type</label>
|
<label class="block text-xs font-semibold text-gray-600 mb-1">Content Type</label>
|
||||||
<select name="content_type" class="input-soft text-sm">
|
<select name="content_type" class="input-soft text-sm">
|
||||||
<option value="post" {{ old('content_type') === 'post' ? 'selected' : '' }}>Post / Feed</option>
|
<option value="post" {{ old('content_type') === 'post' ? 'selected' : '' }}>Post / Feed
|
||||||
<option value="reel" {{ old('content_type') === 'reel' ? 'selected' : '' }}>Reel / Short</option>
|
</option>
|
||||||
|
<option value="reel" {{ old('content_type') === 'reel' ? 'selected' : '' }}>Reel / Short
|
||||||
|
</option>
|
||||||
<option value="story" {{ old('content_type') === 'story' ? 'selected' : '' }}>Story</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p class="sm:col-span-2 text-xs text-purple-500">
|
<p class="sm:col-span-2 text-xs text-purple-500">
|
||||||
@@ -345,8 +359,7 @@
|
|||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Contract Title</label>
|
<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"
|
<input type="text" name="contract_title" class="input-soft text-sm"
|
||||||
placeholder="e.g. Exclusive Posting Contract — BrandX"
|
placeholder="e.g. Exclusive Posting Contract — BrandX" value="{{ old('contract_title') }}">
|
||||||
value="{{ old('contract_title') }}">
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Contract Start</label>
|
<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">
|
<div class="flex flex-col sm:flex-row gap-3 pt-1">
|
||||||
<button type="submit" class="btn-sakura flex-1 py-3"
|
<button type="submit" class="btn-sakura flex-1 py-3"
|
||||||
:class="{ 'opacity-50 cursor-not-allowed': contractWarning }"
|
:class="{ 'opacity-50 cursor-not-allowed': contractWarning }" :disabled="contractWarning">
|
||||||
:disabled="contractWarning">
|
|
||||||
🐾 Schedule It!
|
🐾 Schedule It!
|
||||||
</button>
|
</button>
|
||||||
<a href="{{ route('schedule.index') }}"
|
<a href="{{ route('schedule.index') }}"
|
||||||
|
|||||||
@@ -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) {
|
eventDrop(info) {
|
||||||
fetch(`/schedule/${info.event.id}/dates`, {
|
fetch(`/schedule/${info.event.id}/dates`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|||||||
Reference in New Issue
Block a user