diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php
index 8707970..c039079 100644
--- a/app/Http/Controllers/ClientController.php
+++ b/app/Http/Controllers/ClientController.php
@@ -7,8 +7,10 @@ namespace App\Http\Controllers;
use App\Enums\ClientStatus;
use App\Models\Client;
use App\Models\ClientCategory;
+use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class ClientController extends Controller
@@ -146,4 +148,42 @@ class ClientController extends Controller
return redirect()->route('clients.index')
->with('success', 'π± Client removed.');
}
+
+ public function quickStore(Request $request): JsonResponse
+ {
+ $validated = $request->validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'nullable|email|unique:clients,email',
+ 'phone' => 'nullable|string|max:50',
+ 'company' => 'nullable|string|max:255',
+ ]);
+
+ $client = Client::create(array_merge($validated, ['status' => 'active']));
+
+ return response()->json(['id' => $client->id, 'name' => $client->name]);
+ }
+
+ public function uploadAvatar(Request $request, Client $client): JsonResponse
+ {
+ $request->validate(['avatar' => 'required|image|max:2048']);
+
+ if ($client->avatar) {
+ Storage::disk('public')->delete($client->avatar);
+ }
+
+ $path = $request->file('avatar')->store('client-avatars', 'public');
+ $client->update(['avatar' => $path]);
+
+ return response()->json(['url' => asset('storage/' . $path)]);
+ }
+
+ public function deleteAvatar(Client $client): JsonResponse
+ {
+ if ($client->avatar) {
+ Storage::disk('public')->delete($client->avatar);
+ $client->update(['avatar' => null]);
+ }
+
+ return response()->json(['ok' => true]);
+ }
}
diff --git a/app/Http/Controllers/EventController.php b/app/Http/Controllers/EventController.php
index 09fdf96..26e2f0f 100644
--- a/app/Http/Controllers/EventController.php
+++ b/app/Http/Controllers/EventController.php
@@ -4,10 +4,12 @@ declare(strict_types=1);
namespace App\Http\Controllers;
+use App\Enums\ContentStatus;
use App\Enums\EventStatus;
use App\Models\BrandContract;
use App\Models\Client;
use App\Models\ClientCategory;
+use App\Models\ContentBrief;
use App\Models\Event;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
@@ -25,6 +27,14 @@ class EventController extends Controller
->get();
}
+ public function create(): View
+ {
+ $clients = Client::where('status', 'active')->orderBy('name')->get();
+ $categoryTree = $this->categoryTree();
+
+ return view('schedule.create', compact('clients', 'categoryTree'));
+ }
+
public function index(): View
{
$events = Event::with('client')->orderBy('start_time')->get();
@@ -69,6 +79,9 @@ class EventController extends Controller
'contract_title' => 'nullable|string|max:255',
'contract_start_date' => 'nullable|date',
'contract_end_date' => 'nullable|date|after_or_equal:contract_start_date',
+ 'add_to_content_board' => 'nullable|boolean',
+ 'content_platform' => 'nullable|string|max:50',
+ 'content_type' => 'nullable|string|max:50',
]);
$validated['is_priority'] = $request->boolean('is_priority');
@@ -76,6 +89,7 @@ class EventController extends Controller
$event = Event::create(Arr::except($validated, [
'create_brand_contract', 'contract_title', 'contract_start_date', 'contract_end_date',
+ 'add_to_content_board', 'content_platform', 'content_type',
]));
if ($request->boolean('create_brand_contract') && $event->client_id) {
@@ -89,6 +103,19 @@ class EventController extends Controller
]);
}
+ if ($request->boolean('add_to_content_board')) {
+ ContentBrief::create([
+ 'title' => $event->title,
+ 'client_id' => $event->client_id,
+ 'category_id' => $event->category_id,
+ 'platform' => $request->input('content_platform', 'instagram'),
+ 'content_type' => $request->input('content_type', 'post'),
+ 'status' => ContentStatus::Drafting,
+ 'target_date' => $event->start_time->toDateString(),
+ 'notes' => $event->notes,
+ ]);
+ }
+
return redirect()->route('schedule.index')
->with('success', 'πΎ Event scheduled!');
}
diff --git a/app/Http/Controllers/InvoiceSettingsController.php b/app/Http/Controllers/InvoiceSettingsController.php
index 251187f..6cb3d0b 100644
--- a/app/Http/Controllers/InvoiceSettingsController.php
+++ b/app/Http/Controllers/InvoiceSettingsController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\InvoiceSettings;
+use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
@@ -53,4 +54,35 @@ class InvoiceSettingsController extends Controller
return redirect()->route('invoice-settings.edit')
->with('success', 'π§Ύ Invoice settings saved!');
}
+
+ public function uploadLogo(Request $request): JsonResponse
+ {
+ $request->validate(['header_logo' => 'required|image|max:2048']);
+
+ $settings = InvoiceSettings::instance();
+
+ if ($settings->header_logo) {
+ Storage::disk('public')->delete($settings->header_logo);
+ }
+
+ $path = $request->file('header_logo')->store('invoice-logos', 'public');
+ $settings->update(['header_logo' => $path]);
+
+ return response()->json([
+ 'url' => asset('storage/' . $path),
+ ]);
+ }
+
+ public function deleteLogo(): RedirectResponse
+ {
+ $settings = InvoiceSettings::instance();
+
+ if ($settings->header_logo) {
+ Storage::disk('public')->delete($settings->header_logo);
+ $settings->update(['header_logo' => null]);
+ }
+
+ return redirect()->route('invoice-settings.edit')
+ ->with('success', 'ποΈ Logo removed.');
+ }
}
diff --git a/app/Http/Controllers/NoteController.php b/app/Http/Controllers/NoteController.php
new file mode 100644
index 0000000..6760461
--- /dev/null
+++ b/app/Http/Controllers/NoteController.php
@@ -0,0 +1,64 @@
+orderByDesc('updated_at')
+ ->get();
+
+ return view('notes.index', compact('notes'));
+ }
+
+ public function store(Request $request): JsonResponse
+ {
+ $data = $request->validate([
+ 'title' => 'required|string|max:255',
+ 'content' => 'nullable|array',
+ 'color' => 'nullable|string|max:20',
+ 'is_pinned' => 'boolean',
+ ]);
+
+ $note = Note::create($data);
+
+ return response()->json($note, 201);
+ }
+
+ public function update(Request $request, Note $note): JsonResponse
+ {
+ $data = $request->validate([
+ 'title' => 'required|string|max:255',
+ 'content' => 'nullable|array',
+ 'color' => 'nullable|string|max:20',
+ 'is_pinned' => 'boolean',
+ ]);
+
+ $note->update($data);
+
+ return response()->json($note);
+ }
+
+ public function destroy(Note $note): JsonResponse
+ {
+ $note->delete();
+
+ return response()->json(['ok' => true]);
+ }
+
+ public function togglePin(Note $note): JsonResponse
+ {
+ $note->update(['is_pinned' => ! $note->is_pinned]);
+
+ return response()->json($note);
+ }
+}
diff --git a/app/Models/MediaFile.php b/app/Models/MediaFile.php
index 42c551a..5fe2a08 100644
--- a/app/Models/MediaFile.php
+++ b/app/Models/MediaFile.php
@@ -41,6 +41,11 @@ class MediaFile extends Model
return str_starts_with($this->mime_type, 'image/');
}
+ public function getIsVideoAttribute(): bool
+ {
+ return str_starts_with($this->mime_type, 'video/');
+ }
+
public function getIconAttribute(): string
{
if ($this->is_image) return 'πΌοΈ';
diff --git a/app/Models/Note.php b/app/Models/Note.php
new file mode 100644
index 0000000..3063230
--- /dev/null
+++ b/app/Models/Note.php
@@ -0,0 +1,22 @@
+ 'array',
+ 'is_pinned' => 'boolean',
+ ];
+}
diff --git a/database/migrations/2026_04_02_000000_create_notes_table.php b/database/migrations/2026_04_02_000000_create_notes_table.php
new file mode 100644
index 0000000..77149cc
--- /dev/null
+++ b/database/migrations/2026_04_02_000000_create_notes_table.php
@@ -0,0 +1,25 @@
+id();
+ $table->string('title')->default('Untitled Note');
+ $table->json('content')->nullable(); // array of block objects
+ $table->string('color', 20)->default('#FFFFFF');
+ $table->boolean('is_pinned')->default(false);
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('notes');
+ }
+};
diff --git a/resources/views/categories/index.blade.php b/resources/views/categories/index.blade.php
index 3b14e78..319cb20 100644
--- a/resources/views/categories/index.blade.php
+++ b/resources/views/categories/index.blade.php
@@ -15,45 +15,50 @@
@endif
{{-- Add form --}}
-
+
- β
-
Add New Category
+
-
+
@php $roots = $categories->whereNull('parent_id'); @endphp
@forelse($roots as $root)
diff --git a/resources/views/clients/index.blade.php b/resources/views/clients/index.blade.php
index d5ea3cb..d0373d7 100644
--- a/resources/views/clients/index.blade.php
+++ b/resources/views/clients/index.blade.php
@@ -85,10 +85,18 @@
data-name="{{ strtolower($client->name) }} {{ strtolower($client->company ?? '') }}">
-
- {{ $client->initials }}
-
+ @if($client->avatar)
+
+
 }})
