This commit is contained in:
Casey 2026-02-19 17:18:39 -06:00
parent 84a91359d8
commit 6c703c2c3b
55 changed files with 1130 additions and 358 deletions

View file

@ -69,6 +69,7 @@ const FRAPPE_GET_CLIENT_METHOD = "custom_ui.api.db.clients.get_client_v2";
const FRAPPE_GET_CLIENT_NAMES_METHOD = "custom_ui.api.db.clients.get_client_names";
const FRAPPE_CHECK_CLIENT_EXISTS_METHOD = "custom_ui.api.db.clients.check_client_exists";
const FRAPPE_ADD_ADDRESSES_CONTACTS_METHOD = "custom_ui.api.db.clients.add_addresses_contacts";
const FRAPPE_CREATE_CLIENT_CONTACTS_ADDRESSES_METHOD = "custom_ui.api.db.clients.create_client_contacts_addresses";
// Employee methods
const FRAPPE_GET_EMPLOYEES_METHOD = "custom_ui.api.db.employees.get_employees";
const FRAPPE_GET_EMPLOYEES_ORGANIZED_METHOD = "custom_ui.api.db.employees.get_employees_organized";
@ -188,6 +189,10 @@ class Api {
return await this.request(FRAPPE_ADD_ADDRESSES_CONTACTS_METHOD, { clientName, companyName, addresses, contacts });
}
static async createClientContactsAddresses(clientName, company, contacts = [], addresses = []) {
return await this.request(FRAPPE_CREATE_CLIENT_CONTACTS_ADDRESSES_METHOD, { clientName, company, contacts, addresses });
}
// ============================================================================
// ON-SITE MEETING METHODS
// ============================================================================

View file

@ -66,18 +66,23 @@ import Dialog from 'primevue/dialog';
import Button from 'primevue/button';
import ModalContactForm from './ModalContactForm.vue';
import ModalAddressForm from './ModalAddressForm.vue';
import Api from '../../api';
import { useCompanyStore } from '../../stores/company';
const companyStore = useCompanyStore();
const props = defineProps({
visible: Boolean,
clientName: { type: String, default: '' },
clientContacts: { type: Array, default: () => [] },
existingContacts: { type: Array, default: () => [] },
existingAddresses: { type: Array, default: () => [] },
isSubmitting: { type: Boolean, default: false },
});
const emit = defineEmits(['update:visible', 'created']);
const showContacts = ref(false);
const showAddresses = ref(false);
const isSubmitting = ref(false);
// Direct arrays instead of wrapping in formData objects
const newContacts = ref([
@ -136,16 +141,57 @@ function close() {
emit('update:visible', false);
}
function create() {
const payload = {};
if (showContacts.value) {
payload.contacts = newContacts.value;
async function create() {
isSubmitting.value = true;
try {
const contactsToSend = showContacts.value ? newContacts.value : [];
const addressesToSend = showAddresses.value ? newAddresses.value : [];
// Check if any contacts or addresses already exist
const existingMessages = [];
if (contactsToSend.length > 0) {
const existingContactsResult = await Api.checkContactsExist(contactsToSend);
if (existingContactsResult && existingContactsResult.length > 0) {
const names = existingContactsResult.map(c => `${c.firstName || ''} ${c.lastName || ''}`.trim()).join(', ');
existingMessages.push(`Contact(s) already exist: ${names}`);
}
}
if (addressesToSend.length > 0) {
const existingAddressesResult = await Api.checkAddressesExist(addressesToSend);
if (existingAddressesResult && existingAddressesResult.length > 0) {
const addrs = existingAddressesResult.map(a => `${a.addressLine1 || ''} ${a.city || ''}`).join(', ');
existingMessages.push(`Address(es) already exist: ${addrs}`);
}
}
// If any exist, prompt the user for confirmation
if (existingMessages.length > 0) {
const message = existingMessages.join('\n') + '\n\nWould you like to proceed anyway? Existing records will be linked instead of duplicated.';
if (!window.confirm(message)) {
isSubmitting.value = false;
return;
}
}
// Call API to create/link contacts and addresses
// Address contacts/primaryContact are dicts with firstName, lastName, email
// that the backend matches against created/existing contact docs
const result = await Api.createClientContactsAddresses(
props.clientName,
companyStore.currentCompany,
contactsToSend,
addressesToSend
);
emit('created', result);
close();
} catch (error) {
console.error('Error creating contacts/addresses:', error);
} finally {
isSubmitting.value = false;
}
if (showAddresses.value) {
payload.addresses = newAddresses.value;
}
emit('created', payload);
close();
}
</script>

View file

@ -98,9 +98,11 @@
<AddContactAddressModal
:visible="showAddModal"
@update:visible="showAddModal = $event"
:clientName="clientData.customerName"
:clientContacts="clientData.contacts || []"
:existingContacts="clientData.contacts?.map(c => c.fullName || c.name) || []"
:existingAddresses="clientData.addresses?.map(a => a.addressLine1) || []"
@created="onContactsAddressesCreated"
/>
</div>
</template>
@ -126,6 +128,8 @@ const props = defineProps({
},
});
const emit = defineEmits(['refresh']);
// Check if client is a Lead
const isLead = computed(() => props.clientData.doctype === "Lead");
@ -173,6 +177,11 @@ const formattedCreationDate = computed(() => {
});
});
// Handle successful contact/address creation - emit refresh so parent reloads client data
const onContactsAddressesCreated = () => {
emit('refresh');
};
</script>

View file

@ -29,6 +29,7 @@
<GeneralClientInfo
v-if="client.customerName"
:client-data="client"
@refresh="refreshClient"
/>
<AdditionalInfoBar :address="client.addresses[selectedAddressIdx]" v-if="client.customerName" />
@ -461,6 +462,12 @@ const handleCustomerSelected = (clientData) => {
// Handle customer selected from search
client.value = { ...client.value, ...clientData };
};
const refreshClient = async () => {
if (clientName) {
await getClient(clientName);
}
};
</script>
<style lang="css">
.tab-info-alert {

View file

@ -390,7 +390,6 @@ const loadChartData = async() => {
};
onMounted(async() => {
notifications.addWarning("Dashboard metrics are based on dummy data for demonstration purposes. UPDATES COMING SOON!");
await loadChartData();
});