Added service plan. Password change
This commit is contained in:
312
src/pages/customer/ServicePlanEdit.vue
Normal file
312
src/pages/customer/ServicePlanEdit.vue
Normal file
@@ -0,0 +1,312 @@
|
||||
<!-- src/pages/customer/ServicePlanEdit.vue -->
|
||||
<template>
|
||||
<div class="flex">
|
||||
<div class="w-full px-4 md:px-10 py-4">
|
||||
<!-- Breadcrumbs & Title -->
|
||||
<div class="text-sm breadcrumbs">
|
||||
<ul>
|
||||
<li><router-link :to="{ name: 'home' }">Home</router-link></li>
|
||||
<li><router-link :to="{ name: 'customer' }">Customers</router-link></li>
|
||||
<li><router-link :to="{ name: 'customerProfile', params: { id: customerId } }">
|
||||
{{ customerName }}
|
||||
</router-link></li>
|
||||
<li>Service Plan</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mt-4">
|
||||
<h1 class="text-3xl font-bold">
|
||||
Service Plan: {{ customerName }}
|
||||
</h1>
|
||||
<router-link :to="{ name: 'customerProfile', params: { id: customerId } }" class="btn btn-secondary btn-sm mt-2 sm:mt-0">
|
||||
Back to Profile
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- Main Form Card -->
|
||||
<div class="bg-neutral rounded-lg p-6 mt-6">
|
||||
<form @submit.prevent="onSubmit" class="space-y-6">
|
||||
|
||||
<!-- Current Plan Status -->
|
||||
<div v-if="servicePlan && servicePlan.contract_plan > 0" class="alert alert-info">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1">
|
||||
<h3 class="font-bold">Current Plan: {{ getPlanName(servicePlan.contract_plan) }}</h3>
|
||||
<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>
|
||||
|
||||
<!-- Service Plan Form -->
|
||||
<div>
|
||||
<h2 class="text-lg font-bold">Service Plan Details</h2>
|
||||
<div class="divider mt-2 mb-4"></div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
<!-- Contract Plan -->
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text">Service Plan</span></label>
|
||||
<select v-model="formData.contract_plan" class="select select-bordered select-sm w-full" required>
|
||||
<option value="0">No Contract</option>
|
||||
<option value="1">Standard Plan</option>
|
||||
<option value="2">Premium Plan</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Contract Years -->
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text">Contract Years</span></label>
|
||||
<input v-model.number="formData.contract_years" type="number" min="1" placeholder="1"
|
||||
class="input input-bordered input-sm w-full" required />
|
||||
</div>
|
||||
|
||||
<!-- Contract Start Date -->
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text">Contract Start Date</span></label>
|
||||
<input v-model="formData.contract_start_date" type="date"
|
||||
class="input input-bordered input-sm w-full" required />
|
||||
</div>
|
||||
|
||||
<!-- Contract End Date (Calculated) -->
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text">Contract End Date</span></label>
|
||||
<input :value="formatEndDate(formData.contract_start_date, formData.contract_years)"
|
||||
type="text" readonly class="input input-bordered input-sm w-full bg-gray-100" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Renewal Section (only show if there's an existing plan) -->
|
||||
<div v-if="servicePlan && servicePlan.contract_plan > 0">
|
||||
<h2 class="text-lg font-bold">Contract Renewal</h2>
|
||||
<div class="divider mt-2 mb-4"></div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text">Renewal Date</span></label>
|
||||
<input v-model="renewalDate" type="date" placeholder="Select renewal date"
|
||||
class="input input-bordered input-sm w-full" />
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text"> </span></label>
|
||||
<button @click="renewContract" type="button" class="btn btn-sm btn-success w-full">
|
||||
Renew Contract (+1 Year)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
This will add +1 year to the contract and update the start date to the renewal date.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="pt-4 flex gap-4">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
{{ servicePlan ? 'Update Plan' : 'Create Plan' }}
|
||||
</button>
|
||||
<button v-if="servicePlan" @click="deletePlan" type="button" class="btn btn-error btn-sm">
|
||||
Delete Plan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import axios from 'axios'
|
||||
import authHeader from '../../services/auth.header'
|
||||
import Footer from '../../layouts/footers/footer.vue'
|
||||
import { notify } from "@kyvg/vue3-notification";
|
||||
|
||||
interface ServicePlan {
|
||||
id: number;
|
||||
customer_id: number;
|
||||
contract_plan: number;
|
||||
contract_years: number;
|
||||
contract_start_date: string;
|
||||
}
|
||||
|
||||
interface Customer {
|
||||
id: number;
|
||||
customer_first_name: string;
|
||||
customer_last_name: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ServicePlanEdit',
|
||||
components: { Footer },
|
||||
data() {
|
||||
return {
|
||||
customerId: this.$route.params.id as string,
|
||||
customerName: '',
|
||||
servicePlan: null as ServicePlan | null,
|
||||
formData: {
|
||||
contract_plan: 0,
|
||||
contract_years: 1,
|
||||
contract_start_date: '',
|
||||
},
|
||||
renewalDate: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadCustomer();
|
||||
this.loadServicePlan();
|
||||
},
|
||||
methods: {
|
||||
async loadCustomer() {
|
||||
try {
|
||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${this.customerId}`;
|
||||
const response = await axios.get(path, { headers: authHeader() });
|
||||
const customer: Customer = response.data;
|
||||
this.customerName = `${customer.customer_first_name} ${customer.customer_last_name}`;
|
||||
} catch (error) {
|
||||
console.error('Failed to load customer:', error);
|
||||
notify({ title: "Error", text: "Failed to load customer information.", type: "error" });
|
||||
}
|
||||
},
|
||||
|
||||
async loadServicePlan() {
|
||||
try {
|
||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/customer/${this.customerId}`;
|
||||
const response = await axios.get(path, { headers: authHeader() });
|
||||
|
||||
if (response.data && response.data.contract_plan !== undefined) {
|
||||
this.servicePlan = response.data;
|
||||
this.formData = {
|
||||
contract_plan: response.data.contract_plan,
|
||||
contract_years: response.data.contract_years,
|
||||
contract_start_date: response.data.contract_start_date,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Plan doesn't exist yet, that's okay
|
||||
console.log('No existing service plan found');
|
||||
}
|
||||
},
|
||||
|
||||
async onSubmit() {
|
||||
try {
|
||||
const payload = {
|
||||
customer_id: parseInt(this.customerId),
|
||||
...this.formData
|
||||
};
|
||||
|
||||
let response;
|
||||
if (this.servicePlan) {
|
||||
// Update existing plan
|
||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/update/${this.customerId}`;
|
||||
response = await axios.put(path, payload, { headers: authHeader() });
|
||||
} else {
|
||||
// Create new plan
|
||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/create`;
|
||||
response = await axios.post(path, payload, { headers: authHeader() });
|
||||
}
|
||||
|
||||
if (response.data.ok) {
|
||||
notify({
|
||||
title: "Success",
|
||||
text: `Service plan ${this.servicePlan ? 'updated' : 'created'} successfully!`,
|
||||
type: "success"
|
||||
});
|
||||
// Redirect to profile page after successful submission
|
||||
this.$router.push({ name: 'customerProfile', params: { id: this.customerId } });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save service plan:', error);
|
||||
notify({ title: "Error", text: "Failed to save service plan.", type: "error" });
|
||||
}
|
||||
},
|
||||
|
||||
async deletePlan() {
|
||||
if (!confirm('Are you sure you want to delete this service plan?')) return;
|
||||
|
||||
try {
|
||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/delete/${this.customerId}`;
|
||||
const response = await axios.delete(path, { headers: authHeader() });
|
||||
|
||||
if (response.data.ok) {
|
||||
notify({ title: "Success", text: "Service plan deleted successfully!", type: "success" });
|
||||
this.servicePlan = null;
|
||||
this.formData = {
|
||||
contract_plan: 0,
|
||||
contract_years: 1,
|
||||
contract_start_date: '',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to delete service plan:', error);
|
||||
notify({ title: "Error", text: "Failed to delete service plan.", type: "error" });
|
||||
}
|
||||
},
|
||||
|
||||
renewContract() {
|
||||
if (!this.renewalDate) {
|
||||
notify({ title: "Error", text: "Please select a renewal date.", type: "error" });
|
||||
return;
|
||||
}
|
||||
|
||||
this.formData.contract_years += 1;
|
||||
this.formData.contract_start_date = this.renewalDate;
|
||||
this.renewalDate = '';
|
||||
|
||||
notify({
|
||||
title: "Success",
|
||||
text: "Contract renewed! Years increased by 1 and start date updated.",
|
||||
type: "success"
|
||||
});
|
||||
},
|
||||
|
||||
getPlanName(planType: number): string {
|
||||
const planNames: { [key: number]: string } = {
|
||||
1: 'Standard Plan',
|
||||
2: 'Premium Plan'
|
||||
};
|
||||
return planNames[planType] || 'No Plan';
|
||||
},
|
||||
|
||||
formatEndDate(startDate: string, years: number): string {
|
||||
if (!startDate) return 'N/A';
|
||||
const date = new Date(startDate);
|
||||
date.setFullYear(date.getFullYear() + years);
|
||||
return date.toISOString().split('T')[0];
|
||||
},
|
||||
|
||||
getStatusText(startDate: string, years: number): string {
|
||||
if (!startDate) return 'Unknown';
|
||||
const endDate = new Date(startDate);
|
||||
endDate.setFullYear(endDate.getFullYear() + years);
|
||||
const now = new Date();
|
||||
if (now > endDate) {
|
||||
return 'Expired';
|
||||
} else if (now > new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000)) {
|
||||
return 'Expiring Soon';
|
||||
} else {
|
||||
return 'Active';
|
||||
}
|
||||
},
|
||||
|
||||
getStatusBadge(startDate: string, years: number): string {
|
||||
if (!startDate) return 'badge-ghost';
|
||||
const endDate = new Date(startDate);
|
||||
endDate.setFullYear(endDate.getFullYear() + years);
|
||||
const now = new Date();
|
||||
if (now > endDate) {
|
||||
return 'badge-error';
|
||||
} else if (now > new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000)) {
|
||||
return 'badge-warning';
|
||||
} else {
|
||||
return 'badge-success';
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -143,6 +143,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- SUBMIT BUTTON -->
|
||||
<div class="pt-4">
|
||||
@@ -226,7 +228,13 @@ export default defineComponent({
|
||||
customer_description: "",
|
||||
customer_fill_location: 0,
|
||||
},
|
||||
servicePlan: {
|
||||
contract_plan: 0,
|
||||
contract_years: 1,
|
||||
contract_start_date: "",
|
||||
},
|
||||
},
|
||||
renewalDate: "",
|
||||
}
|
||||
},
|
||||
validations() {
|
||||
@@ -343,8 +351,12 @@ export default defineComponent({
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
// Create payload directly from the form object
|
||||
this.editItem(this.CreateCustomerForm.basicInfo);
|
||||
// Create payload with both basic info and service plan data
|
||||
const payload = {
|
||||
...this.CreateCustomerForm.basicInfo,
|
||||
service_plan: this.CreateCustomerForm.servicePlan
|
||||
};
|
||||
this.editItem(payload);
|
||||
},
|
||||
getCustomerTypeList() {
|
||||
let path = import.meta.env.VITE_BASE_URL + "/query/customertype";
|
||||
@@ -360,7 +372,22 @@ export default defineComponent({
|
||||
this.stateList = response.data;
|
||||
});
|
||||
},
|
||||
formatEndDate(startDate: string, years: number): string {
|
||||
if (!startDate) return 'N/A';
|
||||
const date = new Date(startDate);
|
||||
date.setFullYear(date.getFullYear() + years);
|
||||
return date.toISOString().split('T')[0];
|
||||
},
|
||||
renewContract() {
|
||||
if (!this.renewalDate) {
|
||||
alert('Please select a renewal date');
|
||||
return;
|
||||
}
|
||||
this.CreateCustomerForm.servicePlan.contract_years += 1;
|
||||
this.CreateCustomerForm.servicePlan.contract_start_date = this.renewalDate;
|
||||
this.renewalDate = '';
|
||||
alert('Contract renewed! Years increased by 1 and start date updated.');
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,18 +2,7 @@
|
||||
<template>
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2">
|
||||
<h2 class="card-title text-2xl">{{ customer.account_number }}</h2>
|
||||
<div class="flex flex-wrap gap-2 justify-start sm:justify-end">
|
||||
<router-link :to="{ name: 'deliveryCreate', params: { id: customer.id } }" class="btn btn-sm btn-primary">New Delivery</router-link>
|
||||
<router-link :to="{ name: 'CalenderCustomer', params: { id: customer.id } }" class="btn btn-sm btn-info">New Service</router-link>
|
||||
<router-link :to="{ name: 'customerEdit', params: { id: customer.id } }" class="btn btn-sm btn-secondary">Edit Customer</router-link>
|
||||
<button @click="$emit('toggleAutomatic', customer.id)" class="btn btn-sm"
|
||||
:class="automatic_status === 1 ? 'btn-success' : 'btn-warning'">
|
||||
{{ automatic_status === 1 ? 'Set to Will Call' : 'Set to Automatic' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="card-title text-2xl">{{ customer.account_number }}</h2>
|
||||
<div class="divider my-2"></div>
|
||||
<div class="rounded-lg overflow-hidden" style="height:400px; width:100%">
|
||||
<l-map ref="map" v-model:zoom="zoom" :center="[customer.customer_latitude, customer.customer_longitude]">
|
||||
@@ -35,4 +24,4 @@ defineProps({
|
||||
});
|
||||
defineEmits(['toggleAutomatic']);
|
||||
const zoom = ref(14);
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -2,13 +2,50 @@
|
||||
<template>
|
||||
<div class="card bg-base-100 shadow-xl h-full">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<router-link :to="{ name: 'deliveryCreate', params: { id: customer.id } }" class="btn btn-sm btn-primary">New Delivery</router-link>
|
||||
<router-link :to="{ name: 'CalenderCustomer', params: { id: customer.id } }" class="btn btn-sm btn-info">New Service</router-link>
|
||||
<router-link :to="{ name: 'customerEdit', params: { id: customer.id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||
<button @click="$emit('toggleAutomatic', customer.id)" class="btn btn-sm" :class="automatic_status === 1 ? 'btn-success' : 'btn-warning'">
|
||||
{{ automatic_status === 1 ? 'Set to Will Call' : 'Set to Automatic' }}
|
||||
<!-- Action Buttons - Two Row Layout -->
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<!-- Row 1 -->
|
||||
<router-link :to="{ name: 'deliveryCreate', params: { id: customer.id } }"
|
||||
class="btn btn-primary min-h-[3rem] flex flex-col items-center justify-center gap-1">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
|
||||
</svg>
|
||||
<span class="text-xs font-medium">Delivery</span>
|
||||
</router-link>
|
||||
|
||||
<router-link :to="{ name: 'CalenderCustomer', params: { id: customer.id } }"
|
||||
class="btn btn-info min-h-[3rem] flex flex-col items-center justify-center gap-1">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<span class="text-xs font-medium">Service</span>
|
||||
</router-link>
|
||||
|
||||
<router-link :to="{ name: 'customerEdit', params: { id: customer.id } }"
|
||||
class="btn btn-secondary min-h-[3rem] flex flex-col items-center justify-center gap-1">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
<span class="text-xs font-medium">Edit</span>
|
||||
</router-link>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<router-link :to="{ name: 'servicePlanEdit', params: { id: customer.id } }"
|
||||
class="btn btn-accent min-h-[3rem] flex flex-col items-center justify-center gap-1">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
<span class="text-xs font-medium">Contract</span>
|
||||
</router-link>
|
||||
|
||||
<button @click="$emit('toggleAutomatic', customer.id)"
|
||||
class="btn min-h-[3rem] flex flex-col items-center justify-center gap-1 col-span-2"
|
||||
:class="automatic_status === 1 ? 'btn-success' : 'btn-warning'">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
|
||||
</svg>
|
||||
<span class="text-xs font-medium">{{ automatic_status === 1 ? 'Set to Will Call' : 'Set to Auto' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -61,4 +98,4 @@ defineEmits(['toggleAutomatic']);
|
||||
|
||||
const stateName = (id: number) => ['MA', 'RI', 'NH', 'ME', 'VT', 'CT', 'NY'][id] || 'N/A';
|
||||
const homeTypeName = (id: number) => ['Residential', 'Apartment', 'Condo', 'Commercial', 'Business', 'Construction', 'Container'][id] || 'Unknown';
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -29,6 +29,11 @@ const customerRoutes = [
|
||||
name: 'customerProfile',
|
||||
component: CustomerProfile,
|
||||
},
|
||||
{
|
||||
path: '/customer/:id/service-plan',
|
||||
name: 'servicePlanEdit',
|
||||
component: () => import('./ServicePlanEdit.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tank/edit/:id',
|
||||
name: 'TankEdit',
|
||||
|
||||
Reference in New Issue
Block a user