feat: add notes management feature with CRUD operations
- 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.
This commit is contained in:
@@ -61,29 +61,79 @@
|
||||
<div class="cat-card p-5">
|
||||
<h2 class="text-sm font-bold text-gray-700 mb-4 flex items-center gap-2">🎨 Invoice Header</h2>
|
||||
|
||||
@if($settings->header_logo)
|
||||
<div class="mb-3 flex items-center gap-3">
|
||||
<img src="{{ asset('storage/' . $settings->header_logo) }}"
|
||||
alt="Logo" class="h-16 w-16 object-cover rounded-2xl border border-pink-100">
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600">Current Logo</p>
|
||||
<p class="text-xs text-gray-400 mt-0.5">Upload a new file to replace it</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
{{-- Logo auto-upload (independent of main Save form) --}}
|
||||
<div class="mb-4"
|
||||
x-data="{
|
||||
uploading: false,
|
||||
errorMsg: '',
|
||||
logoUrl: '{{ $settings->header_logo ? asset('storage/' . $settings->header_logo) : '' }}',
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Logo / Profile Picture</label>
|
||||
<input type="file" name="header_logo" accept="image/*"
|
||||
async pickFile(input) {
|
||||
if (!input.files.length) return;
|
||||
this.uploading = true;
|
||||
this.errorMsg = '';
|
||||
const fd = new FormData();
|
||||
fd.append('header_logo', input.files[0]);
|
||||
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
|
||||
try {
|
||||
const res = await fetch('{{ route('invoice-settings.upload-logo') }}', { method: 'POST', body: fd });
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
this.logoUrl = data.url + '?t=' + Date.now();
|
||||
} else {
|
||||
this.errorMsg = data.message ?? 'Upload failed.';
|
||||
}
|
||||
} catch {
|
||||
this.errorMsg = 'Network error. Please try again.';
|
||||
}
|
||||
this.uploading = false;
|
||||
input.value = '';
|
||||
},
|
||||
|
||||
async removeLogo() {
|
||||
if (!confirm('Remove this logo?')) return;
|
||||
const fd = new FormData();
|
||||
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
|
||||
fd.append('_method', 'DELETE');
|
||||
await fetch('{{ route('invoice-settings.delete-logo') }}', { method: 'POST', body: fd });
|
||||
this.logoUrl = '';
|
||||
}
|
||||
}">
|
||||
|
||||
{{-- Current logo preview --}}
|
||||
<template x-if="logoUrl">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<img :src="logoUrl" alt="Logo"
|
||||
class="h-16 w-16 object-cover rounded-2xl border border-pink-100 shrink-0">
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-xs font-semibold text-gray-600">Current Logo</p>
|
||||
<p class="text-xs text-gray-400 mt-0.5">Choose a new file to replace it</p>
|
||||
</div>
|
||||
<button type="button" @click="removeLogo()"
|
||||
class="text-xs px-3 py-1.5 rounded-full border border-red-200 text-red-400 hover:bg-red-50 shrink-0">
|
||||
🗑️ Remove
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Logo / Profile Picture</label>
|
||||
<div class="relative">
|
||||
<input type="file" accept="image/*"
|
||||
@change="pickFile($event.target)"
|
||||
class="w-full text-sm text-gray-600
|
||||
file:mr-3 file:py-1.5 file:px-4
|
||||
file:rounded-full file:border-0
|
||||
file:text-xs file:font-semibold
|
||||
file:bg-pink-50 file:text-pink-600
|
||||
hover:file:bg-pink-100 cursor-pointer">
|
||||
<p class="text-xs text-gray-400 mt-1">Max 2 MB. Shown at the top-left of your invoice.</p>
|
||||
hover:file:bg-pink-100 cursor-pointer"
|
||||
:disabled="uploading">
|
||||
<span x-show="uploading" class="text-xs text-pink-500 mt-1 block">Uploading…</span>
|
||||
<span x-show="errorMsg" x-text="errorMsg" class="text-xs text-red-500 mt-1 block"></span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 mt-1">Max 2 MB. Uploads immediately on file selection.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">Talent / Business Name</label>
|
||||
<input type="text" name="header_name"
|
||||
@@ -101,14 +151,6 @@
|
||||
<div class="flex items-center gap-3 mt-1">
|
||||
<input type="color" name="accent_color" x-model="color"
|
||||
class="w-10 h-10 rounded-xl cursor-pointer border-2 border-pink-100 p-0.5">
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
@foreach(['#FFB7C5','#B5EAD7','#FDFD96','#E8D5F5','#FFD5B8','#AEC6CF','#FF8FAD','#A8D8EA'] as $preset)
|
||||
<button type="button"
|
||||
class="w-7 h-7 rounded-full ring-2 ring-transparent hover:ring-gray-400 transition-all"
|
||||
style="background:{{ $preset }}"
|
||||
@click="color = '{{ $preset }}'"></button>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 mt-1.5">Applied to your invoice header name and totals.</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user