Working site

This commit is contained in:
2026-01-05 08:44:20 -05:00
parent 623cbf8c4c
commit 65cfd0c463
3 changed files with 263 additions and 4 deletions

View File

@@ -0,0 +1,258 @@
<!-- src/pages/customer/profile/TankEstimation.vue -->
<template>
<div class="bg-base-100 rounded-lg p-4 border">
<h3 class="font-semibold mb-4 flex items-center gap-2">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
Fuel Tank Estimation
</h3>
<div v-if="loading" class="flex justify-center items-center py-8">
<span class="loading loading-spinner loading-lg"></span>
</div>
<div v-else-if="error" class="text-center py-4">
<p class="text-sm text-red-600">
<span class="font-medium">Estimation Error:</span> {{ error }}
</p>
</div>
<div v-else-if="estimation">
<div class="space-y-4">
<!-- Tank Level Display -->
<div>
<div class="flex justify-between items-center mb-2">
<label class="label-text font-medium">Current Tank Level</label>
<span class="text-sm text-gray-500">{{ Math.round(estimation.estimated_gallons) }} / {{ Math.round(estimation.tank_size) }} gal</span>
</div>
<div>
<progress
class="progress w-full"
:value="estimation.estimated_gallons"
:max="estimation.tank_size"
:class="{
'progress-success': getTankLevelPercentage() > 60,
'progress-warning': getTankLevelPercentage() >= 25 && getTankLevelPercentage() <= 60,
'progress-error': getTankLevelPercentage() < 25
}"
></progress>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Empty</span>
<span>{{ Math.round(getTankLevelPercentage()) }}% Full</span>
<span>Full</span>
</div>
</div>
</div>
<!-- Usage Information -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label-text font-medium">Usage Factor</label>
<div class="text-lg font-mono">{{ getScalingFactorCategory(estimation.scaling_factor) }}</div>
<div class="text-xs text-gray-500">{{ formatScalingFactor(estimation.scaling_factor) }} gallons per degree day</div>
</div>
<div>
<label class="label-text font-medium">Daily Usage</label>
<div class="text-lg font-mono">{{ calculateDailyUsage() }}</div>
<div class="text-xs text-gray-500">Gallons per day</div>
</div>
</div>
</div>
</div>
<div v-else class="text-center py-8 text-gray-500">
<svg class="w-12 h-12 mx-auto mb-4 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<p>No fuel estimation data available</p>
<p class="text-xs mt-1">Run fuel estimation to see tank levels</p>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import axios from 'axios'
import authHeader from '../../../services/auth.header'
import dayjs from 'dayjs'
interface FuelEstimation {
id: number;
customer_id: number;
total_deliveries: number;
customer_full_name: string;
account_number: string;
address: string;
estimated_gallons: number;
tank_size: number;
scaling_factor: number | null;
last_5_deliveries: Array<{
fill_date: string;
gallons_delivered: number;
price_per_gallon: number | null;
total_amount_customer: number;
}>;
last_fill?: string | null;
}
export default defineComponent({
name: 'TankEstimation',
props: {
customerId: {
type: Number,
required: true
}
},
data() {
return {
estimation: null as FuelEstimation | null,
loading: true,
error: null as string | null
}
},
mounted() {
this.fetchEstimation()
},
watch: {
customerId: {
handler(newId, oldId) {
if (newId !== oldId) {
console.log('Customer ID changed from', oldId, 'to', newId)
this.fetchEstimation()
}
},
immediate: false
}
},
methods: {
async fetchEstimation() {
this.loading = true
this.error = null
this.estimation = null // Clear previous data
try {
console.log('Fetching estimation for customer ID:', this.customerId)
// First check if customer is automatic
const customerPath = `${import.meta.env.VITE_BASE_URL}/customer/${this.customerId}`
console.log('Checking customer type:', customerPath)
const customerResponse = await axios.get(customerPath, { headers: authHeader() })
const isAutomatic = customerResponse.data.customer_automatic === 1
console.log('Customer automatic status:', isAutomatic, customerResponse.data)
let path: string
if (isAutomatic) {
// Fetch from automatic delivery API
path = `${import.meta.env.VITE_AUTO_URL}/delivery/auto/customer/${this.customerId}`
console.log('Fetching automatic data from:', path)
} else {
// Fetch from customer estimation API
path = `${import.meta.env.VITE_AUTO_URL}/fixstuff_customer/estimate_gallons/customer/${this.customerId}`
console.log('Fetching customer data from:', path)
}
const response = await axios.get(path, { headers: authHeader() })
console.log('API Response:', response.data)
if (response.data.error) {
this.error = response.data.error
console.error('API returned error:', response.data.error)
} else {
if (isAutomatic) {
// Transform automatic delivery data to match our interface
if (response.data && response.data.id) {
const autoData = response.data
console.log('Processing automatic data:', autoData)
this.estimation = {
id: autoData.id,
customer_id: autoData.customer_id,
total_deliveries: 0, // Not available in auto data
customer_full_name: autoData.customer_full_name,
account_number: autoData.account_number,
address: autoData.customer_address,
estimated_gallons: autoData.estimated_gallons_left,
tank_size: autoData.tank_size,
scaling_factor: autoData.house_factor,
last_5_deliveries: [],
last_fill: autoData.last_fill
}
console.log('Set automatic estimation:', this.estimation)
} else {
console.warn('No automatic delivery data found for customer', this.customerId)
this.error = 'No automatic delivery data available'
}
} else {
console.log('Setting customer estimation:', response.data)
this.estimation = response.data
}
}
} catch (error: any) {
console.error('Failed to fetch fuel estimation:', error)
if (error.response?.status === 404) {
this.error = 'Customer data not found'
} else if (error.response?.data?.error) {
this.error = error.response.data.error
} else {
this.error = 'Failed to load fuel estimation data'
}
} finally {
this.loading = false
}
},
getTankLevelPercentage(): number {
if (!this.estimation || !this.estimation.tank_size || this.estimation.tank_size === 0) {
return 0
}
return (this.estimation.estimated_gallons / this.estimation.tank_size) * 100
},
calculateDailyUsage(): string {
if (!this.estimation || !this.estimation.scaling_factor) {
return 'N/A'
}
// For a typical day with ~20 degree days (moderate winter day)
const typicalDegreeDays = 20
const dailyUsage = this.estimation.scaling_factor * typicalDegreeDays
return dailyUsage.toFixed(1)
},
formatScalingFactor(scalingFactor: number | null): string {
if (scalingFactor === null || scalingFactor === undefined) {
return 'N/A'
}
return scalingFactor.toFixed(2)
},
getScalingFactorCategory(scalingFactor: number | null): string {
if (scalingFactor === null || scalingFactor === undefined) {
return 'N/A'
}
if (scalingFactor < 0.05) {
return 'Very Low'
} else if (scalingFactor < 0.1) {
return 'Low'
} else if (scalingFactor < 0.2) {
return 'Normal'
} else if (scalingFactor < 0.5) {
return 'High'
} else {
return 'Very High'
}
},
formatDate(dateString: string): string {
if (!dateString) return 'N/A'
return dayjs(dateString).format('MMM D, YYYY')
}
}
})
</script>

