initial commit

This commit is contained in:
I-Fun
2026-04-01 08:04:56 +07:00
commit e09c8a5fa9
111 changed files with 18022 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
@extends('layouts.app')
@section('title', 'Add Client')
@section('page-title', 'Add New Client 🐾')
@section('page-subtitle', 'Fill in the contact details below')
@section('content')
<div class="max-w-2xl mx-auto">
<div class="cat-card p-4 lg:p-8">
<form action="{{ route('clients.store') }}" method="POST" class="space-y-5">
@csrf
{{-- Basic Info --}}
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1 uppercase tracking-wide">Basic Info</label>
<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">Name <span class="text-pink-400">*</span></label>
<input type="text" name="name" value="{{ old('name') }}" class="input-soft" placeholder="Brand / Contact name" required>
@error('name') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Email</label>
<input type="email" name="email" value="{{ old('email') }}" class="input-soft" placeholder="hello@brand.com">
@error('email') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Phone</label>
<input type="text" name="phone" value="{{ old('phone') }}" class="input-soft" placeholder="+62 812 xxxx">
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Company</label>
<input type="text" name="company" value="{{ old('company') }}" class="input-soft" placeholder="Brand / Company name">
</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', 'prospect') === $s->value ? 'selected' : '' }}>
{{ $s->label() }}
</option>
@endforeach
</select>
</div>
</div>
</div>
{{-- Social Handles --}}
<div>
<label class="block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">📱 Social Handles</label>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">📸 Instagram</label>
<input type="text" name="ig_handle" value="{{ old('ig_handle') }}" class="input-soft" placeholder="@handle">
</div>
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">🎵 TikTok</label>
<input type="text" name="tiktok_handle" value="{{ old('tiktok_handle') }}" class="input-soft" placeholder="@handle">
</div>
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">▶️ YouTube</label>
<input type="text" name="youtube_handle" value="{{ old('youtube_handle') }}" class="input-soft" placeholder="Channel name">
</div>
</div>
</div>
{{-- Shipping Address --}}
<div>
<label class="block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">📍 Shipping / PR Address</label>
<div class="grid grid-cols-2 gap-3">
<div class="col-span-2">
<input type="text" name="address_line1" value="{{ old('address_line1') }}" class="input-soft" placeholder="Street address">
</div>
<div class="col-span-2">
<input type="text" name="address_line2" value="{{ old('address_line2') }}" class="input-soft" placeholder="Apartment, suite, etc.">
</div>
<div>
<input type="text" name="city" value="{{ old('city') }}" class="input-soft" placeholder="City">
</div>
<div>
<input type="text" name="state" value="{{ old('state') }}" class="input-soft" placeholder="Province">
</div>
<div>
<input type="text" name="postal_code" value="{{ old('postal_code') }}" class="input-soft" placeholder="Postal code">
</div>
<div>
<input type="text" name="country" value="{{ old('country', 'Indonesia') }}" class="input-soft" placeholder="Country">
</div>
</div>
</div>
{{-- Notes --}}
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes</label>
<textarea name="notes" rows="3" class="input-soft resize-none" placeholder="Any extra notes...">{{ 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">🐱 Save Client</button>
<a href="{{ route('clients.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

View File

@@ -0,0 +1,108 @@
@extends('layouts.app')
@section('title', 'Edit ' . $client->name)
@section('page-title', 'Edit Client 🐾')
@section('page-subtitle', $client->name)
@section('content')
<div class="max-w-2xl mx-auto">
<div class="cat-card p-4 lg:p-8">
<form action="{{ route('clients.update', $client) }}" method="POST" class="space-y-5">
@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">Name <span class="text-pink-400">*</span></label>
<input type="text" name="name" value="{{ old('name', $client->name) }}" class="input-soft" required>
@error('name') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Email</label>
<input type="email" name="email" value="{{ old('email', $client->email) }}" class="input-soft">
@error('email') <p class="text-xs text-red-500 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Phone</label>
<input type="text" name="phone" value="{{ old('phone', $client->phone) }}" class="input-soft">
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Company</label>
<input type="text" name="company" value="{{ old('company', $client->company) }}" 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', $client->status->value) === $s->value ? 'selected' : '' }}>{{ $s->label() }}</option>
@endforeach
</select>
</div>
</div>
<div>
<h3 class="text-sm font-bold text-gray-600 mb-3">📱 Social Handles</h3>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">📸 Instagram</label>
<input type="text" name="ig_handle" value="{{ old('ig_handle', $client->ig_handle) }}"
class="input-soft text-sm" placeholder="@handle">
</div>
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">🎵 TikTok</label>
<input type="text" name="tiktok_handle" value="{{ old('tiktok_handle', $client->tiktok_handle) }}"
class="input-soft text-sm" placeholder="@handle">
</div>
<div>
<label class="block text-xs font-semibold text-gray-500 mb-1">▶️ YouTube</label>
<input type="text" name="youtube_handle" value="{{ old('youtube_handle', $client->youtube_handle) }}"
class="input-soft text-sm">
</div>
</div>
</div>
<div>
<h3 class="text-sm font-bold text-gray-600 mb-3">📍 Shipping Address</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div class="sm:col-span-2">
<input type="text" name="address_line1" value="{{ old('address_line1', $client->address_line1) }}"
class="input-soft" placeholder="Street address">
</div>
<div class="sm:col-span-2">
<input type="text" name="address_line2" value="{{ old('address_line2', $client->address_line2) }}"
class="input-soft" placeholder="Apt, suite...">
</div>
<div>
<input type="text" name="city" value="{{ old('city', $client->city) }}"
class="input-soft" placeholder="City">
</div>
<div>
<input type="text" name="state" value="{{ old('state', $client->state) }}"
class="input-soft" placeholder="Province">
</div>
<div>
<input type="text" name="postal_code" value="{{ old('postal_code', $client->postal_code) }}"
class="input-soft" placeholder="Postal code">
</div>
<div>
<input type="text" name="country" value="{{ old('country', $client->country) }}"
class="input-soft" placeholder="Country">
</div>
</div>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes</label>
<textarea name="notes" rows="3" class="input-soft resize-none">{{ old('notes', $client->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('clients.show', $client) }}"
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

View File

@@ -0,0 +1,92 @@
@extends('layouts.app')
@section('title', 'Clients')
@section('page-title', 'Clients / CRM 👥')
@section('page-subtitle', 'Manage your brand partners and contacts')
@section('content')
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5">
<div class="relative">
<input type="text" placeholder="Search clients..." class="input-soft pl-9 text-sm"
oninput="filterClients(this.value)" id="search-input">
<span class="absolute left-3 top-2.5 text-gray-400 text-sm">🔍</span>
</div>
<a href="{{ route('clients.create') }}" class="btn-sakura text-sm text-center">+ Add Client</a>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4" id="clients-grid">
@forelse($clients as $client)
<div class="cat-card p-4 flex flex-col gap-3 hover:shadow-md transition-shadow duration-200"
data-name="{{ strtolower($client->name) }}">
<div class="flex items-start gap-3">
<div class="w-11 h-11 rounded-2xl flex items-center justify-center text-white font-bold text-base shadow-sm shrink-0"
style="background: linear-gradient(135deg, #FFB7C5, #E8D5F5);">
{{ $client->initials }}
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 flex-wrap">
<h3 class="font-bold text-gray-800 text-sm truncate">{{ $client->name }}</h3>
<span class="badge {{ $client->status->badgeClass() }} text-xs shrink-0">{{ $client->status->label() }}</span>
</div>
@if($client->company)
<div class="text-xs text-gray-400 truncate">{{ $client->company }}</div>
@endif
</div>
</div>
<div class="flex flex-wrap gap-1.5">
@if($client->ig_handle)
<span class="text-xs bg-pink-50 text-pink-600 px-2 py-0.5 rounded-full">📸 {{ $client->ig_handle }}</span>
@endif
@if($client->tiktok_handle)
<span class="text-xs bg-purple-50 text-purple-600 px-2 py-0.5 rounded-full">🎵 {{ $client->tiktok_handle }}</span>
@endif
@if($client->youtube_handle)
<span class="text-xs bg-red-50 text-red-600 px-2 py-0.5 rounded-full">▶️ {{ $client->youtube_handle }}</span>
@endif
</div>
<div class="flex gap-3 text-xs text-gray-500">
<span>📅 {{ $client->events_count }}</span>
<span>🧾 {{ $client->invoices_count }}</span>
<span>🎬 {{ $client->content_briefs_count }}</span>
</div>
@if($client->email || $client->city)
<div class="text-xs text-gray-400 space-y-0.5">
@if($client->email) <div class="truncate">✉️ {{ $client->email }}</div> @endif
@if($client->city) <div>📍 {{ $client->city }}{{ $client->country ? ', '.$client->country : '' }}</div> @endif
</div>
@endif
<div class="flex gap-2 pt-2 border-t border-pink-50">
<a href="{{ route('clients.show', $client) }}" class="btn-sakura text-xs py-1.5 flex-1 text-center">View</a>
<a href="{{ route('clients.edit', $client) }}" class="flex-1 text-center text-xs py-1.5 px-3 rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50">Edit</a>
<form action="{{ route('clients.destroy', $client) }}" method="POST" onsubmit="return confirm('Remove this client?')">
@csrf @method('DELETE')
<button class="text-xs py-1.5 px-3 rounded-full border border-red-100 text-red-400 hover:bg-red-50">Del</button>
</form>
</div>
</div>
@empty
<div class="col-span-full text-center py-16 text-gray-400">
<span class="text-5xl block mb-3">🐱</span>
<p class="text-base font-medium">No clients yet!</p>
<a href="{{ route('clients.create') }}" class="btn-sakura mt-4 inline-block">Add your first client</a>
</div>
@endforelse
</div>
{{ $clients->links() }}
@push('scripts')
<script>
function filterClients(query) {
document.querySelectorAll('#clients-grid > div[data-name]').forEach(card => {
card.style.display = card.dataset.name.includes(query.toLowerCase()) ? '' : 'none';
});
}
</script>
@endpush
@endsection

View File

@@ -0,0 +1,138 @@
@extends('layouts.app')
@section('title', $client->name)
@section('page-title', $client->name)
@section('page-subtitle', $client->company ?? $client->email ?? 'Client Profile')
@section('content')
<div class="max-w-5xl mx-auto">
{{-- Profile Header --}}
<div class="cat-card p-4 mb-4">
<div class="flex items-start gap-4">
<div class="w-14 h-14 rounded-3xl flex items-center justify-center text-white font-bold text-xl shadow shrink-0"
style="background: linear-gradient(135deg, #FFB7C5, #E8D5F5);">
{{ $client->initials }}
</div>
<div class="flex-1 min-w-0">
<h2 class="text-lg font-bold text-gray-800 leading-tight">{{ $client->name }}</h2>
@if($client->company) <p class="text-sm text-gray-500">{{ $client->company }}</p> @endif
<div class="flex gap-2 mt-2 flex-wrap">
<span class="badge {{ $client->status->badgeClass() }}">{{ $client->status->label() }}</span>
@if($client->ig_handle)
<span class="text-xs bg-pink-50 text-pink-600 px-2 py-1 rounded-full">📸 {{ $client->ig_handle }}</span>
@endif
@if($client->tiktok_handle)
<span class="text-xs bg-purple-50 text-purple-600 px-2 py-1 rounded-full">🎵 {{ $client->tiktok_handle }}</span>
@endif
@if($client->youtube_handle)
<span class="text-xs bg-red-50 text-red-600 px-2 py-1 rounded-full">▶️ {{ $client->youtube_handle }}</span>
@endif
</div>
</div>
</div>
{{-- Action buttons below on mobile, inline on larger screens --}}
<div class="flex gap-2 mt-3">
<a href="{{ route('clients.edit', $client) }}" class="btn-sakura text-sm px-4 py-2">Edit</a>
<a href="{{ route('invoices.create') }}?client={{ $client->id }}"
class="text-sm px-4 py-2 rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50 font-semibold">
🧾 New Invoice
</a>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
{{-- Contact Info --}}
<div class="cat-card p-4">
<h3 class="font-bold text-gray-700 mb-3 text-sm flex items-center gap-2">📋 Contact Info</h3>
<div class="space-y-2 text-sm">
@if($client->email)
<div class="flex gap-2 items-start">
<span class="text-gray-400 shrink-0">✉️</span>
<a href="mailto:{{ $client->email }}" class="text-pink-500 hover:underline break-all">{{ $client->email }}</a>
</div>
@endif
@if($client->phone)
<div class="flex gap-2 items-start">
<span class="text-gray-400 shrink-0">📞</span>
<span>{{ $client->phone }}</span>
</div>
@endif
@if($client->full_address)
<div class="flex gap-2 items-start">
<span class="text-gray-400 shrink-0">📍</span>
<span class="text-gray-600">{{ $client->full_address }}</span>
</div>
@endif
@if($client->notes)
<div class="mt-3 pt-3 border-t border-pink-50">
<p class="text-xs font-semibold text-gray-500 mb-1">Notes</p>
<p class="text-gray-600 text-xs">{{ $client->notes }}</p>
</div>
@endif
@if(!$client->email && !$client->phone && !$client->full_address && !$client->notes)
<p class="text-xs text-gray-400">No contact info yet.</p>
@endif
</div>
</div>
{{-- Recent Events --}}
<div class="cat-card p-4">
<div class="flex items-center justify-between mb-3">
<h3 class="font-bold text-gray-700 text-sm flex items-center gap-2">📅 Events</h3>
<a href="{{ route('schedule.index') }}" class="text-xs text-pink-500">All →</a>
</div>
@forelse($client->events as $event)
<div class="py-2 border-b border-pink-50 last:border-0 text-sm">
<div class="font-medium text-gray-700 truncate">{{ $event->title }}</div>
<div class="text-xs text-gray-400">{{ $event->start_time->format('d M Y, H:i') }}</div>
</div>
@empty
<p class="text-xs text-gray-400">No events yet.</p>
@endforelse
</div>
{{-- Recent Invoices --}}
<div class="cat-card p-4">
<div class="flex items-center justify-between mb-3">
<h3 class="font-bold text-gray-700 text-sm flex items-center gap-2">🧾 Invoices</h3>
<a href="{{ route('invoices.index') }}" class="text-xs text-pink-500">All →</a>
</div>
@forelse($client->invoices as $invoice)
<div class="py-2 border-b border-pink-50 last:border-0 text-sm flex justify-between gap-2">
<div class="min-w-0">
<div class="font-medium text-gray-700 truncate">{{ $invoice->invoice_number }}</div>
<div class="text-xs text-gray-400">Due {{ $invoice->due_date->format('d M Y') }}</div>
</div>
<div class="text-right shrink-0">
<div class="font-bold text-gray-700 text-xs">{{ $invoice->formatted_total }}</div>
<span class="badge {{ $invoice->status->badgeClass() }} badge-xs">{{ $invoice->status->label() }}</span>
</div>
</div>
@empty
<p class="text-xs text-gray-400">No invoices yet.</p>
@endforelse
</div>
</div>
{{-- Content Briefs --}}
@if($client->contentBriefs->isNotEmpty())
<div class="cat-card p-4 mt-4">
<h3 class="font-bold text-gray-700 mb-3 text-sm flex items-center gap-2">🎬 Content Briefs</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
@foreach($client->contentBriefs as $brief)
<a href="{{ route('content.show', $brief) }}"
class="bg-pink-50 rounded-2xl p-3 hover:bg-pink-100 transition-colors block">
<div class="text-xs font-bold text-pink-600 flex items-center gap-1">
{{ $brief->platform_icon }} {{ ucfirst($brief->platform) }}
</div>
<div class="font-semibold text-gray-700 text-sm truncate mt-1">{{ $brief->title }}</div>
<div class="text-xs text-gray-400 mt-0.5">{{ $brief->status->label() }}</div>
</a>
@endforeach
</div>
</div>
@endif
</div>
@endsection