feat(vendor): add frontend for contact info inheritance

Add UI for vendor contact field inheritance from company:
- Show "(from company)" indicator for inherited fields
- Add "Reset" button per field to clear override
- Add "Reset All to Company" button for bulk reset
- Purple border styling for inherited fields
- Dynamic placeholder showing company values

JavaScript methods:
- resetFieldToCompany(fieldName): Reset individual field
- resetAllContactToCompany(): Reset all contact fields
- hasAnyContactOverride(): Check if any field is overridden

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-03 22:36:33 +01:00
parent 846f92e7e4
commit c2228bbded
2 changed files with 166 additions and 22 deletions

View File

@@ -164,9 +164,20 @@
<!-- Right Column: Contact Info --> <!-- Right Column: Contact Info -->
<div> <div>
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200"> <div class="flex items-center justify-between mb-4">
Contact Information <h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
</h3> Contact Information
</h3>
<button
type="button"
@click="resetAllContactToCompany()"
:disabled="saving || !hasAnyContactOverride()"
class="text-xs px-2 py-1 text-purple-600 dark:text-purple-400 hover:text-purple-800 dark:hover:text-purple-300 disabled:opacity-50 disabled:cursor-not-allowed"
title="Reset all contact fields to inherit from company">
<span x-html="$icon('refresh', 'w-3 h-3 inline mr-1')"></span>
Reset All to Company
</button>
</div>
<!-- Owner Email (readonly) --> <!-- Owner Email (readonly) -->
<label class="block mb-4 text-sm"> <label class="block mb-4 text-sm">
@@ -186,47 +197,97 @@
<!-- Contact Email --> <!-- Contact Email -->
<label class="block mb-4 text-sm"> <label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400"> <div class="flex items-center justify-between">
Contact Email <span class="text-red-600">*</span> <span class="text-gray-700 dark:text-gray-400">
</span> Contact Email
<span x-show="vendor?.contact_email_inherited"
class="ml-1 text-xs text-purple-500 dark:text-purple-400"
title="Inherited from company">
(from company)
</span>
</span>
<button
type="button"
x-show="!vendor?.contact_email_inherited && formData.contact_email"
@click="resetFieldToCompany('contact_email')"
:disabled="saving"
class="text-xs text-purple-600 hover:text-purple-800 dark:text-purple-400">
Reset
</button>
</div>
<input <input
type="email" type="email"
x-model="formData.contact_email" x-model="formData.contact_email"
required :placeholder="vendor?.company_contact_email || 'contact@company.com'"
:disabled="saving" :disabled="saving"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
:class="{ 'border-red-600 focus:border-red-400 focus:shadow-outline-red': errors.contact_email }" :class="{
'border-red-600 focus:border-red-400 focus:shadow-outline-red': errors.contact_email,
'border-purple-300 dark:border-purple-600': vendor?.contact_email_inherited
}"
> >
<span class="text-xs text-gray-600 dark:text-gray-400 mt-1"> <span class="text-xs text-gray-600 dark:text-gray-400 mt-1">
Public business contact email <span x-show="vendor?.contact_email_inherited">Using company value. Enter a value to override.</span>
<span x-show="!vendor?.contact_email_inherited">Custom value (clear to inherit from company)</span>
</span> </span>
<span x-show="errors.contact_email" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.contact_email"></span> <span x-show="errors.contact_email" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.contact_email"></span>
</label> </label>
<!-- Phone --> <!-- Phone -->
<label class="block mb-4 text-sm"> <label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400"> <div class="flex items-center justify-between">
Phone <span class="text-gray-700 dark:text-gray-400">
</span> Phone
<span x-show="vendor?.contact_phone_inherited"
class="ml-1 text-xs text-purple-500 dark:text-purple-400">
(from company)
</span>
</span>
<button
type="button"
x-show="!vendor?.contact_phone_inherited && formData.contact_phone"
@click="resetFieldToCompany('contact_phone')"
:disabled="saving"
class="text-xs text-purple-600 hover:text-purple-800 dark:text-purple-400">
Reset
</button>
</div>
<input <input
type="tel" type="tel"
x-model="formData.contact_phone" x-model="formData.contact_phone"
:placeholder="vendor?.company_contact_phone || '+352 XXX XXX'"
:disabled="saving" :disabled="saving"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
:class="{ 'border-purple-300 dark:border-purple-600': vendor?.contact_phone_inherited }"
> >
</label> </label>
<!-- Website --> <!-- Website -->
<label class="block mb-4 text-sm"> <label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400"> <div class="flex items-center justify-between">
Website <span class="text-gray-700 dark:text-gray-400">
</span> Website
<span x-show="vendor?.website_inherited"
class="ml-1 text-xs text-purple-500 dark:text-purple-400">
(from company)
</span>
</span>
<button
type="button"
x-show="!vendor?.website_inherited && formData.website"
@click="resetFieldToCompany('website')"
:disabled="saving"
class="text-xs text-purple-600 hover:text-purple-800 dark:text-purple-400">
Reset
</button>
</div>
<input <input
type="url" type="url"
x-model="formData.website" x-model="formData.website"
:placeholder="vendor?.company_website || 'https://company.com'"
:disabled="saving" :disabled="saving"
placeholder="https://example.com"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
:class="{ 'border-purple-300 dark:border-purple-600': vendor?.website_inherited }"
> >
</label> </label>
</div> </div>
@@ -241,27 +302,59 @@
<div class="grid gap-6 md:grid-cols-2"> <div class="grid gap-6 md:grid-cols-2">
<!-- Business Address --> <!-- Business Address -->
<label class="block text-sm"> <label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400"> <div class="flex items-center justify-between">
Business Address <span class="text-gray-700 dark:text-gray-400">
</span> Business Address
<span x-show="vendor?.business_address_inherited"
class="ml-1 text-xs text-purple-500 dark:text-purple-400">
(from company)
</span>
</span>
<button
type="button"
x-show="!vendor?.business_address_inherited && formData.business_address"
@click="resetFieldToCompany('business_address')"
:disabled="saving"
class="text-xs text-purple-600 hover:text-purple-800 dark:text-purple-400">
Reset
</button>
</div>
<textarea <textarea
x-model="formData.business_address" x-model="formData.business_address"
rows="3" rows="3"
:disabled="saving" :disabled="saving"
:placeholder="vendor?.business_address_inherited ? 'Using company address' : 'Enter business address'"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea"
:class="{ 'border-purple-300 dark:border-purple-600': vendor?.business_address_inherited }"
></textarea> ></textarea>
</label> </label>
<!-- Tax Number --> <!-- Tax Number -->
<label class="block text-sm"> <label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400"> <div class="flex items-center justify-between">
Tax Number <span class="text-gray-700 dark:text-gray-400">
</span> Tax Number
<span x-show="vendor?.tax_number_inherited"
class="ml-1 text-xs text-purple-500 dark:text-purple-400">
(from company)
</span>
</span>
<button
type="button"
x-show="!vendor?.tax_number_inherited && formData.tax_number"
@click="resetFieldToCompany('tax_number')"
:disabled="saving"
class="text-xs text-purple-600 hover:text-purple-800 dark:text-purple-400">
Reset
</button>
</div>
<input <input
type="text" type="text"
x-model="formData.tax_number" x-model="formData.tax_number"
:disabled="saving" :disabled="saving"
:placeholder="vendor?.tax_number_inherited ? 'Using company tax number' : 'Enter tax number'"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input" class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
:class="{ 'border-purple-300 dark:border-purple-600': vendor?.tax_number_inherited }"
> >
</label> </label>
</div> </div>

