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

@@ -30,99 +30,99 @@
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4" x-data="{
isNew: false,
saving: false,
newName: '', newEmail: '', newPhone: '', newCompany: '',
errMsg: '',
isNewCat: false,
savingCat: false,
newCatName: '',
catErrMsg: '',
onClientChange(e) {
this.isNew = (e.target.value === '__new__');
if (!this.isNew) {
this.clientId = e.target.value;
$dispatch('contract-check');
}
},
async saveNewClient() {
if (!this.newName.trim()) { this.errMsg = 'Name is required.'; return; }
this.saving = true; this.errMsg = '';
const fd = new FormData();
fd.append('name', this.newName);
fd.append('email', this.newEmail);
fd.append('phone', this.newPhone);
fd.append('company', this.newCompany);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('clients.quick-store', [], false) }}', {
method:'POST',
body: fd,
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
const data = await res.json();
if (res.ok) {
// Add new option & select it
const sel = document.getElementById('client-select');
const opt = new Option(data.name, data.id, true, true);
sel.add(opt, sel.options[1]);
sel.value = data.id;
sel.dispatchEvent(new Event('change'));
this.isNew = false;
this.newName = this.newEmail = this.newPhone = this.newCompany = '';
} else {
this.errMsg = data.message || 'Could not create client.';
isNew: false,
saving: false,
newName: '', newEmail: '', newPhone: '', newCompany: '',
errMsg: '',
isNewCat: false,
savingCat: false,
newCatName: '',
catErrMsg: '',
onClientChange(e) {
this.isNew = (e.target.value === '__new__');
if (!this.isNew) {
this.clientId = e.target.value;
$dispatch('contract-check');
}
} catch (err) {
this.errMsg = 'An unexpected error occurred.';
} finally {
this.saving = false;
}
},
onCategoryChange(e) {
this.isNewCat = (e.target.value === '__new__');
if (!this.isNewCat) {
this.categoryId = e.target.value;
this.checkContract();
}
},
async saveNewCategory() {
if (!this.newCatName.trim()) { this.catErrMsg = 'Category name is required.'; return; }
this.savingCat = true; this.catErrMsg = '';
const fd = new FormData();
fd.append('name', this.newCatName);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('client-categories.quick-store', [], false) }}', {
method:'POST',
body: fd,
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
async saveNewClient() {
if (!this.newName.trim()) { this.errMsg = 'Name is required.'; return; }
this.saving = true; this.errMsg = '';
const fd = new FormData();
fd.append('name', this.newName);
fd.append('email', this.newEmail);
fd.append('phone', this.newPhone);
fd.append('company', this.newCompany);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('clients.quick-store', [], false) }}', {
method:'POST',
body: fd,
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
const data = await res.json();
if (res.ok) {
// Add new option & select it
const sel = document.getElementById('client-select');
const opt = new Option(data.name, data.id, true, true);
sel.add(opt, sel.options[1]);
sel.value = data.id;
sel.dispatchEvent(new Event('change'));
this.isNew = false;
this.newName = this.newEmail = this.newPhone = this.newCompany = '';
} else {
this.errMsg = data.message || 'Could not create client.';
}
});
const data = await res.json();
if (res.ok) {
const sel = document.getElementById('category-select');
const opt = new Option(data.icon + ' ' + data.name, data.id, true, true);
sel.add(opt, sel.options[1]);
sel.value = data.id;
sel.dispatchEvent(new Event('change'));
this.isNewCat = false;
this.newCatName = '';
} else {
this.catErrMsg = data.message || 'Could not create category.';
} catch (err) {
this.errMsg = 'An unexpected error occurred.';
} finally {
this.saving = false;
}
},
onCategoryChange(e) {
this.isNewCat = (e.target.value === '__new__');
if (!this.isNewCat) {
this.categoryId = e.target.value;
this.checkContract();
}
},
async saveNewCategory() {
if (!this.newCatName.trim()) { this.catErrMsg = 'Category name is required.'; return; }
this.savingCat = true; this.catErrMsg = '';
const fd = new FormData();
fd.append('name', this.newCatName);
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
try {
const res = await fetch('{{ route('client-categories.quick-store', [], false) }}', {
method:'POST',
body: fd,
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
const data = await res.json();
if (res.ok) {
const sel = document.getElementById('category-select');
const opt = new Option(data.icon + ' ' + data.name, data.id, true, true);
sel.add(opt, sel.options[1]);
sel.value = data.id;
sel.dispatchEvent(new Event('change'));
this.isNewCat = false;
this.newCatName = '';
} else {
this.catErrMsg = data.message || 'Could not create category.';
}
} catch (err) {
this.catErrMsg = 'An unexpected error occurred.';
} finally {
this.savingCat = false;
}
} catch (err) {
this.catErrMsg = 'An unexpected error occurred.';
} finally {
this.savingCat = false;
}
}
}">
}">
{{-- Type --}}
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Type</label>
@@ -217,8 +217,7 @@
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Category Name <span
class="text-pink-400">*</span></label>
<input type="text" x-model="newCatName" class="input-soft text-sm"
placeholder="e.g. Sponsored Post">
<input type="text" x-model="newCatName" class="input-soft text-sm" placeholder="e.g. Susu UHT">
</div>
<p x-show="catErrMsg" x-text="catErrMsg" class="text-xs text-red-500"></p>
<div class="flex gap-2 pt-1">