335 lines
7.5 KiB
Vue
335 lines
7.5 KiB
Vue
<template>
|
|
<div class="general-client-info">
|
|
<div class="info-grid">
|
|
<!-- Lead Badge -->
|
|
<div v-if="isLead" class="lead-badge-container">
|
|
<Badge value="LEAD" severity="warn" size="large" />
|
|
<div class="action-buttons">
|
|
<v-btn
|
|
size="small"
|
|
variant="outlined"
|
|
color="primary"
|
|
@click="addAddress"
|
|
>
|
|
<v-icon left size="small">mdi-map-marker-plus</v-icon>
|
|
Add Address
|
|
</v-btn>
|
|
<v-btn
|
|
size="small"
|
|
variant="outlined"
|
|
color="primary"
|
|
@click="addContact"
|
|
>
|
|
<v-icon left size="small">mdi-account-plus</v-icon>
|
|
Add Contact
|
|
</v-btn>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Client Name (only show for Company type) -->
|
|
<div v-if="clientData.customerType === 'Company'" class="info-section">
|
|
<label>Company Name</label>
|
|
<span class="info-value large">{{ displayClientName }}</span>
|
|
</div>
|
|
|
|
<!-- Client Type -->
|
|
<div class="info-section">
|
|
<label>Client Type</label>
|
|
<span class="info-value">{{ clientData.customerType || "N/A" }}</span>
|
|
</div>
|
|
|
|
<!-- Associated Companies -->
|
|
<div v-if="associatedCompanies.length > 0" class="info-section">
|
|
<label>Associated Companies</label>
|
|
<div class="companies-list">
|
|
<Tag
|
|
v-for="company in associatedCompanies"
|
|
:key="company"
|
|
:value="company"
|
|
severity="info"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Billing Address -->
|
|
<div v-if="billingAddress" class="info-section">
|
|
<label>Billing Address</label>
|
|
<span class="info-value">{{ billingAddress }}</span>
|
|
</div>
|
|
|
|
<!-- Primary Contact Info -->
|
|
<div v-if="primaryContact" class="info-section primary-contact">
|
|
<label>{{ clientData.customerType === 'Individual' ? 'Contact Information' : 'Primary Contact' }}</label>
|
|
<div class="contact-details">
|
|
<div class="contact-item">
|
|
<i class="pi pi-user"></i>
|
|
<span>{{ primaryContact.fullName || primaryContact.name || "N/A" }}</span>
|
|
</div>
|
|
<div class="contact-item">
|
|
<i class="pi pi-envelope"></i>
|
|
<span>{{ primaryContact.emailId || primaryContact.customEmail || "N/A" }}</span>
|
|
</div>
|
|
<div class="contact-item">
|
|
<i class="pi pi-phone"></i>
|
|
<span>{{ primaryContact.phone || primaryContact.mobileNo || "N/A" }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics -->
|
|
<div class="info-section stats">
|
|
<label>Overview</label>
|
|
<div class="stats-grid">
|
|
<div class="stat-item">
|
|
<i class="pi pi-map-marker"></i>
|
|
<span class="stat-value">{{ addressCount }}</span>
|
|
<span class="stat-label">Addresses</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<i class="pi pi-users"></i>
|
|
<span class="stat-value">{{ contactCount }}</span>
|
|
<span class="stat-label">Contacts</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<i class="pi pi-briefcase"></i>
|
|
<span class="stat-value">{{ projectCount }}</span>
|
|
<span class="stat-label">Projects</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Creation Date -->
|
|
<div class="info-section">
|
|
<label>Created</label>
|
|
<span class="info-value">{{ formattedCreationDate }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from "vue";
|
|
import Badge from "primevue/badge";
|
|
import Tag from "primevue/tag";
|
|
|
|
const props = defineProps({
|
|
clientData: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
});
|
|
|
|
// Check if client is a Lead
|
|
const isLead = computed(() => props.clientData.doctype === "Lead");
|
|
|
|
// Strip "-#-" from client name
|
|
const displayClientName = computed(() => {
|
|
if (!props.clientData.customerName) return "N/A";
|
|
return props.clientData.customerName.split("-#-")[0].trim();
|
|
});
|
|
|
|
// Get associated companies
|
|
const associatedCompanies = computed(() => {
|
|
if (!props.clientData.companies || !Array.isArray(props.clientData.companies)) {
|
|
return [];
|
|
}
|
|
return props.clientData.companies.map(c => c.company).filter(Boolean);
|
|
});
|
|
|
|
// Strip "-#-" from billing address
|
|
const billingAddress = computed(() => {
|
|
if (!props.clientData.customBillingAddress) return null;
|
|
return props.clientData.customBillingAddress.split("-#-")[0].trim();
|
|
});
|
|
|
|
// Get primary contact
|
|
const primaryContact = computed(() => {
|
|
if (!props.clientData.contacts || !props.clientData.primaryContact) return null;
|
|
return props.clientData.contacts.find(
|
|
c => c.name === props.clientData.primaryContact
|
|
);
|
|
});
|
|
|
|
// Counts
|
|
const addressCount = computed(() => props.clientData.addresses?.length || 0);
|
|
const contactCount = computed(() => props.clientData.contacts?.length || 0);
|
|
const projectCount = computed(() => props.clientData.jobs?.length || 0);
|
|
|
|
// Format creation date
|
|
const formattedCreationDate = computed(() => {
|
|
if (!props.clientData.creation) return "N/A";
|
|
const date = new Date(props.clientData.creation);
|
|
return date.toLocaleDateString("en-US", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
});
|
|
|
|
// Placeholder methods for adding address and contact
|
|
const addAddress = () => {
|
|
console.log("Add Address modal would open here");
|
|
// TODO: Open add address modal
|
|
};
|
|
|
|
const addContact = () => {
|
|
console.log("Add Contact modal would open here");
|
|
// TODO: Open add contact modal
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.general-client-info {
|
|
background: var(--surface-card);
|
|
border-radius: 8px;
|
|
padding: 0.5rem 0.75rem;
|
|
border: 1px solid var(--surface-border);
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.lead-badge-container {
|
|
grid-column: 1 / -1;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.info-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 0.75rem 1.5rem;
|
|
align-items: start;
|
|
}
|
|
|
|
.info-section {
|
|
display: grid;
|
|
grid-template-columns: 120px 1fr;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
min-height: 2rem;
|
|
padding: 0.25rem 0;
|
|
}
|
|
|
|
.info-section label {
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
color: var(--text-color-secondary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.3px;
|
|
justify-self: start;
|
|
align-self: center;
|
|
margin: 0;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: 0.85rem;
|
|
color: var(--text-color);
|
|
font-weight: 500;
|
|
justify-self: start;
|
|
align-self: center;
|
|
margin: 0;
|
|
}
|
|
|
|
.info-value.large {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.companies-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.25rem;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
}
|
|
|
|
.primary-contact {
|
|
grid-column: span 1;
|
|
justify-self: stretch;
|
|
}
|
|
|
|
.contact-details {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
padding: 0.5rem 0.75rem;
|
|
background: var(--surface-ground);
|
|
border-radius: 4px;
|
|
width: 100%;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
}
|
|
|
|
.contact-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.contact-item i {
|
|
color: var(--primary-color);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.contact-item span {
|
|
font-size: 0.8rem;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.stats {
|
|
grid-column: span 1;
|
|
justify-self: stretch;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: flex;
|
|
gap: 1rem;
|
|
padding: 0.5rem 0.75rem;
|
|
background: var(--surface-ground);
|
|
border-radius: 4px;
|
|
width: 100%;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
text-align: center;
|
|
min-width: 60px;
|
|
}
|
|
|
|
.stat-item i {
|
|
font-size: 0.9rem;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: var(--text-color);
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 0.7rem;
|
|
color: var(--text-color-secondary);
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.3px;
|
|
line-height: 1.2;
|
|
}
|
|
</style>
|