View File

@@ -254,6 +254,57 @@ function adminVendorEdit() {
} finally { } finally {
this.saving = false; this.saving = false;
} }
},
// ===== Contact Field Inheritance Methods =====
/**
* Reset a single contact field to inherit from company.
* Sets the field to empty string, which the backend converts to null (inherit).
* @param {string} fieldName - The contact field to reset
*/
resetFieldToCompany(fieldName) {
const contactFields = ['contact_email', 'contact_phone', 'website', 'business_address', 'tax_number'];
if (!contactFields.includes(fieldName)) {
editLog.warn('Invalid contact field:', fieldName);
return;
}
editLog.info(`Resetting ${fieldName} to inherit from company`);
this.formData[fieldName] = '';
// Update the vendor object to reflect inheritance (UI indicator)
if (this.vendor) {
this.vendor[`${fieldName}_inherited`] = true;
}
},
/**
* Reset all contact fields to inherit from company.
*/
resetAllContactToCompany() {
editLog.info('Resetting all contact fields to inherit from company');
const contactFields = ['contact_email', 'contact_phone', 'website', 'business_address', 'tax_number'];
contactFields.forEach(field => {
this.formData[field] = '';
if (this.vendor) {
this.vendor[`${field}_inherited`] = true;
}
});
Utils.showToast('All contact fields reset to company defaults', 'info');
},
/**
* Check if any contact field has a vendor-level override (not inherited).
* @returns {boolean} True if at least one contact field is overridden
*/
hasAnyContactOverride() {
if (!this.vendor) return false;
const contactFields = ['contact_email', 'contact_phone', 'website', 'business_address', 'tax_number'];
return contactFields.some(field => !this.vendor[`${field}_inherited`]);
} }
}; };
} }