initial commit
This commit is contained in:
107
resources/views/content/create.blade.php
Normal file
107
resources/views/content/create.blade.php
Normal file
@@ -0,0 +1,107 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'New Content Brief')
|
||||
@section('page-title', 'New Content Brief 🎬')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="cat-card p-4 lg:p-8">
|
||||
<form action="{{ route('content.store') }}" method="POST" class="space-y-4">
|
||||
@csrf
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div class="sm:col-span-2">
|
||||
<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" required
|
||||
placeholder="e.g. GRWM ft. Brand X Skincare">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Platform <span class="text-pink-400">*</span></label>
|
||||
<select name="platform" class="input-soft" required>
|
||||
<option value="instagram">📸 Instagram</option>
|
||||
<option value="tiktok">🎵 TikTok</option>
|
||||
<option value="youtube">▶️ YouTube</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Content Type</label>
|
||||
<input type="text" name="content_type" value="{{ old('content_type') }}" class="input-soft"
|
||||
placeholder="Reel, Short, GRWM, Story...">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Status</label>
|
||||
<select name="status" class="input-soft">
|
||||
@foreach($statuses as $s)
|
||||
<option value="{{ $s->value }}" {{ old('status', request('status', 'drafting')) === $s->value ? 'selected' : '' }}>
|
||||
{{ $s->icon() }} {{ $s->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
|
||||
<select name="client_id" class="input-soft">
|
||||
<option value="">— None —</option>
|
||||
@foreach($clients as $client)
|
||||
<option value="{{ $client->id }}">{{ $client->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Target Date</label>
|
||||
<input type="date" name="target_date" value="{{ old('target_date') }}" class="input-soft">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Storyline Maker --}}
|
||||
<div class="bg-pink-50 rounded-2xl p-4 space-y-3">
|
||||
<h3 class="font-bold text-gray-700 flex items-center gap-2 text-sm">✍️ Storyline Maker</h3>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-pink-600 mb-1">🎣 HOOK — Open with a bang!</label>
|
||||
<textarea name="hook" rows="2" class="input-soft text-sm resize-none bg-white"
|
||||
placeholder="e.g. 'POV: you finally found the moisturizer that doesn't break you out...'">{{ old('hook') }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-pink-600 mb-1">📖 BODY — Deliver the story</label>
|
||||
<textarea name="body" rows="3" class="input-soft text-sm resize-none bg-white"
|
||||
placeholder="The main content — what happens, product demo, value...">{{ old('body') }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-pink-600 mb-1">📣 CTA — Call to Action</label>
|
||||
<textarea name="cta" rows="2" class="input-soft text-sm resize-none bg-white"
|
||||
placeholder="e.g. 'Link in bio to shop! Use code KITTY for 15% off'">{{ old('cta') }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-500 mb-1">📝 Full Script (optional)</label>
|
||||
<textarea name="script" rows="4" class="input-soft text-sm resize-none bg-white"
|
||||
placeholder="Complete spoken script, timestamps, shot list...">{{ old('script') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Caption</label>
|
||||
<textarea name="caption" rows="3" class="input-soft resize-none"
|
||||
placeholder="Post caption text...">{{ old('caption') }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Hashtags</label>
|
||||
<input type="text" name="hashtags" value="{{ old('hashtags') }}" class="input-soft"
|
||||
placeholder="#skincare #grwm #fyp">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none"
|
||||
placeholder="Any extra notes, brand requirements...">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3 pt-1">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3">🎬 Create Brief</button>
|
||||
<a href="{{ route('content.index') }}"
|
||||
class="flex-1 py-3 text-center rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50 font-semibold text-sm">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
103
resources/views/content/edit.blade.php
Normal file
103
resources/views/content/edit.blade.php
Normal file
@@ -0,0 +1,103 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Brief')
|
||||
@section('page-title', 'Edit Content Brief 🎬')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="cat-card p-4 lg:p-8">
|
||||
<form action="{{ route('content.update', $brief) }}" method="POST" class="space-y-4">
|
||||
@csrf @method('PUT')
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Title</label>
|
||||
<input type="text" name="title" value="{{ old('title', $brief->title) }}" class="input-soft" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Platform</label>
|
||||
<select name="platform" class="input-soft">
|
||||
@foreach(['instagram','tiktok','youtube'] as $p)
|
||||
<option value="{{ $p }}" {{ old('platform', $brief->platform) === $p ? 'selected' : '' }}>{{ ucfirst($p) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Content Type</label>
|
||||
<input type="text" name="content_type" value="{{ old('content_type', $brief->content_type) }}" class="input-soft">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Status</label>
|
||||
<select name="status" class="input-soft">
|
||||
@foreach($statuses as $s)
|
||||
<option value="{{ $s->value }}" {{ old('status', $brief->status->value) === $s->value ? 'selected' : '' }}>{{ $s->icon() }} {{ $s->label() }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
|
||||
<select name="client_id" class="input-soft">
|
||||
<option value="">— None —</option>
|
||||
@foreach($clients as $client)
|
||||
<option value="{{ $client->id }}" {{ old('client_id', $brief->client_id) == $client->id ? 'selected' : '' }}>{{ $client->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Target Date</label>
|
||||
<input type="date" name="target_date" value="{{ old('target_date', $brief->target_date?->toDateString()) }}" class="input-soft">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Posted Date</label>
|
||||
<input type="date" name="posted_date" value="{{ old('posted_date', $brief->posted_date?->toDateString()) }}" class="input-soft">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Posted URL</label>
|
||||
<input type="url" name="posted_url" value="{{ old('posted_url', $brief->posted_url) }}" class="input-soft" placeholder="https://...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-pink-50 rounded-2xl p-4 space-y-3">
|
||||
<h3 class="font-bold text-gray-700 text-sm">✍️ Storyline</h3>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-pink-600 mb-1 block">🎣 Hook</label>
|
||||
<textarea name="hook" rows="2" class="input-soft text-sm resize-none bg-white">{{ old('hook', $brief->hook) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-pink-600 mb-1 block">📖 Body</label>
|
||||
<textarea name="body" rows="3" class="input-soft text-sm resize-none bg-white">{{ old('body', $brief->body) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-pink-600 mb-1 block">📣 CTA</label>
|
||||
<textarea name="cta" rows="2" class="input-soft text-sm resize-none bg-white">{{ old('cta', $brief->cta) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-gray-500 mb-1 block">📝 Script</label>
|
||||
<textarea name="script" rows="4" class="input-soft text-sm resize-none bg-white">{{ old('script', $brief->script) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Caption</label>
|
||||
<textarea name="caption" rows="3" class="input-soft resize-none">{{ old('caption', $brief->caption) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Hashtags</label>
|
||||
<input type="text" name="hashtags" value="{{ old('hashtags', $brief->hashtags) }}" class="input-soft">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes</label>
|
||||
<textarea name="notes" rows="2" class="input-soft resize-none">{{ old('notes', $brief->notes) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3 pt-1">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3">💾 Save Changes</button>
|
||||
<a href="{{ route('content.show', $brief) }}"
|
||||
class="flex-1 py-3 text-center rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50 font-semibold text-sm">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
112
resources/views/content/index.blade.php
Normal file
112
resources/views/content/index.blade.php
Normal file
@@ -0,0 +1,112 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Content Board')
|
||||
@section('page-title', 'Content Board 🎬')
|
||||
@section('page-subtitle', 'Drag cards between columns to update status')
|
||||
|
||||
@section('content')
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5">
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
@foreach(['instagram','tiktok','youtube'] as $p)
|
||||
<a href="{{ request()->fullUrlWithQuery(['platform' => $p]) }}"
|
||||
class="px-3 py-1.5 rounded-full text-xs font-semibold transition-colors
|
||||
{{ request('platform') === $p ? 'text-white' : 'bg-white text-gray-500 hover:bg-pink-50 border border-pink-100' }}"
|
||||
style="{{ request('platform') === $p ? 'background:#FFB7C5' : '' }}">
|
||||
{{ ['instagram'=>'📸','tiktok'=>'🎵','youtube'=>'▶️'][$p] }} {{ ucfirst($p) }}
|
||||
</a>
|
||||
@endforeach
|
||||
@if(request('platform'))
|
||||
<a href="{{ route('content.index') }}" class="px-3 py-1.5 rounded-full text-xs text-gray-400 hover:text-gray-600 border border-gray-200 bg-white">✕ Clear</a>
|
||||
@endif
|
||||
</div>
|
||||
<a href="{{ route('content.create') }}" class="btn-sakura text-sm shrink-0">+ New Brief</a>
|
||||
</div>
|
||||
|
||||
{{--
|
||||
Mobile : grid grid-cols-1 → columns stack vertically, each full width
|
||||
Desktop : flex flex-nowrap → columns in a single horizontal row, each flex-1
|
||||
x-sort:group is on the INNER items div (not the column div) to avoid
|
||||
SortableJS mutating the outer layout container.
|
||||
--}}
|
||||
<div class="grid grid-cols-1 gap-4 lg:flex lg:flex-nowrap lg:gap-4"
|
||||
x-data="kanban()">
|
||||
@foreach($columns as $col)
|
||||
@php $status = $col['status']; @endphp
|
||||
|
||||
{{-- Column shell — layout only, NOT the sort target --}}
|
||||
<div class="{{ $status->colorClass() }} flex flex-col w-full lg:flex-1 lg:min-w-36 rounded-2xl p-3"
|
||||
data-status="{{ $status->value }}">
|
||||
|
||||
{{-- Column header --}}
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="text-base">{{ $status->icon() }}</span>
|
||||
<span class="font-bold text-gray-700 text-sm">{{ $status->label() }}</span>
|
||||
</div>
|
||||
<span class="text-xs bg-white rounded-full px-2 py-0.5 font-bold text-gray-500 shadow-sm">{{ count($col['items']) }}</span>
|
||||
</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 }}">
|
||||
|
||||
@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 }}">
|
||||
<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) }}"
|
||||
class="font-semibold text-gray-800 text-xs leading-tight hover:text-pink-500 line-clamp-2 flex-1">
|
||||
{{ $brief->title }}
|
||||
</a>
|
||||
</div>
|
||||
@if($brief->client)
|
||||
<div class="text-xs text-gray-400 mb-1">👥 {{ $brief->client->name }}</div>
|
||||
@endif
|
||||
@if($brief->target_date)
|
||||
<div class="text-xs {{ $brief->target_date->isPast() && $brief->status->value !== 'posted' ? 'text-red-400 font-bold' : 'text-gray-400' }}">
|
||||
📅 {{ $brief->target_date->format('d M') }}
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex justify-end mt-1.5">
|
||||
<a href="{{ route('content.edit', $brief) }}" class="text-xs text-gray-400 hover:text-pink-500">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Add button --}}
|
||||
<a href="{{ route('content.create') }}?status={{ $status->value }}"
|
||||
class="mt-3 text-xs text-gray-400 hover:text-pink-500 text-center py-2
|
||||
border-2 border-dashed border-pink-100 rounded-2xl hover:border-pink-300 transition-colors block">
|
||||
+ Add
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function kanban() {
|
||||
return {
|
||||
moved(event, targetStatus) {
|
||||
const id = event.item?.dataset?.id;
|
||||
if (!id) 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! 🎬'); });
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
93
resources/views/content/show.blade.php
Normal file
93
resources/views/content/show.blade.php
Normal file
@@ -0,0 +1,93 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', $brief->title)
|
||||
@section('page-title', $brief->title)
|
||||
@section('page-subtitle', $brief->platform_icon . ' ' . ucfirst($brief->platform) . ' · ' . $brief->status->label())
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="flex gap-3 mb-5">
|
||||
<a href="{{ route('content.edit', $brief) }}" class="btn-sakura">Edit</a>
|
||||
<form action="{{ route('content.destroy', $brief) }}" method="POST" onsubmit="return confirm('Delete this brief?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-ghost rounded-full text-error">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
|
||||
{{-- Main Brief --}}
|
||||
<div class="lg:col-span-2 space-y-4">
|
||||
{{-- Storyline --}}
|
||||
@foreach([['🎣', 'Hook', $brief->hook], ['📖', 'Body', $brief->body], ['📣', 'CTA', $brief->cta], ['📝', 'Script', $brief->script]] as [$icon, $label, $value])
|
||||
@if($value)
|
||||
<div class="cat-card p-5">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-lg">{{ $icon }}</span>
|
||||
<h3 class="font-bold text-gray-700">{{ $label }}</h3>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 whitespace-pre-wrap leading-relaxed">{{ $value }}</p>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if($brief->caption)
|
||||
<div class="cat-card p-5">
|
||||
<h3 class="font-bold text-gray-700 mb-2">Caption</h3>
|
||||
<p class="text-sm text-gray-600 whitespace-pre-wrap">{{ $brief->caption }}</p>
|
||||
@if($brief->hashtags)
|
||||
<div class="mt-2 text-xs text-pink-500">{{ $brief->hashtags }}</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Sidebar --}}
|
||||
<div class="space-y-4">
|
||||
<div class="cat-card p-5 space-y-3 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-500">Platform</span>
|
||||
<span class="font-medium">{{ $brief->platform_icon }} {{ ucfirst($brief->platform) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-500">Type</span>
|
||||
<span class="font-medium">{{ $brief->content_type ?? '—' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-gray-500">Status</span>
|
||||
<span class="badge {{ $brief->status->badgeClass() }}">{{ $brief->status->label() }}</span>
|
||||
</div>
|
||||
@if($brief->client)
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-500">Client</span>
|
||||
<a href="{{ route('clients.show', $brief->client) }}" class="font-medium text-pink-500 hover:underline">{{ $brief->client->name }}</a>
|
||||
</div>
|
||||
@endif
|
||||
@if($brief->target_date)
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-500">Target</span>
|
||||
<span class="font-medium">{{ $brief->target_date->format('d M Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($brief->posted_date)
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-500">Posted</span>
|
||||
<span class="font-medium text-green-600">{{ $brief->posted_date->format('d M Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($brief->posted_url)
|
||||
<div>
|
||||
<a href="{{ $brief->posted_url }}" target="_blank" class="btn-sakura text-xs px-4 py-1.5 w-full text-center block">🔗 View Post</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($brief->notes)
|
||||
<div class="cat-card p-5">
|
||||
<h4 class="font-bold text-gray-600 text-xs uppercase mb-2">Notes</h4>
|
||||
<p class="text-sm text-gray-500">{{ $brief->notes }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user