Major Changes: - Migrate components from Options API to Composition API with <script setup> - Add centralized service layer (serviceService, deliveryService, adminService) - Implement new reusable components (EnhancedButton, EnhancedModal, StatCard, etc.) - Add theme store for consistent theming across application - Improve ServiceCalendar with federal holidays and better styling - Refactor customer profile and tank estimation components - Update all delivery and payment pages to use centralized services - Add utility functions for formatting and validation - Update Dockerfiles for better environment configuration - Enhance Tailwind config with custom design tokens UI Improvements: - Modern, premium design with glassmorphism effects - Improved form layouts with FloatingInput components - Better loading states and empty states - Enhanced modals and tables with consistent styling - Responsive design improvements across all pages Technical Improvements: - Strict TypeScript types throughout - Better error handling and validation - Removed deprecated api.js in favor of TypeScript services - Improved code organization and maintainability
182 lines
6.4 KiB
Vue
182 lines
6.4 KiB
Vue
<!-- src/pages/customer/tank/edit.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>Edit Tank Details</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mt-4">
|
|
<h1 v-if="customer.id" class="text-3xl font-bold">
|
|
Tank for: {{ customer.customer_first_name }} {{ customer.customer_last_name }}
|
|
</h1>
|
|
<router-link v-if="customer.id" :to="{ name: 'customerProfile', params: { id: customer.id } }" 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">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4">
|
|
|
|
<!-- Inspection Date -->
|
|
<div class="form-control">
|
|
<label class="label"><span class="label-text">Last Inspection Date</span></label>
|
|
<input v-model="TankForm.last_tank_inspection" type="date" class="input input-bordered input-sm w-full" />
|
|
</div>
|
|
|
|
<!-- Tank Size -->
|
|
<div class="form-control">
|
|
<label class="label"><span class="label-text">Tank Size (Gallons)</span></label>
|
|
<input v-model="TankForm.tank_size" type="number" placeholder="e.g., 275" class="input input-bordered input-sm w-full" />
|
|
</div>
|
|
|
|
<!-- Tank Status -->
|
|
<div class="form-control">
|
|
<label class="label"><span class="label-text">Tank Status</span></label>
|
|
<div class="flex items-center gap-6 bg-base-100 p-2 rounded-lg">
|
|
<label class="label cursor-pointer gap-2">
|
|
<span class="label-text">Good</span>
|
|
<input type="radio" v-model="TankForm.tank_status" :value="true" class="radio radio-primary" />
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<span class="label-text">Bad</span>
|
|
<input type="radio" v-model="TankForm.tank_status" :value="false" class="radio radio-primary" />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tank Location -->
|
|
<div class="form-control">
|
|
<label class="label"><span class="label-text">Tank Location</span></label>
|
|
<div class="flex items-center gap-6 bg-base-100 p-2 rounded-lg">
|
|
<label class="label cursor-pointer gap-2">
|
|
<span class="label-text">Inside</span>
|
|
<input type="radio" v-model="TankForm.outside_or_inside" :value="true" class="radio radio-primary" />
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<span class="label-text">Outside</span>
|
|
<input type="radio" v-model="TankForm.outside_or_inside" :value="false" class="radio radio-primary" />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fill Location -->
|
|
<div class="form-control md:col-span-2">
|
|
<label class="label"><span class="label-text">Fill Location </span></label>
|
|
<input v-model="TankForm.fill_location" type="text" placeholder="0-12 only" class="input input-bordered input-sm w-full" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SUBMIT BUTTON -->
|
|
<div class="pt-4">
|
|
<button type="submit" class="btn btn-primary btn-sm">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { authService } from '../../../services/authService'
|
|
import { customerService } from '../../../services/customerService'
|
|
|
|
// Interface for our flat form model
|
|
interface TankFormData {
|
|
last_tank_inspection: string | null;
|
|
tank_status: boolean;
|
|
outside_or_inside: boolean;
|
|
tank_size: number;
|
|
fill_location: string;
|
|
}
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
// Reactive data
|
|
const user = ref(null as any)
|
|
const customer = ref({} as any)
|
|
// --- REFACTORED: Simplified, flat form object ---
|
|
const TankForm = ref({
|
|
last_tank_inspection: null,
|
|
tank_status: true,
|
|
outside_or_inside: true,
|
|
tank_size: 0,
|
|
fill_location: '',
|
|
} as TankFormData)
|
|
|
|
// Functions
|
|
const userStatus = () => {
|
|
authService.whoami()
|
|
.then((response: any) => {
|
|
if (response.data.ok) {
|
|
user.value = response.data.user;
|
|
}
|
|
})
|
|
.catch(() => { user.value = null; });
|
|
}
|
|
|
|
const getCustomer = (userid: number) => {
|
|
customerService.getById(userid)
|
|
.then((response: any) => {
|
|
customer.value = response.data?.customer || response.data;
|
|
});
|
|
}
|
|
|
|
const getCustomerDescription = (userid: number) => {
|
|
customerService.getDescription(userid)
|
|
.then((response: any) => {
|
|
const description = response.data?.description || response.data;
|
|
if (description && description.fill_location) {
|
|
TankForm.value.fill_location = description.fill_location;
|
|
}
|
|
});
|
|
}
|
|
|
|
const getTank = (customer_id: number) => {
|
|
customerService.getTank(customer_id)
|
|
.then((response: any) => {
|
|
if (response.data) {
|
|
TankForm.value.last_tank_inspection = response.data.last_tank_inspection;
|
|
TankForm.value.tank_status = response.data.tank_status;
|
|
TankForm.value.outside_or_inside = response.data.outside_or_inside;
|
|
TankForm.value.tank_size = response.data.tank_size;
|
|
}
|
|
});
|
|
}
|
|
|
|
const editTank = (payload: TankFormData) => {
|
|
customerService.updateTank(parseInt(route.params.id as string), payload)
|
|
.then((response: any) => {
|
|
if (response.data.ok) {
|
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
|
} else {
|
|
console.error("Failed to edit tank:", response.data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
const onSubmit = () => {
|
|
// The payload is simply the entire form object now
|
|
editTank(TankForm.value);
|
|
}
|
|
|
|
// Lifecycle
|
|
onMounted(() => {
|
|
userStatus();
|
|
const customerId = parseInt(route.params.id as string);
|
|
getCustomer(customerId);
|
|
getCustomerDescription(customerId);
|
|
getTank(customerId);
|
|
})
|
|
</script> |