View File

@@ -98,6 +98,8 @@
</div>
</div>
<TankEstimation :customer-id="customer.id" />
<CustomerComments :comments="comments" @add-comment="onSubmitSocial" @delete-comment="deleteCustomerSocial" />
<CustomerStats :stats="customer_stats" :last_delivery="customer_last_delivery" />
<TankInfo :customer_id="customer.id" :tank="customer_tank" :description="customer_description" />
@@ -264,6 +266,7 @@ import EquipmentParts from './profile/EquipmentParts.vue';
import CreditCards from './profile/CreditCards.vue';
import CustomerComments from './profile/CustomerComments.vue';
import HistoryTabs from './profile/HistoryTabs.vue';
import TankEstimation from './TankEstimation.vue';
L.Icon.Default.mergeOptions({
@@ -352,6 +355,7 @@ export default defineComponent({
CreditCards,
CustomerComments,
HistoryTabs,
TankEstimation,
},
data() {
return {

View File

@@ -50,9 +50,6 @@
</div>
<div class="grid grid-cols-12">
<div class="col-span-6 ">
<div class="col-span-12 pl-5">Auburn Oil</div>
@@ -90,7 +87,7 @@
<div class="col-span-12 h-7 pl-4 pt-2"></div>
<div class="col-span-12 h-7 pl-4 pt-2"></div>
<div class="col-span-12 h-7 pl-4 pt-2" ></div>
<div class="col-span-12 h-7 pl-4 pt-2" >{{todays_price }}</div>
<div class="col-span-12 h-7 pl-4 pt-2" ></div>
<div class="col-span-12 h-7 pl-4 pt-4" > </div>
<div class="col-span-12 h-7 pt-6"></div>
<div class="col-span-12 h-7"></div>