386 lines
9.3 KiB
Vue

<template>
<div v-if="addresses.length > 1" class="address-selector">
<div class="selector-header">
<h4>Select Address</h4>
<Button
@click="showAddAddressModal = true"
icon="pi pi-plus"
label="Add An Address"
size="small"
severity="secondary"
/>
</div>
<Select
v-model="selectedAddressIndex"
:options="addressOptions"
optionLabel="label"
optionValue="value"
placeholder="Select an address"
class="w-full address-dropdown"
@change="handleAddressChange"
>
<template #value="slotProps">
<div v-if="slotProps.value !== null && slotProps.value !== undefined" class="dropdown-value">
<span class="address-title">{{ addresses[slotProps.value]?.fullAddress || 'Unnamed Address' }}</span>
<div class="address-badges">
<Badge
v-if="addresses[slotProps.value]?.isPrimaryAddress && !addresses[slotProps.value]?.isServiceAddress"
value="Billing Only"
severity="info"
/>
<Badge
v-if="addresses[slotProps.value]?.isPrimaryAddress && addresses[slotProps.value]?.isServiceAddress"
value="Billing & Service"
severity="success"
/>
<Badge
v-if="!addresses[slotProps.value]?.isPrimaryAddress && addresses[slotProps.value]?.isServiceAddress"
value="Service"
severity="secondary"
/>
<Badge
v-if="addresses[slotProps.value]?.isServiceAddress"
:value="`${addresses[slotProps.value]?.projects?.length || 0} Projects`"
severity="contrast"
/>
</div>
</div>
</template>
<template #option="slotProps">
<div class="dropdown-option">
<span class="option-title">{{ slotProps.option.addressTitle || 'Unnamed Address' }}</span>
<div class="option-badges">
<Badge
v-if="slotProps.option.isPrimaryAddress && !slotProps.option.isServiceAddress"
value="Billing Only"
severity="info"
size="small"
/>
<Badge
v-if="slotProps.option.isPrimaryAddress && slotProps.option.isServiceAddress"
value="Billing & Service"
severity="success"
size="small"
/>
<Badge
v-if="!slotProps.option.isPrimaryAddress && slotProps.option.isServiceAddress"
value="Service"
severity="secondary"
size="small"
/>
<Badge
v-if="slotProps.option.isServiceAddress"
:value="`${slotProps.option.projectCount} Projects`"
severity="contrast"
size="small"
/>
</div>
</div>
</template>
</Select>
<!-- Selected Address Info -->
<div v-if="selectedAddress" class="selected-address-info">
<div class="address-status">
<Badge
v-if="selectedAddress.isPrimaryAddress && !selectedAddress.isServiceAddress"
value="Billing Only Address"
severity="info"
/>
<Badge
v-if="selectedAddress.isPrimaryAddress && selectedAddress.isServiceAddress"
value="Billing & Service Address"
severity="success"
/>
<Badge
v-if="!selectedAddress.isPrimaryAddress && selectedAddress.isServiceAddress"
value="Service Address"
severity="secondary"
/>
</div>
<!-- Service Address Details -->
<div v-if="selectedAddress.isServiceAddress" class="service-details">
<div class="detail-item">
<i class="pi pi-briefcase"></i>
<span>{{ selectedAddress.projects?.length || 0 }} Projects</span>
</div>
<div class="detail-item">
<i class="pi pi-calendar"></i>
<span>{{ selectedAddress.onsiteMeetings?.length || 0 }} Bid Meetings</span>
</div>
<div v-if="primaryContact" class="detail-item primary-contact">
<i class="pi pi-user"></i>
<div class="contact-info">
<span class="contact-name">{{ primaryContactName }}</span>
<span class="contact-detail">{{ primaryContactEmail }}</span>
<span class="contact-detail">{{ primaryContactPhone }}</span>
</div>
</div>
</div>
</div>
<!-- Add Address Modal -->
<Dialog
:visible="showAddAddressModal"
@update:visible="showAddAddressModal = $event"
header="Add Address"
:modal="true"
:closable="true"
class="add-address-dialog"
>
<div class="coming-soon">
<i class="pi pi-hourglass"></i>
<p>Feature coming soon</p>
</div>
<template #footer>
<Button
label="Close"
severity="secondary"
@click="showAddAddressModal = false"
/>
</template>
</Dialog>
</div>
</template>
<script setup>
import { computed, ref, watch, nextTick } from "vue";
import { useRoute } from "vue-router";
import Badge from "primevue/badge";
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import Select from "primevue/select";
import DataUtils from "../../utils";
const route = useRoute();
const addressParam = route.query.address || null;
const props = defineProps({
addresses: {
type: Array,
required: true,
},
selectedAddressIdx: {
type: Number,
default: 0,
},
contacts: {
type: Array,
default: () => [],
},
});
const findAddressIndexByParam = (addressStr) => {
const trimmedParam = addressStr.trim();
for (let i = 0; i < props.addresses.length; i++) {
const addr = props.addresses[i];
const fullAddr = (addr.fullAddress || DataUtils.calculateFullAddress(addr)).trim();
if (fullAddr === trimmedParam) {
return i;
}
}
return null;
};
const emit = defineEmits(["update:selectedAddressIdx"]);
const showAddAddressModal = ref(false);
const selectedAddressIndex = ref(addressParam ? findAddressIndexByParam(addressParam) : props.selectedAddressIdx);
// Emit update if the initial index is different from props
if (addressParam && selectedAddressIndex.value !== null && selectedAddressIndex.value !== props.selectedAddressIdx) {
nextTick(() => emit("update:selectedAddressIdx", selectedAddressIndex.value));
}
// Watch for external changes to selectedAddressIdx
watch(() => props.selectedAddressIdx, (newVal) => {
selectedAddressIndex.value = newVal;
});
// Selected address object
const selectedAddress = computed(() => {
if (selectedAddressIndex.value >= 0 && selectedAddressIndex.value < props.addresses.length) {
return props.addresses[selectedAddressIndex.value];
}
return null;
});
// Address options for dropdown
const addressOptions = computed(() => {
return props.addresses.map((addr, idx) => ({
label: addr.fullAddress || DataUtils.calculateFullAddress(addr),
value: idx,
addressTitle: addr.fullAddress || 'Unnamed Address',
isPrimaryAddress: addr.isPrimaryAddress,
isServiceAddress: addr.isServiceAddress,
projectCount: addr.projects?.length || 0,
}));
});
// Primary contact for selected address
const primaryContact = computed(() => {
if (!selectedAddress.value?.primaryContact || !props.contacts) return null;
return props.contacts.find(c => c.name === selectedAddress.value.primaryContact);
});
const primaryContactName = computed(() => {
if (!primaryContact.value) return "N/A";
return primaryContact.value.fullName || primaryContact.value.name || "N/A";
});
const primaryContactEmail = computed(() => {
if (!primaryContact.value) return "N/A";
return primaryContact.value.emailId || primaryContact.value.customEmail || "N/A";
});
const primaryContactPhone = computed(() => {
if (!primaryContact.value) return "N/A";
return primaryContact.value.phone || primaryContact.value.mobileNo || "N/A";
});
// Handle address change
const handleAddressChange = () => {
emit("update:selectedAddressIdx", selectedAddressIndex.value);
};
</script>
<style scoped>
.address-selector {
background: var(--surface-card);
border-radius: 12px;
padding: 1.5rem;
border: 1px solid var(--surface-border);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: 1rem;
}
.selector-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.selector-header h4 {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
color: var(--text-color);
}
.address-dropdown {
width: 100%;
margin-bottom: 1rem;
}
.dropdown-value,
.dropdown-option {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.25rem 0;
}
.address-title,
.option-title {
font-weight: 600;
color: var(--text-color);
}
.address-badges,
.option-badges {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.selected-address-info {
display: flex;
flex-direction: row;
gap: 1rem;
padding: 0.75rem;
background: var(--surface-ground);
border-radius: 8px;
align-items: center;
}
.address-status {
display: flex;
gap: 0.5rem;
}
.service-details {
display: flex;
flex-direction: row;
gap: 1rem;
flex-wrap: wrap;
}
.detail-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: var(--surface-card);
border-radius: 6px;
}
.detail-item i {
font-size: 1.25rem;
color: var(--primary-color);
}
.detail-item span {
font-size: 0.95rem;
font-weight: 500;
color: var(--text-color);
}
.detail-item.primary-contact {
flex: 1;
}
.contact-info {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.contact-name {
font-weight: 600;
color: var(--text-color);
font-size: 1rem;
}
.contact-detail {
font-size: 0.875rem;
color: var(--text-color-secondary);
}
.coming-soon {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 2rem;
text-align: center;
}
.coming-soon i {
font-size: 3rem;
color: var(--text-color-secondary);
}
.coming-soon p {
font-size: 1.1rem;
color: var(--text-color-secondary);
margin: 0;
}
.w-full {
width: 100%;
}
</style>