custom_ui/frontend/src/components/clientView/GeneralClientInfo.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>