diff --git a/package-lock.json b/package-lock.json index 91e7caa..24a3be2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,14 +19,12 @@ "dayjs": "^1.11.13", "html-to-image": "^1.11.11", "html2canvas": "^1.4.1", - "moment": "^2.30.1", "pinia": "^2.3.1", "v-pagination-3": "^0.1.7", "vue": "^3.3.11", "vue-debounce": "^5.0.0", "vue-router": "^4.2.5", - "vue3-pdfmake": "^2.2.0", - "vuelidate": "^0.7.7" + "vue3-pdfmake": "^2.2.0" }, "devDependencies": { "@types/leaflet": "^1.9.12", @@ -4482,14 +4480,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9129,15 +9119,6 @@ } } }, - "node_modules/vuelidate": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/vuelidate/-/vuelidate-0.7.7.tgz", - "integrity": "sha512-pT/U2lDI67wkIqI4tum7cMSIfGcAMfB+Phtqh2ttdXURwvHRBJEAQ0tVbUsW9Upg83Q5QH59bnCoXI7A9JDGnA==", - "engines": { - "node": ">= 4.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 1759452..e87f64b 100755 --- a/package.json +++ b/package.json @@ -20,14 +20,12 @@ "dayjs": "^1.11.13", "html-to-image": "^1.11.11", "html2canvas": "^1.4.1", - "moment": "^2.30.1", "pinia": "^2.3.1", "v-pagination-3": "^0.1.7", "vue": "^3.3.11", "vue-debounce": "^5.0.0", "vue-router": "^4.2.5", - "vue3-pdfmake": "^2.2.0", - "vuelidate": "^0.7.7" + "vue3-pdfmake": "^2.2.0" }, "devDependencies": { "@types/leaflet": "^1.9.12", diff --git a/src/components/PaymentAuthorizePopup.vue b/src/components/PaymentAuthorizePopup.vue index 32456b5..a0a57d5 100644 --- a/src/components/PaymentAuthorizePopup.vue +++ b/src/components/PaymentAuthorizePopup.vue @@ -116,9 +116,17 @@ \ No newline at end of file +const handleClickOutside = (event: MouseEvent) => { + const container = searchContainer.value; + const searchInput = document.getElementById('customer-search-input'); + + if (container && !container.contains(event.target as Node) && searchInput && !searchInput.contains(event.target as Node)) { + searchStore.clearSearch(); + } +} + +// Lifecycle +onMounted(() => { + document.addEventListener('mousedown', handleClickOutside); +}) + +onBeforeUnmount(() => { + document.removeEventListener('mousedown', handleClickOutside); +}) + diff --git a/src/constants/status.ts b/src/constants/status.ts new file mode 100644 index 0000000..ddbd5e7 --- /dev/null +++ b/src/constants/status.ts @@ -0,0 +1,95 @@ +/** + * EAMCO Office Frontend Status Constants + * + * This file contains all status code constants used throughout the frontend + * to eliminate magic numbers and improve code maintainability. + */ + +export const DELIVERY_STATUS = { + WAITING: 0, + CANCELLED: 1, + OUT_FOR_DELIVERY: 2, + TOMORROW: 3, + PARTIAL_DELIVERY: 4, + ISSUE: 5, + UNKNOWN: 6, + PENDING_PAYMENT: 9, + FINALIZED: 10, + DELIVERED: 11, // New: Replaces previous use of 1 for delivered +} as const; + +export const PAYMENT_STATUS = { + UNPAID: 0, + PRE_AUTHORIZED: 1, + PROCESSING: 2, + PAID: 3, + FAILED: 4, +} as const; + +export const AUTO_STATUS = { + DEFAULT: 0, + WILL_CALL: 1, + READY_FOR_FINALIZATION: 3, +} as const; + +export const TRANSACTION_STATUS = { + APPROVED: 0, + DECLINED: 1, +} as const; + +export const CUSTOMER_AUTOMATIC_STATUS = { + WILL_CALL: 0, + AUTOMATIC: 1, +} as const; + +// Helper functions for type-safe status access +export type DeliveryStatusType = typeof DELIVERY_STATUS[keyof typeof DELIVERY_STATUS]; +export type PaymentStatusType = typeof PAYMENT_STATUS[keyof typeof PAYMENT_STATUS]; +export type AutoStatusType = typeof AUTO_STATUS[keyof typeof AUTO_STATUS]; +export type TransactionStatusType = typeof TRANSACTION_STATUS[keyof typeof TRANSACTION_STATUS]; +export type CustomerAutomaticStatus = typeof CUSTOMER_AUTOMATIC_STATUS[keyof typeof CUSTOMER_AUTOMATIC_STATUS]; + +// Utility functions for status display +export function getDeliveryStatusLabel(status: DeliveryStatusType): string { + switch (status) { + case DELIVERY_STATUS.WAITING: return 'Waiting'; + case DELIVERY_STATUS.CANCELLED: return 'Cancelled'; + case DELIVERY_STATUS.OUT_FOR_DELIVERY: return 'Out for Delivery'; + case DELIVERY_STATUS.TOMORROW: return 'Tomorrow'; + case DELIVERY_STATUS.PARTIAL_DELIVERY: return 'Partial Delivery'; + case DELIVERY_STATUS.ISSUE: return 'Issue'; + case DELIVERY_STATUS.UNKNOWN: return 'Unknown'; + case DELIVERY_STATUS.PENDING_PAYMENT: return 'Pending Payment'; + case DELIVERY_STATUS.FINALIZED: return 'Finalized'; + case DELIVERY_STATUS.DELIVERED: return 'Delivered'; + default: return 'N/A'; + } +} + +export function getPaymentStatusLabel(status: PaymentStatusType): string { + switch (status) { + case PAYMENT_STATUS.UNPAID: return 'Unpaid'; + case PAYMENT_STATUS.PRE_AUTHORIZED: return 'Pre-authorized'; + case PAYMENT_STATUS.PROCESSING: return 'Processing'; + case PAYMENT_STATUS.PAID: return 'Paid'; + case PAYMENT_STATUS.FAILED: return 'Failed'; + default: return 'Unknown'; + } +} + +export function getAutoStatusLabel(status: AutoStatusType): string { + switch (status) { + case AUTO_STATUS.DEFAULT: return 'Default'; + case AUTO_STATUS.WILL_CALL: return 'Will Call'; + case AUTO_STATUS.READY_FOR_FINALIZATION: return 'Ready for Finalization'; + default: return 'Unknown'; + } +} + +export function getTransactionStatusLabel(status: TransactionStatusType): string { + switch (status) { + case TRANSACTION_STATUS.APPROVED: return 'Approved'; + case TRANSACTION_STATUS.DECLINED: return 'Declined'; + default: return 'Unknown'; + } +} \ No newline at end of file diff --git a/src/layouts/headers/headerauth.vue b/src/layouts/headers/headerauth.vue index c6dfbc1..d441baa 100755 --- a/src/layouts/headers/headerauth.vue +++ b/src/layouts/headers/headerauth.vue @@ -212,9 +212,9 @@ - diff --git a/src/layouts/headers/headernoauth.vue b/src/layouts/headers/headernoauth.vue index a6a26ee..787857e 100755 --- a/src/layouts/headers/headernoauth.vue +++ b/src/layouts/headers/headernoauth.vue @@ -15,31 +15,19 @@ - \ No newline at end of file diff --git a/src/pages/Index.vue b/src/pages/Index.vue index f1e22a4..72ed099 100755 --- a/src/pages/Index.vue +++ b/src/pages/Index.vue @@ -110,200 +110,185 @@ - +const employee = ref({ + id: '', + user_id: '', + employee_last_name: "", + employee_first_name: "", + employee_town: "", + employee_address: "", + employee_apt: "", + employee_zip: "", + employee_birthday: "", + employee_phone_number: "", + employee_start_date: "", + employee_end_date: "", + employee_type: '', + employee_state: '', +}) +const total_gallons_past_week = ref(0) +const total_profit_past_week = ref(0) +const total_deliveries = ref(0) +const loaded = ref(false) - - diff --git a/src/pages/admin/routes.ts b/src/pages/admin/routes.ts index e7ef581..9cf8b6c 100755 --- a/src/pages/admin/routes.ts +++ b/src/pages/admin/routes.ts @@ -1,10 +1,10 @@ -import OilPrice from '../admin/oilprice.vue'; +const OilPrice = () => import('../admin/oilprice.vue'); -import Promo from '../admin/promo/promo.vue'; -import PromoCreate from '../admin/promo/create.vue'; -import PromoEdit from '../admin/promo/edit.vue'; +const Promo = () => import('../admin/promo/promo.vue'); +const PromoCreate = () => import('../admin/promo/create.vue'); +const PromoEdit = () => import('../admin/promo/edit.vue'); const adminRoutes = [ @@ -35,5 +35,3 @@ const adminRoutes = [ export default adminRoutes //sourceMappingURL=index.ts.map - - diff --git a/src/pages/auth/routes.ts b/src/pages/auth/routes.ts index ead6f84..112d2ed 100755 --- a/src/pages/auth/routes.ts +++ b/src/pages/auth/routes.ts @@ -1,7 +1,7 @@ -import Login from '../auth/login.vue'; -import Register from '../auth/register.vue'; -import changePassword from '../auth/changepassword.vue'; -import lostPassword from '../auth/lostpassword.vue'; +const Login = () => import('../auth/login.vue'); +const Register = () => import('../auth/register.vue'); +const changePassword = () => import('../auth/changepassword.vue'); +const lostPassword = () => import('../auth/lostpassword.vue'); const authRoutes = [ { @@ -37,4 +37,4 @@ const authRoutes = [ }, ] -export default authRoutes; \ No newline at end of file +export default authRoutes; diff --git a/src/pages/authorize/PaymentForm.vue b/src/pages/authorize/PaymentForm.vue index 5227d3a..0c547f3 100644 --- a/src/pages/authorize/PaymentForm.vue +++ b/src/pages/authorize/PaymentForm.vue @@ -1,5 +1,5 @@ - \ No newline at end of file +// Reactive data +const directCharge = ref({ + cardNumber: '', + expirationDate: '', + cvv: '', + amount: 0, +}) + +const authorization = ref({ + cardNumber: '', + expirationDate: '', + cvv: '', + amount: 0, +}) + +const capture = ref({ + amount: 0, +}) + +const authorizedTransactionId = ref(null) +const transactionResult = ref(null) +const customerId = ref(1) // Assuming a customer with ID 1 exists for this example + +// Functions +const submitDirectCharge = async () => { + try { + const response = await axios.post(`${API_URL}/charge/?customer_id=${customerId.value}`, { + card_number: directCharge.value.cardNumber, + expiration_date: directCharge.value.expirationDate, + cvv: directCharge.value.cvv, + amount: directCharge.value.amount, + transaction_type: 'charge' + }); + transactionResult.value = response.data; + } catch (error) { + console.error(error); + transactionResult.value = null; + } +} + +const submitAuthorization = async () => { + try { + const response = await axios.post(`${API_URL}/authorize/?customer_id=${customerId.value}`, { + card_number: authorization.value.cardNumber, + expiration_date: authorization.value.expirationDate, + cvv: authorization.value.cvv, + amount: authorization.value.amount, + transaction_type: 'auth' + }); + transactionResult.value = response.data; + if (response.data.status === 'authorized') { + authorizedTransactionId.value = response.data.auth_net_transaction_id; + capture.value.amount = authorization.value.amount; // Pre-fill capture amount + } + } catch (error) { + console.error(error); + transactionResult.value = null; + } +} + +const submitCapture = async () => { + try { + const response = await axios.post(`${API_URL}/capture/`, { + amount: capture.value.amount, + auth_net_transaction_id: authorizedTransactionId.value + }); + transactionResult.value = response.data; + } catch (error) { + console.error(error); + transactionResult.value = null; + } +} + diff --git a/src/pages/automatic/home.vue b/src/pages/automatic/home.vue index 0967a62..373626c 100755 --- a/src/pages/automatic/home.vue +++ b/src/pages/automatic/home.vue @@ -177,138 +177,118 @@ - diff --git a/src/pages/automatic/routes.ts b/src/pages/automatic/routes.ts index c65b10a..99ca52f 100755 --- a/src/pages/automatic/routes.ts +++ b/src/pages/automatic/routes.ts @@ -1,7 +1,7 @@ -import AutomaticHome from './home.vue'; -import AutomaticView from './view.vue'; +const AutomaticHome = () => import('./home.vue'); +const AutomaticView = () => import('./view.vue'); const autoRoutes = [ { diff --git a/src/pages/automatic/view.vue b/src/pages/automatic/view.vue index 0a14857..826209a 100644 --- a/src/pages/automatic/view.vue +++ b/src/pages/automatic/view.vue @@ -54,26 +54,26 @@ - - - - - Payment Status - - Unpaid - Pre-authorized - Processing - Paid - Failed - Unknown - - + + + + + Payment Status + + Unpaid + Pre-authorized + Processing + Paid + Failed + Unknown + + Fill Date {{ autoTicket.fill_date }} @@ -150,8 +150,8 @@ Status: - - {{ transaction.status === 0 ? 'Approved' : 'Declined' }} + + {{ transaction.status === TRANSACTION_STATUS.APPROVED ? 'Approved' : 'Declined' }} @@ -242,25 +242,21 @@ import { defineComponent } from 'vue' import axios from 'axios' import authHeader from '../../services/auth.header' - -interface UserCard { - id: number; - last_four: string; - type_of_card: string; - expiration_month: number; - expiration_year: number; - name_on_card: string; - card_number: string; - security_number: string; - main_card?: boolean; -} +import { + PAYMENT_STATUS, + AUTO_STATUS, + TRANSACTION_STATUS, + getPaymentStatusLabel, + getTransactionStatusLabel +} from '../../constants/status'; import Header from '../../layouts/headers/headerauth.vue' import SideBar from '../../layouts/sidebar/sidebar.vue' import Footer from '../../layouts/footers/footer.vue' import useValidate from "@vuelidate/core"; import { notify } from "@kyvg/vue3-notification" -import moment from 'moment'; +import dayjs from 'dayjs'; +import {AutoDelivery, Customer, AuthorizeTransaction, CreditCard} from '../../types/models'; export default defineComponent({ name: 'automaticDeliveryView', @@ -273,62 +269,25 @@ export default defineComponent({ data() { return { v$: useValidate(), - autoTicket: { - id: 0, - customer_id: 0, - account_number: '', - customer_town: '', - customer_state: 0, - customer_address: '', - customer_zip: '', - customer_full_name: '', - customer_apt: '', - fill_date: '', - oil_prices_id: 0, - gallons_delivered: '', - price_per_gallon: '', - total_amount_customer: '', - payment_type: 0, - payment_card_id: null, - payment_status: null, - open_ticket_id: null, - } as any, - autoDelivery: { - id: 0, - customer_id: 0, - account_number: '', - customer_town: '', - customer_state: 0, - customer_address: '', - customer_zip: '', - customer_full_name: '', - last_fill: '', - days_since_last_fill: 0, - last_updated: '', - estimated_gallons_left: 0, - estimated_gallons_left_prev_day: 0, - tank_height: '', - tank_size: '', - house_factor: 0, - auto_status: 0, - open_ticket_id: null, - }, - customer: { - id: 0, - user_id: 0, - customer_first_name: '', - customer_last_name: '', - customer_town: '', - customer_state: 0, - customer_address: '', - customer_zip: '', - customer_apt: '', - customer_home_type: 0, - customer_phone_number: '', - }, - transaction: null as any, + autoTicket: {} as any, + autoDelivery: {} as AutoDelivery, + customer: {} as Customer, + transaction: {} as AuthorizeTransaction, userCardfound: false, - userCard: {} as UserCard, + userCard: {} as CreditCard, + } + }, + + computed: { + // Expose constants to template + PAYMENT_STATUS() { + return PAYMENT_STATUS; + }, + AUTO_STATUS() { + return AUTO_STATUS; + }, + TRANSACTION_STATUS() { + return TRANSACTION_STATUS; } }, @@ -339,10 +298,11 @@ export default defineComponent({ methods: { format_date(value: string) { if (value) { - return moment(String(value)).format('LLLL') + return dayjs(String(value)).format('LLLL') } }, - getTypeColor(transactionType: number) { + getTypeColor(transactionType: number | undefined) { + if (transactionType === undefined) return 'text-gray-600'; switch (transactionType) { case 1: return 'text-blue-600'; // Auth case 0: return 'text-orange-600'; // Charge @@ -360,7 +320,7 @@ export default defineComponent({ } }, - getCustomer(customerId: number) { + getCustomer(customerId: number | undefined) { if (!customerId) return; const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId}`; axios.get(path, { withCredentials: true }) @@ -384,12 +344,12 @@ export default defineComponent({ this.userCard = response.data; this.userCardfound = true; } else { - this.userCard = {} as UserCard; + this.userCard = {} as CreditCard; this.userCardfound = false; } }) .catch((_error: any) => { - this.userCard = {} as UserCard; + this.userCard = {} as CreditCard; this.userCardfound = false; }); }, @@ -438,7 +398,7 @@ export default defineComponent({ }) .catch((error: any) => { console.error("No transaction found for delivery:", error); - this.transaction = null; + this.transaction = {} as AuthorizeTransaction; }); }, }, diff --git a/src/pages/card/routes.ts b/src/pages/card/routes.ts index d9e7184..17fec31 100755 --- a/src/pages/card/routes.ts +++ b/src/pages/card/routes.ts @@ -1,8 +1,8 @@ -import CardHome from '../card/home.vue'; -import AddCardCreate from '../card/addcard.vue'; -import EditCard from "./editcard.vue"; +const CardHome = () => import('../card/home.vue'); +const AddCardCreate = () => import('../card/addcard.vue'); +const EditCard = () => import("./editcard.vue"); const cardRoutes = [ diff --git a/src/pages/customer/ServicePlanEdit.vue b/src/pages/customer/ServicePlanEdit.vue index 95ba63a..43d64da 100644 --- a/src/pages/customer/ServicePlanEdit.vue +++ b/src/pages/customer/ServicePlanEdit.vue @@ -120,193 +120,189 @@ - diff --git a/src/pages/customer/create.vue b/src/pages/customer/create.vue index edfdf9c..b9bfe74 100755 --- a/src/pages/customer/create.vue +++ b/src/pages/customer/create.vue @@ -140,120 +140,114 @@ - \ No newline at end of file + }); +} + +const onSubmit = () => { + v$.value.$validate(); // Trigger validation + if (!v$.value.$error) { + // If validation passes, submit the form + CreateCustomer(CreateCustomerForm.value); + } else { + // If validation fails, show a single notification + notify({ title: "Validation Error", text: "Please fill out all required fields correctly.", type: "error" }); + console.log("Form validation failed."); + } +} + +// Lifecycle +onMounted(() => { + userStatus(); + getCustomerTypeList(); + getStatesList(); +}) + diff --git a/src/pages/customer/edit.vue b/src/pages/customer/edit.vue index 9cb4924..d598d1a 100755 --- a/src/pages/customer/edit.vue +++ b/src/pages/customer/edit.vue @@ -162,235 +162,227 @@ - diff --git a/src/pages/customer/home.vue b/src/pages/customer/home.vue index a18559a..b3e26c0 100755 --- a/src/pages/customer/home.vue +++ b/src/pages/customer/home.vue @@ -105,107 +105,92 @@ - diff --git a/src/pages/customer/list.vue b/src/pages/customer/list.vue index e3ac36c..1c21934 100644 --- a/src/pages/customer/list.vue +++ b/src/pages/customer/list.vue @@ -16,11 +16,11 @@ {{ customer.account_number }} - {{ customer.first_name }} - {{ customer.last_name }} - {{ customer.address }} - {{ customer.town }} - {{ customer.phone_number }} + {{ customer.customer_first_name }} + {{ customer.customer_last_name }} + {{ customer.customer_address }} + {{ customer.customer_town }} + {{ customer.customer_phone_number }} @@ -28,50 +28,38 @@ - diff --git a/src/pages/delivery/update_tickets/finalize_ticket_auto.vue b/src/pages/delivery/update_tickets/finalize_ticket_auto.vue index 1215d1e..4e00aa6 100644 --- a/src/pages/delivery/update_tickets/finalize_ticket_auto.vue +++ b/src/pages/delivery/update_tickets/finalize_ticket_auto.vue @@ -78,7 +78,7 @@ Payment Method No card on file for this customer. - + {{ userCard.name_on_card }} {{ userCard.type_of_card }} @@ -119,471 +119,458 @@ - - - + .then((response: any) => { + if (response.data.ok) { + user.value = response.data.user; + user.value.id = response.data.user_id; + } + }) +} + +const getPaymentCard = (card_id: any) => { + let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + + if (response.data.userCard.card_number === ''){ + userCard.value = null; + userCardfound.value = false; + } + else{ + userCard.value = response.data; + userCardfound.value = true; + } + FinalizeOilOrderForm.value.userCards = response.data.id + }) + .catch(() => { + }); +} + +const getPaymentCards = (user_id: any) => { + let path = import.meta.env.VITE_BASE_URL + "/payment/cards/" + user_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + userCards.value = response.data; + if (userCards.value && userCards.value.length > 0) { + userCardfound.value = true; + userCard.value = userCards.value.find((card: any) => card.main_card) || userCards.value[0]; + } + }) + .catch(() => { + }); +} + +const getCustomer = (user_id: any) => { + if (!user_id || user_id === 'undefined') return; + let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + customer.value = response.data; + if (customer.value.id > 0) { + getPaymentCards(customer.value.user_id || customer.value.id); + } + }) + .catch(() => { + notify({ + title: "Error", + text: "Could not find customer", + type: "error", + }); + customer.value = { id: 0, user_id: 0, customer_address: '', customer_first_name: '', customer_last_name: '', customer_town: '', customer_state: 0, customer_zip: '', customer_apt: '', customer_home_type: 0, customer_phone_number: '' }; + }); +} + +const getCustomerDescription = (user_id: any) => { + let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + customerDescription.value = response.data; + loaded.value = true + }) + .catch(() => { + notify({ + title: "Error", + text: "Could not find customer", + type: "error", + }); + }); +} + +const getAutoTicket = (delivery_id: any) => { + let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + autoTicket.value = response.data; + getCustomer(autoTicket.value.customer_id) + + getAutoDelivery(autoTicket.value.id) + getCustomerDescription(autoTicket.value.customer_id) + + }) + .catch(() => { + notify({ + title: "Error", + text: "Could not get automatic", + type: "error", + }); + }); +} + +const getAutoDelivery = (delivery_id: any) => { + let path = import.meta.env.VITE_AUTO_URL + "/delivery/finddelivery/" + delivery_id; + axios({ + method: "get", + url: path, + withCredentials: true, + }) + .then((response: any) => { + + autoDelivery.value = response.data; + getCustomer(autoDelivery.value.customer_id) + getCustomerDescription(autoDelivery.value.customer_id) + + }) + .catch(() => { + notify({ + title: "Error", + text: "Could not get automatic", + type: "error", + }); + }); +} + +const today_price_oil = () => { + let path = import.meta.env.VITE_BASE_URL + '/info/price/oil' + axios({ + method: "get", + url: path, + withCredentials: true, + headers: authHeader(), + }) + .then((response: any) => { + today_oil_price.value = response.data.price_for_customer; + }) +} + +const UpdateAuto = (payload: { + gallons: string, + delivery_id: string, +}) => { + let path = import.meta.env.VITE_AUTO_URL + "/confirm/delivery" + axios({ + method: "put", + url: path, + data: payload, + withCredentials: true, + headers: authHeader(), + }) + .then((response: any) => { + if (response.data.ok) { + notify({ + text: 'Update', + type: 'postive', + title: 'top' + }) + router.push({ name: "auto" }); + } + else { + notify({ + text: 'Auto Failure', + type: 'negative', + title: 'Update' + }) + } + }) +} + +const ConfirmAuto = (payload: { + gallons_delivered: string, +}) => { + let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/create/" + autoDelivery.value.id; + axios({ + method: "post", + url: path, + data: payload, + withCredentials: true, + headers: authHeader(), + }) + .then((response: any) => { + if (response.data) { + + notify({ + title: "Success", + text: "Auto Delivered", + type: "success", + }); + CreateTransaction(response.data['0']['auto_ticket_id']) + updateTransactionDelivery(autoDelivery.value.id, response.data['0']['auto_ticket_id']) + router.push({ name: "payAutoCapture", params: { id: response.data['0']['auto_ticket_id'] } }); + + } + if (response.data.error) { + notify({ + title: "Error", + text: "Could not finalize auto", + type: "error", + }); + router.push("auto"); + } + }) +} + +const closeTicket = (ticketId: number) => { + let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/close_ticket/" + ticketId; + axios({ + method: "put", + url: path, + withCredentials: true, + headers: authHeader(), + }) + .then(() => { + // Ticket closed successfully + }) +} + +const UpdateDeliveredAuto = (payload: { + gallons_delivered: string, +}) => { + console.log(autoDelivery.value) + let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/update/" + autoDelivery.value.id; + axios({ + method: "put", + url: path, + data: payload, + withCredentials: true, + headers: authHeader(), + }) + .then((response: any) => { + if (response.data) { + notify({ + title: "Success", + text: "Auto Updated", + type: "success", + }); + // Removed redirect from here, will handle in onSubmit + } + }) +} + +const updateTransactionDelivery = (current_delivery_id: any, new_delivery_id: any) => { + const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/auto/transaction/delivery/${current_delivery_id}/update/${new_delivery_id}`; + axios.put(path, {}, { withCredentials: true, headers: authHeader() }) + .then(() => console.log("Transaction auto_id updated")) + .catch(() => console.error("Error updating transaction auto_id")); +} + +const CreateTransaction = (auto_ticket_id: string) => { + let path = import.meta.env.VITE_MONEY_URL + "/delivery/add/auto/" + auto_ticket_id; + axios({ + method: "post", + url: path, + withCredentials: true, + headers: authHeader(), + }) + .then((response: any) => { + if (response.status == 201) { + notify({ + message: 'Confirmed Transaction', + type: 'positive', + position: 'top' + }) + } + else { + notify({ + message: 'Form Error', + type: 'negative', + position: 'top' + }) + } + }) +} + +const onSubmit = () => { + let payload = { + gallons_delivered: FinalizeOilOrderForm.value.gallons_delivered, + }; + UpdateDeliveredAuto(payload); + if (autoTicket.value.payment_status == '1') { + // Pre-authorized: redirect to capture page + router.push({ name: "payAutoCapture", params: { id: autoTicket.value.id } }); + } else { + // Fully charged: close ticket + if (autoDelivery.value.open_ticket_id) { + closeTicket(autoDelivery.value.open_ticket_id); + } + router.push({ name: "auto" }); +} + + + diff --git a/src/pages/delivery/update_tickets/finalize_ticket_auto_nocc.vue b/src/pages/delivery/update_tickets/finalize_ticket_auto_nocc.vue index c6c70b4..075869d 100644 --- a/src/pages/delivery/update_tickets/finalize_ticket_auto_nocc.vue +++ b/src/pages/delivery/update_tickets/finalize_ticket_auto_nocc.vue @@ -78,7 +78,7 @@ Payment Method No card on file for this customer. - + {{ userCard.name_on_card }} {{ userCard.type_of_card }} @@ -121,8 +121,9 @@ - diff --git a/src/pages/delivery/update_tickets/missing_data_home.vue b/src/pages/delivery/update_tickets/missing_data_home.vue index 2c7b8eb..364f8cc 100755 --- a/src/pages/delivery/update_tickets/missing_data_home.vue +++ b/src/pages/delivery/update_tickets/missing_data_home.vue @@ -71,72 +71,54 @@ - diff --git a/src/pages/delivery/viewstatus/cancelled.vue b/src/pages/delivery/viewstatus/cancelled.vue index 9189bee..f45ec7a 100755 --- a/src/pages/delivery/viewstatus/cancelled.vue +++ b/src/pages/delivery/viewstatus/cancelled.vue @@ -121,104 +121,93 @@ - diff --git a/src/pages/delivery/viewstatus/delivered.vue b/src/pages/delivery/viewstatus/delivered.vue index 512cd2f..aac649c 100755 --- a/src/pages/delivery/viewstatus/delivered.vue +++ b/src/pages/delivery/viewstatus/delivered.vue @@ -121,105 +121,93 @@ - diff --git a/src/pages/delivery/viewstatus/finalized.vue b/src/pages/delivery/viewstatus/finalized.vue index cd90baa..6840919 100644 --- a/src/pages/delivery/viewstatus/finalized.vue +++ b/src/pages/delivery/viewstatus/finalized.vue @@ -121,109 +121,95 @@ - + .then((response: any) => { + if (response.data.ok) { + user.value = response.data.user; + } + }) + .catch(() => { + user.value = null + }) +} + +const get_oil_orders = async (pageVal: number) => { + try { + const response = await deliveryService.getFinalized(pageVal) + deliveries.value = response.data || [] + } catch (error) { + console.error('Error fetching finalized deliveries:', error) + deliveries.value = [] + } +} + +const deleteCall = (delivery_id: any) => { + let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id; + axios({ + method: 'delete', + url: path, + headers: authHeader(), + }).then((response: any) => { + if (response.data.ok) { + notify({ + title: "Success", + text: "deleted delivery", + type: "success", + }); + getPage(page.value) + } else { + notify({ + title: "Failure", + text: "error deleting delivery", + type: "success", + }); + } + }) +} + +// Lifecycle +onMounted(() => { + userStatus() + getPage(page.value) +}) + \ No newline at end of file diff --git a/src/pages/delivery/viewstatus/todaysdeliveries.vue b/src/pages/delivery/viewstatus/todaysdeliveries.vue index 4671001..d7f9f45 100755 --- a/src/pages/delivery/viewstatus/todaysdeliveries.vue +++ b/src/pages/delivery/viewstatus/todaysdeliveries.vue @@ -164,174 +164,116 @@ - diff --git a/src/pages/delivery/viewstatus/tommorrow.vue b/src/pages/delivery/viewstatus/tommorrow.vue index 6801da3..9431e3a 100644 --- a/src/pages/delivery/viewstatus/tommorrow.vue +++ b/src/pages/delivery/viewstatus/tommorrow.vue @@ -157,173 +157,164 @@ - diff --git a/src/pages/delivery/viewstatus/waiting.vue b/src/pages/delivery/viewstatus/waiting.vue index dad3325..9c00fac 100755 --- a/src/pages/delivery/viewstatus/waiting.vue +++ b/src/pages/delivery/viewstatus/waiting.vue @@ -137,127 +137,112 @@ - diff --git a/src/pages/employee/changepassword.vue b/src/pages/employee/changepassword.vue index ed4f867..d70c1bb 100644 --- a/src/pages/employee/changepassword.vue +++ b/src/pages/employee/changepassword.vue @@ -62,6 +62,7 @@ import useValidate from "@vuelidate/core"; import { required, minLength, helpers } from "@vuelidate/validators"; import Header from "../../layouts/headers/headerauth.vue"; import authHeader from "../../services/auth.header"; +import {Employee} from '../../types/models'; export default defineComponent({ name: "EmployeeChangePassword", @@ -74,7 +75,7 @@ export default defineComponent({ user: null, user_admin: 0, loaded: false, - employee: null as any, + employee: {} as Employee, ChangePasswordForm: { new_password: "", password_confirm: "", diff --git a/src/pages/employee/home.vue b/src/pages/employee/home.vue index 89afec2..9e4863c 100755 --- a/src/pages/employee/home.vue +++ b/src/pages/employee/home.vue @@ -45,7 +45,8 @@ {{ person.employee_phone_number }} - Edit + Edit + Edit View Change Password @@ -73,7 +74,7 @@ {{ person.employee_phone_number }} - Edit + Edit View Change Password @@ -97,6 +98,7 @@ import axios from 'axios' import authHeader from '../../services/auth.header' import PaginationComp from '../../components/pagination.vue' import Footer from '../../layouts/footers/footer.vue' +import {Employee, User} from '../../types/models' export default defineComponent({ name: 'EmployeeHome', @@ -105,8 +107,8 @@ export default defineComponent({ }, data() { return { - user: {} as any, - employees: [] as any[], + user: {} as User, + employees: [] as Employee[], page: 1, perPage: 50, recordsLength: 0, @@ -136,7 +138,7 @@ export default defineComponent({ } }) .catch(() => { - this.user = null; + this.user = {} as User; }); }, // --- METHOD CORRECTED TO MATCH YOUR SIMPLE ARRAY API RESPONSE --- diff --git a/src/pages/employee/routes.ts b/src/pages/employee/routes.ts index f909561..57e772a 100755 --- a/src/pages/employee/routes.ts +++ b/src/pages/employee/routes.ts @@ -1,10 +1,10 @@ -import EmployeeHome from '../employee/home.vue'; -import EmployeeCreate from "../employee/create.vue"; -import EmployeeEdit from "../employee/edit.vue"; -import EmployeeProfile from "../employee/profile/home.vue"; -import EmployeeChangePassword from "../employee/changepassword.vue"; +const EmployeeHome = () => import('../employee/home.vue'); +const EmployeeCreate = () => import("../employee/create.vue"); +const EmployeeEdit = () => import("../employee/edit.vue"); +const EmployeeProfile = () => import("../employee/profile/home.vue"); +const EmployeeChangePassword = () => import("../employee/changepassword.vue"); const employeeRoutes = [ { diff --git a/src/pages/money/routes.ts b/src/pages/money/routes.ts index d0b06a7..e166998 100644 --- a/src/pages/money/routes.ts +++ b/src/pages/money/routes.ts @@ -1,6 +1,6 @@ -import MoneyYear from '../money/profit_year.vue'; +const MoneyYear = () => import('../money/profit_year.vue'); diff --git a/src/pages/pay/auto/authorize_precharge_autho.vue b/src/pages/pay/auto/authorize_precharge_autho.vue index 9abe235..5796d6d 100644 --- a/src/pages/pay/auto/authorize_precharge_autho.vue +++ b/src/pages/pay/auto/authorize_precharge_autho.vue @@ -202,463 +202,468 @@ - diff --git a/src/pages/pay/auto/capture_authorize_autho.vue b/src/pages/pay/auto/capture_authorize_autho.vue index 205b4cb..80a2ae8 100644 --- a/src/pages/pay/auto/capture_authorize_autho.vue +++ b/src/pages/pay/auto/capture_authorize_autho.vue @@ -237,341 +237,349 @@ - diff --git a/src/pages/pay/oil/authorize_preauthcharge.vue b/src/pages/pay/oil/authorize_preauthcharge.vue index 94833ef..d3a25bf 100644 --- a/src/pages/pay/oil/authorize_preauthcharge.vue +++ b/src/pages/pay/oil/authorize_preauthcharge.vue @@ -172,501 +172,503 @@ - diff --git a/src/pages/pay/oil/capture_authorize.vue b/src/pages/pay/oil/capture_authorize.vue index db919eb..d8d0d9f 100644 --- a/src/pages/pay/oil/capture_authorize.vue +++ b/src/pages/pay/oil/capture_authorize.vue @@ -278,403 +278,409 @@ - diff --git a/src/pages/pay/oil/pay_oil.vue b/src/pages/pay/oil/pay_oil.vue index f5fcc0b..b1a9c63 100755 --- a/src/pages/pay/oil/pay_oil.vue +++ b/src/pages/pay/oil/pay_oil.vue @@ -335,502 +335,480 @@ - diff --git a/src/pages/pay/routes.ts b/src/pages/pay/routes.ts index 7e95409..64a45f0 100755 --- a/src/pages/pay/routes.ts +++ b/src/pages/pay/routes.ts @@ -1,13 +1,13 @@ -import PayOil from './oil/pay_oil.vue'; -import AuthorizePreauthCharge from './oil/authorize_preauthcharge.vue'; -import CaptureAuthorize from './oil/capture_authorize.vue'; -import PayService from './service/pay_service.vue'; -import AuthorizeServicePreauthCharge from './service/authorize_preauthcharge.vue'; -import ChargeServiceAuthorize from './service/capture_authorize.vue'; -import AuthorizePrechargeAutho from './auto/authorize_precharge_autho.vue'; -import CaptureAuthorizeAutho from './auto/capture_authorize_autho.vue'; +const PayOil = () => import('./oil/pay_oil.vue'); +const AuthorizePreauthCharge = () => import('./oil/authorize_preauthcharge.vue'); +const CaptureAuthorize = () => import('./oil/capture_authorize.vue'); +const PayService = () => import('./service/pay_service.vue'); +const AuthorizeServicePreauthCharge = () => import('./service/authorize_preauthcharge.vue'); +const ChargeServiceAuthorize = () => import('./service/capture_authorize.vue'); +const AuthorizePrechargeAutho = () => import('./auto/authorize_precharge_autho.vue'); +const CaptureAuthorizeAutho = () => import('./auto/capture_authorize_autho.vue'); const payRoutes = [ // This is for oil delivery diff --git a/src/pages/pay/service/authorize_preauthcharge.vue b/src/pages/pay/service/authorize_preauthcharge.vue index 21ac37e..72f2daa 100644 --- a/src/pages/pay/service/authorize_preauthcharge.vue +++ b/src/pages/pay/service/authorize_preauthcharge.vue @@ -154,383 +154,382 @@ - diff --git a/src/pages/pay/service/capture_authorize.vue b/src/pages/pay/service/capture_authorize.vue index ef35c3d..1fdec06 100644 --- a/src/pages/pay/service/capture_authorize.vue +++ b/src/pages/pay/service/capture_authorize.vue @@ -264,7 +264,7 @@ const service = ref(null); const preAuthCard = ref(null); const selectedCard = ref(null); const chargeAmount = ref(0); -const transaction = ref(null as any); +const transaction = ref(null); const preAuthAmount = ref(0); const serviceTransactions = ref([]); const showPaymentModal = ref(false); @@ -356,7 +356,8 @@ const updateServiceCost = async (serviceId: number, newCost: number): Promise { throw new Error("Invalid response from server during capture."); } - } catch (error: any) { + } catch (err: unknown) { + const error = err as { response?: { data?: { detail?: string } }; message?: string }; const detail = error.response?.data?.detail || "Failed to process payment due to a server error."; notify({ title: "Error", text: detail, type: "error" }); console.error("Charge/Capture Service Error:", error); @@ -489,7 +491,7 @@ const getTransaction = async () => { selectedCard.value = cardResponse.data; chargeAmount.value = preAuthAmount.value; } - } catch (error: any) { + } catch (error: unknown) { console.error("No pre-authorized transaction found for service:", error); preAuthAmount.value = service.value?.service_cost || 0; } diff --git a/src/pages/pay/service/pay_service.vue b/src/pages/pay/service/pay_service.vue index 2803958..fea6642 100755 --- a/src/pages/pay/service/pay_service.vue +++ b/src/pages/pay/service/pay_service.vue @@ -271,420 +271,377 @@ - + diff --git a/src/pages/service/ServiceCalendar.vue b/src/pages/service/ServiceCalendar.vue index 947fe14..ad33d47 100644 --- a/src/pages/service/ServiceCalendar.vue +++ b/src/pages/service/ServiceCalendar.vue @@ -35,8 +35,8 @@ /> - \ No newline at end of file diff --git a/src/pages/service/ServiceEditModal.vue b/src/pages/service/ServiceEditModal.vue index 3e2714e..be21390 100644 --- a/src/pages/service/ServiceEditModal.vue +++ b/src/pages/service/ServiceEditModal.vue @@ -99,8 +99,8 @@ - diff --git a/src/pages/service/ServiceHome.vue b/src/pages/service/ServiceHome.vue index d23118c..b63f647 100644 --- a/src/pages/service/ServiceHome.vue +++ b/src/pages/service/ServiceHome.vue @@ -156,187 +156,176 @@ @delete-service="handleDeleteService" /> - diff --git a/src/pages/service/ServicePast.vue b/src/pages/service/ServicePast.vue index 380e7dd..c4c9e95 100644 --- a/src/pages/service/ServicePast.vue +++ b/src/pages/service/ServicePast.vue @@ -163,193 +163,176 @@ /> - diff --git a/src/pages/service/ServicePlans.vue b/src/pages/service/ServicePlans.vue index 2ba0584..d1ba56c 100644 --- a/src/pages/service/ServicePlans.vue +++ b/src/pages/service/ServicePlans.vue @@ -123,124 +123,109 @@ - diff --git a/src/pages/service/ServiceToday.vue b/src/pages/service/ServiceToday.vue index 379c40d..2a2651c 100644 --- a/src/pages/service/ServiceToday.vue +++ b/src/pages/service/ServiceToday.vue @@ -163,193 +163,176 @@ /> - diff --git a/src/pages/service/calender/CalendarCustomer.vue b/src/pages/service/calender/CalendarCustomer.vue index fb5ce6c..99f223e 100644 --- a/src/pages/service/calender/CalendarCustomer.vue +++ b/src/pages/service/calender/CalendarCustomer.vue @@ -55,156 +55,152 @@ /> - \ No newline at end of file diff --git a/src/pages/service/calender/EventSidebar.vue b/src/pages/service/calender/EventSidebar.vue index 603209b..26fab75 100644 --- a/src/pages/service/calender/EventSidebar.vue +++ b/src/pages/service/calender/EventSidebar.vue @@ -77,8 +77,8 @@ - diff --git a/src/pages/service/routes.ts b/src/pages/service/routes.ts index 9ebb418..c62d5b1 100644 --- a/src/pages/service/routes.ts +++ b/src/pages/service/routes.ts @@ -1,10 +1,10 @@ // Import the new component at the top -import ServiceHome from './ServiceHome.vue' -import ServicePast from './ServicePast.vue' -import CalendarCustomer from './calender/CalendarCustomer.vue' -import ServiceCalendar from './ServiceCalendar.vue' -import ServiceToday from './ServiceToday.vue' -import ServicePlans from './ServicePlans.vue' +const ServiceHome = () => import('./ServiceHome.vue') +const ServicePast = () => import('./ServicePast.vue') +const CalendarCustomer = () => import('./calender/CalendarCustomer.vue') +const ServiceCalendar = () => import('./ServiceCalendar.vue') +const ServiceToday = () => import('./ServiceToday.vue') +const ServicePlans = () => import('./ServicePlans.vue') const serviceRoutes = [ { diff --git a/src/pages/ticket/routes.ts b/src/pages/ticket/routes.ts index 4cb1fb7..a4734ed 100755 --- a/src/pages/ticket/routes.ts +++ b/src/pages/ticket/routes.ts @@ -1,8 +1,8 @@ -import Ticket from "../ticket/ticket.vue"; -import TicketAuto from "../ticket/ticketauto.vue"; +const Ticket = () => import("../ticket/ticket.vue"); +const TicketAuto = () => import("../ticket/ticketauto.vue"); const ticketRoutes = [ diff --git a/src/pages/transactions/authorize/index.vue b/src/pages/transactions/authorize/index.vue index 67f3ade..1aa6c91 100644 --- a/src/pages/transactions/authorize/index.vue +++ b/src/pages/transactions/authorize/index.vue @@ -170,6 +170,7 @@ import authHeader from '../../../services/auth.header' import Header from '../../../layouts/headers/headerauth.vue' import SideBar from '../../../layouts/sidebar/sidebar.vue' import Footer from '../../../layouts/footers/footer.vue' +import {AuthorizeTransaction} from '../../../types/models' export default defineComponent({ name: 'transactionsAuthorize', @@ -182,7 +183,7 @@ export default defineComponent({ data() { return { - transactions: [] as any[], + transactions: [] as AuthorizeTransaction[], } }, @@ -208,7 +209,7 @@ export default defineComponent({ getStatusText(status: number) { return status === 0 ? 'Approved' : 'Declined' }, - getSourceText(transaction: any) { + getSourceText(transaction: AuthorizeTransaction) { if (transaction.auto_id) { return 'Automatic' } else if (transaction.delivery_id) { @@ -222,7 +223,7 @@ export default defineComponent({ formatDate(dateStr: string) { return dateStr.split('T')[0]; // YYYY-MM-DD }, - getCaptureRoute(transaction: any) { + getCaptureRoute(transaction: AuthorizeTransaction) { if (transaction.service_id) { return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } }; } else if (transaction.delivery_id) { @@ -230,7 +231,7 @@ export default defineComponent({ } return {}; // fallback, though condition should prevent this }, - getPreauthRoute(transaction: any) { + getPreauthRoute(transaction: AuthorizeTransaction) { if (transaction.service_id) { return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } }; } else if (transaction.delivery_id) { diff --git a/src/pages/transactions/routes.ts b/src/pages/transactions/routes.ts index f44ca46..99fbb43 100644 --- a/src/pages/transactions/routes.ts +++ b/src/pages/transactions/routes.ts @@ -1,4 +1,4 @@ -import AuthorizePage from './authorize/index.vue'; +const AuthorizePage = () => import('./authorize/index.vue'); const transactionsRoutes = [ { diff --git a/src/services/adminService.ts b/src/services/adminService.ts new file mode 100644 index 0000000..32a8cf2 --- /dev/null +++ b/src/services/adminService.ts @@ -0,0 +1,139 @@ +import api from './api'; + +export const adminService = { + // Oil pricing + getOilPricing: () => + api.get('/admin/oil/get'), + + updateOilPricing: (data: any) => + api.post('/admin/oil/create', data), + + // VoIP + getVoipRouting: () => + api.get('/admin/voip_routing'), + + // Employee management + employees: { + create: (data: any) => + api.post('/employee/create', data), + + getById: (id: number) => + api.get(`/employee/${id}`), + + getByIdAlt: (id: number) => + api.get(`/employee/byid/${id}`), + + getByUserId: (id: number) => + api.get(`/employee/userid/${id}`), + + update: (id: number, data: any) => + api.put(`/employee/edit/${id}`, data), + + getAll: (page: number = 1) => + api.get(`/employee/all/${page}`), + + getDrivers: () => + api.get('/employee/drivers'), + }, + + // Promotions + promos: { + getAll: () => + api.get('/promo/all'), + + getById: (id: number) => + api.get(`/promo/${id}`), + + getPrice: (id: number) => + api.get(`/promo/promoprice/${id}`), + + create: (data: any) => + api.post('/promo/create', data), + + update: (id: number, data: any) => + api.put(`/promo/edit/${id}`, data), + + delete: (id: number) => + api.delete(`/promo/delete/${id}`), + + enable: (id: number) => + api.put(`/promo/on/${id}`), + + disable: (id: number) => + api.put(`/promo/off/${id}`), + }, + + // Statistics + stats: { + deliveryCountToday: () => + api.get('/stats/delivery/count/today'), + + deliveredCountToday: () => + api.get('/stats/delivery/count/delivered/today'), + + employeeDeliveryTotal: (employeeId: number) => + api.get(`/stats/delivery/total/${employeeId}`), + + employeeGallonsTotal: (employeeId: number) => + api.get(`/stats/gallons/total/${employeeId}`), + + weeklyGallons: () => + api.get('/stats/gallons/week'), + + customerGallonsTotal: (customerId: number) => + api.get(`/stats/gallons/check/total/${customerId}`), + + employeePrimesTotal: (employeeId: number) => + api.get(`/stats/primes/total/${employeeId}`), + + userLastDelivery: (userId: number) => + api.get(`/stats/user/lastdelivery/${userId}`), + + userStats: (userId: number) => + api.get(`/stats/user/${userId}`), + + serviceCallsToday: () => + api.get('/stats/call/count/today'), + + sidebarCounts: () => + api.get('/deliverystatus/stats/sidebar-counts'), + + todayTotals: () => + api.get('/deliverystatus/today-totals'), + + tomorrowTotals: () => + api.get('/deliverystatus/tomorrow-totals'), + + waitingTotals: () => + api.get('/deliverystatus/waiting-totals'), + + pendingStatus: () => + api.get('/deliverystatus/pending'), + }, + + // Money & reporting + money: { + weeklyProfit: () => + api.get('/money/profit/week'), + + yearlyProfit: () => + api.get('/money/profit/year'), + + customerListReport: () => + api.get('/report/customers/list'), + }, + + // Social/comments + social: { + getPosts: (customerId: number, page: number = 1) => + api.get(`/social/posts/${customerId}/${page}`), + + createPost: (customerId: number, data: any) => + api.post(`/social/create/${customerId}`, data), + + deletePost: (postId: number) => + api.delete(`/social/delete/${postId}`), + }, +}; + +export default adminService; diff --git a/src/services/api.ts b/src/services/api.ts new file mode 100644 index 0000000..8109159 --- /dev/null +++ b/src/services/api.ts @@ -0,0 +1,51 @@ +import axios from 'axios'; + +// Main Flask API +const api = axios.create({ + baseURL: import.meta.env.VITE_BASE_URL, + withCredentials: true, +}); + +// Authorize.net FastAPI +const authorizeApi = axios.create({ + baseURL: import.meta.env.VITE_AUTHORIZE_URL, + withCredentials: true, +}); + +// Automatic Delivery System +const autoApi = axios.create({ + baseURL: import.meta.env.VITE_AUTO_URL, + withCredentials: true, +}); + +// Request interceptor - add auth token +function addAuthHeader(config: { headers: { Authorization?: string } }) { + const token = localStorage.getItem('auth_token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +api.interceptors.request.use(addAuthHeader as any); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +authorizeApi.interceptors.request.use(addAuthHeader as any); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +autoApi.interceptors.request.use(addAuthHeader as any); + +// Response error handler - handle 401 errors +function handleResponseError(error: unknown) { + const err = error as { response?: { status?: number } }; + if (err?.response?.status === 401) { + localStorage.removeItem('auth_token'); + } + return Promise.reject(error); +} + +api.interceptors.response.use(undefined, handleResponseError); +authorizeApi.interceptors.response.use(undefined, handleResponseError); +autoApi.interceptors.response.use(undefined, handleResponseError); + +export { api, authorizeApi, autoApi }; +export default api; diff --git a/src/services/authService.ts b/src/services/authService.ts new file mode 100644 index 0000000..712c858 --- /dev/null +++ b/src/services/authService.ts @@ -0,0 +1,40 @@ +import api, { authorizeApi } from './api'; + +export const authService = { + // Authentication + login: (data: { username: string; password: string }) => + api.post('/auth/login', data), + + register: (data: any) => + api.post('/auth/register', data), + + logout: () => + api.post('/auth/logout'), + + whoami: () => + api.get('/auth/whoami'), + + // Password management + changePassword: (data: { old_password: string; new_password: string }) => + api.put('/auth/change-password', data), + + adminChangePassword: (data: { user_id: number; new_password: string }) => + api.put('/auth/admin-change-password', data), + + unlockAccount: (data: any) => + api.post('/auth/unlock-account', data), + + // Authorize.net account management + authorize: { + checkAccount: (customerId: number) => + authorizeApi.get(`/user/check-authorize-account/${customerId}`), + + createAccount: (customerId: number, data?: any) => + authorizeApi.post(`/user/create-account/${customerId}`, data), + + deleteAccount: (customerId: number) => + authorizeApi.delete(`/user/delete-account/${customerId}`), + }, +}; + +export default authService; diff --git a/src/services/customerService.ts b/src/services/customerService.ts new file mode 100644 index 0000000..e830173 --- /dev/null +++ b/src/services/customerService.ts @@ -0,0 +1,56 @@ +import api from './api'; +import { + Customer, + CustomerDescription, + TankInspection, + CustomerStats, + CreateCustomerRequest, + UpdateCustomerRequest, + CustomerListResponse, + ApiResponse +} from '../types/models'; + +export const customerService = { + // CRUD operations + getAll: (page: number = 1): Promise => + api.get(`/customer/all/${page}`), + + getById: (id: number): Promise> => + api.get(`/customer/${id}`), + + create: (data: CreateCustomerRequest): Promise> => + api.post('/customer/create', data), + + update: (id: number, data: UpdateCustomerRequest): Promise> => + api.put(`/customer/edit/${id}`, data), + + delete: (id: number): Promise> => + api.delete(`/customer/delete/${id}`), + + getCount: (): Promise> => + api.get('/customer/count'), + + // Profile & details + getDescription: (id: number): Promise> => + api.get(`/customer/description/${id}`), + + // Tank information + getTank: (id: number): Promise> => + api.get(`/customer/tank/${id}`), + + updateTank: (id: number, data: Partial): Promise> => + api.put(`/customer/edit/tank/${id}`, data), + + // Automatic delivery + getAutomaticStatus: (id: number): Promise> => + api.get(`/customer/automatic/status/${id}`), + + assignAutomatic: (id: number, data: { status: number }): Promise> => + api.put(`/customer/automatic/assign/${id}`, data), + + // Search + search: (query: string): Promise> => + api.get(`/search/customer?q=${encodeURIComponent(query)}`), +}; + +export default customerService; diff --git a/src/services/deliveryService.ts b/src/services/deliveryService.ts new file mode 100644 index 0000000..b0eb8a3 --- /dev/null +++ b/src/services/deliveryService.ts @@ -0,0 +1,128 @@ +import api, { autoApi } from './api'; +import { + Delivery, + DeliveryNote, + AutoDelivery, + DeliveryListResponse, + CreateDeliveryRequest, + UpdateDeliveryRequest, + ApiResponse +} from '../types/models'; + +export const deliveryService = { + // CRUD operations + create: (customerId: number, data: CreateDeliveryRequest): Promise> => + api.post(`/delivery/create/${customerId}`, data), + + getById: (id: number): Promise> => + api.get(`/delivery/${id}`), + + getOrder: (id: number): Promise> => + api.get(`/delivery/order/${id}`), + + update: (id: number, data: UpdateDeliveryRequest): Promise> => + api.put(`/delivery/edit/${id}`, data), + + delete: (id: number): Promise> => + api.delete(`/delivery/delete/${id}`), + + cancel: (id: number): Promise> => + api.put(`/delivery/cancel/${id}`), + + markCancelled: (id: number): Promise> => + api.put(`/delivery/cancelled/${id}`), + + // List operations + getAll: (page: number = 1): Promise => + api.get(`/delivery/all/${page}`), + + getByCustomer: (customerId: number, page: number = 1): Promise => + api.get(`/delivery/customer/${customerId}/${page}`), + + getPast1: (customerId: number): Promise> => + api.get(`/delivery/past1/${customerId}`), + + getPast2: (customerId: number): Promise> => + api.get(`/delivery/past2/${customerId}`), + + // Status-based lists + getWaiting: (page: number = 1): Promise => + api.get(`/delivery/waiting/${page}`), + + getTomorrow: (page: number = 1): Promise => + api.get(`/delivery/tommorrow/${page}`), + + getOutForDelivery: (page: number = 1): Promise => + api.get(`/delivery/outfordelivery/${page}`), + + getDelivered: (page: number = 1): Promise => + api.get(`/delivery/delivered/${page}`), + + getFinalized: (page: number = 1): Promise => + api.get(`/delivery/finalized/${page}`), + + getPending: (page: number = 1): Promise => + api.get(`/delivery/pending/${page}`), + + getIssues: (page: number = 1): Promise => + api.get(`/delivery/issue/${page}`), + + // Status & totals + updateStatus: (data: { id: number; status: number }): Promise> => + api.put('/delivery/updatestatus', data), + + getTotal: (id: number): Promise> => + api.get(`/delivery/total/${id}`), + + // Cash handling + handleCash: (id: number, type: string): Promise> => + api.get(`/delivery/cash/${id}/${type}`), + + // Finalize + finalize: (id: number, data?: { final_price: number }): Promise> => + api.put(`/deliverydata/finalize/${id}`, data), + + // Auto system endpoints (VITE_AUTO_URL) + auto: { + getDelivery: (id: number) => + autoApi.get(`/delivery/delivery/${id}`), + + getProfileDeliveries: (id: number) => + autoApi.get(`/delivery/all/profile/${id}`), + + getAllCustomers: () => + autoApi.get('/delivery/all/customers'), + + getByCustomer: (id: number) => + autoApi.get(`/delivery/auto/customer/${id}`), + + getTicket: (id: number) => + autoApi.get(`/delivery/autoticket/${id}`), + + findDelivery: (id: number) => + autoApi.get(`/delivery/finddelivery/${id}`), + + updateStatus: (id: number, data: any) => + autoApi.put(`/delivery/update_status/${id}`, data), + + confirm: (data: any) => + autoApi.post('/confirm/delivery', data), + + createTicket: (id: number, data: any) => + autoApi.post(`/confirm/auto/create/${id}`, data), + + createTicketNoPreauth: (id: number, data: any) => + autoApi.post(`/confirm/auto/create/nopreauth/${id}`, data), + + closeTicket: (id: number, data?: any) => + autoApi.put(`/confirm/auto/close_ticket/${id}`, data), + + updateTicket: (id: number, data: any) => + autoApi.put(`/confirm/auto/update/${id}`, data), + + estimateGallons: (customerId: number) => + autoApi.get(`/fixstuff_customer/estimate_gallons/customer/${customerId}`), + }, +}; + +export default deliveryService; diff --git a/src/services/paymentService.ts b/src/services/paymentService.ts new file mode 100644 index 0000000..3e84185 --- /dev/null +++ b/src/services/paymentService.ts @@ -0,0 +1,111 @@ +import api, { authorizeApi } from './api'; +import { + PaymentTransaction, + CreditCard, + AuthorizeTransaction, + TransactionListResponse, + CardListResponse, + CreateCardRequest, + PaymentRequest, + ApiResponse, + TokenizeCardRequest, + UpdateTokenizedCardRequest, + ChargeSavedCardRequest, + ChargeDirectRequest, + CaptureRequest, + PreauthorizeSavedCardRequest, + AuthorizeNetTransactionResponse +} from '../types/models'; + +export const paymentService = { + // Card management (Main API) + getCard: (id: number): Promise> => + api.get(`/payment/card/${id}`), + + getCards: (customerId: number): Promise> => + api.get(`/payment/cards/${customerId}`), + + getCardsOnFile: (customerId: number): Promise> => + api.get(`/payment/cards/onfile/${customerId}`), + + getAllCards: (page: number = 1): Promise => + api.get(`/payment/cards/all/${page}`), + + createCard: (customerId: number, data: CreateCardRequest): Promise> => + api.post(`/payment/card/create/${customerId}`, data), + + updateCard: (id: number, data: Partial): Promise> => + api.put(`/payment/card/edit/${id}`, data), + + removeCard: (id: number): Promise> => + api.delete(`/payment/card/remove/${id}`), + + removeCardAlt: (id: number): Promise> => + api.delete(`/payment/cards/remove/${id}`), + + updatePaymentProfile: (id: number, data: { profile_id: string }): Promise> => + api.put(`/payment/card/update_payment_profile/${id}`, data), + + // Authorization & capture (Main API) + authorizeDelivery: (id: number, data: PaymentRequest): Promise> => + api.put(`/payment/authorize/${id}`, data), + + authorizeService: (id: number, data: PaymentRequest): Promise> => + api.put(`/payment/authorize/service/${id}`, data), + + cleanupAuthorization: (id: number): Promise> => + api.put(`/payment/authorize/cleanup/${id}`), + + captureServicePayment: (id: number, data: { amount: number }): Promise> => + api.put(`/payment/capture/service/${id}`, data), + + // Service payment + getServicePayment: (id: number, type: string): Promise> => + api.get(`/payment/service/payment/${id}/${type}`), + + // Transactions + getDeliveryTransaction: (id: number): Promise> => + api.get(`/payment/transaction/delivery/${id}`), + + getAuthorizeTransactions: (): Promise> => + api.get('/payment/transactions/authorize/1'), + + getCustomerTransactions: (customerId: number, page: number = 1): Promise => + api.get(`/payment/transactions/customer/${customerId}/${page}`), + + getServiceTransactions: (serviceId: number): Promise> => + api.get(`/payment/transactions/service/${serviceId}`), + + // Authorize.net endpoints + authorize: { + tokenizeCard: (customerId: number, data: TokenizeCardRequest): Promise> => + authorizeApi.post(`/api/payments/customers/${customerId}/cards`, data), + + updateTokenizedCard: (customerId: number, cardId: number, data: UpdateTokenizedCardRequest): Promise> => + authorizeApi.put(`/api/payments/customers/${customerId}/cards/${cardId}`, data), + + authorizeSavedCard: (customerId: number, data: PreauthorizeSavedCardRequest): Promise> => + authorizeApi.post(`/api/payments/authorize/saved-card/${customerId}`, data), + + chargeSavedCard: (customerId: number, data: ChargeSavedCardRequest): Promise> => + authorizeApi.post(`/api/payments/charge/saved-card/${customerId}`, data), + + charge: (customerId: number, data: ChargeDirectRequest): Promise> => + authorizeApi.post(`/api/charge/${customerId}`, data), + + capture: (data: CaptureRequest): Promise> => + authorizeApi.post('/api/capture/', data), + + // Auto transaction endpoints + getAutoTransaction: (deliveryId: number): Promise> => + authorizeApi.get(`/api/auto/transaction/delivery/${deliveryId}`), + + updateAutoTransactionId: (deliveryId: number, newId: number, data?: Record): Promise> => + authorizeApi.put(`/api/auto/transaction/delivery/${deliveryId}/update/${newId}`, data ?? {}), + + linkTransactionToAuto: (transactionId: number, autoId: number, data?: Record): Promise> => + authorizeApi.put(`/api/transaction/${transactionId}/update_auto_id/${autoId}`, data ?? {}), + }, +}; + +export default paymentService; diff --git a/src/services/queryService.ts b/src/services/queryService.ts new file mode 100644 index 0000000..95d4e04 --- /dev/null +++ b/src/services/queryService.ts @@ -0,0 +1,31 @@ +import api from './api'; + +export const queryService = { + // Customer types + getCustomerTypes: () => + api.get('/query/customertype'), + + // States + getStates: () => + api.get('/query/states'), + + // Employee types + getEmployeeTypes: () => + api.get('/query/employeetype'), + + // Delivery statuses + getDeliveryStatuses: () => + api.get('/query/deliverystatus'), + + // Oil pricing info + getOilPrice: () => + api.get('/info/price/oil'), + + getOilPriceTable: () => + api.get('/info/price/oil/table'), + + getOilPriceTiers: () => + api.get('/info/price/oil/tiers'), +}; + +export default queryService; diff --git a/src/services/serviceService.ts b/src/services/serviceService.ts new file mode 100644 index 0000000..6868af8 --- /dev/null +++ b/src/services/serviceService.ts @@ -0,0 +1,63 @@ +import api from './api'; + +export const serviceService = { + // CRUD operations + create: (data: any) => + api.post('/service/create', data), + + getById: (id: number) => + api.get(`/service/${id}`), + + update: (id: number, data: any) => + api.put(`/service/update/${id}`, data), + + delete: (id: number) => + api.delete(`/service/delete/${id}`), + + // List operations + getAll: () => + api.get('/service/all'), + + getToday: () => + api.get('/service/today'), + + getUpcoming: () => + api.get('/service/upcoming'), + + getPast: () => + api.get('/service/past'), + + getForCustomer: (customerId: number) => + api.get(`/service/for-customer/${customerId}`), + + // Cost management + updateCost: (id: number, data: any) => + api.put(`/service/update-cost/${id}`, data), + + // Parts + getPartsForCustomer: (customerId: number) => + api.get(`/service/parts/customer/${customerId}`), + + updateParts: (id: number, data: any) => + api.put(`/service/parts/update/${id}`, data), + + // Service plans + plans: { + getActive: () => + api.get('/service/plans/active'), + + getForCustomer: (customerId: number) => + api.get(`/service/plans/customer/${customerId}`), + + create: (data: any) => + api.post('/service/plans/create', data), + + update: (id: number, data: any) => + api.put(`/service/plans/update/${id}`, data), + + delete: (id: number) => + api.delete(`/service/plans/delete/${id}`), + }, +}; + +export default serviceService; diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 7cc7bf0..f11a536 100755 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -3,7 +3,7 @@ import { ref, computed } from 'vue' import { defineStore } from 'pinia' import axios from 'axios' -import authHeader from '../services/auth.header' // Adjust path if needed +import { authService } from '../services/authService' interface User { user_name: string; @@ -38,7 +38,7 @@ export const useAuthStore = defineStore('auth', () => { localStorage.setItem('auth_user', JSON.stringify(newUser)) user.value = newUser } - + // --- THIS IS THE FIX --- // The string must be a template literal (using backticks ``) // and should follow the "Bearer " format. @@ -60,9 +60,8 @@ export const useAuthStore = defineStore('auth', () => { return false // No token, definitely not authenticated } try { - // Use your existing endpoint to verify the token - const path = `${import.meta.env.VITE_BASE_URL}/auth/whoami`; -const response = await axios.get(path, { headers: authHeader() }); + // Use the centralized auth service + const response = await authService.whoami(); if (response.data && response.data.ok) { user.value = response.data.user return true @@ -85,4 +84,4 @@ const response = await axios.get(path, { headers: authHeader() }); clearAuth, checkAuthStatus, } -}) \ No newline at end of file +}) diff --git a/src/stores/counts.ts b/src/stores/counts.ts index 90e42c6..baef9e6 100644 --- a/src/stores/counts.ts +++ b/src/stores/counts.ts @@ -2,8 +2,7 @@ import { ref } from 'vue' import { defineStore } from 'pinia' -import axios from 'axios' -import authHeader from '../services/auth.header' // Adjust path if needed +import { adminService } from '../services/adminService' export const useCountsStore = defineStore('counts', () => { // --- STATE --- @@ -16,13 +15,12 @@ export const useCountsStore = defineStore('counts', () => { const upcoming_service = ref(0) const today_service = ref(0) const transaction = ref(0) - + // --- ACTIONS --- // A single action to fetch ALL counts from our new, efficient endpoint. async function fetchSidebarCounts() { try { - const path = `${import.meta.env.VITE_BASE_URL}/deliverystatus/stats/sidebar-counts`; - const response = await axios.get(path, { headers: authHeader() }); + const response = await adminService.stats.sidebarCounts(); if (response.data && response.data.ok) { const counts = response.data.counts; diff --git a/src/stores/search.ts b/src/stores/search.ts index 74d9c17..ce48fea 100644 --- a/src/stores/search.ts +++ b/src/stores/search.ts @@ -2,7 +2,7 @@ import { ref, computed } from 'vue' import { defineStore } from 'pinia' -import axios from 'axios' +import { customerService } from '../services/customerService' // Define a type for what a search result looks like. interface CustomerSearchResult { @@ -38,9 +38,7 @@ export const useSearchStore = defineStore('search', () => { isLoading.value = true; try { - // NOTE: Make sure this URL is correct. You may need to add your VITE_BASE_URL - const path = `${import.meta.env.VITE_BASE_URL}/search/customer?q=${searchTerm.value}`; - const response = await axios.get(path); + const response = await customerService.search(searchTerm.value); searchResults.value = response.data; } catch { // No `error` parameter as requested searchResults.value = []; @@ -71,4 +69,4 @@ export const useSearchStore = defineStore('search', () => { debouncedSearch, clearSearch, } -}) \ No newline at end of file +}) diff --git a/src/types/models.ts b/src/types/models.ts new file mode 100644 index 0000000..bf67947 --- /dev/null +++ b/src/types/models.ts @@ -0,0 +1,725 @@ +/** + * EAMCO Office Frontend API Response Models + * + * This file contains TypeScript interfaces for all API responses + * to replace 'any' types throughout the application. + */ + +import { + DeliveryStatusType, + PaymentStatusType, + AutoStatusType, + TransactionStatusType, + CustomerAutomaticStatus +} from '../constants/status'; + +// Base interfaces +export interface BaseEntity { + id: number; +} + +export interface ApiResponse { + ok: boolean; + data?: T; + error?: string; +} + +export interface PaginatedResponse { + data: T[]; + total: number; + page: number; + per_page: number; +} + +// Customer interfaces +export interface Customer extends BaseEntity { + auth_net_profile_id?: string; + account_number: string; + customer_first_name: string; + customer_last_name: string; + customer_town: string; + customer_state: number; + customer_zip: string; + customer_first_call?: string; + customer_email?: string; + customer_automatic: CustomerAutomaticStatus; + customer_phone_number?: string; + customer_home_type: number; + customer_apt?: string; + customer_address: string; + company_id: number; + customer_latitude?: string; + customer_longitude?: string; + correct_address: boolean; +} + +export interface CustomerDescription extends BaseEntity { + customer_id: number; + account_number: string; + company_id: number; + fill_location?: number; + description?: string; +} + +export interface TankInspection extends BaseEntity { + customer_id: number; + last_tank_inspection?: string; + tank_status: boolean; + outside_or_inside: boolean; + tank_size: number; + tank_images?: number; + tank_image_upload_dates?: string[]; +} + +export interface CustomerStats extends BaseEntity { + customer_id: number; + total_calls: number; + service_calls_total: number; + service_calls_total_spent: number; + service_calls_total_profit: number; + oil_deliveries: number; + oil_total_gallons: number; + oil_total_spent: number; + oil_total_profit: number; +} + +// Delivery interfaces +export interface Delivery extends BaseEntity { + customer_id: number; + customer_name: string; + customer_address: string; + customer_town: string; + customer_state: string; + customer_zip: number; + gallons_ordered: number; + customer_asked_for_fill: number; + gallons_delivered: number; + customer_filled: number; + delivery_status: DeliveryStatusType; + when_ordered?: string; + when_delivered?: string; + expected_delivery_date?: string; + automatic: number; + automatic_id?: number; + oil_id?: number; + supplier_price?: number; + customer_price?: number; + customer_temperature?: number; + dispatcher_notes?: string; + prime: number; + same_day: number; + emergency: number; + payment_type: number; + payment_card_id?: number; + cash_recieved?: number; + driver_employee_id?: number; + driver_first_name?: string; + driver_last_name?: string; + pre_charge_amount?: number; + total_price: number; + final_price: number; + check_number?: string; + promo_id?: number; + promo_money_discount?: number; +} + +export interface DeliveryNote extends BaseEntity { + delivery_id: number; + driver_comments?: string; + time_added: string; + driver_id: number; + driver_name: string; +} + +// Payment interfaces +export interface PaymentTransaction extends BaseEntity { + customer_id: number; + delivery_id?: number; + service_id?: number; + amount: number; + payment_status: PaymentStatusType; + transaction_id?: string; + card_id?: number; + transaction_type?: TransactionStatusType; + created_at?: string; + updated_at?: string; +} + +export interface CreditCard extends BaseEntity { + user_id: number; + date_added?: string; + card_number: string; + last_four_digits: number; + name_on_card: string; + expiration_month: string; + expiration_year: string; + type_of_card: string; + security_number?: string; + accepted_or_declined?: number; + main_card: boolean; + zip_code?: string; + auth_net_payment_profile_id?: string; +} + +export interface AuthorizeTransaction extends BaseEntity { + transaction_id: string; + amount: number; + status: TransactionStatusType; + response_code?: string; + auth_code?: string; + created_at: string; + auth_net_transaction_id?: string; + customer_name?: string; + preauthorize_amount: number | null; + charge_amount: number | null; + transaction_type?: number; + delivery_id?: number; + service_id?: number; + auto_id?: number; + customer_id?: number; + rejection_reason?: string; +} + +// Service interfaces +export interface ServiceCall extends BaseEntity { + customer_id: number; + customer_name?: string; + customer_address?: string; + customer_town?: string; + customer_state?: string; + customer_zip?: string; + type_service_call: number; + when_ordered?: string; + scheduled_date: string; + description: string; + service_cost: string; + payment_type?: number; + payment_card_id?: number; + payment_status?: number; +} + +export interface ServicePart extends BaseEntity { + service_call_id: number; + part_name: string; + part_number?: string; + quantity: number; + unit_cost: number; + total_cost: number; +} + +export interface ServicePlan extends BaseEntity { + customer_id: number; + customer_name: string; + customer_address: string; + customer_town: string; + contract_plan: number; + contract_years: number; + contract_start_date: string; +} + +// Employee interfaces +export interface Employee extends BaseEntity { + employee_first_name: string; + employee_last_name: string; + employee_email?: string; + employee_phone?: string; + employee_role: string; + is_active: boolean; + hire_date?: string; + admin_role: boolean; + employee_type?: number; + employee_town?: string; + employee_phone_number?: string; + user_id?: number; +} + +export interface User { + user_admin?: number; + // Add other user properties as needed +} + +// Auto delivery interfaces +export interface AutoDelivery extends BaseEntity { + customer_id: number; + customer_full_name: string; + account_number: string; + customer_town: string; + customer_state: number; + customer_zip: string; + customer_address: string; + last_fill?: string; + last_updated?: string; + estimated_gallons_left: number; + estimated_gallons_left_prev_day: number; + tank_height?: string; + tank_size: number; + house_factor: number; + auto_status: AutoStatusType; + days_since_last_fill: number; + hot_water_summer: number; + open_ticket_id?: number; +} + +// Promo interfaces +export interface Promo extends BaseEntity { + promo_code: string; + promo_name: string; + discount_type: string; + discount_value: number; + is_active: boolean; + start_date?: string; + end_date?: string; + usage_limit?: number; + usage_count: number; +} + +// Query/Lookup interfaces +export interface StateOption { + value: number; + text: string; + abbreviation?: string; +} + +export interface HomeTypeOption { + value: number; + text: string; +} + +export interface PaymentTypeOption { + id: number; + name: string; +} + +export interface OilPrice { + id: number; + supplier_name: string; + price_per_gallon: number; + effective_date: string; +} + +// Search interfaces +export interface SearchResult { + id: number; + type: 'customer' | 'delivery' | 'service'; + title: string; + subtitle?: string; + data: any; // Keep as any for now since search results can be various types +} + +// Stats interfaces +export interface DashboardStats { + total_customers: number; + active_deliveries: number; + pending_payments: number; + monthly_revenue: number; + todays_deliveries: number; +} + +export interface CompanyStats { + total_revenue: number; + total_profit: number; + total_deliveries: number; + total_service_calls: number; + average_delivery_time: number; +} + +// Request interfaces +export interface CreateCustomerRequest { + customer_last_name: string; + customer_first_name: string; + customer_town: string; + customer_state: number; + customer_zip: string; + customer_email?: string; + customer_home_type: number; + customer_phone_number?: string; + customer_address: string; + customer_apt?: string; + customer_description?: string; +} + +export interface UpdateCustomerRequest { + customer_last_name?: string; + customer_first_name?: string; + customer_town?: string; + customer_state?: number; + customer_zip?: string; + customer_email?: string; + customer_home_type?: number; + customer_phone_number?: string; + customer_address?: string; + customer_apt?: string; + customer_automatic?: CustomerAutomaticStatus; +} + +export interface CreateDeliveryRequest { + customer_id: number; + gallons_ordered: number; + expected_delivery_date?: string; + payment_type: number; + payment_card_id?: number; + promo_id?: number; + dispatcher_notes?: string; +} + +export interface UpdateDeliveryRequest { + gallons_delivered?: number; + delivery_status?: DeliveryStatusType; + payment_type?: number; + total_price?: number; + final_price?: number; + driver_employee_id?: number; + dispatcher_notes?: string; +} + +export interface CreateCardRequest { + customer_id: number; + card_number: string; + expiration_month: number; + expiration_year: number; + security_number: string; + cardholder_name: string; +} + +export interface PaymentRequest { + customer_id: number; + amount: number; + card_id?: number; + delivery_id?: number; + service_id?: number; +} + +// Calendar/Event interfaces +export interface CalendarEvent { + id: string; + title: string; + start: string; + end?: string; + backgroundColor?: string; + extendedProps?: { + customer_id: number; + delivery_id?: number; + service_id?: number; + type: 'delivery' | 'service' | 'maintenance'; + status: string; + }; +} + +// Form interfaces +export interface LoginForm { + username: string; + password: string; +} + +export interface RegisterForm { + username: string; + email: string; + password: string; + confirm_password: string; +} + +export interface ChangePasswordForm { + current_password: string; + new_password: string; + confirm_password: string; +} + +// ============================================ +// Payment Request Interfaces (Authorize.net) +// ============================================ + +export interface TokenizeCardRequest { + card_number: string; + expiration_month: string; + expiration_year: string; + cvv: string; + cardholder_name: string; + zip_code?: string; +} + +export interface UpdateTokenizedCardRequest { + card_number?: string; + expiration_month?: string; + expiration_year?: string; + cvv?: string; + cardholder_name?: string; + zip_code?: string; +} + +export interface ChargeSavedCardRequest { + card_id: number; + charge_amount: string; + delivery_id?: number; + service_id?: number | null; +} + +export interface PreauthorizeSavedCardRequest { + card_id: number; + preauthorize_amount: string; + delivery_id?: number; + auto_id?: number; +} + +export interface ChargeDirectRequest { + card_number: string; + expiration_date: string; + cvv: string; + charge_amount: string; + preauthorize_amount?: string; + transaction_type: number; + delivery_id?: number; + service_id?: number | null; + card_id?: number; +} + +export interface CaptureRequest { + charge_amount: number; + auth_net_transaction_id: string; +} + +// ============================================ +// API Response Interfaces +// ============================================ + +export interface AuthorizeNetTransactionResponse { + id: number; + auth_net_transaction_id: string; + preauthorize_amount: number | null; + charge_amount: number | null; + status: number; // 0 = approved, 1 = declined + transaction_type: number; // 0 = charge, 1 = auth, 2 = capture + created_at: string; + rejection_reason?: string; + customer_id?: number; + delivery_id?: number; + service_id?: number; + auto_id?: number; +} + +export interface DeliveryOrderResponse { + ok: boolean; + delivery: Delivery; + error?: string; +} + +export interface DeliveryTotalResponse { + ok: boolean; + priceprime: number; + pricesameday: number; + priceemergency: number; + total_amount: number; + discount: number; + total_amount_after_discount: number; +} + +export interface OilPricingResponse { + price_from_supplier: number; + price_for_customer: number; + price_for_employee: number; + price_same_day: number; + price_prime: number; + price_emergency: number; + date: string; +} + +export interface WhoAmIResponse { + ok: boolean; + user: { + user_id: number; + user_admin?: number; + }; +} + +export interface AuthorizeCheckResponse { + profile_exists: boolean; + has_payment_methods: boolean; + missing_components: string[]; + valid_for_charging: boolean; +} + +export interface CreateAuthorizeAccountResponse { + success: boolean; + profile_id?: string; + message?: string; + error_detail?: string; + is_duplicate?: boolean; +} + +export interface PromoResponse { + id: number; + name_of_promotion: string; + description: string; + money_off_delivery: number; + text_on_ticket: string; +} + +export interface CardsOnFileResponse { + cards: number; +} + +export interface PaymentCardResponse { + userCard: CreditCard; +} + +export interface UpdateStatusResponse { + update: boolean; +} + +// ============================================ +// Component Local State Interfaces +// ============================================ + +export interface DeliveryFormData { + id: number; + customer_id: number; + customer_name: string; + customer_address: string; + customer_town: string; + customer_state: number; + customer_zip: string; + gallons_ordered: number; + customer_asked_for_fill: number; + gallons_delivered: number; + customer_filled: number; + delivery_status: number; + when_ordered: string; + when_delivered: string; + expected_delivery_date: string; + automatic: number; + automatic_id?: number; + oil_id: number; + supplier_price: number; + customer_price: number; + customer_temperature: number; + dispatcher_notes: string; + prime: number; + promo_id: number | null; + emergency: number; + same_day: number; + payment_type: number; + payment_card_id: number; + driver_employee_id: number; + driver_first_name: string; + driver_last_name: string; + pre_charge_amount: number; + total_price: number; + final_price?: number; + service_id?: number | null; +} + +export interface CustomerFormData { + id: number; + user_id?: number; + customer_first_name: string; + customer_last_name: string; + customer_town: string; + customer_address: string; + customer_state: number; + customer_zip: string; + customer_apt: string; + customer_home_type: number; + customer_phone_number: string; + account_number: string; + auth_net_profile_id?: string | null; + customer_email?: string; +} + +export interface CreditCardFormData { + id: number; + name_on_card: string; + main_card: boolean; + card_number: string; + expiration_month: string; + type_of_card: string; + last_four_digits: string | number; + expiration_year: string; + security_number: string; + date_added?: string; + user_id?: number | string; + accepted_or_declined?: number | string; + auth_net_payment_profile_id?: string; + zip_code?: string; +} + +export interface PricingData { + price_from_supplier: number; + price_for_customer: number; + price_for_employee: number; + price_same_day: number; + price_prime: number; + price_emergency: number; + date: string; +} + +export interface PromoData { + name_of_promotion: string; + description: string; + money_off_delivery: number; + text_on_ticket: string; +} + +export interface AutoTicketData { + id: number; + customer_id: number | string; + account_number: string; + customer_town: string; + customer_state: number | string; + customer_address: string; + customer_zip: string; + customer_full_name: string; + oil_prices_id: number | string; + fill_date: string; + gallons_delivered: string; + price_per_gallon: string; + total_amount_customer: string; + payment_type: number | string; + payment_card_id: number | string; + payment_status: number; + open_ticket_id: number | null; +} + +export interface AutoDeliveryData { + id: number; + customer_id: number; + account_number: string; + customer_town: string; + customer_state: number; + customer_address: string; + customer_zip: string; + customer_full_name: string; + last_fill: string; + days_since_last_fill: number; + last_updated: string; + estimated_gallons_left: number; + estimated_gallons_left_prev_day: number; + tank_height: string; + tank_size: string | number; + house_factor: number; + auto_status: number; + open_ticket_id: number | null; +} + +export interface CustomerDescriptionData { + customer_id: number; + account_number: string; + company_id: number; + fill_location: number; + description: string; +} + +// ============================================ +// Axios Response Type Helper +// ============================================ +export interface AxiosApiResponse { + data: T; + status: number; + statusText: string; +} + +// Utility types +export type CustomerListResponse = PaginatedResponse; +export type DeliveryListResponse = PaginatedResponse; +export type TransactionListResponse = PaginatedResponse; +export type CardListResponse = PaginatedResponse; +export type ServiceListResponse = PaginatedResponse; +export type EmployeeListResponse = PaginatedResponse; \ No newline at end of file
{{ person.employee_phone_number }}