+
+ @else
+
+ {{ $client->initials }}
+
+ @endif
{{ $client->name }}
diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php
index 4759129..59df90f 100644
--- a/resources/views/clients/show.blade.php
+++ b/resources/views/clients/show.blade.php
@@ -8,12 +8,55 @@
{{-- Profile Header --}}
-
+
-
- {{ $client->initials }}
+ {{-- Avatar with upload overlay --}}
+
+
+
+
+
+
+ {{ $client->initials }}
+
+
+ {{-- Upload overlay --}}
+
+
{{ $client->name }}
@if($client->company)
{{ $client->company }}
@endif
@@ -31,13 +74,17 @@
- {{-- Action buttons below on mobile, inline on larger screens --}}
-
diff --git a/resources/views/contracts/index.blade.php b/resources/views/contracts/index.blade.php
index baf9bcd..5b082ee 100644
--- a/resources/views/contracts/index.blade.php
+++ b/resources/views/contracts/index.blade.php
@@ -15,73 +15,78 @@
@endif
{{-- Add Contract --}}
-
+
{{-- Contracts List --}}
π
-
Active & Upcoming Contracts
+
All Contracts
{{ $contracts->count() }} total
diff --git a/resources/views/files/index.blade.php b/resources/views/files/index.blade.php
index 54fe180..6f9ceb0 100644
--- a/resources/views/files/index.blade.php
+++ b/resources/views/files/index.blade.php
@@ -6,6 +6,29 @@
@section('content')
+{{-- ββ Video Player Modal βββββββββββββββββββββββββββββββββββββ --}}
+
+
{{-- ββ File Detail Popup Modal ββββββββββββββββββββββββββββββββ --}}
@endif
+ @php $fileData = ['name' => $file->name ?: $file->original_name, 'url' => $file->url, 'size' => $file->human_size, 'folder' => $file->folder, 'client' => $file->client?->name, 'is_image' => $file->is_image, 'is_video' => $file->is_video, 'icon' => $file->icon, 'created_at' => $file->created_at->format('d M Y')] @endphp
{{-- Preview / Icon --}}
@if($file->is_image)
+ onclick="showFileDetail({{ json_encode($fileData) }})">
+ @elseif($file->is_video)
+
+ {{-- Native video poster (browser generates a frame) --}}
+
+ {{-- Play overlay --}}
+
+
@else
+ onclick="showFileDetail({{ json_encode($fileData) }})">
{{ $file->icon }}
@endif
@@ -256,7 +299,6 @@ function showFileDetail(file) {
document.getElementById('fdm-name').textContent = file.name;
document.getElementById('fdm-view-btn').href = file.url;
- // Preview area
const preview = document.getElementById('fdm-preview');
if (file.is_image) {
preview.innerHTML = `

`;
@@ -264,7 +306,6 @@ function showFileDetail(file) {
preview.innerHTML = `
${file.icon}`;
}
- // Info rows
const rows = [
['Size', file.size],
['Folder', file.folder],
@@ -278,6 +319,32 @@ function showFileDetail(file) {
document.getElementById('file-detail-modal').classList.remove('hidden');
}
+
+function openVideoPlayer(file) {
+ const player = document.getElementById('vpm-player');
+ player.src = file.url;
+ player.load();
+
+ document.getElementById('vpm-title').textContent = file.name;
+ document.getElementById('vpm-meta').textContent = file.size + (file.client ? ' Β· ' + file.client : '');
+ document.getElementById('vpm-dl').href = file.url;
+
+ document.getElementById('video-player-modal').classList.remove('hidden');
+ document.body.style.overflow = 'hidden';
+}
+
+function closeVideoModal() {
+ const player = document.getElementById('vpm-player');
+ player.pause();
+ player.src = '';
+ document.getElementById('video-player-modal').classList.add('hidden');
+ document.body.style.overflow = '';
+}
+
+// Close video modal on Escape
+document.addEventListener('keydown', e => {
+ if (e.key === 'Escape') closeVideoModal();
+});
@endpush
diff --git a/resources/views/invoices/create.blade.php b/resources/views/invoices/create.blade.php
index 1fdfb1b..a976d6a 100644
--- a/resources/views/invoices/create.blade.php
+++ b/resources/views/invoices/create.blade.php
@@ -121,7 +121,7 @@
Rp
- Total (you receive)
+ Grand Total
Rp
diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php
index 2ceff02..6ee8b6a 100644
--- a/resources/views/invoices/edit.blade.php
+++ b/resources/views/invoices/edit.blade.php
@@ -116,7 +116,7 @@
Rp
- Total (you receive)
+ Grand Total
Rp
diff --git a/resources/views/invoices/index.blade.php b/resources/views/invoices/index.blade.php
index 9025055..8fd1191 100644
--- a/resources/views/invoices/index.blade.php
+++ b/resources/views/invoices/index.blade.php
@@ -6,18 +6,7 @@
@section('content')
-
+
diff --git a/resources/views/invoices/pdf.blade.php b/resources/views/invoices/pdf.blade.php
index 36017a1..e48b65e 100644
--- a/resources/views/invoices/pdf.blade.php
+++ b/resources/views/invoices/pdf.blade.php
@@ -2,119 +2,180 @@
+ @php
+ $accent = $invoiceSettings->accent_color ?? '#FFB7C5';
+ $hex = ltrim($accent, '#');
+ $r = hexdec(substr($hex, 0, 2));
+ $g = hexdec(substr($hex, 2, 2));
+ $b = hexdec(substr($hex, 4, 2));
+ // 92% white + 8% accent β very light tint
+ $tint = sprintf('#%02X%02X%02X',
+ (int)(255 * 0.92 + $r * 0.08),
+ (int)(255 * 0.92 + $g * 0.08),
+ (int)(255 * 0.92 + $b * 0.08)
+ );
+ // 80% white + 20% accent β medium-light tint (for borders)
+ $tintMid = sprintf('#%02X%02X%02X',
+ (int)(255 * 0.80 + $r * 0.20),
+ (int)(255 * 0.80 + $g * 0.20),
+ (int)(255 * 0.80 + $b * 0.20)
+ );
+ @endphp
-
+{{-- ββ Header βββββββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
-
-
-
Bill To
- @if($invoice->client)
-
{{ $invoice->client->name }}
- @if($invoice->client->company)
{{ $invoice->client->company }}
@endif
- @if($invoice->client->email)
{{ $invoice->client->email }}
@endif
- @if($invoice->client->full_address)
{{ $invoice->client->full_address }}
@endif
- @else
-
β
- @endif
-
-
-
Invoice Details
-
- | Issue Date | {{ $invoice->issue_date->format('d M Y') }} |
- | Due Date | {{ $invoice->due_date->format('d M Y') }} |
- @if($invoice->paid_date)
- | Paid Date | {{ $invoice->paid_date->format('d M Y') }} |
+{{-- ββ Billing Info βββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+ |
+ Bill To
+ @if($invoice->client)
+ {{ $invoice->client->name }}
+ @if($invoice->client->company) {{ $invoice->client->company }} @endif
+ @if($invoice->client->email) {{ $invoice->client->email }} @endif
+ @if($invoice->client->full_address) {{ $invoice->client->full_address }} @endif
+ @else
+ β
@endif
- |
| Currency | {{ $invoice->currency }} |
-
-
-
+
+
+ Invoice Details
+
+ | Issue Date | {{ $invoice->issue_date->format('d M Y') }} |
+ | Due Date | {{ $invoice->due_date->format('d M Y') }} |
+ @if($invoice->paid_date)
+ | Paid Date | {{ $invoice->paid_date->format('d M Y') }} |
+ @endif
+
+ |
+
+
-
+{{-- ββ Line Items βββββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
- | Description |
- Qty |
- Unit Price |
- Total |
+ Description |
+ Qty |
+ Unit Price |
+ Total |
@foreach($invoice->items as $item)
-
- |
- {{ $item->description }}
- @if(!$item->is_taxable)
- Non-Taxable
- @endif
- |
- {{ $item->quantity }} |
- Rp {{ number_format($item->unit_price, 0, ',', '.') }} |
- Rp {{ number_format($item->total, 0, ',', '.') }} |
-
+
+ |
+ {{ $item->description }}
+ @if(!$item->is_taxable)
+ Non-Taxable
+ @endif
+ |
+ {{ $item->quantity }} |
+ Rp {{ number_format($item->unit_price, 0, ',', '.') }} |
+ Rp {{ number_format($item->total, 0, ',', '.') }} |
+
@endforeach
-
-
-
SubtotalRp {{ number_format($invoice->subtotal, 0, ',', '.') }}
- @if($invoice->tax_rate > 0)
-
Tax ({{ $invoice->tax_rate }}% β paid by client)Rp {{ number_format($invoice->tax_amount, 0, ',', '.') }}
- @endif
-
Total (you receive){{ $invoice->formatted_total }}
+{{-- ββ Totals βββββββββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
+
+ | Subtotal |
+ Rp {{ number_format($invoice->subtotal, 0, ',', '.') }} |
+
+ @if($invoice->tax_rate > 0)
+
+ | Tax ({{ $invoice->tax_rate }}% β paid by client) |
+ Rp {{ number_format($invoice->tax_amount, 0, ',', '.') }} |
+
+ @endif
+
+ | Grand Total |
+ {{ $invoice->formatted_total }} |
+
+
+{{-- ββ Payment Info ββββββββββββββββββββββββββββββββββββββββββββββββ --}}
@if($invoice->bank_name)
Payment Information
@@ -130,7 +191,7 @@
{{ $invoice->notes }}
@endif
-
+
diff --git a/resources/views/invoices/settings.blade.php b/resources/views/invoices/settings.blade.php
index 150f0af..561256d 100644
--- a/resources/views/invoices/settings.blade.php
+++ b/resources/views/invoices/settings.blade.php
@@ -61,29 +61,79 @@
π¨ Invoice Header
- @if($settings->header_logo)
-
-
 }})
-
-
Current Logo
-
Upload a new file to replace it
-
-
- @endif
+ {{-- Logo auto-upload (independent of main Save form) --}}
+
-
-
-
+
+ {{-- Current logo preview --}}
+
+
+
![Logo]()
+
+
Current Logo
+
Choose a new file to replace it
+
+
+
+
+
+
+
+
-
Max 2 MB. Shown at the top-left of your invoice.
+ hover:file:bg-pink-100 cursor-pointer"
+ :disabled="uploading">
+
Uploadingβ¦
+
+
Max 2 MB. Uploads immediately on file selection.
+
+
+
Applied to your invoice header name and totals.
diff --git a/resources/views/invoices/show.blade.php b/resources/views/invoices/show.blade.php
index 78a868e..6dce7f3 100644
--- a/resources/views/invoices/show.blade.php
+++ b/resources/views/invoices/show.blade.php
@@ -63,9 +63,21 @@
{{-- Line Items --}}
+ @php
+ $accent = $invoiceSettings->accent_color ?? '#FFB7C5';
+ $hex = ltrim($accent, '#');
+ $r = hexdec(substr($hex, 0, 2));
+ $g = hexdec(substr($hex, 2, 2));
+ $b = hexdec(substr($hex, 4, 2));
+ $tint = sprintf('#%02X%02X%02X',
+ (int)(255 * 0.92 + $r * 0.08),
+ (int)(255 * 0.92 + $g * 0.08),
+ (int)(255 * 0.92 + $b * 0.08)
+ );
+ @endphp
-
-
+
+
| Description |
Qty |
Unit Price |
@@ -103,14 +115,14 @@
@endif
- Total (you receive){{ $invoice->formatted_total }}
+ Grand Total{{ $invoice->formatted_total }}
{{-- Bank Info --}}
@if($invoice->bank_name)
-
+
π³ Payment Info
{{ $invoice->bank_name }}
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index b033676..b4aec8b 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -121,6 +121,12 @@
π Files
+
+ π Notes
+
+
Settings
- KOL
+ FUN
diff --git a/resources/views/notes/index.blade.php b/resources/views/notes/index.blade.php
new file mode 100644
index 0000000..e23a800
--- /dev/null
+++ b/resources/views/notes/index.blade.php
@@ -0,0 +1,483 @@
+@extends('layouts.app')
+
+@section('title', 'Notes')
+@section('page-title', 'π Notes')
+@section('page-subtitle', 'Quick notes, checklists & reminders')
+
+@section('content')
+
+
+
+ {{-- ββ Toolbar ββββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
+ {{-- Search --}}
+
+ π
+
+
+
+ {{-- Count --}}
+
+
+
+ {{-- ββ Pinned section βββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
+ π Pinned
+
+
+
+
+
+ {{-- Pin badge --}}
+
π
+
+ {{-- Title --}}
+
+
+ {{-- Content preview --}}
+
+
+
+
+ β’
+
+
+
+
+
+
+
+
+
+ {{-- Footer --}}
+
+
+
+
+
+
+
+
+
+
+ {{-- ββ All / Other notes ββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
+ Others
+
+
+
+
+
+ {{-- Title --}}
+
+
+ {{-- Content preview --}}
+
+
+
+
+ β’
+
+
+
+
+
+
+
+
+
+ {{-- Footer --}}
+
+
+
+
+
+
+
+
+
+
+ {{-- ββ Empty state ββββββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
π
+
No notes yet.
+
+
+
+
+
+
+
π
+
No notes match ""
+
+
+
+
+ {{-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ Note Editor Modal
+ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ --}}
+
+
+
+
+ {{-- ββ Modal Header βββββββββββββββββββββββββββββββ --}}
+
+
+ {{-- Color swatches --}}
+
+
+
+
+
+
+ {{-- Pin toggle --}}
+
+
+ {{-- Close --}}
+
+
+
+ {{-- ββ Scrollable Body ββββββββββββββββββββββββββββ --}}
+
+
+ {{-- Title --}}
+
+
+ {{-- Blocks --}}
+
+
+
+ {{-- Left side: bullet / checkbox / empty --}}
+
+ β’
+
+
+
+
+
+
+
+
+ {{-- Text input --}}
+
+
+ {{-- Block type switcher (hover) --}}
+
+
+
+
+
+
+
+
+
+ {{-- Add block toolbar --}}
+
+
+
+
+
+
+
+ {{-- ββ Modal Footer βββββββββββββββββββββββββββββββ --}}
+
+
+
+
+
+
+
+
{{-- end x-data --}}
+
+@push('scripts')
+
+@endpush
+
+@endsection
diff --git a/resources/views/rate-cards/public.blade.php b/resources/views/rate-cards/public.blade.php
index bccabbd..6bec702 100644
--- a/resources/views/rate-cards/public.blade.php
+++ b/resources/views/rate-cards/public.blade.php
@@ -73,10 +73,6 @@
βοΈ {{ $profile->email }}
@endif
-
-
@forelse($rateCards as $platform => $cards)
diff --git a/resources/views/schedule/create.blade.php b/resources/views/schedule/create.blade.php
new file mode 100644
index 0000000..a2a3628
--- /dev/null
+++ b/resources/views/schedule/create.blade.php
@@ -0,0 +1,323 @@
+@extends('layouts.app')
+
+@section('title', 'Add Event')
+@section('page-title', 'Add Event π
')
+@section('page-subtitle', 'Schedule a new task, meeting, or posting')
+
+@section('content')
+
+
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/schedule/edit.blade.php b/resources/views/schedule/edit.blade.php
index 18c5295..3056fb0 100644
--- a/resources/views/schedule/edit.blade.php
+++ b/resources/views/schedule/edit.blade.php
@@ -137,14 +137,6 @@
diff --git a/resources/views/schedule/index.blade.php b/resources/views/schedule/index.blade.php
index 4906d20..9924ed6 100644
--- a/resources/views/schedule/index.blade.php
+++ b/resources/views/schedule/index.blade.php
@@ -6,143 +6,9 @@
@section('content')
-{{-- ββ Add Event Form (always accessible, collapsible) βββββββ --}}
-
-
-
β New Event
-
-
-
-
+{{-- ββ Add Event Button βββββββββββββββββββββββββββββββββββββββ --}}
+
{{-- ββ Upcoming Alert Strip βββββββββββββββββββββββββββββββββββ --}}
diff --git a/routes/web.php b/routes/web.php
index 08061ba..8018327 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -3,6 +3,7 @@
declare(strict_types=1);
use App\Http\Controllers\BrandContractController;
+use App\Http\Controllers\NoteController;
use App\Http\Controllers\ClientCategoryController;
use App\Http\Controllers\ClientController;
use App\Http\Controllers\ContentBriefController;
@@ -21,6 +22,9 @@ Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
// CRM β Clients
Route::resource('clients', ClientController::class);
+Route::post('/clients/quick', [ClientController::class, 'quickStore'])->name('clients.quick-store');
+Route::post('/clients/{client}/avatar', [ClientController::class, 'uploadAvatar'])->name('clients.upload-avatar');
+Route::delete('/clients/{client}/avatar', [ClientController::class, 'deleteAvatar'])->name('clients.delete-avatar');
// Client Categories
Route::prefix('client-categories')->name('client-categories.')->group(function (): void {
@@ -43,6 +47,7 @@ Route::prefix('contracts')->name('contracts.')->group(function (): void {
// Schedule & Calendar
Route::prefix('schedule')->name('schedule.')->group(function (): void {
Route::get('/', [EventController::class, 'index'])->name('index');
+ Route::get('/create', [EventController::class, 'create'])->name('create');
Route::post('/', [EventController::class, 'store'])->name('store');
Route::get('/{event}/edit', [EventController::class, 'edit'])->name('edit');
Route::put('/{event}', [EventController::class, 'update'])->name('update');
@@ -62,6 +67,8 @@ Route::resource('invoices', InvoiceController::class);
Route::get('/invoices/{invoice}/pdf', [InvoiceController::class, 'pdf'])->name('invoices.pdf');
Route::get('/invoice-settings', [InvoiceSettingsController::class, 'edit'])->name('invoice-settings.edit');
Route::post('/invoice-settings', [InvoiceSettingsController::class, 'update'])->name('invoice-settings.update');
+Route::post('/invoice-settings/logo', [InvoiceSettingsController::class, 'uploadLogo'])->name('invoice-settings.upload-logo');
+Route::delete('/invoice-settings/logo', [InvoiceSettingsController::class, 'deleteLogo'])->name('invoice-settings.delete-logo');
// Financial β Rate Cards
Route::get('/rate-cards/public', [RateCardController::class, 'public'])->name('rate-cards.public');
@@ -97,6 +104,15 @@ Route::prefix('search')->name('search.')->group(function (): void {
Route::get('/quick', [SearchController::class, 'quick'])->name('quick');
});
+// Notes
+Route::prefix('notes')->name('notes.')->group(function (): void {
+ Route::get('/', [NoteController::class, 'index'])->name('index');
+ Route::post('/', [NoteController::class, 'store'])->name('store');
+ Route::put('/{note}', [NoteController::class, 'update'])->name('update');
+ Route::delete('/{note}', [NoteController::class, 'destroy'])->name('destroy');
+ Route::patch('/{note}/pin', [NoteController::class, 'togglePin'])->name('toggle-pin');
+});
+
// Settings
Route::prefix('settings')->name('settings.')->group(function (): void {
Route::get('/notifications', [NotificationSettingController::class, 'index'])->name('notifications');