Added service plan. Password change

This commit is contained in:
2025-09-06 12:28:44 -04:00
parent 9d86b4a60e
commit 3282229116
14 changed files with 977 additions and 51 deletions

View File

@@ -3,8 +3,33 @@
<div class="w-full min-h-screen bg-base-200 px-4 md:px-10">
<!-- ... breadcrumbs ... -->
<div v-if="customer && customer.id" class="bg-neutral rounded-lg p-4 sm:p-6 mt-6">
<div v-if="customer && customer.id" class="bg-neutral rounded-lg p-4 sm:p-6 mt-6">
<!-- Current Plan Status Banner - Same as ServicePlanEdit.vue -->
<div v-if="servicePlan && servicePlan.contract_plan > 0"
class="alert alert-info mb-6"
:class="servicePlan.contract_plan === 2 ? 'border-4 border-yellow-400 bg-yellow-50' : ''">
<div class="flex items-center">
<div class="flex-1">
<div class="flex items-center gap-3">
<h3 class="font-bold">Current Plan: {{ getPlanName(servicePlan.contract_plan) }}</h3>
<!-- Premium Star Icon -->
<svg v-if="servicePlan.contract_plan === 2"
class="w-8 h-8 text-yellow-500 fill-current"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/>
</svg>
</div>
<p>{{ servicePlan.contract_years }} Year{{ servicePlan.contract_years > 1 ? 's' : '' }} Contract</p>
<p class="text-sm">Expires: {{ formatEndDate(servicePlan.contract_start_date, servicePlan.contract_years) }}</p>
</div>
<div class="badge" :class="getStatusBadge(servicePlan.contract_start_date, servicePlan.contract_years)">
{{ getStatusText(servicePlan.contract_start_date, servicePlan.contract_years) }}
</div>
</div>
</div>
<!-- FIX: Changed `lg:` to `xl:` for a later breakpoint -->
<div class="grid grid-cols-1 xl:grid-cols-12 gap-6">
@@ -177,6 +202,14 @@ interface ServiceParts {
oil_nozzle_2: string;
}
interface ServicePlan {
id: number;
customer_id: number;
contract_plan: number;
contract_years: number;
contract_start_date: string;
}
export default defineComponent({
@@ -225,6 +258,7 @@ export default defineComponent({
selectedServiceForEdit: null as ServiceCall | null,
isPartsModalOpen: false,
currentParts: null as ServiceParts | null,
servicePlan: null as ServicePlan | null,
}
},
computed: {
@@ -282,6 +316,7 @@ export default defineComponent({
this.getCustomerLastDelivery(this.customer.id);
this.getServiceCalls(this.customer.id);
this.fetchCustomerParts(this.customer.id);
this.loadServicePlan(this.customer.id);
}).catch((error: any) => {
console.error("CRITICAL: Failed to fetch main customer data. Aborting other calls.", error);
@@ -615,6 +650,78 @@ onSubmitSocial(commentText: string) {
getServiceTypeColor(typeId: number): string {
const colorMap: { [key: number]: string } = { 0: 'blue', 1: 'red', 2: 'green', 3: '#B58900', 4: 'black' };
return colorMap[typeId] || 'gray';
},
formatEndDate(startDate: string, years: number): string {
if (!startDate) return 'N/A';
return dayjs(startDate).add(years, 'year').format('MMM D, YYYY');
},
getPlanStatusText(startDate: string, years: number): string {
if (!startDate) return 'Unknown';
const endDate = dayjs(startDate).add(years, 'year');
const now = dayjs();
if (now.isAfter(endDate)) {
return 'Expired';
} else if (now.isAfter(endDate.subtract(30, 'day'))) {
return 'Expiring Soon';
} else {
return 'Active';
}
},
getPlanStatusBadge(startDate: string, years: number): string {
if (!startDate) return 'badge-ghost';
const endDate = dayjs(startDate).add(years, 'year');
const now = dayjs();
if (now.isAfter(endDate)) {
return 'badge-error';
} else if (now.isAfter(endDate.subtract(30, 'day'))) {
return 'badge-warning';
} else {
return 'badge-success';
}
},
getPlanName(planType: number): string {
const planNames: { [key: number]: string } = {
1: 'Standard Plan',
2: 'Premium Plan'
};
return planNames[planType] || 'No Plan';
},
getStatusText(startDate: string, years: number): string {
if (!startDate) return 'Unknown';
const endDate = dayjs(startDate).add(years, 'year');
const now = dayjs();
if (now.isAfter(endDate)) {
return 'Expired';
} else if (now.isAfter(endDate.subtract(30, 'day'))) {
return 'Expiring Soon';
} else {
return 'Active';
}
},
getStatusBadge(startDate: string, years: number): string {
if (!startDate) return 'badge-ghost';
const endDate = dayjs(startDate).add(years, 'year');
const now = dayjs();
if (now.isAfter(endDate)) {
return 'badge-error';
} else if (now.isAfter(endDate.subtract(30, 'day'))) {
return 'badge-warning';
} else {
return 'badge-success';
}
},
async loadServicePlan(customerId: number) {
try {
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/customer/${customerId}`;
const response = await axios.get(path, { headers: authHeader() });
if (response.data && response.data.contract_plan !== undefined) {
this.servicePlan = response.data;
}
} catch (error) {
// Plan doesn't exist yet, that's okay
console.log('No existing service plan found');
}
}
},
})