- Created Note model and controller for handling notes. - Added routes for notes CRUD operations. - Implemented notes index view with search and pinning functionality. - Created migration for notes table with necessary fields. - Developed event creation form with client and contract options.
190 lines
6.5 KiB
PHP
190 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
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
|
|
{
|
|
private function categoryTree(): \Illuminate\Support\Collection
|
|
{
|
|
return ClientCategory::with('children')
|
|
->whereNull('parent_id')
|
|
->orderBy('sort_order')
|
|
->get();
|
|
}
|
|
|
|
private function flatCategories(): \Illuminate\Support\Collection
|
|
{
|
|
$flat = collect();
|
|
foreach ($this->categoryTree() as $parent) {
|
|
$flat->push($parent);
|
|
foreach ($parent->children as $child) {
|
|
$flat->push($child);
|
|
}
|
|
}
|
|
return $flat;
|
|
}
|
|
|
|
public function index(Request $request): View
|
|
{
|
|
$query = Client::with('category')
|
|
->withCount(['invoices', 'events', 'contentBriefs'])
|
|
->latest();
|
|
|
|
if ($request->filled('category')) {
|
|
$query->where('category_id', $request->category);
|
|
}
|
|
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
$clients = $query->paginate(12)->withQueryString();
|
|
$categories = $this->categoryTree();
|
|
$statuses = ClientStatus::cases();
|
|
|
|
return view('clients.index', compact('clients', 'categories', 'statuses'));
|
|
}
|
|
|
|
public function create(): View
|
|
{
|
|
$statuses = ClientStatus::cases();
|
|
$categories = $this->flatCategories();
|
|
$categoryTree = $this->categoryTree();
|
|
|
|
return view('clients.create', compact('statuses', 'categories', 'categoryTree'));
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'email' => 'nullable|email|unique:clients,email',
|
|
'phone' => 'nullable|string|max:50',
|
|
'company' => 'nullable|string|max:255',
|
|
'ig_handle' => 'nullable|string|max:100',
|
|
'tiktok_handle' => 'nullable|string|max:100',
|
|
'youtube_handle' => 'nullable|string|max:100',
|
|
'address_line1' => 'nullable|string|max:255',
|
|
'address_line2' => 'nullable|string|max:255',
|
|
'city' => 'nullable|string|max:100',
|
|
'state' => 'nullable|string|max:100',
|
|
'postal_code' => 'nullable|string|max:20',
|
|
'country' => 'nullable|string|max:100',
|
|
'status' => 'required|in:active,inactive,prospect',
|
|
'category_id' => 'nullable|exists:client_categories,id',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
Client::create($validated);
|
|
|
|
return redirect()->route('clients.index')
|
|
->with('success', '🐱 Client added successfully!');
|
|
}
|
|
|
|
public function show(Client $client): View
|
|
{
|
|
$client->load([
|
|
'category',
|
|
'events' => fn ($q) => $q->latest()->limit(5),
|
|
'invoices' => fn ($q) => $q->latest()->limit(5),
|
|
'contentBriefs'=> fn ($q) => $q->latest()->limit(5),
|
|
'mediaFiles' => fn ($q) => $q->latest()->limit(10),
|
|
]);
|
|
|
|
return view('clients.show', compact('client'));
|
|
}
|
|
|
|
public function edit(Client $client): View
|
|
{
|
|
$statuses = ClientStatus::cases();
|
|
$categories = $this->flatCategories();
|
|
$categoryTree = $this->categoryTree();
|
|
|
|
return view('clients.edit', compact('client', 'statuses', 'categories', 'categoryTree'));
|
|
}
|
|
|
|
public function update(Request $request, Client $client): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'email' => 'nullable|email|unique:clients,email,' . $client->id,
|
|
'phone' => 'nullable|string|max:50',
|
|
'company' => 'nullable|string|max:255',
|
|
'ig_handle' => 'nullable|string|max:100',
|
|
'tiktok_handle' => 'nullable|string|max:100',
|
|
'youtube_handle' => 'nullable|string|max:100',
|
|
'address_line1' => 'nullable|string|max:255',
|
|
'address_line2' => 'nullable|string|max:255',
|
|
'city' => 'nullable|string|max:100',
|
|
'state' => 'nullable|string|max:100',
|
|
'postal_code' => 'nullable|string|max:20',
|
|
'country' => 'nullable|string|max:100',
|
|
'status' => 'required|in:active,inactive,prospect',
|
|
'category_id' => 'nullable|exists:client_categories,id',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
$client->update($validated);
|
|
|
|
return redirect()->route('clients.show', $client)
|
|
->with('success', '🐱 Client updated successfully!');
|
|
}
|
|
|
|
public function destroy(Client $client): RedirectResponse
|
|
{
|
|
$client->delete();
|
|
|
|
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]);
|
|
}
|
|
}
|