feat: extend rate card profiles with brands, terms, and custom HTML, and remove search bar from search index view

This commit is contained in:
I-Fun
2026-04-05 18:28:48 +07:00
parent d6b10f725b
commit 1c2c10d253
22 changed files with 1024 additions and 388 deletions

View File

@@ -38,7 +38,7 @@ class ClientController extends Controller
public function index(Request $request): View
{
$query = Client::with('category')
->withCount(['invoices', 'events', 'contentBriefs'])
->withCount(['invoices', 'events', 'contentBriefs', 'mediaFiles'])
->latest();
if ($request->filled('category')) {

View File

@@ -52,7 +52,7 @@ class MediaFileController extends Controller
$this->fileService->uploadAsset(
$request->file('file'),
$request->input('folder'),
$request->input('client_id')
$request->filled('client_id') ? (int) $request->input('client_id') : null
);
if ($request->expectsJson() || $request->ajax()) {

View File

@@ -119,8 +119,11 @@ class RateCardController extends Controller
'bg_color' => 'nullable|string|max:20',
'accent_color' => 'nullable|string|max:20',
'text_color' => 'nullable|string|max:20',
'custom_footer' => 'nullable|string|max:500',
'show_prices' => 'boolean',
'custom_footer' => 'nullable|string|max:255',
'show_prices' => 'nullable|boolean',
'terms_and_conditions' => 'nullable|string',
'brand_logo_size' => 'nullable|in:small,medium,large',
'contact_person_html' => 'nullable|string',
'logo' => 'nullable|image|max:2048',
]);
@@ -131,7 +134,7 @@ class RateCardController extends Controller
$validated['logo_path'] = $path;
}
$validated['show_prices'] = $request->boolean('show_prices');
$validated['show_prices'] = $request->has('show_prices');
unset($validated['logo']);
$profile->update($validated);
@@ -139,6 +142,69 @@ class RateCardController extends Controller
return back()->with('success', '🎨 Profile updated!');
}
public function uploadLogo(Request $request)
{
$request->validate([
'logo' => 'required|image|max:2048',
]);
$profile = RateCardProfile::getSingleton();
if ($profile->logo_path) {
\Storage::disk('private')->delete($profile->logo_path);
}
$path = $request->file('logo')->store('profile', 'private');
$profile->update(['logo_path' => $path]);
return response()->json([
'url' => route('files.serve-profile-logo'),
]);
}
public function deleteLogo()
{
$profile = RateCardProfile::getSingleton();
if ($profile->logo_path) {
\Storage::disk('private')->delete($profile->logo_path);
$profile->update(['logo_path' => null]);
}
return response()->json(['success' => true]);
}
public function uploadBrandLogo(Request $request)
{
$request->validate(['logo' => 'required|image|max:2048']);
$profile = RateCardProfile::getSingleton();
$path = $request->file('logo')->store('brands', 'public');
$brands = $profile->past_brands ?? [];
$brands[] = $path;
$profile->update(['past_brands' => $brands]);
return response()->json([
'path' => $path,
'url' => asset('storage/' . $path)
]);
}
public function deleteBrandLogo(Request $request)
{
$path = $request->input('path');
$profile = RateCardProfile::getSingleton();
$brands = $profile->past_brands ?? [];
if (($key = array_search($path, $brands)) !== false) {
unset($brands[$key]);
$profile->update(['past_brands' => array_values($brands)]);
Storage::disk('public')->delete($path);
}
return response()->json(['success' => true]);
}
public function serveProfileLogo(): Response
{
$profile = RateCardProfile::getSingleton();
@@ -165,7 +231,7 @@ class RateCardController extends Controller
->groupBy('platform');
$pdf = Pdf::loadView('rate-cards.pdf', compact('rateCards', 'profile'))
->setPaper('a4', 'portrait');
->setPaper('a4', 'landscape');
$filename = 'rate-card-' . now()->format('Y-m-d') . '.pdf';

View File

@@ -17,7 +17,7 @@ class SearchController extends Controller
{
public function index(Request $request): View
{
$q = trim($request->get('q', ''));
$q = trim((string) $request->get('q', ''));
$results = $q ? $this->search($q) : [];
return view('search.index', compact('q', 'results'));
@@ -26,7 +26,7 @@ class SearchController extends Controller
/** Quick AJAX endpoint — returns grouped results for the dropdown */
public function quick(Request $request): JsonResponse
{
$q = trim($request->get('q', ''));
$q = trim((string) $request->get('q', ''));
if (strlen($q) < 2) {
return response()->json([]);
@@ -138,6 +138,24 @@ class SearchController extends Controller
];
}
// ── Notes ───────────────────────────────────────────
$notes = \App\Models\Note::where('title', 'LIKE', $like)
->orWhere('content', 'LIKE', $like)
->limit($limit)->get();
if ($notes->isNotEmpty()) {
$groups[] = [
'type' => 'notes',
'label' => '📝 Notes',
'items' => $notes->map(fn ($n) => [
'icon' => '📝',
'title' => $n->title ?: 'Untitled',
'subtitle' => 'Notes Management',
'url' => route('notes.index'),
])->values()->all(),
];
}
return $groups;
}
}