major claude changes
This commit is contained in:
21
package-lock.json
generated
21
package-lock.json
generated
@@ -19,14 +19,12 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"moment": "^2.30.1",
|
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
"v-pagination-3": "^0.1.7",
|
"v-pagination-3": "^0.1.7",
|
||||||
"vue": "^3.3.11",
|
"vue": "^3.3.11",
|
||||||
"vue-debounce": "^5.0.0",
|
"vue-debounce": "^5.0.0",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vue3-pdfmake": "^2.2.0",
|
"vue3-pdfmake": "^2.2.0"
|
||||||
"vuelidate": "^0.7.7"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/leaflet": "^1.9.12",
|
"@types/leaflet": "^1.9.12",
|
||||||
@@ -4482,14 +4480,6 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"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": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"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": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -20,14 +20,12 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"moment": "^2.30.1",
|
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
"v-pagination-3": "^0.1.7",
|
"v-pagination-3": "^0.1.7",
|
||||||
"vue": "^3.3.11",
|
"vue": "^3.3.11",
|
||||||
"vue-debounce": "^5.0.0",
|
"vue-debounce": "^5.0.0",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vue3-pdfmake": "^2.2.0",
|
"vue3-pdfmake": "^2.2.0"
|
||||||
"vuelidate": "^0.7.7"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/leaflet": "^1.9.12",
|
"@types/leaflet": "^1.9.12",
|
||||||
|
|||||||
@@ -116,9 +116,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent, PropType } from 'vue'
|
||||||
import axios from 'axios'
|
import axios, { AxiosError } from 'axios'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import { TRANSACTION_STATUS } from '../constants/status'
|
||||||
|
import type {
|
||||||
|
DeliveryFormData,
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
PromoData
|
||||||
|
} from '../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PaymentAuthorizePopup',
|
name: 'PaymentAuthorizePopup',
|
||||||
@@ -128,19 +136,19 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
delivery: {
|
delivery: {
|
||||||
type: Object,
|
type: Object as PropType<DeliveryFormData>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
type: Object,
|
type: Object as PropType<CustomerFormData>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
creditCards: {
|
creditCards: {
|
||||||
type: Array,
|
type: Array as PropType<CreditCardFormData[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
pricing: {
|
pricing: {
|
||||||
type: Object,
|
type: Object as PropType<PricingData>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
promoActive: {
|
promoActive: {
|
||||||
@@ -148,7 +156,7 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
promo: {
|
promo: {
|
||||||
type: Object,
|
type: Object as PropType<PromoData>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
totalAmount: {
|
totalAmount: {
|
||||||
@@ -175,9 +183,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedCard(): any {
|
selectedCard(): CreditCardFormData | undefined {
|
||||||
// Find the card that matches the delivery's payment_card_id
|
// Find the card that matches the delivery's payment_card_id
|
||||||
return this.creditCards.find((card: any) => card.id === this.delivery.payment_card_id)
|
return this.creditCards.find((card) => card.id === this.delivery.payment_card_id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -223,14 +231,14 @@ export default defineComponent({
|
|||||||
try {
|
try {
|
||||||
const endpoint = actionType === 'preauthorize' ? 'authorize' : 'charge'
|
const endpoint = actionType === 'preauthorize' ? 'authorize' : 'charge'
|
||||||
const payload = {
|
const payload = {
|
||||||
card_number: (this.selectedCard as any).card_number,
|
card_number: this.selectedCard.card_number,
|
||||||
expiration_date: `${(this.selectedCard as any).expiration_month}${(this.selectedCard as any).expiration_year}`,
|
expiration_date: `${this.selectedCard.expiration_month}${this.selectedCard.expiration_year}`,
|
||||||
cvv: (this.selectedCard as any).security_number,
|
cvv: this.selectedCard.security_number,
|
||||||
[actionType === 'preauthorize' ? 'preauthorize_amount' : 'charge_amount']: this.chargeAmount.toString(),
|
[actionType === 'preauthorize' ? 'preauthorize_amount' : 'charge_amount']: this.chargeAmount.toString(),
|
||||||
transaction_type: actionType === 'preauthorize' ? 1 : 0,
|
transaction_type: actionType === 'preauthorize' ? 1 : 0,
|
||||||
service_id: this.delivery.service_id || null, // Add service_id from delivery
|
service_id: this.delivery.service_id || null, // Add service_id from delivery
|
||||||
delivery_id: this.delivery.id, // Add delivery_id from delivery
|
delivery_id: this.delivery.id, // Add delivery_id from delivery
|
||||||
card_id: (this.selectedCard as any).id // Add card_id from selected card
|
card_id: this.selectedCard.id // Add card_id from selected card
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== DEBUG: PaymentAuthorizePopup payload ===')
|
console.log('=== DEBUG: PaymentAuthorizePopup payload ===')
|
||||||
@@ -245,7 +253,7 @@ export default defineComponent({
|
|||||||
{ withCredentials: true }
|
{ withCredentials: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (response.data && response.data.status === 0) {
|
if (response.data && response.data.status === TRANSACTION_STATUS.APPROVED) {
|
||||||
this.success = `${actionType === 'preauthorize' ? 'Preauthorization' : 'Charge'} successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`
|
this.success = `${actionType === 'preauthorize' ? 'Preauthorization' : 'Charge'} successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`
|
||||||
this.$emit('payment-success', {
|
this.$emit('payment-success', {
|
||||||
action: actionType,
|
action: actionType,
|
||||||
@@ -255,7 +263,8 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`Payment ${actionType} failed: ${response.data?.status || 'Unknown error'}`)
|
throw new Error(`Payment ${actionType} failed: ${response.data?.status || 'Unknown error'}`)
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
|
const error = err as AxiosError<{ detail?: string }>
|
||||||
this.error = error.response?.data?.detail || `Failed to ${actionType} payment`
|
this.error = error.response?.data?.detail || `Failed to ${actionType} payment`
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
|
|||||||
@@ -52,49 +52,51 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { mapState, mapActions } from 'pinia';
|
import { useSearchStore } from '../stores/search'
|
||||||
import { useSearchStore } from '../stores/search'; // Adjust path if needed
|
|
||||||
|
|
||||||
export default defineComponent({
|
// Store
|
||||||
name: 'SearchResults',
|
const searchStore = useSearchStore()
|
||||||
|
|
||||||
data() {
|
// Template ref
|
||||||
return {
|
const searchContainer = ref<HTMLElement>()
|
||||||
stateMap: {
|
|
||||||
|
// Reactive data
|
||||||
|
const stateMap = ref({
|
||||||
0: 'MA', 1: 'RI', 2: 'NH', 3: 'ME', 4: 'VT', 5: 'CT', 6: 'NY',
|
0: 'MA', 1: 'RI', 2: 'NH', 3: 'ME', 4: 'VT', 5: 'CT', 6: 'NY',
|
||||||
} as Record<number, string>,
|
} as Record<number, string>)
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// Computed properties
|
||||||
...mapState(useSearchStore, ['searchTerm', 'searchResults', 'isLoading']),
|
const searchTerm = computed(() => searchStore.searchTerm)
|
||||||
},
|
const searchResults = computed(() => searchStore.searchResults)
|
||||||
|
const isLoading = computed(() => searchStore.isLoading)
|
||||||
|
|
||||||
methods: {
|
// Functions
|
||||||
...mapActions(useSearchStore, ['clearSearch']),
|
const getStateName = (stateValue: number | string): string => {
|
||||||
|
|
||||||
getStateName(stateValue: number | string): string {
|
|
||||||
const stateNumber = Number(stateValue);
|
const stateNumber = Number(stateValue);
|
||||||
return this.stateMap[stateNumber] || 'N/A';
|
return stateMap.value[stateNumber] || 'N/A';
|
||||||
},
|
}
|
||||||
|
|
||||||
handleClickOutside(event: MouseEvent) {
|
const clearSearch = () => {
|
||||||
const searchContainer = this.$refs.searchContainer as HTMLElement;
|
searchStore.clearSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
const container = searchContainer.value;
|
||||||
const searchInput = document.getElementById('customer-search-input');
|
const searchInput = document.getElementById('customer-search-input');
|
||||||
|
|
||||||
if (searchContainer && !searchContainer.contains(event.target as Node) && searchInput && !searchInput.contains(event.target as Node)) {
|
if (container && !container.contains(event.target as Node) && searchInput && !searchInput.contains(event.target as Node)) {
|
||||||
this.clearSearch();
|
searchStore.clearSearch();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
document.addEventListener('mousedown', this.handleClickOutside);
|
onMounted(() => {
|
||||||
},
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
beforeUnmount() {
|
})
|
||||||
document.removeEventListener('mousedown', this.handleClickOutside);
|
|
||||||
},
|
onBeforeUnmount(() => {
|
||||||
});
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
95
src/constants/status.ts
Normal file
95
src/constants/status.ts
Normal file
@@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -212,9 +212,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
// NO CHANGES to the script block were made. All your logic remains intact.
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { defineComponent } from 'vue'
|
import { useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
import { useSearchStore } from '../../stores/search' // Adjust path if needed
|
import { useSearchStore } from '../../stores/search' // Adjust path if needed
|
||||||
@@ -231,64 +231,60 @@ interface RoutingOption {
|
|||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Router
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
data() {
|
const user = ref({} as User)
|
||||||
return {
|
const currentPhone = ref('')
|
||||||
// Initialize with empty objects to prevent template errors
|
const routingOptions = ref([
|
||||||
user: {} as User,
|
|
||||||
currentPhone: '',
|
|
||||||
routingOptions: [
|
|
||||||
{ value: 'main', label: '407323' },
|
{ value: 'main', label: '407323' },
|
||||||
{ value: 'sip', label: '407323_auburnoil' },
|
{ value: 'sip', label: '407323_auburnoil' },
|
||||||
{ value: 'cellphone1', label: 'Ed Cell' },
|
{ value: 'cellphone1', label: 'Ed Cell' },
|
||||||
{ value: 'cellphone2', label: 'Aneta Cell' },
|
{ value: 'cellphone2', label: 'Aneta Cell' },
|
||||||
{ value: 'test_did', label: 'Test DID' }
|
{ value: 'test_did', label: 'Test DID' }
|
||||||
] as RoutingOption[],
|
] as RoutingOption[])
|
||||||
selectedOption: null as RoutingOption | null,
|
const selectedOption = ref<RoutingOption | null>(null)
|
||||||
isRouteModalVisible: false,
|
const isRouteModalVisible = ref(false)
|
||||||
routeModalMode: 'confirm',
|
const routeModalMode = ref('confirm')
|
||||||
routeResponse: null as any,
|
const routeResponse = ref(null as any)
|
||||||
isTestModalVisible: false,
|
const isTestModalVisible = ref(false)
|
||||||
isTestLoading: false,
|
const isTestLoading = ref(false)
|
||||||
testResponse: null as any
|
const testResponse = ref(null as any)
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// Computed properties
|
||||||
searchStore() {
|
const searchStore = computed(() => useSearchStore())
|
||||||
return useSearchStore();
|
|
||||||
|
|
||||||
},
|
const userInitials = computed((): string => {
|
||||||
userInitials(): string {
|
if (!user.value || !user.value.user_name) return '';
|
||||||
if (!this.user || !this.user.user_name) return '';
|
const parts = user.value.user_name.split(' ');
|
||||||
const parts = this.user.user_name.split(' ');
|
|
||||||
return parts.length > 1
|
return parts.length > 1
|
||||||
? `${parts[0][0]}${parts[1][0]}`.toUpperCase()
|
? `${parts[0][0]}${parts[1][0]}`.toUpperCase()
|
||||||
: this.user.user_name.substring(0, 2).toUpperCase();
|
: user.value.user_name.substring(0, 2).toUpperCase();
|
||||||
},
|
})
|
||||||
currentDate(): string {
|
|
||||||
|
const currentDate = computed((): string => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||||
const day = now.getDate().toString().padStart(2, '0');
|
const day = now.getDate().toString().padStart(2, '0');
|
||||||
const year = now.getFullYear().toString().slice(-2);
|
const year = now.getFullYear().toString().slice(-2);
|
||||||
return `${month}/${day}/${year}`;
|
return `${month}/${day}/${year}`;
|
||||||
},
|
})
|
||||||
dayOfWeek(): string {
|
|
||||||
|
const dayOfWeek = computed((): string => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return now.toLocaleDateString('en-US', { weekday: 'long' });
|
return now.toLocaleDateString('en-US', { weekday: 'long' });
|
||||||
}
|
})
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Lifecycle
|
||||||
this.userStatus();
|
onMounted(() => {
|
||||||
},
|
userStatus()
|
||||||
mounted() {
|
updatestatus()
|
||||||
this.updatestatus();
|
fetchCurrentPhone()
|
||||||
this.fetchCurrentPhone();
|
})
|
||||||
},
|
|
||||||
methods: {
|
// Functions
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -299,15 +295,16 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
this.$router.push('/login');
|
router.push('/login');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
updatestatus() {
|
|
||||||
|
const updatestatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -317,15 +314,17 @@ export default defineComponent({
|
|||||||
if (response.data.update)
|
if (response.data.update)
|
||||||
console.log("Updated Status of Deliveries")
|
console.log("Updated Status of Deliveries")
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
logout() {
|
|
||||||
|
const logout = () => {
|
||||||
// Clear auth data
|
// Clear auth data
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
authStore.clearAuth();
|
authStore.clearAuth();
|
||||||
// Redirect to login
|
// Redirect to login
|
||||||
this.$router.push({ name: 'login' });
|
router.push({ name: 'login' });
|
||||||
},
|
}
|
||||||
fetchCurrentPhone() {
|
|
||||||
|
const fetchCurrentPhone = () => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/admin/voip_routing';
|
const path = import.meta.env.VITE_BASE_URL + '/admin/voip_routing';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -335,14 +334,15 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.current_phone) {
|
if (response.data.current_phone) {
|
||||||
this.currentPhone = response.data.current_phone;
|
currentPhone.value = response.data.current_phone;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error('Failed to fetch current routing:', error);
|
console.error('Failed to fetch current routing:', error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
routeTo(route: string): Promise<any> {
|
|
||||||
|
const routeTo = (route: string): Promise<any> => {
|
||||||
const path = `${import.meta.env.VITE_VOIPMS_URL}/route/${route}`;
|
const path = `${import.meta.env.VITE_VOIPMS_URL}/route/${route}`;
|
||||||
return axios({
|
return axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@@ -350,46 +350,50 @@ export default defineComponent({
|
|||||||
withCredentials: true, headers: authHeader()
|
withCredentials: true, headers: authHeader()
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.routeResponse = response.data;
|
routeResponse.value = response.data;
|
||||||
// Find the corresponding label
|
// Find the corresponding label
|
||||||
const option = this.routingOptions.find(opt => opt.value === route);
|
const option = routingOptions.value.find(opt => opt.value === route);
|
||||||
if (option) {
|
if (option) {
|
||||||
this.currentPhone = option.label;
|
currentPhone.value = option.label;
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
showConfirmRoute(option: RoutingOption) {
|
|
||||||
this.selectedOption = option;
|
const showConfirmRoute = (option: RoutingOption) => {
|
||||||
|
selectedOption.value = option;
|
||||||
if (option.value === 'test_did') {
|
if (option.value === 'test_did') {
|
||||||
this.testDid();
|
testDid();
|
||||||
} else {
|
} else {
|
||||||
this.isRouteModalVisible = true;
|
isRouteModalVisible.value = true;
|
||||||
this.routeModalMode = 'confirm';
|
routeModalMode.value = 'confirm';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
proceedRoute() {
|
|
||||||
if (this.selectedOption && this.selectedOption.value !== 'test_did') {
|
const proceedRoute = () => {
|
||||||
this.routeModalMode = 'loading';
|
if (selectedOption.value && selectedOption.value.value !== 'test_did') {
|
||||||
this.routeTo(this.selectedOption.value)
|
routeModalMode.value = 'loading';
|
||||||
|
routeTo(selectedOption.value.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.routeModalMode = 'result';
|
routeModalMode.value = 'result';
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
this.routeResponse = { error: error.message };
|
routeResponse.value = { error: error.message };
|
||||||
this.routeModalMode = 'result';
|
routeModalMode.value = 'result';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
closeRouteModal() {
|
|
||||||
this.isRouteModalVisible = false;
|
const closeRouteModal = () => {
|
||||||
this.routeModalMode = 'confirm';
|
isRouteModalVisible.value = false;
|
||||||
this.routeResponse = null;
|
routeModalMode.value = 'confirm';
|
||||||
this.selectedOption = null;
|
routeResponse.value = null;
|
||||||
},
|
selectedOption.value = null;
|
||||||
testDid() {
|
}
|
||||||
this.isTestModalVisible = true;
|
|
||||||
this.isTestLoading = true;
|
const testDid = () => {
|
||||||
|
isTestModalVisible.value = true;
|
||||||
|
isTestLoading.value = true;
|
||||||
const path = `${import.meta.env.VITE_VOIPMS_URL}/test/did`;
|
const path = `${import.meta.env.VITE_VOIPMS_URL}/test/did`;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -397,14 +401,12 @@ export default defineComponent({
|
|||||||
withCredentials: true, headers: authHeader()
|
withCredentials: true, headers: authHeader()
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.testResponse = response.data;
|
testResponse.value = response.data;
|
||||||
this.isTestLoading = false;
|
isTestLoading.value = false;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
this.testResponse = { status: 'error', message: error.message };
|
testResponse.value = { status: 'error', message: error.message };
|
||||||
this.isTestLoading = false;
|
isTestLoading.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,31 +15,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
import { defineComponent } from "vue";
|
// Reactive data (though not used in template)
|
||||||
|
const user = ref(null)
|
||||||
|
const loaded = ref(false)
|
||||||
|
const clicked = ref(false)
|
||||||
|
const hovered = ref(false)
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
export default defineComponent({
|
// Component mounted
|
||||||
name: "HeaderNoAuth",
|
})
|
||||||
mounted() {
|
|
||||||
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
user: null,
|
|
||||||
loaded: false,
|
|
||||||
clicked: false,
|
|
||||||
hovered: false,
|
|
||||||
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
@@ -110,42 +110,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../services/auth.header'
|
import authHeader from '../services/auth.header'
|
||||||
import Header from '../layouts/headers/headerauth.vue'
|
import Header from '../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../layouts/sidebar/sidebar.vue'
|
import SideBar from '../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../layouts/footers/footer.vue'
|
import Footer from '../layouts/footers/footer.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
// Props
|
||||||
name: 'Home',
|
const props = defineProps<{
|
||||||
|
clickCount?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
components: {
|
// Router
|
||||||
Header,
|
const router = useRouter()
|
||||||
SideBar,
|
|
||||||
Footer,
|
// Reactive data
|
||||||
},
|
const token = ref(null)
|
||||||
props: {
|
const call_count = ref(0)
|
||||||
clickCount: Number
|
const delivery_count = ref(0)
|
||||||
},
|
const delivery_count_delivered = ref(0)
|
||||||
data() {
|
const price_from_supplier = ref(0)
|
||||||
return {
|
const today_oil_price = ref(0)
|
||||||
token: null,
|
const price_for_employee = ref(0)
|
||||||
call_count:0,
|
const price_same_day = ref(0)
|
||||||
delivery_count: 0,
|
const price_prime = ref(0)
|
||||||
delivery_count_delivered: 0,
|
const price_emergency = ref(0)
|
||||||
price_from_supplier: 0,
|
const user = ref({
|
||||||
today_oil_price: 0,
|
|
||||||
price_for_employee: 0,
|
|
||||||
price_same_day: 0,
|
|
||||||
price_prime: 0,
|
|
||||||
price_emergency: 0,
|
|
||||||
user: {
|
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
user_name: '',
|
user_name: '',
|
||||||
},
|
})
|
||||||
employee: {
|
const employee = ref({
|
||||||
id: '',
|
id: '',
|
||||||
user_id: '',
|
user_id: '',
|
||||||
employee_last_name: "",
|
employee_last_name: "",
|
||||||
@@ -160,30 +157,24 @@ export default defineComponent({
|
|||||||
employee_end_date: "",
|
employee_end_date: "",
|
||||||
employee_type: '',
|
employee_type: '',
|
||||||
employee_state: '',
|
employee_state: '',
|
||||||
},
|
})
|
||||||
total_gallons_past_week: 0,
|
const total_gallons_past_week = ref(0)
|
||||||
total_profit_past_week: 0,
|
const total_profit_past_week = ref(0)
|
||||||
total_deliveries: 0,
|
const total_deliveries = ref(0)
|
||||||
|
const loaded = ref(false)
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
today_delivery_count()
|
||||||
|
today_delivery_delivered()
|
||||||
|
today_price_oil()
|
||||||
|
totalgallonsweek()
|
||||||
|
totalprofitweek()
|
||||||
|
})
|
||||||
|
|
||||||
loaded: false,
|
// Functions
|
||||||
|
const userStatus = () => {
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.userStatus()
|
|
||||||
this.today_delivery_count()
|
|
||||||
this.today_delivery_delivered()
|
|
||||||
this.today_price_oil()
|
|
||||||
this.totalgallonsweek()
|
|
||||||
this.totalprofitweek()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -193,18 +184,16 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
this.employeeStatus()
|
employeeStatus()
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
this.$router.push('/login');
|
router.push('/login');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
},
|
const totalgallonsweek = () => {
|
||||||
|
|
||||||
totalgallonsweek() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/gallons/week';
|
let path = import.meta.env.VITE_BASE_URL + '/stats/gallons/week';
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -213,11 +202,11 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.total_gallons_past_week = response.data.total;
|
total_gallons_past_week.value = response.data.total;
|
||||||
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
totalprofitweek() {
|
|
||||||
|
const totalprofitweek = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/money/profit/week';
|
let path = import.meta.env.VITE_BASE_URL + '/money/profit/week';
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -226,13 +215,13 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.total_profit_past_week = response.data.total_profit;
|
total_profit_past_week.value = response.data.total_profit;
|
||||||
this.total_deliveries = response.data.total_deliveries;
|
total_deliveries.value = response.data.total_deliveries;
|
||||||
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
employeeStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/employee/userid/' + this.user.user_id;
|
const employeeStatus = () => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + '/employee/userid/' + user.value.user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
@@ -240,12 +229,12 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.employee = response.data;
|
employee.value = response.data;
|
||||||
this.loaded = true;
|
loaded.value = true;
|
||||||
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
total_calls() {
|
|
||||||
|
const total_calls = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/call/count/today'
|
let path = import.meta.env.VITE_BASE_URL + '/stats/call/count/today'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -254,10 +243,11 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.call_count = response.data.data;
|
call_count.value = response.data.data;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
today_delivery_count() {
|
|
||||||
|
const today_delivery_count = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/today'
|
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/today'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -266,10 +256,11 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.delivery_count = response.data.data;
|
delivery_count.value = response.data.data;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
today_delivery_delivered() {
|
|
||||||
|
const today_delivery_delivered = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/delivered/today'
|
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/delivered/today'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -279,10 +270,11 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
console.log(response.data)
|
console.log(response.data)
|
||||||
this.delivery_count_delivered = response.data.data;
|
delivery_count_delivered.value = response.data.data;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
today_price_oil() {
|
|
||||||
|
const today_price_oil = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -291,19 +283,12 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.price_from_supplier = response.data.price_from_supplier;
|
price_from_supplier.value = response.data.price_from_supplier;
|
||||||
this.today_oil_price = response.data.price_for_customer;
|
today_oil_price.value = response.data.price_for_customer;
|
||||||
this.price_for_employee = response.data.price_for_employee;
|
price_for_employee.value = response.data.price_for_employee;
|
||||||
this.price_same_day = response.data.price_same_day;
|
price_same_day.value = response.data.price_same_day;
|
||||||
this.price_prime = response.data.price_prime;
|
price_prime.value = response.data.price_prime;
|
||||||
this.price_emergency = response.data.price_emergency;
|
price_emergency.value = response.data.price_emergency;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
<script setup lang="ts">
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import OilPrice from '../admin/oilprice.vue';
|
const OilPrice = () => import('../admin/oilprice.vue');
|
||||||
|
|
||||||
import Promo from '../admin/promo/promo.vue';
|
const Promo = () => import('../admin/promo/promo.vue');
|
||||||
import PromoCreate from '../admin/promo/create.vue';
|
const PromoCreate = () => import('../admin/promo/create.vue');
|
||||||
import PromoEdit from '../admin/promo/edit.vue';
|
const PromoEdit = () => import('../admin/promo/edit.vue');
|
||||||
|
|
||||||
|
|
||||||
const adminRoutes = [
|
const adminRoutes = [
|
||||||
@@ -35,5 +35,3 @@ const adminRoutes = [
|
|||||||
|
|
||||||
export default adminRoutes
|
export default adminRoutes
|
||||||
//sourceMappingURL=index.ts.map
|
//sourceMappingURL=index.ts.map
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Login from '../auth/login.vue';
|
const Login = () => import('../auth/login.vue');
|
||||||
import Register from '../auth/register.vue';
|
const Register = () => import('../auth/register.vue');
|
||||||
import changePassword from '../auth/changepassword.vue';
|
const changePassword = () => import('../auth/changepassword.vue');
|
||||||
import lostPassword from '../auth/lostpassword.vue';
|
const lostPassword = () => import('../auth/lostpassword.vue');
|
||||||
|
|
||||||
const authRoutes = [
|
const authRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- src/pages/authorize/PaymentForm.vue -->
|
<!-- src/pages/authorize/PaymentForm.vue -->
|
||||||
<!-- <template>
|
<template>
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
<h1 class="text-2xl font-bold mb-4">HVAC Payment</h1>
|
<h1 class="text-2xl font-bold mb-4">HVAC Payment</h1>
|
||||||
|
|
||||||
@@ -87,89 +87,91 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="transactionResult" class="mt-8 p-4 rounded-lg" :class="transactionResult.status === 'approved' || transactionResult.status === 'authorized' || transactionResult.status === 'captured' ? 'bg-green-200' : 'bg-red-200'">
|
<div v-if="transactionResult" class="mt-8 p-4 rounded-lg" :class="transactionResult.status === 0 || transactionResult.status === 1 || transactionResult.status === 2 ? 'bg-green-200' : 'bg-red-200'">
|
||||||
<h3 class="font-bold">Transaction Result</h3>
|
<h3 class="font-bold">Transaction Result</h3>
|
||||||
<p>Status: {{ transactionResult.status }}</p>
|
<p>Status: {{ transactionResult.status }}</p>
|
||||||
<p v-if="transactionResult.auth_net_transaction_id">Transaction ID: {{ transactionResult.auth_net_transaction_id }}</p>
|
<p v-if="transactionResult.transaction_id">Transaction ID: {{ transactionResult.transaction_id }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import axios from 'axios';
|
import { ref } from 'vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { AuthorizeTransaction } from '../../types/models'
|
||||||
|
|
||||||
const API_URL = 'http://localhost:8000/api'; // Your FastAPI backend URL
|
const API_URL = 'http://localhost:8000/api'; // Your FastAPI backend URL
|
||||||
|
|
||||||
export default {
|
// Reactive data
|
||||||
data() {
|
const directCharge = ref({
|
||||||
return {
|
|
||||||
directCharge: {
|
|
||||||
cardNumber: '',
|
cardNumber: '',
|
||||||
expirationDate: '',
|
expirationDate: '',
|
||||||
cvv: '',
|
cvv: '',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
},
|
})
|
||||||
authorization: {
|
|
||||||
|
const authorization = ref({
|
||||||
cardNumber: '',
|
cardNumber: '',
|
||||||
expirationDate: '',
|
expirationDate: '',
|
||||||
cvv: '',
|
cvv: '',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
},
|
})
|
||||||
capture: {
|
|
||||||
|
const capture = ref({
|
||||||
amount: 0,
|
amount: 0,
|
||||||
},
|
})
|
||||||
authorizedTransactionId: null,
|
|
||||||
transactionResult: null,
|
const authorizedTransactionId = ref<string | null>(null)
|
||||||
customerId: 1 // Assuming a customer with ID 1 exists for this example
|
const transactionResult = ref<AuthorizeTransaction | null>(null)
|
||||||
};
|
const customerId = ref(1) // Assuming a customer with ID 1 exists for this example
|
||||||
},
|
|
||||||
methods: {
|
// Functions
|
||||||
async submitDirectCharge() {
|
const submitDirectCharge = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${API_URL}/charge/?customer_id=${this.customerId}`, {
|
const response = await axios.post(`${API_URL}/charge/?customer_id=${customerId.value}`, {
|
||||||
card_number: this.directCharge.cardNumber,
|
card_number: directCharge.value.cardNumber,
|
||||||
expiration_date: this.directCharge.expirationDate,
|
expiration_date: directCharge.value.expirationDate,
|
||||||
cvv: this.directCharge.cvv,
|
cvv: directCharge.value.cvv,
|
||||||
amount: this.directCharge.amount,
|
amount: directCharge.value.amount,
|
||||||
transaction_type: 'charge'
|
transaction_type: 'charge'
|
||||||
});
|
});
|
||||||
this.transactionResult = response.data;
|
transactionResult.value = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.transactionResult = { status: 'Error processing transaction' };
|
transactionResult.value = null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async submitAuthorization() {
|
|
||||||
|
const submitAuthorization = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${API_URL}/authorize/?customer_id=${this.customerId}`, {
|
const response = await axios.post(`${API_URL}/authorize/?customer_id=${customerId.value}`, {
|
||||||
card_number: this.authorization.cardNumber,
|
card_number: authorization.value.cardNumber,
|
||||||
expiration_date: this.authorization.expirationDate,
|
expiration_date: authorization.value.expirationDate,
|
||||||
cvv: this.authorization.cvv,
|
cvv: authorization.value.cvv,
|
||||||
amount: this.authorization.amount,
|
amount: authorization.value.amount,
|
||||||
transaction_type: 'auth'
|
transaction_type: 'auth'
|
||||||
});
|
});
|
||||||
this.transactionResult = response.data;
|
transactionResult.value = response.data;
|
||||||
if (response.data.status === 'authorized') {
|
if (response.data.status === 'authorized') {
|
||||||
this.authorizedTransactionId = response.data.auth_net_transaction_id;
|
authorizedTransactionId.value = response.data.auth_net_transaction_id;
|
||||||
this.capture.amount = this.authorization.amount; // Pre-fill capture amount
|
capture.value.amount = authorization.value.amount; // Pre-fill capture amount
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.transactionResult = { status: 'Error processing authorization' };
|
transactionResult.value = null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async submitCapture() {
|
|
||||||
|
const submitCapture = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`${API_URL}/capture/`, {
|
const response = await axios.post(`${API_URL}/capture/`, {
|
||||||
amount: this.capture.amount,
|
amount: capture.value.amount,
|
||||||
auth_net_transaction_id: this.authorizedTransactionId
|
auth_net_transaction_id: authorizedTransactionId.value
|
||||||
});
|
});
|
||||||
this.transactionResult = response.data;
|
transactionResult.value = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.transactionResult = { status: 'Error processing capture' };
|
transactionResult.value = null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
</script>
|
||||||
};
|
|
||||||
</script> -->
|
|
||||||
|
|||||||
@@ -177,48 +177,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { AutoDelivery } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
|
|
||||||
// Define a type for the delivery object for better code quality
|
// Reactive data
|
||||||
interface AutoDelivery {
|
const user = ref(null)
|
||||||
id: number;
|
const deliveries = ref<AutoDelivery[]>([])
|
||||||
customer_id: number;
|
// --- NEW: Data properties for sorting ---
|
||||||
last_fill: string | null;
|
const sortKey = ref<'tank_level_percent' | 'hot_water_summer' | keyof AutoDelivery>('tank_level_percent')
|
||||||
estimated_gallons_left: number;
|
const sortAsc = ref(true)
|
||||||
days_since_last_fill: number;
|
|
||||||
customer_full_name: string;
|
|
||||||
customer_address: string;
|
|
||||||
customer_town: string;
|
|
||||||
house_factor: number;
|
|
||||||
tank_size: number;
|
|
||||||
auto_status: number;
|
|
||||||
hot_water_summer: number;
|
|
||||||
open_ticket_id?: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
// Computed properties
|
||||||
name: 'AutomaticHome',
|
// --- NEW: Computed property to handle sorting ---
|
||||||
components: {
|
const sortedDeliveries = computed((): AutoDelivery[] => {
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as AutoDelivery[],
|
|
||||||
// --- NEW: Data properties for sorting ---
|
|
||||||
sortKey: 'tank_level_percent' as keyof AutoDelivery | 'tank_level_percent' | 'hot_water_summer',
|
|
||||||
sortAsc: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// --- NEW: Computed property to handle sorting ---
|
|
||||||
sortedDeliveries(): AutoDelivery[] {
|
|
||||||
// Create a copy to avoid mutating the original array
|
// Create a copy to avoid mutating the original array
|
||||||
const sorted = [...this.deliveries];
|
const sorted = [...deliveries.value];
|
||||||
|
|
||||||
sorted.sort((a, b) => {
|
sorted.sort((a, b) => {
|
||||||
// First, prioritize auto_status = 3 to be at the top
|
// First, prioritize auto_status = 3 to be at the top
|
||||||
@@ -234,14 +211,14 @@ export default defineComponent({
|
|||||||
let valB: any;
|
let valB: any;
|
||||||
|
|
||||||
// Special case for our calculated percentage
|
// Special case for our calculated percentage
|
||||||
if (this.sortKey === 'tank_level_percent') {
|
if (sortKey.value === 'tank_level_percent') {
|
||||||
valA = this.getTankLevelPercentage(a);
|
valA = getTankLevelPercentage(a);
|
||||||
valB = this.getTankLevelPercentage(b);
|
valB = getTankLevelPercentage(b);
|
||||||
} else {
|
} else {
|
||||||
valA = a[this.sortKey as keyof AutoDelivery];
|
valA = a[sortKey.value as keyof AutoDelivery];
|
||||||
valB = b[this.sortKey as keyof AutoDelivery];
|
valB = b[sortKey.value as keyof AutoDelivery];
|
||||||
// Special handling for hot_water_summer to ensure it's number
|
// Special handling for hot_water_summer to ensure it's number
|
||||||
if (this.sortKey === 'hot_water_summer') {
|
if (sortKey.value === 'hot_water_summer') {
|
||||||
valA = valA || 0;
|
valA = valA || 0;
|
||||||
valB = valB || 0;
|
valB = valB || 0;
|
||||||
}
|
}
|
||||||
@@ -253,62 +230,65 @@ export default defineComponent({
|
|||||||
|
|
||||||
// Comparison logic
|
// Comparison logic
|
||||||
if (valA < valB) {
|
if (valA < valB) {
|
||||||
return this.sortAsc ? -1 : 1;
|
return sortAsc.value ? -1 : 1;
|
||||||
}
|
}
|
||||||
if (valA > valB) {
|
if (valA > valB) {
|
||||||
return this.sortAsc ? 1 : -1;
|
return sortAsc.value ? 1 : -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
})
|
||||||
},
|
|
||||||
created() {
|
// Lifecycle
|
||||||
this.userStatus();
|
onMounted(() => {
|
||||||
this.get_oil_orders();
|
userStatus();
|
||||||
},
|
get_oil_orders();
|
||||||
methods: {
|
})
|
||||||
// --- NEW: Method to handle sorting ---
|
|
||||||
sortBy(key: keyof AutoDelivery | 'tank_level_percent' | 'hot_water_summer') {
|
// Functions
|
||||||
if (this.sortKey === key) {
|
// --- NEW: Method to handle sorting ---
|
||||||
|
const sortBy = (key: keyof AutoDelivery | 'tank_level_percent' | 'hot_water_summer') => {
|
||||||
|
if (sortKey.value === key) {
|
||||||
// If clicking the same key, reverse the direction
|
// If clicking the same key, reverse the direction
|
||||||
this.sortAsc = !this.sortAsc;
|
sortAsc.value = !sortAsc.value;
|
||||||
} else {
|
} else {
|
||||||
// If clicking a new key, set it and default to ascending
|
// If clicking a new key, set it and default to ascending
|
||||||
this.sortKey = key;
|
sortKey.value = key;
|
||||||
this.sortAsc = true;
|
sortAsc.value = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// --- NEW: Helper method for percentage calculation ---
|
|
||||||
getTankLevelPercentage(oil: AutoDelivery): number {
|
// --- NEW: Helper method for percentage calculation ---
|
||||||
|
const getTankLevelPercentage = (oil: AutoDelivery): number => {
|
||||||
if (!oil.tank_size || oil.tank_size === 0 || oil.last_fill === null) {
|
if (!oil.tank_size || oil.tank_size === 0 || oil.last_fill === null) {
|
||||||
return 0; // Return 0 if tank size is invalid or it's a new customer
|
return 0; // Return 0 if tank size is invalid or it's a new customer
|
||||||
}
|
}
|
||||||
return (oil.estimated_gallons_left / oil.tank_size) * 100;
|
return (oil.estimated_gallons_left / oil.tank_size) * 100;
|
||||||
},
|
}
|
||||||
userStatus() {
|
|
||||||
|
const userStatus = () => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null;
|
user.value = null;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
get_oil_orders() {
|
|
||||||
|
const get_oil_orders = () => {
|
||||||
const path = import.meta.env.VITE_AUTO_URL + '/delivery/all/customers';
|
const path = import.meta.env.VITE_AUTO_URL + '/delivery/all/customers';
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.deliveries = response.data;
|
deliveries.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("Failed to fetch automatic deliveries:", error);
|
console.error("Failed to fetch automatic deliveries:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import AutomaticHome from './home.vue';
|
const AutomaticHome = () => import('./home.vue');
|
||||||
import AutomaticView from './view.vue';
|
const AutomaticView = () => import('./view.vue');
|
||||||
|
|
||||||
const autoRoutes = [
|
const autoRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,16 +61,16 @@
|
|||||||
<div class="font-bold text-sm">Payment Status</div>
|
<div class="font-bold text-sm">Payment Status</div>
|
||||||
<div class="badge badge-lg"
|
<div class="badge badge-lg"
|
||||||
:class="{
|
:class="{
|
||||||
'badge-success': autoTicket.payment_status == 3,
|
'badge-success': autoTicket.payment_status === PAYMENT_STATUS.PAID,
|
||||||
'badge-info': autoTicket.payment_status == 1,
|
'badge-info': autoTicket.payment_status === PAYMENT_STATUS.PRE_AUTHORIZED,
|
||||||
'badge-error': autoTicket.payment_status == 0 || autoTicket.payment_status == null,
|
'badge-error': autoTicket.payment_status === PAYMENT_STATUS.UNPAID || autoTicket.payment_status == null,
|
||||||
'badge-warning': [2, 4].includes(autoTicket.payment_status)
|
'badge-warning': [PAYMENT_STATUS.PROCESSING, PAYMENT_STATUS.FAILED].includes(autoTicket.payment_status)
|
||||||
}">
|
}">
|
||||||
<span v-if="autoTicket.payment_status == 0 || autoTicket.payment_status == null">Unpaid</span>
|
<span v-if="autoTicket.payment_status === PAYMENT_STATUS.UNPAID || autoTicket.payment_status == null">Unpaid</span>
|
||||||
<span v-else-if="autoTicket.payment_status == 1">Pre-authorized</span>
|
<span v-else-if="autoTicket.payment_status === PAYMENT_STATUS.PRE_AUTHORIZED">Pre-authorized</span>
|
||||||
<span v-else-if="autoTicket.payment_status == 2">Processing</span>
|
<span v-else-if="autoTicket.payment_status === PAYMENT_STATUS.PROCESSING">Processing</span>
|
||||||
<span v-else-if="autoTicket.payment_status == 3">Paid</span>
|
<span v-else-if="autoTicket.payment_status === PAYMENT_STATUS.PAID">Paid</span>
|
||||||
<span v-else-if="autoTicket.payment_status == 4">Failed</span>
|
<span v-else-if="autoTicket.payment_status === PAYMENT_STATUS.FAILED">Failed</span>
|
||||||
<span v-else>Unknown</span>
|
<span v-else>Unknown</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,8 +150,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span>Status:</span>
|
<span>Status:</span>
|
||||||
<span :class="transaction.status === 0 ? 'text-success' : 'text-error'">
|
<span :class="transaction.status === TRANSACTION_STATUS.APPROVED ? 'text-success' : 'text-error'">
|
||||||
{{ transaction.status === 0 ? 'Approved' : 'Declined' }}
|
{{ transaction.status === TRANSACTION_STATUS.APPROVED ? 'Approved' : 'Declined' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,25 +242,21 @@
|
|||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import {
|
||||||
interface UserCard {
|
PAYMENT_STATUS,
|
||||||
id: number;
|
AUTO_STATUS,
|
||||||
last_four: string;
|
TRANSACTION_STATUS,
|
||||||
type_of_card: string;
|
getPaymentStatusLabel,
|
||||||
expiration_month: number;
|
getTransactionStatusLabel
|
||||||
expiration_year: number;
|
} from '../../constants/status';
|
||||||
name_on_card: string;
|
|
||||||
card_number: string;
|
|
||||||
security_number: string;
|
|
||||||
main_card?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
import Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import useValidate from "@vuelidate/core";
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
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({
|
export default defineComponent({
|
||||||
name: 'automaticDeliveryView',
|
name: 'automaticDeliveryView',
|
||||||
@@ -273,62 +269,25 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
v$: useValidate(),
|
v$: useValidate(),
|
||||||
autoTicket: {
|
autoTicket: {} as any,
|
||||||
id: 0,
|
autoDelivery: {} as AutoDelivery,
|
||||||
customer_id: 0,
|
customer: {} as Customer,
|
||||||
account_number: '',
|
transaction: {} as AuthorizeTransaction,
|
||||||
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,
|
|
||||||
userCardfound: false,
|
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: {
|
methods: {
|
||||||
format_date(value: string) {
|
format_date(value: string) {
|
||||||
if (value) {
|
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) {
|
switch (transactionType) {
|
||||||
case 1: return 'text-blue-600'; // Auth
|
case 1: return 'text-blue-600'; // Auth
|
||||||
case 0: return 'text-orange-600'; // Charge
|
case 0: return 'text-orange-600'; // Charge
|
||||||
@@ -360,7 +320,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getCustomer(customerId: number) {
|
getCustomer(customerId: number | undefined) {
|
||||||
if (!customerId) return;
|
if (!customerId) return;
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId}`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
@@ -384,12 +344,12 @@ export default defineComponent({
|
|||||||
this.userCard = response.data;
|
this.userCard = response.data;
|
||||||
this.userCardfound = true;
|
this.userCardfound = true;
|
||||||
} else {
|
} else {
|
||||||
this.userCard = {} as UserCard;
|
this.userCard = {} as CreditCard;
|
||||||
this.userCardfound = false;
|
this.userCardfound = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((_error: any) => {
|
.catch((_error: any) => {
|
||||||
this.userCard = {} as UserCard;
|
this.userCard = {} as CreditCard;
|
||||||
this.userCardfound = false;
|
this.userCardfound = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -438,7 +398,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("No transaction found for delivery:", error);
|
console.error("No transaction found for delivery:", error);
|
||||||
this.transaction = null;
|
this.transaction = {} as AuthorizeTransaction;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
import CardHome from '../card/home.vue';
|
const CardHome = () => import('../card/home.vue');
|
||||||
import AddCardCreate from '../card/addcard.vue';
|
const AddCardCreate = () => import('../card/addcard.vue');
|
||||||
import EditCard from "./editcard.vue";
|
const EditCard = () => import("./editcard.vue");
|
||||||
|
|
||||||
|
|
||||||
const cardRoutes = [
|
const cardRoutes = [
|
||||||
|
|||||||
@@ -120,68 +120,60 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { ServicePlan, Customer } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
interface ServicePlan {
|
const route = useRoute()
|
||||||
id: number;
|
const router = useRouter()
|
||||||
customer_id: number;
|
|
||||||
contract_plan: number;
|
|
||||||
contract_years: number;
|
|
||||||
contract_start_date: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Customer {
|
// Reactive data
|
||||||
id: number;
|
const customerId = ref(route.params.id as string)
|
||||||
customer_first_name: string;
|
const customerName = ref('')
|
||||||
customer_last_name: string;
|
const servicePlan = ref(null as ServicePlan | null)
|
||||||
}
|
const formData = ref({
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'ServicePlanEdit',
|
|
||||||
components: { Footer },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
customerId: this.$route.params.id as string,
|
|
||||||
customerName: '',
|
|
||||||
servicePlan: null as ServicePlan | null,
|
|
||||||
formData: {
|
|
||||||
contract_plan: 0,
|
contract_plan: 0,
|
||||||
contract_years: 1,
|
contract_years: 1,
|
||||||
contract_start_date: '',
|
contract_start_date: '',
|
||||||
},
|
})
|
||||||
renewalDate: '',
|
const renewalDate = ref('')
|
||||||
|
|
||||||
|
// Computed
|
||||||
|
const computedEndDate = computed(() => {
|
||||||
|
return (startDate: string, years: number): string => {
|
||||||
|
if (!startDate) return 'N/A';
|
||||||
|
const date = new Date(startDate);
|
||||||
|
date.setFullYear(date.getFullYear() + years);
|
||||||
|
return date.toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
created() {
|
|
||||||
this.loadCustomer();
|
// Functions
|
||||||
this.loadServicePlan();
|
const loadCustomer = async () => {
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async loadCustomer() {
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${this.customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId.value}`;
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
const response = await axios.get(path, { headers: authHeader() });
|
||||||
const customer: Customer = response.data;
|
const customer: Customer = response.data;
|
||||||
this.customerName = `${customer.customer_first_name} ${customer.customer_last_name}`;
|
customerName.value = `${customer.customer_first_name} ${customer.customer_last_name}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load customer:', error);
|
console.error('Failed to load customer:', error);
|
||||||
notify({ title: "Error", text: "Failed to load customer information.", type: "error" });
|
notify({ title: "Error", text: "Failed to load customer information.", type: "error" });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async loadServicePlan() {
|
const loadServicePlan = async () => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/customer/${this.customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/customer/${customerId.value}`;
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
const response = await axios.get(path, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data && response.data.contract_plan !== undefined) {
|
if (response.data && response.data.contract_plan !== undefined) {
|
||||||
this.servicePlan = response.data;
|
servicePlan.value = response.data;
|
||||||
this.formData = {
|
formData.value = {
|
||||||
contract_plan: response.data.contract_plan,
|
contract_plan: response.data.contract_plan,
|
||||||
contract_years: response.data.contract_years,
|
contract_years: response.data.contract_years,
|
||||||
contract_start_date: response.data.contract_start_date,
|
contract_start_date: response.data.contract_start_date,
|
||||||
@@ -191,19 +183,19 @@ export default defineComponent({
|
|||||||
// Plan doesn't exist yet, that's okay
|
// Plan doesn't exist yet, that's okay
|
||||||
console.log('No existing service plan found');
|
console.log('No existing service plan found');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async onSubmit() {
|
const onSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
customer_id: parseInt(this.customerId),
|
customer_id: parseInt(customerId.value),
|
||||||
...this.formData
|
...formData.value
|
||||||
};
|
};
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
if (this.servicePlan) {
|
if (servicePlan.value) {
|
||||||
// Update existing plan
|
// Update existing plan
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/update/${this.customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/update/${customerId.value}`;
|
||||||
response = await axios.put(path, payload, { headers: authHeader() });
|
response = await axios.put(path, payload, { headers: authHeader() });
|
||||||
} else {
|
} else {
|
||||||
// Create new plan
|
// Create new plan
|
||||||
@@ -214,29 +206,29 @@ export default defineComponent({
|
|||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
text: `Service plan ${this.servicePlan ? 'updated' : 'created'} successfully!`,
|
text: `Service plan ${servicePlan.value ? 'updated' : 'created'} successfully!`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
// Redirect to profile page after successful submission
|
// Redirect to profile page after successful submission
|
||||||
this.$router.push({ name: 'customerProfile', params: { id: this.customerId } });
|
router.push({ name: 'customerProfile', params: { id: customerId.value } });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save service plan:', error);
|
console.error('Failed to save service plan:', error);
|
||||||
notify({ title: "Error", text: "Failed to save service plan.", type: "error" });
|
notify({ title: "Error", text: "Failed to save service plan.", type: "error" });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async deletePlan() {
|
const deletePlan = async () => {
|
||||||
if (!confirm('Are you sure you want to delete this service plan?')) return;
|
if (!confirm('Are you sure you want to delete this service plan?')) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/delete/${this.customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/plans/delete/${customerId.value}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader() });
|
const response = await axios.delete(path, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
notify({ title: "Success", text: "Service plan deleted successfully!", type: "success" });
|
notify({ title: "Success", text: "Service plan deleted successfully!", type: "success" });
|
||||||
this.servicePlan = null;
|
servicePlan.value = null;
|
||||||
this.formData = {
|
formData.value = {
|
||||||
contract_plan: 0,
|
contract_plan: 0,
|
||||||
contract_years: 1,
|
contract_years: 1,
|
||||||
contract_start_date: '',
|
contract_start_date: '',
|
||||||
@@ -246,41 +238,41 @@ export default defineComponent({
|
|||||||
console.error('Failed to delete service plan:', error);
|
console.error('Failed to delete service plan:', error);
|
||||||
notify({ title: "Error", text: "Failed to delete service plan.", type: "error" });
|
notify({ title: "Error", text: "Failed to delete service plan.", type: "error" });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
renewContract() {
|
const renewContract = () => {
|
||||||
if (!this.renewalDate) {
|
if (!renewalDate.value) {
|
||||||
notify({ title: "Error", text: "Please select a renewal date.", type: "error" });
|
notify({ title: "Error", text: "Please select a renewal date.", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.formData.contract_years += 1;
|
formData.value.contract_years += 1;
|
||||||
this.formData.contract_start_date = this.renewalDate;
|
formData.value.contract_start_date = renewalDate.value;
|
||||||
this.renewalDate = '';
|
renewalDate.value = '';
|
||||||
|
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
text: "Contract renewed! Years increased by 1 and start date updated.",
|
text: "Contract renewed! Years increased by 1 and start date updated.",
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPlanName(planType: number): string {
|
const getPlanName = (planType: number): string => {
|
||||||
const planNames: { [key: number]: string } = {
|
const planNames: { [key: number]: string } = {
|
||||||
1: 'Standard Plan',
|
1: 'Standard Plan',
|
||||||
2: 'Premium Plan'
|
2: 'Premium Plan'
|
||||||
};
|
};
|
||||||
return planNames[planType] || 'No Plan';
|
return planNames[planType] || 'No Plan';
|
||||||
},
|
}
|
||||||
|
|
||||||
formatEndDate(startDate: string, years: number): string {
|
const formatEndDate = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'N/A';
|
if (!startDate) return 'N/A';
|
||||||
const date = new Date(startDate);
|
const date = new Date(startDate);
|
||||||
date.setFullYear(date.getFullYear() + years);
|
date.setFullYear(date.getFullYear() + years);
|
||||||
return date.toISOString().split('T')[0];
|
return date.toISOString().split('T')[0];
|
||||||
},
|
}
|
||||||
|
|
||||||
getStatusText(startDate: string, years: number): string {
|
const getStatusText = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'Unknown';
|
if (!startDate) return 'Unknown';
|
||||||
const endDate = new Date(startDate);
|
const endDate = new Date(startDate);
|
||||||
endDate.setFullYear(endDate.getFullYear() + years);
|
endDate.setFullYear(endDate.getFullYear() + years);
|
||||||
@@ -292,9 +284,9 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'Active';
|
return 'Active';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getStatusBadge(startDate: string, years: number): string {
|
const getStatusBadge = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'badge-ghost';
|
if (!startDate) return 'badge-ghost';
|
||||||
const endDate = new Date(startDate);
|
const endDate = new Date(startDate);
|
||||||
endDate.setFullYear(endDate.getFullYear() + years);
|
endDate.setFullYear(endDate.getFullYear() + years);
|
||||||
@@ -306,7 +298,11 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'badge-success';
|
return 'badge-success';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
loadCustomer();
|
||||||
|
loadServicePlan();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -140,33 +140,25 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { StateOption, HomeTypeOption } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import useValidate from "@vuelidate/core";
|
||||||
import { email, minLength, required } from "@vuelidate/validators";
|
import { email, minLength, required } from "@vuelidate/validators";
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
interface SelectOption {
|
// Reactive data
|
||||||
text: string;
|
const router = useRouter()
|
||||||
value: number;
|
const user = ref(null)
|
||||||
}
|
const stateList = ref<StateOption[]>([])
|
||||||
|
const custList = ref<HomeTypeOption[]>([])
|
||||||
|
|
||||||
export default defineComponent({
|
// Form object
|
||||||
name: 'CustomerCreate',
|
const CreateCustomerForm = ref({
|
||||||
components: {
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
user: null,
|
|
||||||
stateList: [] as SelectOption[],
|
|
||||||
custList: [] as SelectOption[],
|
|
||||||
// --- REFACTORED: Simplified, flat form object ---
|
|
||||||
CreateCustomerForm: {
|
|
||||||
customer_last_name: "",
|
customer_last_name: "",
|
||||||
customer_first_name: "",
|
customer_first_name: "",
|
||||||
customer_town: "",
|
customer_town: "",
|
||||||
@@ -176,84 +168,86 @@ export default defineComponent({
|
|||||||
customer_email: "",
|
customer_email: "",
|
||||||
customer_phone_number: "",
|
customer_phone_number: "",
|
||||||
customer_description: "",
|
customer_description: "",
|
||||||
// --- FIX: Initialized as numbers for proper v-model binding ---
|
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_state: 0,
|
customer_state: 0,
|
||||||
},
|
})
|
||||||
}
|
|
||||||
},
|
// Validation rules
|
||||||
validations() {
|
const rules = {
|
||||||
return {
|
|
||||||
// --- REFACTORED: Validation rules point to the flat form object ---
|
|
||||||
CreateCustomerForm: {
|
CreateCustomerForm: {
|
||||||
customer_last_name: { required, minLength: minLength(1) },
|
customer_last_name: { required, minLength: minLength(1) },
|
||||||
customer_first_name: { required, minLength: minLength(1) },
|
customer_first_name: { required, minLength: minLength(1) },
|
||||||
customer_town: { required, minLength: minLength(1) },
|
customer_town: { required, minLength: minLength(1) },
|
||||||
customer_zip: { required, minLength: minLength(5) },
|
customer_zip: { required, minLength: minLength(5) },
|
||||||
customer_email: { email }, // Optional, so only validate format
|
customer_email: { email },
|
||||||
customer_phone_number: { required },
|
customer_phone_number: { required },
|
||||||
customer_home_type: { required },
|
customer_home_type: { required },
|
||||||
customer_state: { required },
|
customer_state: { required },
|
||||||
customer_address: { required },
|
customer_address: { required },
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
|
||||||
created() {
|
// Vuelidate instance
|
||||||
this.userStatus();
|
const v$ = useValidate(rules, { CreateCustomerForm })
|
||||||
},
|
|
||||||
mounted() {
|
// Functions
|
||||||
this.getCustomerTypeList();
|
const acceptNumber = () => {
|
||||||
this.getStatesList();
|
const x = CreateCustomerForm.value.customer_phone_number.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
acceptNumber() {
|
|
||||||
const x = this.CreateCustomerForm.customer_phone_number.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
|
|
||||||
if (x) {
|
if (x) {
|
||||||
this.CreateCustomerForm.customer_phone_number = !x[2] ? x[1] : `(${x[1]}) ${x[2]}${x[3] ? `-${x[3]}` : ''}`;
|
CreateCustomerForm.value.customer_phone_number = !x[2] ? x[1] : `(${x[1]}) ${x[2]}${x[3] ? `-${x[3]}` : ''}`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
userStatus() {
|
|
||||||
|
const userStatus = () => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) { this.user = response.data.user; }
|
if (response.data.ok) { user.value = response.data.user; }
|
||||||
})
|
})
|
||||||
.catch(() => { this.user = null; });
|
.catch(() => { user.value = null; });
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomerTypeList() {
|
const getCustomerTypeList = () => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + "/query/customertype";
|
const path = import.meta.env.VITE_BASE_URL + "/query/customertype";
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
.then((response: any) => { this.custList = response.data; });
|
.then((response: any) => { custList.value = response.data; });
|
||||||
},
|
}
|
||||||
getStatesList() {
|
|
||||||
|
const getStatesList = () => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + "/query/states";
|
const path = import.meta.env.VITE_BASE_URL + "/query/states";
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
.then((response: any) => { this.stateList = response.data; });
|
.then((response: any) => { stateList.value = response.data; });
|
||||||
},
|
}
|
||||||
CreateCustomer(payload: any) {
|
|
||||||
|
const CreateCustomer = (payload: any) => {
|
||||||
const path = import.meta.env.VITE_BASE_URL + "/customer/create";
|
const path = import.meta.env.VITE_BASE_URL + "/customer/create";
|
||||||
axios.post(path, payload, { withCredentials: true, headers: authHeader() })
|
axios.post(path, payload, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
const new_user_id = response.data.user.user_id;
|
const new_user_id = response.data.user.user_id;
|
||||||
this.$router.push({ name: 'customerProfile', params: { id: new_user_id } });
|
router.push({ name: 'customerProfile', params: { id: new_user_id } });
|
||||||
} else {
|
} else {
|
||||||
notify({ title: "Error", text: response.data.error || "Failed to create customer.", type: "error" });
|
notify({ title: "Error", text: response.data.error || "Failed to create customer.", type: "error" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
onSubmit() {
|
|
||||||
this.v$.$validate(); // Trigger validation
|
const onSubmit = () => {
|
||||||
if (!this.v$.$error) {
|
v$.value.$validate(); // Trigger validation
|
||||||
|
if (!v$.value.$error) {
|
||||||
// If validation passes, submit the form
|
// If validation passes, submit the form
|
||||||
this.CreateCustomer(this.CreateCustomerForm);
|
CreateCustomer(CreateCustomerForm.value);
|
||||||
} else {
|
} else {
|
||||||
// If validation fails, show a single notification
|
// If validation fails, show a single notification
|
||||||
notify({ title: "Validation Error", text: "Please fill out all required fields correctly.", type: "error" });
|
notify({ title: "Validation Error", text: "Please fill out all required fields correctly.", type: "error" });
|
||||||
console.log("Form validation failed.");
|
console.log("Form validation failed.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus();
|
||||||
|
getCustomerTypeList();
|
||||||
|
getStatesList();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -162,36 +162,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { StateOption, HomeTypeOption } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import useValidate from "@vuelidate/core";
|
||||||
import { email, minLength, required } from "@vuelidate/validators";
|
import { email, minLength, required } from "@vuelidate/validators";
|
||||||
|
|
||||||
// --- NEW: Interface for select options for better type safety ---
|
// Reactive data
|
||||||
interface SelectOption {
|
const route = useRoute()
|
||||||
text: string;
|
const router = useRouter()
|
||||||
value: number;
|
const user = ref(null)
|
||||||
}
|
const stateList = ref<StateOption[]>([])
|
||||||
|
const custList = ref<HomeTypeOption[]>([])
|
||||||
export default defineComponent({
|
const customer = ref({
|
||||||
name: 'CustomerEdit',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
// Removed unused Header and SideBar
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
user: null,
|
|
||||||
|
|
||||||
stateList: [] as SelectOption[],
|
|
||||||
custList: [] as SelectOption[],
|
|
||||||
customer: {
|
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -204,28 +191,26 @@ export default defineComponent({
|
|||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
},
|
})
|
||||||
customerDescription: {
|
const customerDescription = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
company_id: '',
|
company_id: '',
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
description: '',
|
description: '',
|
||||||
},
|
})
|
||||||
CreateCustomerForm: {
|
const CreateCustomerForm = ref({
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
customer_last_name: "",
|
customer_last_name: "",
|
||||||
customer_first_name: "",
|
customer_first_name: "",
|
||||||
customer_town: "",
|
customer_town: "",
|
||||||
customer_apt: "",
|
customer_apt: "",
|
||||||
// --- FIX: Initialized as a number ---
|
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_zip: "",
|
customer_zip: "",
|
||||||
customer_automatic: false,
|
customer_automatic: false,
|
||||||
customer_email: "",
|
customer_email: "",
|
||||||
customer_phone_number: "",
|
customer_phone_number: "",
|
||||||
// --- FIX: Initialized as a number ---
|
|
||||||
customer_state: 0,
|
customer_state: 0,
|
||||||
customer_address: "",
|
customer_address: "",
|
||||||
customer_description: "",
|
customer_description: "",
|
||||||
@@ -236,12 +221,11 @@ export default defineComponent({
|
|||||||
contract_years: 1,
|
contract_years: 1,
|
||||||
contract_start_date: "",
|
contract_start_date: "",
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
renewalDate: "",
|
const renewalDate = ref("")
|
||||||
}
|
|
||||||
},
|
// Validation rules
|
||||||
validations() {
|
const rules = {
|
||||||
return {
|
|
||||||
CreateCustomerForm: {
|
CreateCustomerForm: {
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
customer_last_name: { required, minLength: minLength(1) },
|
customer_last_name: { required, minLength: minLength(1) },
|
||||||
@@ -249,32 +233,28 @@ export default defineComponent({
|
|||||||
customer_town: { required, minLength: minLength(1) },
|
customer_town: { required, minLength: minLength(1) },
|
||||||
customer_home_type: { required },
|
customer_home_type: { required },
|
||||||
customer_zip: { required, minLength: minLength(5) },
|
customer_zip: { required, minLength: minLength(5) },
|
||||||
customer_email: { email }, // Removed required to match template label "Optional"
|
customer_email: { email },
|
||||||
customer_phone_number: { required },
|
customer_phone_number: { required },
|
||||||
customer_state: { required },
|
customer_state: { required },
|
||||||
customer_address: { required },
|
customer_address: { required },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
|
||||||
created() {
|
// Vuelidate instance
|
||||||
this.userStatus()
|
const v$ = useValidate(rules, { CreateCustomerForm })
|
||||||
this.getCustomer(this.$route.params.id)
|
|
||||||
},
|
// Functions
|
||||||
mounted() {
|
const acceptNumber = () => {
|
||||||
this.getCustomerTypeList();
|
let x = CreateCustomerForm.value.basicInfo.customer_phone_number.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
|
||||||
this.getStatesList();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
acceptNumber() {
|
|
||||||
let x = this.CreateCustomerForm.basicInfo.customer_phone_number.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
|
|
||||||
if (x) {
|
if (x) {
|
||||||
this.CreateCustomerForm.basicInfo.customer_phone_number = !x[2] ? x[1] : '(' + x[1] + ') ' + x[2] + (x[3] ? '-' + x[3] : '');
|
CreateCustomerForm.value.basicInfo.customer_phone_number = !x[2] ? x[1] : '(' + x[1] + ') ' + x[2] + (x[3] ? '-' + x[3] : '');
|
||||||
} else {
|
} else {
|
||||||
this.CreateCustomerForm.basicInfo.customer_phone_number = '';
|
CreateCustomerForm.value.basicInfo.customer_phone_number = '';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
userStatus() {
|
|
||||||
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -284,26 +264,28 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCustomerDescription(userid: any) {
|
|
||||||
|
const getCustomerDescription = (userid: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/description/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/description/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.customerDescription = response.data
|
customerDescription.value = response.data
|
||||||
this.CreateCustomerForm.basicInfo.customer_description = this.customerDescription.description;
|
CreateCustomerForm.value.basicInfo.customer_description = customerDescription.value.description;
|
||||||
this.CreateCustomerForm.basicInfo.customer_fill_location = this.customerDescription.fill_location
|
CreateCustomerForm.value.basicInfo.customer_fill_location = customerDescription.value.fill_location
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCustomer(userid: any) {
|
|
||||||
|
const getCustomer = (userid: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/" + userid;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/" + userid;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -313,30 +295,31 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
this.getCustomerDescription(this.customer.id);
|
getCustomerDescription(customer.value.id);
|
||||||
this.CreateCustomerForm.basicInfo.customer_last_name = response.data.customer_last_name;
|
CreateCustomerForm.value.basicInfo.customer_last_name = response.data.customer_last_name;
|
||||||
this.CreateCustomerForm.basicInfo.customer_first_name = response.data.customer_first_name;
|
CreateCustomerForm.value.basicInfo.customer_first_name = response.data.customer_first_name;
|
||||||
this.CreateCustomerForm.basicInfo.customer_town = response.data.customer_town;
|
CreateCustomerForm.value.basicInfo.customer_town = response.data.customer_town;
|
||||||
this.CreateCustomerForm.basicInfo.customer_state = response.data.customer_state;
|
CreateCustomerForm.value.basicInfo.customer_state = response.data.customer_state;
|
||||||
this.CreateCustomerForm.basicInfo.customer_zip = response.data.customer_zip;
|
CreateCustomerForm.value.basicInfo.customer_zip = response.data.customer_zip;
|
||||||
this.CreateCustomerForm.basicInfo.customer_phone_number = response.data.customer_phone_number;
|
CreateCustomerForm.value.basicInfo.customer_phone_number = response.data.customer_phone_number;
|
||||||
this.CreateCustomerForm.basicInfo.customer_home_type = response.data.customer_home_type;
|
CreateCustomerForm.value.basicInfo.customer_home_type = response.data.customer_home_type;
|
||||||
this.CreateCustomerForm.basicInfo.customer_apt = response.data.customer_apt;
|
CreateCustomerForm.value.basicInfo.customer_apt = response.data.customer_apt;
|
||||||
this.CreateCustomerForm.basicInfo.customer_email = response.data.customer_email;
|
CreateCustomerForm.value.basicInfo.customer_email = response.data.customer_email;
|
||||||
this.CreateCustomerForm.basicInfo.customer_address = response.data.customer_address;
|
CreateCustomerForm.value.basicInfo.customer_address = response.data.customer_address;
|
||||||
|
|
||||||
if (response.data.customer_automatic === 1) {
|
if (response.data.customer_automatic === 1) {
|
||||||
this.CreateCustomerForm.basicInfo.customer_automatic = true
|
CreateCustomerForm.value.basicInfo.customer_automatic = true
|
||||||
}
|
}
|
||||||
if (response.data.customer_automatic === 0) {
|
if (response.data.customer_automatic === 0) {
|
||||||
this.CreateCustomerForm.basicInfo.customer_automatic = false
|
CreateCustomerForm.value.basicInfo.customer_automatic = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
editItem(payload: any) { // Simplified payload type for brevity
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/edit/" + this.customer.id;
|
const editItem = (payload: any) => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + "/customer/edit/" + customer.value.id;
|
||||||
axios({
|
axios({
|
||||||
method: "put",
|
method: "put",
|
||||||
url: path,
|
url: path,
|
||||||
@@ -346,51 +329,60 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id }, query: { success: 'true' } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id }, query: { success: 'true' } });
|
||||||
} else if (response.data.error) {
|
} else if (response.data.error) {
|
||||||
// Handle specific errors if needed
|
router.push("/");
|
||||||
this.$router.push("/");
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
onSubmit() {
|
|
||||||
// Create payload with both basic info and service plan data
|
const onSubmit = () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
...this.CreateCustomerForm.basicInfo,
|
...CreateCustomerForm.value.basicInfo,
|
||||||
service_plan: this.CreateCustomerForm.servicePlan
|
service_plan: CreateCustomerForm.value.servicePlan
|
||||||
};
|
};
|
||||||
this.editItem(payload);
|
editItem(payload);
|
||||||
},
|
}
|
||||||
getCustomerTypeList() {
|
|
||||||
|
const getCustomerTypeList = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/query/customertype";
|
let path = import.meta.env.VITE_BASE_URL + "/query/customertype";
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.custList = response.data;
|
custList.value = response.data;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getStatesList() {
|
|
||||||
|
const getStatesList = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/query/states";
|
let path = import.meta.env.VITE_BASE_URL + "/query/states";
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.stateList = response.data;
|
stateList.value = response.data;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
formatEndDate(startDate: string, years: number): string {
|
|
||||||
|
const formatEndDate = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'N/A';
|
if (!startDate) return 'N/A';
|
||||||
const date = new Date(startDate);
|
const date = new Date(startDate);
|
||||||
date.setFullYear(date.getFullYear() + years);
|
date.setFullYear(date.getFullYear() + years);
|
||||||
return date.toISOString().split('T')[0];
|
return date.toISOString().split('T')[0];
|
||||||
},
|
}
|
||||||
renewContract() {
|
|
||||||
if (!this.renewalDate) {
|
const renewContract = () => {
|
||||||
|
if (!renewalDate.value) {
|
||||||
alert('Please select a renewal date');
|
alert('Please select a renewal date');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.CreateCustomerForm.servicePlan.contract_years += 1;
|
CreateCustomerForm.value.servicePlan.contract_years += 1;
|
||||||
this.CreateCustomerForm.servicePlan.contract_start_date = this.renewalDate;
|
CreateCustomerForm.value.servicePlan.contract_start_date = renewalDate.value;
|
||||||
this.renewalDate = '';
|
renewalDate.value = '';
|
||||||
alert('Contract renewed! Years increased by 1 and start date updated.');
|
alert('Contract renewed! Years increased by 1 and start date updated.');
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getCustomer(route.params.id)
|
||||||
|
getCustomerTypeList();
|
||||||
|
getStatesList();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -105,58 +105,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { customerService } from '../../services/customerService'
|
||||||
|
import { Customer } from '../../types/models'
|
||||||
import Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../components/pagination.vue'
|
import PaginationComp from '../../components/pagination.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'CustomerHome',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const customers = ref<Customer[]>([])
|
||||||
Header,
|
const customer_count = ref(0)
|
||||||
SideBar,
|
const page = ref(1)
|
||||||
Footer,
|
const perPage = ref(50)
|
||||||
},
|
const recordsLength = ref(0)
|
||||||
|
const options = ref({
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
customers: [] as any[],
|
|
||||||
customer_count: 0,
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
customers.value = [];
|
||||||
mounted() {
|
get_customers(pageVal)
|
||||||
this.getPage(this.page)
|
get_customer_count()
|
||||||
|
}
|
||||||
|
|
||||||
},
|
const userStatus = () => {
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.customers = [];
|
|
||||||
this.get_customers(page)
|
|
||||||
this.get_customer_count()
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -166,46 +147,50 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_customers(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/all/' + page;
|
const get_customers = async (pageVal: number) => {
|
||||||
axios({
|
try {
|
||||||
method: 'get',
|
const response = await customerService.getAll(pageVal)
|
||||||
url: path,
|
customers.value = response.data || []
|
||||||
headers: authHeader(),
|
} catch (error) {
|
||||||
}).then((response: any) => {
|
console.error('Error fetching customers:', error)
|
||||||
this.customers = response.data
|
customers.value = []
|
||||||
})
|
}
|
||||||
},
|
}
|
||||||
get_customer_count() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/count';
|
const get_customer_count = async () => {
|
||||||
axios({
|
try {
|
||||||
method: 'get',
|
const response = await customerService.getCount()
|
||||||
url: path,
|
if (response.data) {
|
||||||
headers: authHeader(),
|
customer_count.value = response.data.count
|
||||||
}).then((response: any) => {
|
}
|
||||||
this.customer_count = response.data.count
|
} catch (error) {
|
||||||
})
|
console.error('Error fetching customer count:', error)
|
||||||
},
|
}
|
||||||
deleteCustomer(user_id: any) {
|
}
|
||||||
|
|
||||||
|
const deleteCustomer = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/delete/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/delete/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
get_customers(1)
|
||||||
this.get_customers(1)
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="customer in customers" :key="customer.account_number">
|
<tr v-for="customer in customers" :key="customer.account_number">
|
||||||
<td>{{ customer.account_number }}</td>
|
<td>{{ customer.account_number }}</td>
|
||||||
<td>{{ customer.first_name }}</td>
|
<td>{{ customer.customer_first_name }}</td>
|
||||||
<td>{{ customer.last_name }}</td>
|
<td>{{ customer.customer_last_name }}</td>
|
||||||
<td>{{ customer.address }}</td>
|
<td>{{ customer.customer_address }}</td>
|
||||||
<td>{{ customer.town }}</td>
|
<td>{{ customer.customer_town }}</td>
|
||||||
<td>{{ customer.phone_number }}</td>
|
<td>{{ customer.customer_phone_number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -28,32 +28,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios';
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header';
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { Customer } from '../../types/models'
|
||||||
|
|
||||||
interface Customer {
|
// Reactive data
|
||||||
account_number: string;
|
const customers = ref<Customer[]>([])
|
||||||
first_name: string;
|
|
||||||
last_name: string;
|
|
||||||
address: string;
|
|
||||||
town: string;
|
|
||||||
phone_number: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
// Functions
|
||||||
name: 'CustomerList',
|
const fetchCustomers = () => {
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
customers: [] as Customer[]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fetchCustomers();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchCustomers() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/report/customers/list';
|
let path = import.meta.env.VITE_BASE_URL + '/report/customers/list';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -63,15 +48,18 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.customers = response.data.customers;
|
customers.value = response.data.customers;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: unknown) => {
|
.catch((error: unknown) => {
|
||||||
console.error('Error fetching customer data:', error);
|
console.error('Error fetching customer data:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCustomers();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -76,8 +76,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -101,46 +101,40 @@ interface FuelEstimation {
|
|||||||
last_fill?: string | null;
|
last_fill?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
// Props
|
||||||
name: 'TankEstimation',
|
const props = defineProps<{
|
||||||
props: {
|
customerId: number
|
||||||
customerId: {
|
}>()
|
||||||
type: Number,
|
|
||||||
required: true
|
// Reactive data
|
||||||
}
|
const estimation = ref<FuelEstimation | null>(null)
|
||||||
},
|
const loading = ref(true)
|
||||||
data() {
|
const error = ref<string | null>(null)
|
||||||
return {
|
|
||||||
estimation: null as FuelEstimation | null,
|
// Lifecycle
|
||||||
loading: true,
|
onMounted(() => {
|
||||||
error: null as string | null
|
fetchEstimation()
|
||||||
}
|
})
|
||||||
},
|
|
||||||
mounted() {
|
// Watchers
|
||||||
this.fetchEstimation()
|
watch(() => props.customerId, (newId, oldId) => {
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
customerId: {
|
|
||||||
handler(newId, oldId) {
|
|
||||||
if (newId !== oldId) {
|
if (newId !== oldId) {
|
||||||
console.log('Customer ID changed from', oldId, 'to', newId)
|
console.log('Customer ID changed from', oldId, 'to', newId)
|
||||||
this.fetchEstimation()
|
fetchEstimation()
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
immediate: false
|
|
||||||
}
|
// Functions
|
||||||
},
|
const fetchEstimation = async () => {
|
||||||
methods: {
|
loading.value = true
|
||||||
async fetchEstimation() {
|
error.value = null
|
||||||
this.loading = true
|
estimation.value = null // Clear previous data
|
||||||
this.error = null
|
|
||||||
this.estimation = null // Clear previous data
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Fetching estimation for customer ID:', this.customerId)
|
console.log('Fetching estimation for customer ID:', props.customerId)
|
||||||
|
|
||||||
// First check if customer is automatic
|
// First check if customer is automatic
|
||||||
const customerPath = `${import.meta.env.VITE_BASE_URL}/customer/${this.customerId}`
|
const customerPath = `${import.meta.env.VITE_BASE_URL}/customer/${props.customerId}`
|
||||||
console.log('Checking customer type:', customerPath)
|
console.log('Checking customer type:', customerPath)
|
||||||
const customerResponse = await axios.get(customerPath, { headers: authHeader() })
|
const customerResponse = await axios.get(customerPath, { headers: authHeader() })
|
||||||
const isAutomatic = customerResponse.data.customer_automatic === 1
|
const isAutomatic = customerResponse.data.customer_automatic === 1
|
||||||
@@ -150,11 +144,11 @@ export default defineComponent({
|
|||||||
let path: string
|
let path: string
|
||||||
if (isAutomatic) {
|
if (isAutomatic) {
|
||||||
// Fetch from automatic delivery API
|
// Fetch from automatic delivery API
|
||||||
path = `${import.meta.env.VITE_AUTO_URL}/delivery/auto/customer/${this.customerId}`
|
path = `${import.meta.env.VITE_AUTO_URL}/delivery/auto/customer/${props.customerId}`
|
||||||
console.log('Fetching automatic data from:', path)
|
console.log('Fetching automatic data from:', path)
|
||||||
} else {
|
} else {
|
||||||
// Fetch from customer estimation API
|
// Fetch from customer estimation API
|
||||||
path = `${import.meta.env.VITE_AUTO_URL}/fixstuff_customer/estimate_gallons/customer/${this.customerId}`
|
path = `${import.meta.env.VITE_AUTO_URL}/fixstuff_customer/estimate_gallons/customer/${props.customerId}`
|
||||||
console.log('Fetching customer data from:', path)
|
console.log('Fetching customer data from:', path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +156,7 @@ export default defineComponent({
|
|||||||
console.log('API Response:', response.data)
|
console.log('API Response:', response.data)
|
||||||
|
|
||||||
if (response.data.error) {
|
if (response.data.error) {
|
||||||
this.error = response.data.error
|
error.value = response.data.error
|
||||||
console.error('API returned error:', response.data.error)
|
console.error('API returned error:', response.data.error)
|
||||||
} else {
|
} else {
|
||||||
if (isAutomatic) {
|
if (isAutomatic) {
|
||||||
@@ -170,7 +164,7 @@ export default defineComponent({
|
|||||||
if (response.data && response.data.id) {
|
if (response.data && response.data.id) {
|
||||||
const autoData = response.data
|
const autoData = response.data
|
||||||
console.log('Processing automatic data:', autoData)
|
console.log('Processing automatic data:', autoData)
|
||||||
this.estimation = {
|
estimation.value = {
|
||||||
id: autoData.id,
|
id: autoData.id,
|
||||||
customer_id: autoData.customer_id,
|
customer_id: autoData.customer_id,
|
||||||
total_deliveries: 0, // Not available in auto data
|
total_deliveries: 0, // Not available in auto data
|
||||||
@@ -183,55 +177,55 @@ export default defineComponent({
|
|||||||
last_5_deliveries: [],
|
last_5_deliveries: [],
|
||||||
last_fill: autoData.last_fill
|
last_fill: autoData.last_fill
|
||||||
}
|
}
|
||||||
console.log('Set automatic estimation:', this.estimation)
|
console.log('Set automatic estimation:', estimation.value)
|
||||||
} else {
|
} else {
|
||||||
console.warn('No automatic delivery data found for customer', this.customerId)
|
console.warn('No automatic delivery data found for customer', props.customerId)
|
||||||
this.error = 'No automatic delivery data available'
|
error.value = 'No automatic delivery data available'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('Setting customer estimation:', response.data)
|
console.log('Setting customer estimation:', response.data)
|
||||||
this.estimation = response.data
|
estimation.value = response.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to fetch fuel estimation:', error)
|
console.error('Failed to fetch fuel estimation:', error)
|
||||||
if (error.response?.status === 404) {
|
if (error.response?.status === 404) {
|
||||||
this.error = 'Customer data not found'
|
error.value = 'Customer data not found'
|
||||||
} else if (error.response?.data?.error) {
|
} else if (error.response?.data?.error) {
|
||||||
this.error = error.response.data.error
|
error.value = error.response.data.error
|
||||||
} else {
|
} else {
|
||||||
this.error = 'Failed to load fuel estimation data'
|
error.value = 'Failed to load fuel estimation data'
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getTankLevelPercentage(): number {
|
const getTankLevelPercentage = (): number => {
|
||||||
if (!this.estimation || !this.estimation.tank_size || this.estimation.tank_size === 0) {
|
if (!estimation.value || !estimation.value.tank_size || estimation.value.tank_size === 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return (this.estimation.estimated_gallons / this.estimation.tank_size) * 100
|
return (estimation.value.estimated_gallons / estimation.value.tank_size) * 100
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateDailyUsage(): string {
|
const calculateDailyUsage = (): string => {
|
||||||
if (!this.estimation || !this.estimation.scaling_factor) {
|
if (!estimation.value || !estimation.value.scaling_factor) {
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
}
|
}
|
||||||
// For a typical day with ~20 degree days (moderate winter day)
|
// For a typical day with ~20 degree days (moderate winter day)
|
||||||
const typicalDegreeDays = 20
|
const typicalDegreeDays = 20
|
||||||
const dailyUsage = this.estimation.scaling_factor * typicalDegreeDays
|
const dailyUsage = estimation.value.scaling_factor * typicalDegreeDays
|
||||||
return dailyUsage.toFixed(1)
|
return dailyUsage.toFixed(1)
|
||||||
},
|
}
|
||||||
|
|
||||||
formatScalingFactor(scalingFactor: number | null): string {
|
const formatScalingFactor = (scalingFactor: number | null): string => {
|
||||||
if (scalingFactor === null || scalingFactor === undefined) {
|
if (scalingFactor === null || scalingFactor === undefined) {
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
}
|
}
|
||||||
return scalingFactor.toFixed(2)
|
return scalingFactor.toFixed(2)
|
||||||
},
|
}
|
||||||
|
|
||||||
getScalingFactorCategory(scalingFactor: number | null): string {
|
const getScalingFactorCategory = (scalingFactor: number | null): string => {
|
||||||
if (scalingFactor === null || scalingFactor === undefined) {
|
if (scalingFactor === null || scalingFactor === undefined) {
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
}
|
}
|
||||||
@@ -247,12 +241,10 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'Very High'
|
return 'Very High'
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
const formatDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A'
|
if (!dateString) return 'N/A'
|
||||||
return dayjs(dateString).format('MMM D, YYYY')
|
return dayjs(dateString).format('MMM D, YYYY')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { AuthorizeTransaction } from '../../../../types/models';
|
||||||
import DeliveriesTable from './DeliveriesTable.vue';
|
import DeliveriesTable from './DeliveriesTable.vue';
|
||||||
import ServiceCallsTable from './ServiceCallsTable.vue';
|
import ServiceCallsTable from './ServiceCallsTable.vue';
|
||||||
import TransactionsTable from './TransactionsTable.vue';
|
import TransactionsTable from './TransactionsTable.vue';
|
||||||
@@ -69,26 +70,12 @@ interface ServiceCall {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Transaction {
|
|
||||||
id: number;
|
|
||||||
preauthorize_amount: number | null;
|
|
||||||
charge_amount: number | null;
|
|
||||||
transaction_type: number;
|
|
||||||
status: number;
|
|
||||||
created_at: string;
|
|
||||||
auth_net_transaction_id: string | null;
|
|
||||||
rejection_reason: string | null;
|
|
||||||
delivery_id: number | null;
|
|
||||||
service_id: number | null;
|
|
||||||
auto_id: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Define the Props interface
|
// 2. Define the Props interface
|
||||||
interface Props {
|
interface Props {
|
||||||
deliveries: Delivery[];
|
deliveries: Delivery[];
|
||||||
autodeliveries: AutomaticDelivery[];
|
autodeliveries: AutomaticDelivery[];
|
||||||
serviceCalls: ServiceCall[];
|
serviceCalls: ServiceCall[];
|
||||||
transactions: Transaction[];
|
transactions: AuthorizeTransaction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Use the typed defineProps
|
// 3. Use the typed defineProps
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<div><strong>Pre-Auth:</strong> ${{ transaction.preauthorize_amount || '0.00' }}</div>
|
<div><strong>Pre-Auth:</strong> ${{ transaction.preauthorize_amount || '0.00' }}</div>
|
||||||
<div><strong>Charge:</strong> ${{ transaction.charge_amount || '0.00' }}</div>
|
<div><strong>Charge:</strong> ${{ transaction.charge_amount || '0.00' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div><strong>Type:</strong> <span :class="getTypeColor(transaction.transaction_type)">{{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}</span></div>
|
<div><strong>Type:</strong> <span :class="getTypeColor(transaction.transaction_type)">{{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : transaction.transaction_type === 2 ? 'Capture' : 'Unknown' }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
<p><strong>Transaction ID:</strong> {{ transaction.auth_net_transaction_id || 'N/A' }}</p>
|
<p><strong>Transaction ID:</strong> {{ transaction.auth_net_transaction_id || 'N/A' }}</p>
|
||||||
<p><strong>Pre-Auth:</strong> ${{ transaction.preauthorize_amount || '0.00' }}</p>
|
<p><strong>Pre-Auth:</strong> ${{ transaction.preauthorize_amount || '0.00' }}</p>
|
||||||
<p><strong>Charge:</strong> ${{ transaction.charge_amount || '0.00' }}</p>
|
<p><strong>Charge:</strong> ${{ transaction.charge_amount || '0.00' }}</p>
|
||||||
<p><strong>Type:</strong> <span :class="getTypeColor(transaction.transaction_type)">{{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : 'Capture' }}</span></p>
|
<p><strong>Type:</strong> <span :class="getTypeColor(transaction.transaction_type)">{{ transaction.transaction_type === 0 ? 'Charge' : transaction.transaction_type === 1 ? 'Auth' : transaction.transaction_type === 2 ? 'Capture' : 'Unknown' }}</span></p>
|
||||||
<p><strong>Source:</strong> {{ getSourceText(transaction) }}</p>
|
<p><strong>Source:</strong> {{ getSourceText(transaction) }}</p>
|
||||||
<!-- Rejection Reason in Mobile View -->
|
<!-- Rejection Reason in Mobile View -->
|
||||||
<div v-if="transaction.rejection_reason && transaction.rejection_reason.trim()" class="bg-transparent border border-gray-300 rounded-md p-3 mt-2">
|
<div v-if="transaction.rejection_reason && transaction.rejection_reason.trim()" class="bg-transparent border border-gray-300 rounded-md p-3 mt-2">
|
||||||
@@ -103,29 +103,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Transaction {
|
import { AuthorizeTransaction } from '../../../../types/models';
|
||||||
id: number;
|
|
||||||
preauthorize_amount: number | null;
|
|
||||||
charge_amount: number | null;
|
|
||||||
transaction_type: number;
|
|
||||||
status: number;
|
|
||||||
created_at: string;
|
|
||||||
auth_net_transaction_id: string | null;
|
|
||||||
rejection_reason: string | null;
|
|
||||||
delivery_id: number | null;
|
|
||||||
service_id: number | null;
|
|
||||||
auto_id: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
transactions: Transaction[];
|
transactions: AuthorizeTransaction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>();
|
||||||
|
|
||||||
const getStatusClass = (status: number) => status === 0 ? 'badge-success' : 'badge-error';
|
const getStatusClass = (status: number) => status === 0 ? 'badge-success' : 'badge-error';
|
||||||
const getStatusText = (status: number) => status === 0 ? 'Approved' : 'Declined';
|
const getStatusText = (status: number) => status === 0 ? 'Approved' : 'Declined';
|
||||||
const getSourceText = (transaction: Transaction) => {
|
const getSourceText = (transaction: AuthorizeTransaction) => {
|
||||||
if (transaction.auto_id) {
|
if (transaction.auto_id) {
|
||||||
return 'Automatic';
|
return 'Automatic';
|
||||||
} else if (transaction.delivery_id) {
|
} else if (transaction.delivery_id) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import CustomerHome from '../customer/home.vue';
|
const CustomerHome = () => import('../customer/home.vue');
|
||||||
import CustomerCreate from '../customer/create.vue';
|
const CustomerCreate = () => import('../customer/create.vue');
|
||||||
import CustomerEdit from "../customer/edit.vue";
|
const CustomerEdit = () => import("../customer/edit.vue");
|
||||||
import CustomerProfile from "./profile/profile.vue"
|
const CustomerProfile = () => import("./profile/profile.vue")
|
||||||
import TankEdit from "./tank/edit.vue"
|
const TankEdit = () => import("./tank/edit.vue")
|
||||||
|
|
||||||
|
|
||||||
const customerRoutes = [
|
const customerRoutes = [
|
||||||
|
|||||||
@@ -96,8 +96,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
// Interfaces remain the same
|
// Interfaces remain the same
|
||||||
interface ServiceParts {
|
interface ServiceParts {
|
||||||
@@ -116,31 +116,27 @@ interface NozzleParts {
|
|||||||
part3: string;
|
part3: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
// Props
|
||||||
name: 'PartsEditModal',
|
const props = defineProps<{
|
||||||
props: {
|
existingParts: ServiceParts
|
||||||
existingParts: {
|
customerId: number
|
||||||
type: Object as PropType<ServiceParts>,
|
}>()
|
||||||
required: true
|
|
||||||
},
|
|
||||||
customerId: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
emits: ['close-modal', 'save-parts'],
|
|
||||||
|
|
||||||
data() {
|
// Emits
|
||||||
return {
|
const emit = defineEmits<{
|
||||||
// All reactive state goes inside the data object
|
'close-modal': []
|
||||||
editableParts: {} as Partial<ServiceParts>,
|
'save-parts': [parts: Partial<ServiceParts>]
|
||||||
nozzle1: { part1: '', part2: '', part3: '' } as NozzleParts,
|
}>()
|
||||||
nozzle2: { part1: '', part2: '', part3: '' } as NozzleParts,
|
|
||||||
|
|
||||||
// ======================================================
|
// Reactive data
|
||||||
// ============== THE UPDATED OIL FILTER LIST ==============
|
const editableParts = ref({} as Partial<ServiceParts>)
|
||||||
// ======================================================
|
const nozzle1 = ref({ part1: '', part2: '', part3: '' } as NozzleParts)
|
||||||
oilFilterOptions: [
|
const nozzle2 = ref({ part1: '', part2: '', part3: '' } as NozzleParts)
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
// ============== THE UPDATED OIL FILTER LIST ==============
|
||||||
|
// ======================================================
|
||||||
|
const oilFilterOptions = ref([
|
||||||
'RF-1',
|
'RF-1',
|
||||||
'RF-4',
|
'RF-4',
|
||||||
'88CR',
|
'88CR',
|
||||||
@@ -148,32 +144,24 @@ export default defineComponent({
|
|||||||
'Garber Model R',
|
'Garber Model R',
|
||||||
'Garber Model M',
|
'Garber Model M',
|
||||||
'PurePro f100-10W'
|
'PurePro f100-10W'
|
||||||
],
|
])
|
||||||
|
|
||||||
nozzlePart1Options: ['a', 'b', 'w'],
|
const nozzlePart1Options = ref(['a', 'b', 'w'])
|
||||||
nozzlePart2Options: Array.from({ length: (200 - 45) / 5 + 1 }, (_, i) => (45 + i * 5).toString()),
|
const nozzlePart2Options = ref(Array.from({ length: (200 - 45) / 5 + 1 }, (_, i) => (45 + i * 5).toString()))
|
||||||
nozzlePart3Options: Array.from({ length: (100 - 45) / 5 + 1 }, (_, i) => (45 + i * 5).toString()),
|
const nozzlePart3Options = ref(Array.from({ length: (100 - 45) / 5 + 1 }, (_, i) => (45 + i * 5).toString()))
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
// Watchers
|
||||||
// Watch for changes to the `existingParts` prop
|
watch(() => props.existingParts, (newVal) => {
|
||||||
existingParts: {
|
|
||||||
handler(newVal) {
|
|
||||||
if (!newVal) return;
|
if (!newVal) return;
|
||||||
// Update the component's internal data when the prop changes
|
// Update the component's internal data when the prop changes
|
||||||
this.editableParts = { ...newVal, customer_id: this.customerId };
|
editableParts.value = { ...newVal, customer_id: props.customerId };
|
||||||
this.nozzle1 = this.splitNozzle(newVal.oil_nozzle);
|
nozzle1.value = splitNozzle(newVal.oil_nozzle);
|
||||||
this.nozzle2 = this.splitNozzle(newVal.oil_nozzle_2);
|
nozzle2.value = splitNozzle(newVal.oil_nozzle_2);
|
||||||
},
|
}, { immediate: true, deep: true })
|
||||||
immediate: true, // Run the handler immediately when the component is created
|
|
||||||
deep: true, // Watch for nested changes within the object
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// Functions
|
||||||
// Function to split a combined nozzle string (e.g., "a 55 75") into its parts
|
// Function to split a combined nozzle string (e.g., "a 55 75") into its parts
|
||||||
splitNozzle(nozzleStr: string | undefined): NozzleParts {
|
const splitNozzle = (nozzleStr: string | undefined): NozzleParts => {
|
||||||
if (!nozzleStr || typeof nozzleStr !== 'string' || nozzleStr.trim() === '') {
|
if (!nozzleStr || typeof nozzleStr !== 'string' || nozzleStr.trim() === '') {
|
||||||
return { part1: '', part2: '', part3: '' };
|
return { part1: '', part2: '', part3: '' };
|
||||||
}
|
}
|
||||||
@@ -183,25 +171,23 @@ export default defineComponent({
|
|||||||
part2: parts[1] || '',
|
part2: parts[1] || '',
|
||||||
part3: parts[2] || '',
|
part3: parts[2] || '',
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// Function to combine nozzle parts into a single string
|
// Function to combine nozzle parts into a single string
|
||||||
combineNozzle(parts: NozzleParts): string {
|
const combineNozzle = (parts: NozzleParts): string => {
|
||||||
if (parts.part1 && parts.part2 && parts.part3) {
|
if (parts.part1 && parts.part2 && parts.part3) {
|
||||||
return `${parts.part1} ${parts.part2} ${parts.part3}`;
|
return `${parts.part1} ${parts.part2} ${parts.part3}`;
|
||||||
}
|
}
|
||||||
return ''; // Return empty if any part is missing
|
return ''; // Return empty if any part is missing
|
||||||
},
|
}
|
||||||
|
|
||||||
// Form submission handler
|
// Form submission handler
|
||||||
saveChanges() {
|
const saveChanges = () => {
|
||||||
// Before emitting, combine the nozzle parts back into a single string
|
// Before emitting, combine the nozzle parts back into a single string
|
||||||
this.editableParts.oil_nozzle = this.combineNozzle(this.nozzle1);
|
editableParts.value.oil_nozzle = combineNozzle(nozzle1.value);
|
||||||
this.editableParts.oil_nozzle_2 = this.combineNozzle(this.nozzle2);
|
editableParts.value.oil_nozzle_2 = combineNozzle(nozzle2.value);
|
||||||
|
|
||||||
// Emit the save event with the final payload
|
// Emit the save event with the final payload
|
||||||
this.$emit('save-parts', this.editableParts);
|
emit('save-parts', editableParts.value);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>```
|
</script>```
|
||||||
|
|||||||
@@ -85,8 +85,9 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
@@ -100,88 +101,89 @@ interface TankFormData {
|
|||||||
fill_location: string;
|
fill_location: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'TankEdit',
|
const router = useRouter()
|
||||||
components: {
|
|
||||||
Footer,
|
// Reactive data
|
||||||
},
|
const user = ref(null as any)
|
||||||
data() {
|
const customer = ref({} as any)
|
||||||
return {
|
// --- REFACTORED: Simplified, flat form object ---
|
||||||
user: null as any,
|
const TankForm = ref({
|
||||||
customer: {} as any,
|
|
||||||
// --- REFACTORED: Simplified, flat form object ---
|
|
||||||
TankForm: {
|
|
||||||
last_tank_inspection: null,
|
last_tank_inspection: null,
|
||||||
tank_status: true,
|
tank_status: true,
|
||||||
outside_or_inside: true,
|
outside_or_inside: true,
|
||||||
tank_size: 0,
|
tank_size: 0,
|
||||||
fill_location: '',
|
fill_location: '',
|
||||||
} as TankFormData,
|
} as TankFormData)
|
||||||
}
|
|
||||||
},
|
// Functions
|
||||||
created() {
|
const userStatus = () => {
|
||||||
this.userStatus();
|
|
||||||
const customerId = this.$route.params.id;
|
|
||||||
this.getCustomer(customerId);
|
|
||||||
this.getCustomerDescription(customerId);
|
|
||||||
this.getTank(customerId);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
userStatus() {
|
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
const path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => { this.user = null; });
|
.catch(() => { user.value = null; });
|
||||||
},
|
}
|
||||||
getCustomer(userid: any) {
|
|
||||||
|
const getCustomer = (userid: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${userid}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${userid}`;
|
||||||
axios.get(path, { headers: authHeader() })
|
axios.get(path, { headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomerDescription(userid: any) {
|
|
||||||
|
const getCustomerDescription = (userid: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/description/${userid}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/description/${userid}`;
|
||||||
axios.get(path, { headers: authHeader() })
|
axios.get(path, { headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
// Only update fill_location if the response has it
|
// Only update fill_location if the response has it
|
||||||
if (response.data && response.data.fill_location) {
|
if (response.data && response.data.fill_location) {
|
||||||
this.TankForm.fill_location = response.data.fill_location;
|
TankForm.value.fill_location = response.data.fill_location;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getTank(customer_id: any) {
|
|
||||||
|
const getTank = (customer_id: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/tank/${customer_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/tank/${customer_id}`;
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
// Update the form model with data from the tank endpoint
|
// Update the form model with data from the tank endpoint
|
||||||
this.TankForm.last_tank_inspection = response.data.last_tank_inspection;
|
TankForm.value.last_tank_inspection = response.data.last_tank_inspection;
|
||||||
this.TankForm.tank_status = response.data.tank_status;
|
TankForm.value.tank_status = response.data.tank_status;
|
||||||
this.TankForm.outside_or_inside = response.data.outside_or_inside;
|
TankForm.value.outside_or_inside = response.data.outside_or_inside;
|
||||||
this.TankForm.tank_size = response.data.tank_size;
|
TankForm.value.tank_size = response.data.tank_size;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
editTank(payload: TankFormData) {
|
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/edit/tank/${this.$route.params.id}`;
|
const editTank = (payload: TankFormData) => {
|
||||||
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/edit/tank/${route.params.id}`;
|
||||||
axios.put(path, payload, { withCredentials: true, headers: authHeader() })
|
axios.put(path, payload, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to edit tank:", response.data.error);
|
console.error("Failed to edit tank:", response.data.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
onSubmit() {
|
|
||||||
|
const onSubmit = () => {
|
||||||
// The payload is simply the entire form object now
|
// The payload is simply the entire form object now
|
||||||
this.editTank(this.TankForm);
|
editTank(TankForm.value);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus();
|
||||||
|
const customerId = route.params.id;
|
||||||
|
getCustomer(customerId);
|
||||||
|
getCustomerDescription(customerId);
|
||||||
|
getTank(customerId);
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -229,7 +229,7 @@
|
|||||||
<div class="font-bold text-sm">{{ card.name_on_card }}</div>
|
<div class="font-bold text-sm">{{ card.name_on_card }}</div>
|
||||||
<div class="text-sm">{{ card.type_of_card }}</div>
|
<div class="text-sm">{{ card.type_of_card }}</div>
|
||||||
<div class="text-sm">{{ card.card_number }}</div>
|
<div class="text-sm">{{ card.card_number }}</div>
|
||||||
<p class="text-sm">Exp: <span v-if="card.expiration_month < 10">0</span>{{ card.expiration_month }} / {{ card.expiration_year }}</p>
|
<p class="text-sm">Exp: <span v-if="Number(card.expiration_month) < 10">0</span>{{ card.expiration_month }} / {{ card.expiration_year }}</p>
|
||||||
<p class="text-sm">CVV: {{ card.security_number }}</p>
|
<p class="text-sm">CVV: {{ card.security_number }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end gap-2 mt-2">
|
<div class="flex justify-end gap-2 mt-2">
|
||||||
@@ -305,43 +305,21 @@
|
|||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { Customer, CreditCard, CreateCardRequest } from '../../types/models'
|
||||||
import Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import { useVuelidate } from "@vuelidate/core";
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
import { minLength, required, requiredIf } from "@vuelidate/validators";
|
import { minLength, required, requiredIf } from "@vuelidate/validators";
|
||||||
|
|
||||||
// --- TYPE DEFINITIONS (MODIFIED) ---
|
// --- TYPE DEFINITIONS (MODIFIED) ---
|
||||||
interface SimpleResponse<T> { data: T; }
|
interface SimpleResponse<T> { data: T; }
|
||||||
interface Customer {
|
|
||||||
id: number;
|
|
||||||
customer_last_name: string;
|
|
||||||
customer_first_name: string;
|
|
||||||
customer_town: string;
|
|
||||||
customer_state: number;
|
|
||||||
customer_zip: string;
|
|
||||||
customer_phone_number: string;
|
|
||||||
customer_home_type: number;
|
|
||||||
customer_apt: string;
|
|
||||||
customer_address: string;
|
|
||||||
account_number: string;
|
|
||||||
}
|
|
||||||
// This interface now reflects the full card data from the backend
|
|
||||||
interface UserCard {
|
|
||||||
id: number;
|
|
||||||
last_four_digits?: number;
|
|
||||||
type_of_card: string;
|
|
||||||
expiration_month: number;
|
|
||||||
expiration_year: number;
|
|
||||||
name_on_card: string;
|
|
||||||
card_number: string;
|
|
||||||
security_number: string;
|
|
||||||
}
|
|
||||||
interface Promo { id: number; name_of_promotion: string; money_off_delivery: number; }
|
interface Promo { id: number; name_of_promotion: string; money_off_delivery: number; }
|
||||||
interface Driver { id: number; employee_first_name: string; employee_last_name: string; }
|
interface Driver { id: number; employee_first_name: string; employee_last_name: string; }
|
||||||
interface PricingTier { gallons: number | string; price: number | string; }
|
interface PricingTier { gallons: number | string; price: number | string; }
|
||||||
@@ -370,25 +348,23 @@ interface CardFormData {
|
|||||||
type_of_card: string;
|
type_of_card: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
const router = useRouter()
|
||||||
name: 'deliveryCreate',
|
const route = useRoute()
|
||||||
components: { Header, SideBar, Footer },
|
|
||||||
data() {
|
// Reactive data
|
||||||
return {
|
const user = ref(null as any)
|
||||||
v$: useValidate(),
|
const customer = ref({} as any)
|
||||||
user: null as any,
|
const isLoading = ref(false) // For main form submission
|
||||||
customer: {} as any,
|
const isCardSaving = ref(false) // For quick add card form
|
||||||
isLoading: false, // For main form submission
|
const quickGallonAmounts = ref([100, 125, 150, 175, 200, 220])
|
||||||
isCardSaving: false, // For quick add card form
|
const userCards = ref<CreditCard[]>([])
|
||||||
quickGallonAmounts: [100, 125, 150, 175, 200, 220],
|
const promos = ref([] as Promo[])
|
||||||
userCards: [] as UserCard[],
|
const truckDriversList = ref([] as Driver[])
|
||||||
promos: [] as Promo[],
|
const pricingTiers = ref([] as PricingTier[])
|
||||||
truckDriversList: [] as Driver[],
|
const isLoadingAuthorize = ref(true)
|
||||||
pricingTiers: [] as PricingTier[],
|
const authorizeCheck = ref({ profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false })
|
||||||
isLoadingAuthorize: true,
|
const isConfirmationModalVisible = ref(false)
|
||||||
authorizeCheck: { profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false },
|
const formDelivery = ref({
|
||||||
isConfirmationModalVisible: false,
|
|
||||||
formDelivery: {
|
|
||||||
gallons_ordered: '',
|
gallons_ordered: '',
|
||||||
customer_asked_for_fill: false,
|
customer_asked_for_fill: false,
|
||||||
expected_delivery_date: '',
|
expected_delivery_date: '',
|
||||||
@@ -403,62 +379,26 @@ export default defineComponent({
|
|||||||
credit_card_id: 0,
|
credit_card_id: 0,
|
||||||
promo_id: 0,
|
promo_id: 0,
|
||||||
driver_employee_id: 0,
|
driver_employee_id: 0,
|
||||||
} as DeliveryFormData,
|
} as DeliveryFormData)
|
||||||
// Simplified formCard data
|
// Simplified formCard data
|
||||||
formCard: {
|
const formCard = ref({
|
||||||
card_number: '',
|
card_number: '',
|
||||||
expiration_month: '',
|
expiration_month: '',
|
||||||
expiration_year: '',
|
expiration_year: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
card_name: '',
|
card_name: '',
|
||||||
type_of_card: '',
|
type_of_card: '',
|
||||||
} as CardFormData,
|
} as CardFormData)
|
||||||
|
|
||||||
}
|
// Computed
|
||||||
},
|
const preAuthAmount = computed((): number => {
|
||||||
validations() {
|
if (!formDelivery.value.credit || formDelivery.value.customer_asked_for_fill) return 0;
|
||||||
return {
|
const gallons = Number(formDelivery.value.gallons_ordered);
|
||||||
formDelivery: {
|
if (isNaN(gallons) || gallons <= 0 || pricingTiers.value.length === 0) return 0;
|
||||||
gallons_ordered: {
|
|
||||||
required: requiredIf(function(this: any) { return !this.formDelivery.customer_asked_for_fill; }),
|
|
||||||
minValue: function(this: any, value: string) {
|
|
||||||
if (this.formDelivery.customer_asked_for_fill) return true;
|
|
||||||
if (!value) return true;
|
|
||||||
const num = parseInt(value, 10);
|
|
||||||
return num >= 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expected_delivery_date: { required },
|
|
||||||
driver_employee_id: { required },
|
|
||||||
credit_card_id: {
|
|
||||||
creditCardRequired: function(this: any, value: number) {
|
|
||||||
if (this.formDelivery.credit) { return value !== 0; }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isAnyPaymentMethodSelected: { mustBeTrue: (value: boolean) => value === true, },
|
|
||||||
// Simplified validations for quick add card
|
|
||||||
formCard: {
|
|
||||||
card_name: { required, minLength: minLength(1) },
|
|
||||||
security_number: { required, minLength: minLength(1) },
|
|
||||||
type_of_card: { required },
|
|
||||||
card_number: { required, minLength: minLength(1) },
|
|
||||||
expiration_month: { required },
|
|
||||||
expiration_year: { required },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// New computed property to estimate pre-auth amount
|
|
||||||
preAuthAmount(): number {
|
|
||||||
if (!this.formDelivery.credit || this.formDelivery.customer_asked_for_fill) return 0;
|
|
||||||
const gallons = Number(this.formDelivery.gallons_ordered);
|
|
||||||
if (isNaN(gallons) || gallons <= 0 || this.pricingTiers.length === 0) return 0;
|
|
||||||
|
|
||||||
// Find the correct price tier. Assumes tiers are for total price, not price/gallon.
|
// Find the correct price tier. Assumes tiers are for total price, not price/gallon.
|
||||||
let priceForGallons = 0;
|
let priceForGallons = 0;
|
||||||
const sortedTiers = [...this.pricingTiers].sort((a, b) => Number(a.gallons) - Number(b.gallons));
|
const sortedTiers = [...pricingTiers.value].sort((a, b) => Number(a.gallons) - Number(b.gallons));
|
||||||
|
|
||||||
// Find the highest tier that is less than or equal to the gallons ordered
|
// Find the highest tier that is less than or equal to the gallons ordered
|
||||||
let applicableTier = sortedTiers.filter(t => gallons >= Number(t.gallons)).pop();
|
let applicableTier = sortedTiers.filter(t => gallons >= Number(t.gallons)).pop();
|
||||||
@@ -474,132 +414,176 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return priceForGallons;
|
return priceForGallons;
|
||||||
},
|
})
|
||||||
customerStateName(): string {
|
|
||||||
|
const customerStateName = computed((): string => {
|
||||||
const states: Record<number, string> = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
const states: Record<number, string> = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
||||||
return states[this.customer.customer_state] || 'Unknown state';
|
return states[customer.value.customer_state] || 'Unknown state';
|
||||||
},
|
})
|
||||||
customerHomeTypeName(): string {
|
|
||||||
|
const customerHomeTypeName = computed((): string => {
|
||||||
const types: Record<number, string> = { 0: 'Residential', 1: 'apartment', 2: 'condo', 3: 'commercial', 4: 'business', 5: 'construction', 6: 'container' };
|
const types: Record<number, string> = { 0: 'Residential', 1: 'apartment', 2: 'condo', 3: 'commercial', 4: 'business', 5: 'construction', 6: 'container' };
|
||||||
return types[this.customer.customer_home_type] || 'Unknown type';
|
return types[customer.value.customer_home_type] || 'Unknown type';
|
||||||
},
|
})
|
||||||
isAnyPaymentMethodSelected(): boolean {
|
|
||||||
return !!(this.formDelivery?.credit || this.formDelivery?.cash || this.formDelivery?.check || this.formDelivery?.other);
|
const isAnyPaymentMethodSelected = computed((): boolean => {
|
||||||
},
|
return !!(formDelivery.value?.credit || formDelivery.value?.cash || formDelivery.value?.check || formDelivery.value?.other);
|
||||||
selectedGallonsAmount(): number {
|
})
|
||||||
const value = this.formDelivery.gallons_ordered ?? '';
|
|
||||||
|
const selectedGallonsAmount = computed((): number => {
|
||||||
|
const value = formDelivery.value.gallons_ordered ?? '';
|
||||||
return Number(value);
|
return Number(value);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validations
|
||||||
|
const validations = {
|
||||||
|
formDelivery: {
|
||||||
|
gallons_ordered: {
|
||||||
|
required: requiredIf(() => !formDelivery.value.customer_asked_for_fill),
|
||||||
|
minValue: (value: string) => {
|
||||||
|
if (formDelivery.value.customer_asked_for_fill) return true;
|
||||||
|
if (!value) return true;
|
||||||
|
const num = parseInt(value, 10);
|
||||||
|
return num >= 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
expected_delivery_date: { required },
|
||||||
this.getDriversList()
|
driver_employee_id: { required },
|
||||||
this.getPromos()
|
credit_card_id: {
|
||||||
this.getPricingTiers()
|
creditCardRequired: (value: number) => {
|
||||||
},
|
if (formDelivery.value.credit) { return value !== 0; }
|
||||||
watch: {
|
return true;
|
||||||
$route() {
|
}
|
||||||
const customerId = this.$route.params.id;
|
|
||||||
this.getCustomer(customerId);
|
|
||||||
this.getPaymentCards(customerId);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
isAnyPaymentMethodSelected: { mustBeTrue: (value: boolean) => value === true, },
|
||||||
const customerId = this.$route.params.id;
|
// Simplified validations for quick add card
|
||||||
this.getCustomer(customerId)
|
formCard: {
|
||||||
this.getPaymentCards(customerId);
|
card_name: { required, minLength: minLength(1) },
|
||||||
|
security_number: { required, minLength: minLength(1) },
|
||||||
|
type_of_card: { required },
|
||||||
|
card_number: { required, minLength: minLength(1) },
|
||||||
|
expiration_month: { required },
|
||||||
|
expiration_year: { required },
|
||||||
},
|
},
|
||||||
methods: {
|
}
|
||||||
setGallons(amount: number) { this.formDelivery.gallons_ordered = String(amount); this.formDelivery.customer_asked_for_fill = false; },
|
|
||||||
selectCreditCard(cardId: number) { this.formDelivery.credit_card_id = cardId; },
|
const v$ = useVuelidate(validations, { formDelivery, formCard, isAnyPaymentMethodSelected })
|
||||||
setDeliveryDate(days: number) { const date = new Date(); date.setDate(date.getDate() + days); this.formDelivery.expected_delivery_date = date.toISOString().split('T')[0]; },
|
|
||||||
isDeliveryDateSelected(days: number): boolean { const date = new Date(); date.setDate(date.getDate() + days); return this.formDelivery.expected_delivery_date === date.toISOString().split('T')[0]; },
|
// Functions
|
||||||
isPricingTierSelected(tierGallons: number | string): boolean {
|
const setGallons = (amount: number) => {
|
||||||
if (!this.formDelivery.gallons_ordered) return false;
|
formDelivery.value.gallons_ordered = String(amount);
|
||||||
const selectedGallons = Number(this.formDelivery.gallons_ordered);
|
formDelivery.value.customer_asked_for_fill = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectCreditCard = (cardId: number) => {
|
||||||
|
formDelivery.value.credit_card_id = cardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDeliveryDate = (days: number) => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + days);
|
||||||
|
formDelivery.value.expected_delivery_date = date.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDeliveryDateSelected = (days: number): boolean => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + days);
|
||||||
|
return formDelivery.value.expected_delivery_date === date.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPricingTierSelected = (tierGallons: number | string): boolean => {
|
||||||
|
if (!formDelivery.value.gallons_ordered) return false;
|
||||||
|
const selectedGallons = Number(formDelivery.value.gallons_ordered);
|
||||||
if (isNaN(selectedGallons)) return false;
|
if (isNaN(selectedGallons)) return false;
|
||||||
const tierNum = Number(tierGallons);
|
const tierNum = Number(tierGallons);
|
||||||
if (isNaN(tierNum)) return false;
|
if (isNaN(tierNum)) return false;
|
||||||
return selectedGallons === tierNum;
|
return selectedGallons === tierNum;
|
||||||
},
|
}
|
||||||
getPricingTiers() {
|
|
||||||
|
const getPricingTiers = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
||||||
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
||||||
.then((response: SimpleResponse<{ [key: string]: string }>) => {
|
.then((response: SimpleResponse<{ [key: string]: string }>) => {
|
||||||
this.pricingTiers = Object.entries(response.data).map(([gallons, price]) => ({ gallons: parseInt(gallons, 10), price: price }));
|
pricingTiers.value = Object.entries(response.data).map(([gallons, price]) => ({ gallons: parseInt(gallons, 10), price: price }));
|
||||||
})
|
})
|
||||||
.catch(() => notify({ title: "Pricing Error", text: "Could not retrieve today's pricing.", type: "error" }));
|
.catch(() => notify({ title: "Pricing Error", text: "Could not retrieve today's pricing.", type: "error" }));
|
||||||
},
|
}
|
||||||
getCustomer(user_id: string | number | string[]) {
|
|
||||||
|
const getCustomer = (user_id: string | number | string[]) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
||||||
axios({ method: "get", url: path, withCredentials: true })
|
axios({ method: "get", url: path, withCredentials: true })
|
||||||
.then((response: SimpleResponse<Customer>) => { this.customer = response.data; })
|
.then((response: SimpleResponse<Customer>) => { customer.value = response.data; })
|
||||||
.catch(() => notify({ title: "Error", text: "Could not find customer", type: "error" }));
|
.catch(() => notify({ title: "Error", text: "Could not find customer", type: "error" }));
|
||||||
},
|
}
|
||||||
getPaymentCards(user_id: string | number | string[]) {
|
|
||||||
|
const getPaymentCards = (user_id: string | number | string[]) => {
|
||||||
// IMPORTANT: This endpoint points to the Flask API that returns the secure card list.
|
// IMPORTANT: This endpoint points to the Flask API that returns the secure card list.
|
||||||
let path = `${import.meta.env.VITE_BASE_URL}/payment/cards/${user_id}`;
|
let path = `${import.meta.env.VITE_BASE_URL}/payment/cards/${user_id}`;
|
||||||
return axios.get(path, { withCredentials: true, headers: authHeader() })
|
return axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: SimpleResponse<UserCard[]>) => { this.userCards = response.data; })
|
.then((response: SimpleResponse<CreditCard[]>) => { userCards.value = response.data; })
|
||||||
.catch(() => { this.userCards = []; }); // Clear cards on error
|
.catch(() => { userCards.value = []; }); // Clear cards on error
|
||||||
},
|
}
|
||||||
getPromos() {
|
|
||||||
|
const getPromos = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/all";
|
let path = import.meta.env.VITE_BASE_URL + "/promo/all";
|
||||||
axios({ method: "get", url: path, withCredentials: true })
|
axios({ method: "get", url: path, withCredentials: true })
|
||||||
.then((response: SimpleResponse<Promo[]>) => {
|
.then((response: SimpleResponse<Promo[]>) => {
|
||||||
this.promos = response.data;
|
promos.value = response.data;
|
||||||
})
|
})
|
||||||
.catch(() => { /* empty */ });
|
.catch(() => { /* empty */ });
|
||||||
},
|
}
|
||||||
getDriversList() {
|
|
||||||
|
const getDriversList = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/employee/drivers";
|
let path = import.meta.env.VITE_BASE_URL + "/employee/drivers";
|
||||||
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
||||||
.then((response: SimpleResponse<Driver[]>) => {
|
.then((response: SimpleResponse<Driver[]>) => {
|
||||||
this.truckDriversList = response.data;
|
truckDriversList.value = response.data;
|
||||||
})
|
})
|
||||||
.catch(() => { /* empty */ });
|
.catch(() => { /* empty */ });
|
||||||
},
|
}
|
||||||
|
|
||||||
editCard(card_id: number) {
|
const editCard = (card_id: number) => {
|
||||||
this.$router.push({ name: "cardedit", params: { id: card_id } });
|
router.push({ name: "cardedit", params: { id: card_id } });
|
||||||
},
|
}
|
||||||
|
|
||||||
removeCard(card_id: number) {
|
const removeCard = (card_id: number) => {
|
||||||
if (window.confirm("Are you sure you want to remove this card?")) {
|
if (window.confirm("Are you sure you want to remove this card?")) {
|
||||||
// You will need a new backend endpoint for this: DELETE /payments/customers/{customer_id}/cards/{card_id}
|
// You will need a new backend endpoint for this: DELETE /payments/customers/{customer_id}/cards/{card_id}
|
||||||
let path = `${import.meta.env.VITE_BASE_URL}/payment/card/remove/${card_id}`; // Keep old path or update to new
|
let path = `${import.meta.env.VITE_BASE_URL}/payment/card/remove/${card_id}`; // Keep old path or update to new
|
||||||
axios.delete(path, { headers: authHeader() })
|
axios.delete(path, { headers: authHeader() })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
notify({ title: "Card Removed", type: "success" });
|
notify({ title: "Card Removed", type: "success" });
|
||||||
this.getPaymentCards(this.customer.id);
|
getPaymentCards(customer.value.id);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({ title: "Error", text: "Could not remove card.", type: "error" });
|
notify({ title: "Error", text: "Could not remove card.", type: "error" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async onDeliverySubmit() {
|
const onDeliverySubmit = async () => {
|
||||||
const isFormValid = await this.v$.formDelivery.$validate();
|
const isFormValid = await v$.value.formDelivery.$validate();
|
||||||
if (!isFormValid) {
|
if (!isFormValid) {
|
||||||
notify({ title: "Form Incomplete", text: "Please review the fields marked in red.", type: "warn" });
|
notify({ title: "Form Incomplete", text: "Please review the fields marked in red.", type: "warn" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.formDelivery.cash || this.formDelivery.check) {
|
if (formDelivery.value.cash || formDelivery.value.check) {
|
||||||
this.isConfirmationModalVisible = true;
|
isConfirmationModalVisible.value = true;
|
||||||
} else {
|
} else {
|
||||||
this.proceedWithSubmission();
|
proceedWithSubmission();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async proceedWithSubmission() {
|
const proceedWithSubmission = async () => {
|
||||||
this.isConfirmationModalVisible = false;
|
isConfirmationModalVisible.value = false;
|
||||||
this.isLoading = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
// Step 1: Create the delivery order record
|
// Step 1: Create the delivery order record
|
||||||
const createDeliveryPath = `${import.meta.env.VITE_BASE_URL}/delivery/create/${this.customer.id}`;
|
const createDeliveryPath = `${import.meta.env.VITE_BASE_URL}/delivery/create/${customer.value.id}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const deliveryResponse = await axios.post(createDeliveryPath, this.formDelivery, { withCredentials: true, headers: authHeader() });
|
const deliveryResponse = await axios.post(createDeliveryPath, formDelivery.value, { withCredentials: true, headers: authHeader() });
|
||||||
const deliveryData = deliveryResponse.data;
|
const deliveryData = deliveryResponse.data;
|
||||||
|
|
||||||
if (!deliveryData.ok || !deliveryData.delivery_id) {
|
if (!deliveryData.ok || !deliveryData.delivery_id) {
|
||||||
@@ -610,69 +594,69 @@ export default defineComponent({
|
|||||||
notify({
|
notify({
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
text: `Delivery #${deliveryData.delivery_id} created. ${
|
text: `Delivery #${deliveryData.delivery_id} created. ${
|
||||||
this.formDelivery.credit
|
formDelivery.value.credit
|
||||||
? "Redirecting to payment page."
|
? "Redirecting to payment page."
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
this.$router.push({ name: "payOil", params: { id: deliveryData.delivery_id } });
|
router.push({ name: "payOil", params: { id: deliveryData.delivery_id } });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.detail || "An error occurred during submission.";
|
const errorMessage = error.response?.data?.detail || "An error occurred during submission.";
|
||||||
notify({ title: "Submission Error", text: errorMessage, type: "error" });
|
notify({ title: "Submission Error", text: errorMessage, type: "error" });
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async checkAuthorizeAccount() {
|
|
||||||
if (!this.customer.id) return;
|
|
||||||
|
|
||||||
this.isLoadingAuthorize = true;
|
const checkAuthorizeAccount = async () => {
|
||||||
|
if (!customer.value.id) return;
|
||||||
|
|
||||||
|
isLoadingAuthorize.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${customer.value.id}`;
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
const response = await axios.get(path, { headers: authHeader() });
|
||||||
this.authorizeCheck = response.data;
|
authorizeCheck.value = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check authorize account:", error);
|
console.error("Failed to check authorize account:", error);
|
||||||
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
||||||
// Set default error state
|
// Set default error state
|
||||||
this.authorizeCheck = {
|
authorizeCheck.value = {
|
||||||
profile_exists: false,
|
profile_exists: false,
|
||||||
has_payment_methods: false,
|
has_payment_methods: false,
|
||||||
missing_components: ['api_error'],
|
missing_components: ['api_error'],
|
||||||
valid_for_charging: false
|
valid_for_charging: false
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingAuthorize = false;
|
isLoadingAuthorize.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async onCardSubmit() {
|
|
||||||
this.v$.formCard.$validate();
|
const onCardSubmit = async () => {
|
||||||
if (this.v$.formCard.$error) {
|
v$.value.formCard.$validate();
|
||||||
|
if (v$.value.formCard.$error) {
|
||||||
notify({ title: "Validation Error", text: "Please fill out all required fields.", type: "error" });
|
notify({ title: "Validation Error", text: "Please fill out all required fields.", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isCardSaving = true;
|
isCardSaving.value = true;
|
||||||
|
|
||||||
// --- STEP 1: PREPARE PAYLOADS FOR BOTH SERVICES ---
|
// --- STEP 1: PREPARE PAYLOADS FOR BOTH SERVICES ---
|
||||||
// Payload for Flask backend (it takes all the raw details for your DB)
|
// Payload for Flask backend (it takes all the raw details for your DB)
|
||||||
const flaskPayload = {
|
const flaskPayload = {
|
||||||
card_number: this.formCard.card_number,
|
card_number: formCard.value.card_number,
|
||||||
expiration_month: this.formCard.expiration_month,
|
expiration_month: formCard.value.expiration_month,
|
||||||
expiration_year: this.formCard.expiration_year,
|
expiration_year: formCard.value.expiration_year,
|
||||||
type_of_card: this.formCard.type_of_card,
|
type_of_card: formCard.value.type_of_card,
|
||||||
security_number: this.formCard.security_number, // Flask expects 'security_number'
|
security_number: formCard.value.security_number, // Flask expects 'security_number'
|
||||||
main_card: false,
|
main_card: false,
|
||||||
name_on_card: this.formCard.card_name, // Map card_name to name_on_card for Flask
|
name_on_card: formCard.value.card_name, // Map card_name to name_on_card for Flask
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- STEP 2: CRITICAL CALL - SAVE CARD TO LOCAL DATABASE VIA FLASK ---
|
// --- STEP 2: CRITICAL CALL - SAVE CARD TO LOCAL DATABASE VIA FLASK ---
|
||||||
try {
|
try {
|
||||||
const flaskPath = `${import.meta.env.VITE_BASE_URL}/payment/card/create/${this.customer.id}`;
|
const flaskPath = `${import.meta.env.VITE_BASE_URL}/payment/card/create/${customer.value.id}`;
|
||||||
console.log("Attempting to save card to local DB via Flask:", flaskPath);
|
console.log("Attempting to save card to local DB via Flask:", flaskPath);
|
||||||
const flaskResponse = await axios.post(flaskPath, flaskPayload, { withCredentials: true, headers: authHeader() });
|
const flaskResponse = await axios.post(flaskPath, flaskPayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
@@ -685,22 +669,22 @@ export default defineComponent({
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.error || "A critical error occurred while saving the card.";
|
const errorMessage = error.response?.data?.error || "A critical error occurred while saving the card.";
|
||||||
notify({ title: "Error", text: errorMessage, type: "error" });
|
notify({ title: "Error", text: errorMessage, type: "error" });
|
||||||
this.isCardSaving = false; // Stop loading spinner
|
isCardSaving.value = false; // Stop loading spinner
|
||||||
return; // End the function here
|
return; // End the function here
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- STEP 3: BEST-EFFORT CALL - TOKENIZE CARD VIA FASTAPI ---
|
// --- STEP 3: BEST-EFFORT CALL - TOKENIZE CARD VIA FASTAPI ---
|
||||||
if (this.authorizeCheck.profile_exists) {
|
if (authorizeCheck.value.profile_exists) {
|
||||||
// Payload for FastAPI backend (it only needs the essentials for Authorize.Net)
|
// Payload for FastAPI backend (it only needs the essentials for Authorize.Net)
|
||||||
const fastapiPayload = {
|
const fastapiPayload = {
|
||||||
card_number: this.formCard.card_number.replace(/\s/g, ''),
|
card_number: formCard.value.card_number.replace(/\s/g, ''),
|
||||||
expiration_date: `${this.formCard.expiration_year}-${this.formCard.expiration_month}`,
|
expiration_date: `${formCard.value.expiration_year}-${formCard.value.expiration_month}`,
|
||||||
cvv: this.formCard.security_number, // Map security_number to cvv for FastAPI
|
cvv: formCard.value.security_number, // Map security_number to cvv for FastAPI
|
||||||
main_card: false, // Send this to FastAPI as well
|
main_card: false, // Send this to FastAPI as well
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fastapiPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/customers/${this.customer.id}/cards`;
|
const fastapiPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/customers/${customer.value.id}/cards`;
|
||||||
console.log("Attempting to tokenize card with Authorize.Net via FastAPI:", fastapiPath);
|
console.log("Attempting to tokenize card with Authorize.Net via FastAPI:", fastapiPath);
|
||||||
await axios.post(fastapiPath, fastapiPayload, { withCredentials: true, headers: authHeader() });
|
await axios.post(fastapiPath, fastapiPayload, { withCredentials: true, headers: authHeader() });
|
||||||
console.log("Card successfully tokenized with Authorize.Net via FastAPI.");
|
console.log("Card successfully tokenized with Authorize.Net via FastAPI.");
|
||||||
@@ -708,25 +692,40 @@ export default defineComponent({
|
|||||||
// If this fails, we just log it for the developers. We DON'T show an error to the user.
|
// If this fails, we just log it for the developers. We DON'T show an error to the user.
|
||||||
console.warn("NON-CRITICAL-ERROR: Tokenization with Authorize.Net failed, but the card was saved locally.", error.response?.data || error.message);
|
console.warn("NON-CRITICAL-ERROR: Tokenization with Authorize.Net failed, but the card was saved locally.", error.response?.data || error.message);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
console.log("Skipping Authorize.Net tokenization as no profile exists for customer.");
|
console.log("Skipping Authorize.Net tokenization as no profile exists for customer.");
|
||||||
}
|
}
|
||||||
// --- STEP 4: ALWAYS SHOW SUCCESS, REFRESH CARDS, STAY ON PAGE ---
|
// --- STEP 4: ALWAYS SHOW SUCCESS, REFRESH CARDS, STAY ON PAGE ---
|
||||||
// This code runs as long as the first (Flask) call was successful.
|
// This code runs as long as the first (Flask) call was successful.
|
||||||
notify({ type: 'success', title: 'Card Saved!' });
|
notify({ type: 'success', title: 'Card Saved!' });
|
||||||
|
|
||||||
// Refresh the card list and try to auto-select if possible
|
// Refresh the card list and try to auto-select if possible
|
||||||
await this.getPaymentCards(this.customer.id);
|
await getPaymentCards(customer.value.id);
|
||||||
if (this.userCards.length > 0) {
|
if (userCards.value.length > 0) {
|
||||||
this.formDelivery.credit = true;
|
formDelivery.value.credit = true;
|
||||||
this.formDelivery.credit_card_id = this.userCards[this.userCards.length - 1].id;
|
formDelivery.value.credit_card_id = userCards.value[userCards.value.length - 1].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the quick add form
|
// Reset the quick add form
|
||||||
Object.assign(this.formCard, { card_number: '', expiration_month: '', expiration_year: '', security_number: '', card_name: '', type_of_card: '' });
|
Object.assign(formCard.value, { card_number: '', expiration_month: '', expiration_year: '', security_number: '', card_name: '', type_of_card: '' });
|
||||||
this.v$.formCard.$reset();
|
v$.value.formCard.$reset();
|
||||||
this.isCardSaving = false;
|
isCardSaving.value = false;
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Watchers
|
||||||
|
watch(route, () => {
|
||||||
|
const customerId = route.params.id;
|
||||||
|
getCustomer(customerId);
|
||||||
|
getPaymentCards(customerId);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
getDriversList()
|
||||||
|
getPromos()
|
||||||
|
getPricingTiers()
|
||||||
|
const customerId = route.params.id;
|
||||||
|
getCustomer(customerId)
|
||||||
|
getPaymentCards(customerId);
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -223,7 +223,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-2 text-sm font-mono tracking-wider">
|
<div class="mt-2 text-sm font-mono tracking-wider">
|
||||||
<p>{{ card.card_number }}</p>
|
<p>{{ card.card_number }}</p>
|
||||||
<p>Exp: <span v-if="card.expiration_month < 10">0</span>{{ card.expiration_month }} / {{ card.expiration_year }}</p>
|
<p>Exp: <span v-if="Number(card.expiration_month) < 10">0</span>{{ card.expiration_month }} / {{ card.expiration_year }}</p>
|
||||||
<p>CVV: {{ card.security_number }}</p>
|
<p>CVV: {{ card.security_number }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end gap-2 mt-2">
|
<div class="flex justify-end gap-2 mt-2">
|
||||||
@@ -240,42 +240,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { Customer, CreditCard } from '../../types/models'
|
||||||
import Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import { useVuelidate } from "@vuelidate/core";
|
||||||
import { required, requiredIf } from "@vuelidate/validators";
|
import { required, requiredIf } from "@vuelidate/validators";
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
// Interfaces to describe the shape of your data
|
// Interfaces to describe the shape of your data
|
||||||
interface Customer { account_number: string; id: number; customer_first_name: string; customer_last_name: string; customer_address: string; customer_apt: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; }
|
|
||||||
interface DeliveryOrder { id: string; customer_id: number; payment_type: number; payment_card_id: number; gallons_ordered: number; customer_asked_for_fill: boolean | number; delivery_status: number; driver_employee_id: number; promo_id: number; expected_delivery_date: string; when_ordered: string; prime: boolean | number; emergency: boolean | number; same_day: boolean | number; dispatcher_notes: string; }
|
interface DeliveryOrder { id: string; customer_id: number; payment_type: number; payment_card_id: number; gallons_ordered: number; customer_asked_for_fill: boolean | number; delivery_status: number; driver_employee_id: number; promo_id: number; expected_delivery_date: string; when_ordered: string; prime: boolean | number; emergency: boolean | number; same_day: boolean | number; dispatcher_notes: string; }
|
||||||
interface UserCard { id: number; type_of_card: string; card_number: string; name_on_card: string; expiration_month: number; expiration_year: number; last_four_digits: string; security_number: string; main_card: boolean; }
|
|
||||||
interface PricingTier { gallons: number; price: string | number; }
|
interface PricingTier { gallons: number; price: string | number; }
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const STATE_MAP: { [key: number]: string } = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
const STATE_MAP: { [key: number]: string } = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryEdit',
|
const quickGallonAmounts = ref([100, 125, 150, 175, 200, 220])
|
||||||
components: { Header, SideBar, Footer },
|
const deliveryStatus = ref([] as any[])
|
||||||
data() {
|
const truckDriversList = ref([] as any[])
|
||||||
return {
|
const userCards = ref<CreditCard[]>([])
|
||||||
v$: useValidate(),
|
const promos = ref([] as any[])
|
||||||
quickGallonAmounts: [100, 125, 150, 175, 200, 220],
|
const pricingTiers = ref([] as PricingTier[])
|
||||||
deliveryStatus: [] as any[],
|
const customer = ref({} as Customer)
|
||||||
truckDriversList: [] as any[],
|
const deliveryOrder = ref({} as DeliveryOrder)
|
||||||
userCards: [] as UserCard[],
|
const userCard = ref({} as CreditCard)
|
||||||
promos: [] as any[],
|
// RESTORED: Add all form fields to the data model
|
||||||
pricingTiers: [] as PricingTier[],
|
const CreateOilOrderForm = ref({
|
||||||
customer: {} as Customer,
|
|
||||||
deliveryOrder: {} as DeliveryOrder,
|
|
||||||
userCard: {} as UserCard,
|
|
||||||
// RESTORED: Add all form fields to the data model
|
|
||||||
CreateOilOrderForm: {
|
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
gallons_ordered: '',
|
gallons_ordered: '',
|
||||||
customer_asked_for_fill: false,
|
customer_asked_for_fill: false,
|
||||||
@@ -295,20 +293,46 @@ export default defineComponent({
|
|||||||
check: false,
|
check: false,
|
||||||
other: false,
|
other: false,
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validations() {
|
// Computed
|
||||||
return {
|
const stateName = computed((): string => {
|
||||||
|
if (customer.value && customer.value.customer_state !== undefined) {
|
||||||
|
return STATE_MAP[customer.value.customer_state];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
})
|
||||||
|
|
||||||
|
const isPricingTierSelected = computed(() => {
|
||||||
|
return (tierGallons: number | string): boolean => {
|
||||||
|
if (!CreateOilOrderForm.value.basicInfo.gallons_ordered) return false;
|
||||||
|
const selectedGallons = Number(CreateOilOrderForm.value.basicInfo.gallons_ordered);
|
||||||
|
if (isNaN(selectedGallons)) return false;
|
||||||
|
|
||||||
|
const tierNum = Number(tierGallons);
|
||||||
|
if (isNaN(tierNum)) return false;
|
||||||
|
|
||||||
|
return selectedGallons === tierNum;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
const isAnyPaymentMethodSelected = computed((): boolean => {
|
||||||
|
return !!(CreateOilOrderForm.value.basicInfo?.credit || CreateOilOrderForm.value.basicInfo?.cash || CreateOilOrderForm.value.basicInfo?.check || CreateOilOrderForm.value.basicInfo?.other);
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedGallonsAmount = computed((): number => {
|
||||||
|
const value = CreateOilOrderForm.value.basicInfo.gallons_ordered ?? '';
|
||||||
|
return Number(value);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validations
|
||||||
|
const validations = {
|
||||||
CreateOilOrderForm: {
|
CreateOilOrderForm: {
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
gallons_ordered: {
|
gallons_ordered: {
|
||||||
required: requiredIf(function(this: any) {
|
required: requiredIf(() => !CreateOilOrderForm.value.basicInfo.customer_asked_for_fill),
|
||||||
return !this.CreateOilOrderForm.basicInfo.customer_asked_for_fill;
|
minValue: (value: string) => {
|
||||||
}),
|
if (CreateOilOrderForm.value.basicInfo.customer_asked_for_fill) return true;
|
||||||
minValue: function(this: any, value: string) {
|
|
||||||
if (this.CreateOilOrderForm.basicInfo.customer_asked_for_fill) return true;
|
|
||||||
if (!value) return true; // if empty, required will catch it
|
if (!value) return true; // if empty, required will catch it
|
||||||
const num = parseInt(value, 10);
|
const num = parseInt(value, 10);
|
||||||
return num >= 1;
|
return num >= 1;
|
||||||
@@ -316,8 +340,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
expected_delivery_date: { required },
|
expected_delivery_date: { required },
|
||||||
credit_card_id: {
|
credit_card_id: {
|
||||||
creditCardRequired: function(this: any, value: number) {
|
creditCardRequired: (value: number) => {
|
||||||
if (this.CreateOilOrderForm.basicInfo.credit) {
|
if (CreateOilOrderForm.value.basicInfo.credit) {
|
||||||
return value !== 0;
|
return value !== 0;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -328,74 +352,43 @@ export default defineComponent({
|
|||||||
isAnyPaymentMethodSelected: {
|
isAnyPaymentMethodSelected: {
|
||||||
mustBeTrue: (value: boolean) => value === true,
|
mustBeTrue: (value: boolean) => value === true,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const v$ = useVuelidate(validations, { CreateOilOrderForm, isAnyPaymentMethodSelected })
|
||||||
stateName(): string {
|
|
||||||
if (this.customer && this.customer.customer_state !== undefined) {
|
|
||||||
return STATE_MAP[this.customer.customer_state];
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
isPricingTierSelected() {
|
|
||||||
return (tierGallons: number | string): boolean => {
|
|
||||||
if (!this.CreateOilOrderForm.basicInfo.gallons_ordered) return false;
|
|
||||||
const selectedGallons = Number(this.CreateOilOrderForm.basicInfo.gallons_ordered);
|
|
||||||
if (isNaN(selectedGallons)) return false;
|
|
||||||
|
|
||||||
const tierNum = Number(tierGallons);
|
// Functions
|
||||||
if (isNaN(tierNum)) return false;
|
const fetchInitialData = () => {
|
||||||
|
const deliveryId = route.params.id as string;
|
||||||
|
getPromos();
|
||||||
|
getDriversList();
|
||||||
|
getDeliveryStatusList();
|
||||||
|
getPricingTiers();
|
||||||
|
getDeliveryOrder(deliveryId);
|
||||||
|
}
|
||||||
|
|
||||||
return selectedGallons === tierNum;
|
const getDeliveryOrder = (deliveryId: string) => {
|
||||||
};
|
|
||||||
},
|
|
||||||
isAnyPaymentMethodSelected(): boolean {
|
|
||||||
return !!(this.CreateOilOrderForm.basicInfo?.credit || this.CreateOilOrderForm.basicInfo?.cash || this.CreateOilOrderForm.basicInfo?.check || this.CreateOilOrderForm.basicInfo?.other);
|
|
||||||
},
|
|
||||||
selectedGallonsAmount(): number {
|
|
||||||
const value = this.CreateOilOrderForm.basicInfo.gallons_ordered ?? '';
|
|
||||||
return Number(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.fetchInitialData();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
fetchInitialData() {
|
|
||||||
const deliveryId = this.$route.params.id as string;
|
|
||||||
this.getPromos();
|
|
||||||
this.getDriversList();
|
|
||||||
this.getDeliveryStatusList();
|
|
||||||
this.getPricingTiers();
|
|
||||||
this.getDeliveryOrder(deliveryId);
|
|
||||||
},
|
|
||||||
|
|
||||||
getDeliveryOrder(deliveryId: string) {
|
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/delivery/order/${deliveryId}`, { withCredentials: true, headers: authHeader() })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/delivery/order/${deliveryId}`, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
// FIX: Check for the 'ok' flag and access the nested 'delivery' object
|
// FIX: Check for the 'ok' flag and access the nested 'delivery' object
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
this.deliveryOrder = response.data.delivery; // <-- THIS IS THE CRITICAL CHANGE
|
deliveryOrder.value = response.data.delivery; // <-- THIS IS THE CRITICAL CHANGE
|
||||||
|
|
||||||
// RESTORED: Populate all form fields from the API response
|
// RESTORED: Populate all form fields from the API response
|
||||||
const paymentType = this.deliveryOrder.payment_type;
|
const paymentType = deliveryOrder.value.payment_type;
|
||||||
this.CreateOilOrderForm.basicInfo = {
|
CreateOilOrderForm.value.basicInfo = {
|
||||||
gallons_ordered: String(this.deliveryOrder.gallons_ordered),
|
gallons_ordered: String(deliveryOrder.value.gallons_ordered),
|
||||||
customer_asked_for_fill: !!this.deliveryOrder.customer_asked_for_fill,
|
customer_asked_for_fill: !!deliveryOrder.value.customer_asked_for_fill,
|
||||||
created_delivery_date: this.deliveryOrder.when_ordered,
|
created_delivery_date: deliveryOrder.value.when_ordered,
|
||||||
expected_delivery_date: this.deliveryOrder.expected_delivery_date,
|
expected_delivery_date: deliveryOrder.value.expected_delivery_date,
|
||||||
prime: !!this.deliveryOrder.prime,
|
prime: !!deliveryOrder.value.prime,
|
||||||
emergency: !!this.deliveryOrder.emergency,
|
emergency: !!deliveryOrder.value.emergency,
|
||||||
same_day: !!this.deliveryOrder.same_day,
|
same_day: !!deliveryOrder.value.same_day,
|
||||||
delivery_status: this.deliveryOrder.delivery_status,
|
delivery_status: deliveryOrder.value.delivery_status,
|
||||||
driver_employee_id: this.deliveryOrder.driver_employee_id || 0,
|
driver_employee_id: deliveryOrder.value.driver_employee_id || 0,
|
||||||
dispatcher_notes_taken: this.deliveryOrder.dispatcher_notes,
|
dispatcher_notes_taken: deliveryOrder.value.dispatcher_notes,
|
||||||
promo_id: this.deliveryOrder.promo_id || 0,
|
promo_id: deliveryOrder.value.promo_id || 0,
|
||||||
payment_type: paymentType,
|
payment_type: paymentType,
|
||||||
credit_card_id: this.deliveryOrder.payment_card_id || 0,
|
credit_card_id: deliveryOrder.value.payment_card_id || 0,
|
||||||
// Set the correct payment method checkbox based on payment_type
|
// Set the correct payment method checkbox based on payment_type
|
||||||
credit: paymentType === 1,
|
credit: paymentType === 1,
|
||||||
cash: paymentType === 0,
|
cash: paymentType === 0,
|
||||||
@@ -403,56 +396,59 @@ export default defineComponent({
|
|||||||
other: paymentType === 4,
|
other: paymentType === 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getCustomer(this.deliveryOrder.customer_id);
|
getCustomer(deliveryOrder.value.customer_id);
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => console.error("Error fetching delivery order:", error));
|
.catch((error: any) => console.error("Error fetching delivery order:", error));
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomer(customerId: number) {
|
const getCustomer = (customerId: number) => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/customer/${customerId}`, { withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/customer/${customerId}`, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
this.getPaymentCards(customerId);
|
getPaymentCards(customerId);
|
||||||
if (this.deliveryOrder.payment_type === 1 && this.deliveryOrder.payment_card_id) {
|
if (deliveryOrder.value.payment_type === 1 && deliveryOrder.value.payment_card_id) {
|
||||||
this.getPaymentCard(this.deliveryOrder.payment_card_id);
|
getPaymentCard(deliveryOrder.value.payment_card_id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => console.error("Error fetching customer:", error));
|
.catch((error: any) => console.error("Error fetching customer:", error));
|
||||||
},
|
}
|
||||||
|
|
||||||
getPaymentCards(customerId: number) {
|
const getPaymentCards = (customerId: number) => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/payment/cards/${customerId}`, { withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/payment/cards/${customerId}`, { withCredentials: true })
|
||||||
.then((response: any) => { this.userCards = response.data; })
|
.then((response: any) => { userCards.value = response.data; })
|
||||||
.catch((error: any) => console.error("Error fetching payment cards:", error));
|
.catch((error: any) => console.error("Error fetching payment cards:", error));
|
||||||
},
|
}
|
||||||
|
|
||||||
getPaymentCard(cardId: number) {
|
const getPaymentCard = (cardId: number) => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/payment/card/${cardId}`, { withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/payment/card/${cardId}`, { withCredentials: true })
|
||||||
.then((response: any) => { this.userCard = response.data; })
|
.then((response: any) => { userCard.value = response.data; })
|
||||||
.catch((error: any) => console.error("Error fetching specific payment card:", error));
|
.catch((error: any) => console.error("Error fetching specific payment card:", error));
|
||||||
},
|
}
|
||||||
|
|
||||||
getPromos() {
|
const getPromos = () => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/promo/all`, { withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/promo/all`, { withCredentials: true })
|
||||||
.then((response: any) => { this.promos = response.data; });
|
.then((response: any) => { promos.value = response.data; });
|
||||||
},
|
}
|
||||||
getDriversList() {
|
|
||||||
|
const getDriversList = () => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/employee/drivers`, { headers: authHeader(), withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/employee/drivers`, { headers: authHeader(), withCredentials: true })
|
||||||
.then((response: any) => { this.truckDriversList = response.data; });
|
.then((response: any) => { truckDriversList.value = response.data; });
|
||||||
},
|
}
|
||||||
getDeliveryStatusList() {
|
|
||||||
|
const getDeliveryStatusList = () => {
|
||||||
axios.get(`${import.meta.env.VITE_BASE_URL}/query/deliverystatus`, { withCredentials: true })
|
axios.get(`${import.meta.env.VITE_BASE_URL}/query/deliverystatus`, { withCredentials: true })
|
||||||
.then((response: any) => { this.deliveryStatus = response.data; });
|
.then((response: any) => { deliveryStatus.value = response.data; });
|
||||||
},
|
}
|
||||||
getPricingTiers() {
|
|
||||||
|
const getPricingTiers = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
||||||
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
axios({ method: "get", url: path, withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
const tiersObject = response.data;
|
const tiersObject = response.data;
|
||||||
this.pricingTiers = Object.entries(tiersObject).map(([gallons, price]) => ({
|
pricingTiers.value = Object.entries(tiersObject).map(([gallons, price]) => ({
|
||||||
gallons: parseInt(gallons, 10),
|
gallons: parseInt(gallons, 10),
|
||||||
price: price as string | number,
|
price: price as string | number,
|
||||||
}));
|
}));
|
||||||
@@ -460,50 +456,57 @@ export default defineComponent({
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({ title: "Pricing Error", text: "Could not retrieve today's pricing.", type: "error" });
|
notify({ title: "Pricing Error", text: "Could not retrieve today's pricing.", type: "error" });
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
editCard(card_id: number) {
|
|
||||||
this.$router.push({ name: "cardedit", params: { id: card_id } });
|
const editCard = (card_id: number) => {
|
||||||
},
|
router.push({ name: "cardedit", params: { id: card_id } });
|
||||||
removeCard(card_id: number) {
|
}
|
||||||
|
|
||||||
|
const removeCard = (card_id: number) => {
|
||||||
if (window.confirm("Are you sure you want to remove this card?")) {
|
if (window.confirm("Are you sure you want to remove this card?")) {
|
||||||
let path = `${import.meta.env.VITE_BASE_URL}/payment/card/remove/${card_id}`;
|
let path = `${import.meta.env.VITE_BASE_URL}/payment/card/remove/${card_id}`;
|
||||||
axios.delete(path, { headers: authHeader() })
|
axios.delete(path, { headers: authHeader() })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
notify({ title: "Card Removed", type: "success" });
|
notify({ title: "Card Removed", type: "success" });
|
||||||
this.getPaymentCards(this.customer.id);
|
getPaymentCards(customer.value.id);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({ title: "Error", text: "Could not remove card.", type: "error" });
|
notify({ title: "Error", text: "Could not remove card.", type: "error" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
selectCreditCard(cardId: number) {
|
|
||||||
this.CreateOilOrderForm.basicInfo.credit_card_id = cardId;
|
const selectCreditCard = (cardId: number) => {
|
||||||
},
|
CreateOilOrderForm.value.basicInfo.credit_card_id = cardId;
|
||||||
setGallons(amount: number) {
|
}
|
||||||
this.CreateOilOrderForm.basicInfo.gallons_ordered = String(amount);
|
|
||||||
this.CreateOilOrderForm.basicInfo.customer_asked_for_fill = false;
|
const setGallons = (amount: number) => {
|
||||||
},
|
CreateOilOrderForm.value.basicInfo.gallons_ordered = String(amount);
|
||||||
setDeliveryDate(days: number) {
|
CreateOilOrderForm.value.basicInfo.customer_asked_for_fill = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDeliveryDate = (days: number) => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setDate(date.getDate() + days);
|
date.setDate(date.getDate() + days);
|
||||||
this.CreateOilOrderForm.basicInfo.expected_delivery_date = date.toISOString().split('T')[0];
|
CreateOilOrderForm.value.basicInfo.expected_delivery_date = date.toISOString().split('T')[0];
|
||||||
},
|
}
|
||||||
isDeliveryDateSelected(days: number): boolean {
|
|
||||||
|
const isDeliveryDateSelected = (days: number): boolean => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setDate(date.getDate() + days);
|
date.setDate(date.getDate() + days);
|
||||||
return this.CreateOilOrderForm.basicInfo.expected_delivery_date === date.toISOString().split('T')[0];
|
return CreateOilOrderForm.value.basicInfo.expected_delivery_date === date.toISOString().split('T')[0];
|
||||||
},
|
}
|
||||||
async onSubmit() {
|
|
||||||
const isFormValid = await this.v$.CreateOilOrderForm.$validate();
|
const onSubmit = async () => {
|
||||||
const isPaymentValid = await this.v$.isAnyPaymentMethodSelected.$validate();
|
const isFormValid = await v$.value.CreateOilOrderForm.$validate();
|
||||||
|
const isPaymentValid = await v$.value.isAnyPaymentMethodSelected.$validate();
|
||||||
|
|
||||||
if (!isFormValid || !isPaymentValid) {
|
if (!isFormValid || !isPaymentValid) {
|
||||||
notify({ title: "Form Incomplete", text: "Please review the fields marked in red.", type: "warn" });
|
notify({ title: "Form Incomplete", text: "Please review the fields marked in red.", type: "warn" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formInfo = this.CreateOilOrderForm.basicInfo;
|
const formInfo = CreateOilOrderForm.value.basicInfo;
|
||||||
// Convert checkboxes back to payment_type number for API
|
// Convert checkboxes back to payment_type number for API
|
||||||
let paymentType = 0; // Default to cash
|
let paymentType = 0; // Default to cash
|
||||||
if (formInfo.credit) paymentType = 1;
|
if (formInfo.credit) paymentType = 1;
|
||||||
@@ -522,20 +525,23 @@ export default defineComponent({
|
|||||||
credit_card_id: formInfo.credit ? formInfo.credit_card_id : null,
|
credit_card_id: formInfo.credit ? formInfo.credit_card_id : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
axios.post(`${import.meta.env.VITE_BASE_URL}/delivery/edit/${this.deliveryOrder.id}`, payload, { withCredentials: true, headers: authHeader() })
|
axios.post(`${import.meta.env.VITE_BASE_URL}/delivery/edit/${deliveryOrder.value.id}`, payload, { withCredentials: true, headers: authHeader() })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
notify({ type: 'success', title: 'Success!', text: 'Delivery updated.' });
|
notify({ type: 'success', title: 'Success!', text: 'Delivery updated.' });
|
||||||
if (paymentType === 1) {
|
if (paymentType === 1) {
|
||||||
this.$router.push({ name: 'payOil', params: { id: this.deliveryOrder.id } });
|
router.push({ name: 'payOil', params: { id: deliveryOrder.value.id } });
|
||||||
} else {
|
} else {
|
||||||
this.$router.push({ name: 'deliveryOrder', params: { id: this.deliveryOrder.id } });
|
router.push({ name: 'deliveryOrder', params: { id: deliveryOrder.value.id } });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("Error submitting form:", error);
|
console.error("Error submitting form:", error);
|
||||||
notify({ type: 'error', title: 'Update Failed', text: 'Could not save changes.' });
|
notify({ type: 'error', title: 'Update Failed', text: 'Could not save changes.' });
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
fetchInitialData();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -55,18 +55,18 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-sm" :class="{
|
<span class="badge badge-sm" :class="{
|
||||||
'badge-warning': oil.delivery_status == 0,
|
'badge-warning': oil.delivery_status === computedDELIVERY_STATUS.WAITING,
|
||||||
'badge-success': [1, 10].includes(oil.delivery_status),
|
'badge-success': [computedDELIVERY_STATUS.DELIVERED, computedDELIVERY_STATUS.FINALIZED].includes(oil.delivery_status as any),
|
||||||
'badge-info': oil.delivery_status == 2,
|
'badge-info': oil.delivery_status === computedDELIVERY_STATUS.OUT_FOR_DELIVERY,
|
||||||
'badge-error': [3, 5].includes(oil.delivery_status),
|
'badge-error': [computedDELIVERY_STATUS.TOMORROW, computedDELIVERY_STATUS.ISSUE].includes(oil.delivery_status as any),
|
||||||
}">
|
}">
|
||||||
<span v-if="oil.delivery_status == 0">Waiting</span>
|
<span v-if="oil.delivery_status === computedDELIVERY_STATUS.WAITING">Waiting</span>
|
||||||
<span v-else-if="oil.delivery_status == 1">Cancelled</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.CANCELLED">Cancelled</span>
|
||||||
<span v-else-if="oil.delivery_status == 2">Today</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.OUT_FOR_DELIVERY">Today</span>
|
||||||
<span v-else-if="oil.delivery_status == 3">Tomorrow_Delivery</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.TOMORROW">Tomorrow_Delivery</span>
|
||||||
<span v-else-if="oil.delivery_status == 4">Partial_Delivery</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.PARTIAL_DELIVERY">Partial_Delivery</span>
|
||||||
<span v-else-if="oil.delivery_status == 5">Issue</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.ISSUE">Issue</span>
|
||||||
<span v-else-if="oil.delivery_status == 10">Finalized</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.FINALIZED">Finalized</span>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<router-link :to="{ name: 'deliveryOrder', params: { id: oil.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
<router-link :to="{ name: 'deliveryOrder', params: { id: oil.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
||||||
<router-link :to="{ name: 'deliveryEdit', params: { id: oil.id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
<router-link :to="{ name: 'deliveryEdit', params: { id: oil.id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||||
<router-link :to="{ name: 'finalizeTicket', params: { id: oil.id } }" v-if="oil.delivery_status != 10" class="btn btn-sm btn-accent">Finalize</router-link>
|
<router-link :to="{ name: 'finalizeTicket', params: { id: oil.id } }" v-if="!isFinalizedStatus(oil.delivery_status)" class="btn btn-sm btn-accent">Finalize</router-link>
|
||||||
<router-link :to="{ name: 'Ticket', params: { id: oil.id } }" class="btn btn-sm btn-success">Print</router-link>
|
<router-link :to="{ name: 'Ticket', params: { id: oil.id } }" class="btn btn-sm btn-success">Print</router-link>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -115,18 +115,18 @@
|
|||||||
<p class="text-xs text-gray-400">Delivery #{{ oil.id }}</p>
|
<p class="text-xs text-gray-400">Delivery #{{ oil.id }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="badge" :class="{
|
<div class="badge" :class="{
|
||||||
'badge-warning': oil.delivery_status == 0,
|
'badge-warning': oil.delivery_status === computedDELIVERY_STATUS.WAITING,
|
||||||
'badge-success': [1, 10].includes(oil.delivery_status),
|
'badge-success': [computedDELIVERY_STATUS.DELIVERED, computedDELIVERY_STATUS.FINALIZED].includes(oil.delivery_status as any),
|
||||||
'badge-info': oil.delivery_status == 2,
|
'badge-info': oil.delivery_status === computedDELIVERY_STATUS.OUT_FOR_DELIVERY,
|
||||||
'badge-error': [3, 5].includes(oil.delivery_status),
|
'badge-error': [computedDELIVERY_STATUS.TOMORROW, computedDELIVERY_STATUS.ISSUE].includes(oil.delivery_status as any),
|
||||||
}">
|
}">
|
||||||
<span v-if="oil.delivery_status == 0">Waiting</span>
|
<span v-if="oil.delivery_status === computedDELIVERY_STATUS.WAITING">Waiting</span>
|
||||||
<span v-else-if="oil.delivery_status == 1">Delivered</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.DELIVERED">Delivered</span>
|
||||||
<span v-else-if="oil.delivery_status == 2">Today_Delivery</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.OUT_FOR_DELIVERY">Today_Delivery</span>
|
||||||
<span v-else-if="oil.delivery_status == 3">Tommorrow_Delivery</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.TOMORROW">Tommorrow_Delivery</span>
|
||||||
<span v-else-if="oil.delivery_status == 4">Partial Delivery</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.PARTIAL_DELIVERY">Partial Delivery</span>
|
||||||
<span v-else-if="oil.delivery_status == 5">Issue</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.ISSUE">Issue</span>
|
||||||
<span v-else-if="oil.delivery_status == 10">Finalized</span>
|
<span v-else-if="oil.delivery_status === computedDELIVERY_STATUS.FINALIZED">Finalized</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
<div class="card-actions justify-end flex-wrap gap-2 mt-2">
|
<div class="card-actions justify-end flex-wrap gap-2 mt-2">
|
||||||
<router-link :to="{ name: 'deliveryOrder', params: { id: oil.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
<router-link :to="{ name: 'deliveryOrder', params: { id: oil.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
||||||
<router-link :to="{ name: 'deliveryEdit', params: { id: oil.id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
<router-link :to="{ name: 'deliveryEdit', params: { id: oil.id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||||
<router-link :to="{ name: 'finalizeTicket', params: { id: oil.id } }" v-if="oil.delivery_status != 10" class="btn btn-sm btn-accent">Finalize</router-link>
|
<router-link :to="{ name: 'finalizeTicket', params: { id: oil.id } }" v-if="!isFinalizedStatus(oil.delivery_status)" class="btn btn-sm btn-accent">Finalize</router-link>
|
||||||
<router-link :to="{ name: 'Ticket', params: { id: oil.id } }" class="btn btn-sm btn-success">Print</router-link>
|
<router-link :to="{ name: 'Ticket', params: { id: oil.id } }" class="btn btn-sm btn-success">Print</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,59 +169,52 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { deliveryService } from '../../services/deliveryService'
|
||||||
|
import { Delivery } from '../../types/models'
|
||||||
|
import { DELIVERY_STATUS, DeliveryStatusType, getDeliveryStatusLabel } from '../../constants/status';
|
||||||
import Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../components/pagination.vue'
|
import PaginationComp from '../../components/pagination.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryHome',
|
const delivery_count = ref(0)
|
||||||
|
const delivery_count_delivered = ref(0)
|
||||||
components: {
|
const token = ref(null)
|
||||||
Header,
|
const user = ref(null)
|
||||||
SideBar,
|
const deliveries = ref<Delivery[]>([])
|
||||||
Footer,
|
const page = ref(1)
|
||||||
},
|
const perPage = ref(50)
|
||||||
|
const recordsLength = ref(0)
|
||||||
data() {
|
const options = ref({
|
||||||
return {
|
|
||||||
delivery_count: 0,
|
|
||||||
delivery_count_delivered: 0,
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Computed
|
||||||
this.userStatus()
|
const computedDELIVERY_STATUS = computed(() => DELIVERY_STATUS)
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getPage(this.page);
|
|
||||||
this.today_delivery_count();
|
|
||||||
this.today_delivery_delivered();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
|
|
||||||
userStatus() {
|
// Functions
|
||||||
|
const isDeliveredStatus = (status: DeliveryStatusType): boolean => {
|
||||||
|
return status === DELIVERY_STATUS.DELIVERED || status === 1; // Support both old (1) and new (11) values
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFinalizedStatus = (status: DeliveryStatusType): boolean => {
|
||||||
|
return status === DELIVERY_STATUS.FINALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPage = (pageVal: any) => {
|
||||||
|
deliveries.value = [];
|
||||||
|
get_oil_orders(pageVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -231,27 +224,25 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/all/' + page;
|
|
||||||
axios({
|
|
||||||
method: 'get',
|
|
||||||
url: path,
|
|
||||||
headers: authHeader(),
|
|
||||||
}).then((response: any) => {
|
|
||||||
this.deliveries = response.data
|
|
||||||
|
|
||||||
})
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
},
|
try {
|
||||||
|
const response = await deliveryService.getAll(pageVal)
|
||||||
|
deliveries.value = response.data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching deliveries:', error)
|
||||||
|
deliveries.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteCall = (delivery_id: any) => {
|
||||||
deleteCall(delivery_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -264,7 +255,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -273,8 +264,9 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
today_delivery_count() {
|
|
||||||
|
const today_delivery_count = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/today'
|
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/today'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -283,10 +275,11 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.delivery_count = response.data.data;
|
delivery_count.value = response.data.data;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
today_delivery_delivered() {
|
|
||||||
|
const today_delivery_delivered = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/delivered/today'
|
let path = import.meta.env.VITE_BASE_URL + '/stats/delivery/count/delivered/today'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -295,10 +288,16 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.delivery_count_delivered = response.data.data;
|
delivery_count_delivered.value = response.data.data;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value);
|
||||||
|
today_delivery_count();
|
||||||
|
today_delivery_delivered();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import DeliveryHome from './home.vue';
|
const DeliveryHome = () => import('./home.vue');
|
||||||
import DeliveryCreate from "./create.vue";
|
const DeliveryCreate = () => import("./create.vue");
|
||||||
import DeliveryEdit from './edit.vue';
|
const DeliveryEdit = () => import('./edit.vue');
|
||||||
import DeliveryOrder from './view.vue';
|
const DeliveryOrder = () => import('./view.vue');
|
||||||
import deliveryTicketsMissing from './update_tickets/missing_data_home.vue';
|
const deliveryTicketsMissing = () => import('./update_tickets/missing_data_home.vue');
|
||||||
|
|
||||||
import deliveryPending from './viewstatus/pending.vue';
|
const deliveryPending = () => import('./viewstatus/pending.vue');
|
||||||
import deliveryCancelled from './viewstatus/cancelled.vue';
|
const deliveryCancelled = () => import('./viewstatus/cancelled.vue');
|
||||||
import deliveryIssue from './viewstatus/issue.vue';
|
const deliveryIssue = () => import('./viewstatus/issue.vue');
|
||||||
import deliveryDelivered from './viewstatus/delivered.vue';
|
const deliveryDelivered = () => import('./viewstatus/delivered.vue');
|
||||||
import deliveryOutForDelivery from './viewstatus/todaysdeliveries.vue';
|
const deliveryOutForDelivery = () => import('./viewstatus/todaysdeliveries.vue');
|
||||||
import deliveryWaiting from './viewstatus/waiting.vue';
|
const deliveryWaiting = () => import('./viewstatus/waiting.vue');
|
||||||
import deliveryFinalized from './viewstatus/finalized.vue'
|
const deliveryFinalized = () => import('./viewstatus/finalized.vue')
|
||||||
import deliveryTommorrow from './viewstatus/tommorrow.vue'
|
const deliveryTommorrow = () => import('./viewstatus/tommorrow.vue')
|
||||||
import finalizeTicket from './update_tickets/finalize_ticket.vue';
|
const finalizeTicket = () => import('./update_tickets/finalize_ticket.vue');
|
||||||
import finalizeTicketAuto from './update_tickets/finalize_ticket_auto.vue';
|
const finalizeTicketAuto = () => import('./update_tickets/finalize_ticket_auto.vue');
|
||||||
import finalizeTicketAutoNocc from './update_tickets/finalize_ticket_auto_nocc.vue';
|
const finalizeTicketAutoNocc = () => import('./update_tickets/finalize_ticket_auto_nocc.vue');
|
||||||
|
|
||||||
const deliveryRoutes = [
|
const deliveryRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -252,8 +252,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
@@ -272,24 +273,23 @@ interface UserCard {
|
|||||||
security_number: string;
|
security_number: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Route and router
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'finalizeTicket',
|
const isLoading = ref(false)
|
||||||
components: { Header, SideBar, Footer },
|
const user = ref({ id: 0 })
|
||||||
data() {
|
const userCardfound = ref(false)
|
||||||
return {
|
const preChargeTotal = ref(0)
|
||||||
isLoading: false,
|
const FinalizeOilOrderForm = ref({
|
||||||
user: { id: 0 },
|
|
||||||
userCardfound: false,
|
|
||||||
preChargeTotal: 0,
|
|
||||||
FinalizeOilOrderForm: {
|
|
||||||
cash_recieved: '',
|
cash_recieved: '',
|
||||||
fill_location: '',
|
fill_location: '',
|
||||||
check_number: '',
|
check_number: '',
|
||||||
gallons_delivered: '',
|
gallons_delivered: '',
|
||||||
},
|
})
|
||||||
userCard: {} as UserCard,
|
const userCard = ref({} as UserCard)
|
||||||
customer: {
|
const customer = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -300,9 +300,9 @@ export default defineComponent({
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
})
|
||||||
customerDescription: { fill_location: '' },
|
const customerDescription = ref({ fill_location: '' })
|
||||||
deliveryOrder: {
|
const deliveryOrder = ref({
|
||||||
id: '',
|
id: '',
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
gallons_ordered: 0,
|
gallons_ordered: 0,
|
||||||
@@ -318,55 +318,56 @@ export default defineComponent({
|
|||||||
payment_type: 0,
|
payment_type: 0,
|
||||||
payment_card_id: '',
|
payment_card_id: '',
|
||||||
promo_id: null,
|
promo_id: null,
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref({
|
||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_same_day: 0,
|
price_same_day: 0,
|
||||||
},
|
})
|
||||||
promo_active: false,
|
const promo_active = ref(false)
|
||||||
promo: {
|
const promo = ref({
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: 0,
|
money_off_delivery: 0,
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
transaction: null as any,
|
const transaction = ref(null as any)
|
||||||
}
|
|
||||||
},
|
// Computed properties
|
||||||
computed: {
|
const finalChargeAmount = computed((): number => {
|
||||||
finalChargeAmount(): number {
|
|
||||||
// If promo is active, use server-calculated totals with fees added
|
// If promo is active, use server-calculated totals with fees added
|
||||||
if (this.promo_active && this.total_amount_after_discount > 0) {
|
if (promo_active.value && total_amount_after_discount.value > 0) {
|
||||||
let total = this.total_amount_after_discount;
|
let total = total_amount_after_discount.value;
|
||||||
if (this.deliveryOrder.prime === 1) total += Number(this.pricing.price_prime);
|
if (deliveryOrder.value.prime === 1) total += Number(pricing.value.price_prime);
|
||||||
if (this.deliveryOrder.same_day === 1) total += Number(this.pricing.price_same_day);
|
if (deliveryOrder.value.same_day === 1) total += Number(pricing.value.price_same_day);
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, calculate locally
|
// Otherwise, calculate locally
|
||||||
const gallons = Number(this.FinalizeOilOrderForm.gallons_delivered);
|
const gallons = Number(FinalizeOilOrderForm.value.gallons_delivered);
|
||||||
const pricePerGallon = Number(this.deliveryOrder.customer_price);
|
const pricePerGallon = Number(deliveryOrder.value.customer_price);
|
||||||
if (isNaN(gallons) || isNaN(pricePerGallon) || gallons <= 0) {
|
if (isNaN(gallons) || isNaN(pricePerGallon) || gallons <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let total = gallons * pricePerGallon;
|
let total = gallons * pricePerGallon;
|
||||||
if (this.deliveryOrder.prime === 1) total += Number(this.pricing.price_prime);
|
if (deliveryOrder.value.prime === 1) total += Number(pricing.value.price_prime);
|
||||||
if (this.deliveryOrder.same_day === 1) total += Number(this.pricing.price_same_day);
|
if (deliveryOrder.value.same_day === 1) total += Number(pricing.value.price_same_day);
|
||||||
return total;
|
return total;
|
||||||
}
|
})
|
||||||
},
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
const deliveryId = this.$route.params.id;
|
onMounted(() => {
|
||||||
|
const deliveryId = route.params.id;
|
||||||
// --- DEBUGGING STEP 1 ---
|
// --- DEBUGGING STEP 1 ---
|
||||||
console.log(`[DEBUG] Component Mounted. Fetching data for delivery ID: ${deliveryId}`);
|
console.log(`[DEBUG] Component Mounted. Fetching data for delivery ID: ${deliveryId}`);
|
||||||
this.getOilOrder(deliveryId);
|
getOilOrder(deliveryId);
|
||||||
this.getOilPricing();
|
getOilPricing();
|
||||||
},
|
})
|
||||||
methods: {
|
|
||||||
async getOilOrder(delivery_id: any) {
|
// Functions
|
||||||
|
const getOilOrder = async (delivery_id: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/delivery/order/${delivery_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/delivery/order/${delivery_id}`;
|
||||||
// --- DEBUGGING STEP 2 ---
|
// --- DEBUGGING STEP 2 ---
|
||||||
console.log(`[DEBUG] Calling getOilOrder API at: ${path}`);
|
console.log(`[DEBUG] Calling getOilOrder API at: ${path}`);
|
||||||
@@ -378,30 +379,30 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
console.log('[DEBUG] Response is OK. Processing data...');
|
console.log('[DEBUG] Response is OK. Processing data...');
|
||||||
this.deliveryOrder = response.data.delivery;
|
deliveryOrder.value = response.data.delivery;
|
||||||
|
|
||||||
// --- DEBUGGING STEP 4 ---
|
// --- DEBUGGING STEP 4 ---
|
||||||
console.log(`[DEBUG] Value of response.data.total_amount is:`, response.data.total_amount);
|
console.log(`[DEBUG] Value of response.data.total_amount is:`, response.data.total_amount);
|
||||||
|
|
||||||
this.total_amount = response.data.delivery.total_amount || 0;
|
total_amount.value = response.data.delivery.total_amount || 0;
|
||||||
this.preChargeTotal = response.data.delivery.total_amount || 0;
|
preChargeTotal.value = response.data.delivery.total_amount || 0;
|
||||||
|
|
||||||
|
|
||||||
await this.getCustomer(this.deliveryOrder.customer_id);
|
await getCustomer(deliveryOrder.value.customer_id);
|
||||||
|
|
||||||
if ([1, 2, 11].includes(this.deliveryOrder.payment_type) && this.deliveryOrder.payment_card_id) {
|
if ([1, 2, 11].includes(deliveryOrder.value.payment_type) && deliveryOrder.value.payment_card_id) {
|
||||||
this.getPaymentCard(this.deliveryOrder.payment_card_id);
|
getPaymentCard(deliveryOrder.value.payment_card_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.deliveryOrder.promo_id != null) {
|
if (deliveryOrder.value.promo_id != null) {
|
||||||
this.getPromo(this.deliveryOrder.promo_id);
|
getPromo(deliveryOrder.value.promo_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch calculated totals including discounts
|
// Fetch calculated totals including discounts
|
||||||
this.sumdelivery(delivery_id);
|
sumdelivery(delivery_id);
|
||||||
|
|
||||||
// Call transaction fetch after customer is loaded
|
// Call transaction fetch after customer is loaded
|
||||||
setTimeout(() => this.getTransaction(delivery_id), 500);
|
setTimeout(() => getTransaction(delivery_id), 500);
|
||||||
} else {
|
} else {
|
||||||
console.error('[DEBUG] getOilOrder response was not OK or data is missing.');
|
console.error('[DEBUG] getOilOrder response was not OK or data is missing.');
|
||||||
notify({ title: "Data Error", text: "Could not retrieve complete delivery details.", type: "error" });
|
notify({ title: "Data Error", text: "Could not retrieve complete delivery details.", type: "error" });
|
||||||
@@ -411,43 +412,46 @@ export default defineComponent({
|
|||||||
console.error("[DEBUG] The getOilOrder API call FAILED. Error object:", error);
|
console.error("[DEBUG] The getOilOrder API call FAILED. Error object:", error);
|
||||||
notify({ title: "Network Error", text: "Could not fetch delivery order.", type: "error" });
|
notify({ title: "Network Error", text: "Could not fetch delivery order.", type: "error" });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const getPaymentCard = async (card_id: any) => {
|
||||||
async getPaymentCard(card_id: any) {
|
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/card/${card_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/card/${card_id}`;
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(path, { withCredentials: true, headers: authHeader() });
|
const response = await axios.get(path, { withCredentials: true, headers: authHeader() });
|
||||||
this.userCard = response.data;
|
userCard.value = response.data;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
console.error(`[DEBUG] Error fetching payment card ${card_id}:`, error);
|
console.error(`[DEBUG] Error fetching payment card ${card_id}:`, error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async getCustomer(user_id: any) {
|
|
||||||
|
const getCustomer = async (user_id: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(path, { withCredentials: true });
|
const response = await axios.get(path, { withCredentials: true });
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
await this.getCustomerDescription(this.deliveryOrder.customer_id);
|
await getCustomerDescription(deliveryOrder.value.customer_id);
|
||||||
} catch (error) { console.error("[DEBUG] Error fetching customer:", error); }
|
} catch (error) { console.error("[DEBUG] Error fetching customer:", error); }
|
||||||
},
|
}
|
||||||
async getCustomerDescription(user_id: any) {
|
|
||||||
|
const getCustomerDescription = async (user_id: any) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/description/${user_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/description/${user_id}`;
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(path, { withCredentials: true });
|
const response = await axios.get(path, { withCredentials: true });
|
||||||
this.customerDescription = response.data;
|
customerDescription.value = response.data;
|
||||||
this.FinalizeOilOrderForm.fill_location = this.customerDescription.fill_location;
|
FinalizeOilOrderForm.value.fill_location = customerDescription.value.fill_location;
|
||||||
} catch (error) { console.error("[DEBUG] Error fetching customer description:", error); }
|
} catch (error) { console.error("[DEBUG] Error fetching customer description:", error); }
|
||||||
},
|
}
|
||||||
getOilPricing() {
|
|
||||||
|
const getOilPricing = () => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/info/price/oil/table`;
|
const path = `${import.meta.env.VITE_BASE_URL}/info/price/oil/table`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get(path, { withCredentials: true })
|
||||||
.then((response: any) => { this.pricing = response.data; })
|
.then((response: any) => { pricing.value = response.data; })
|
||||||
.catch((error: any) => { console.error("[DEBUG] Error fetching oil pricing:", error); });
|
.catch((error: any) => { console.error("[DEBUG] Error fetching oil pricing:", error); });
|
||||||
},
|
}
|
||||||
getPromo(promo_id: any) {
|
|
||||||
|
const getPromo = (promo_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -457,12 +461,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.promo = response.data
|
promo.value = response.data
|
||||||
this.promo_active = true
|
promo_active.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
sumdelivery(delivery_id: any) {
|
|
||||||
|
const sumdelivery = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -471,9 +476,9 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.total_amount = parseFloat(response.data.total_amount) || 0;
|
total_amount.value = parseFloat(response.data.total_amount) || 0;
|
||||||
this.discount = parseFloat(response.data.discount) || 0;
|
discount.value = parseFloat(response.data.discount) || 0;
|
||||||
this.total_amount_after_discount = parseFloat(response.data.total_amount_after_discount) || 0;
|
total_amount_after_discount.value = parseFloat(response.data.total_amount_after_discount) || 0;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -483,16 +488,17 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getTransaction(delivery_id: any) {
|
|
||||||
|
const getTransaction = (delivery_id: any) => {
|
||||||
// Add guard to prevent undefined customer ID API calls
|
// Add guard to prevent undefined customer ID API calls
|
||||||
if (!delivery_id || !this.customer || !this.customer.id) {
|
if (!delivery_id || !customer.value || !customer.value.id) {
|
||||||
console.log("Skipping transaction fetch - delivery or customer data not available");
|
console.log("Skipping transaction fetch - delivery or customer data not available");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consistent with delivery/view.vue - use customer transaction endpoint
|
// Consistent with delivery/view.vue - use customer transaction endpoint
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/transactions/customer/${this.customer.id}/1`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/transactions/customer/${customer.value.id}/1`;
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
console.log("Transaction API response:", response.data);
|
console.log("Transaction API response:", response.data);
|
||||||
@@ -504,40 +510,41 @@ export default defineComponent({
|
|||||||
txn.transaction_id === delivery_id ||
|
txn.transaction_id === delivery_id ||
|
||||||
txn.delivery_number === delivery_id
|
txn.delivery_number === delivery_id
|
||||||
);
|
);
|
||||||
this.transaction = deliveryTransaction || null;
|
transaction.value = deliveryTransaction || null;
|
||||||
} else if (response.data && !Array.isArray(response.data)) {
|
} else if (response.data && !Array.isArray(response.data)) {
|
||||||
// If single transaction, check if it's for this delivery
|
// If single transaction, check if it's for this delivery
|
||||||
const txn = response.data;
|
const txn = response.data;
|
||||||
if (txn.delivery_id === parseInt(delivery_id) ||
|
if (txn.delivery_id === parseInt(delivery_id) ||
|
||||||
txn.transaction_id === delivery_id ||
|
txn.transaction_id === delivery_id ||
|
||||||
txn.delivery_number === delivery_id) {
|
txn.delivery_number === delivery_id) {
|
||||||
this.transaction = txn;
|
transaction.value = txn;
|
||||||
} else {
|
} else {
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.transaction) {
|
if (!transaction.value) {
|
||||||
console.log(`No transaction found for delivery ${delivery_id} among customer transactions`);
|
console.log(`No transaction found for delivery ${delivery_id} among customer transactions`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
// Handle various error responses gracefully
|
// Handle various error responses gracefully
|
||||||
if (error.response && error.response.status === 404) {
|
if (error.response && error.response.status === 404) {
|
||||||
console.log(`No transactions found for customer ${this.customer.id}`);
|
console.log(`No transactions found for customer ${customer.value.id}`);
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
} else if (error.response && error.response.status === 400) {
|
} else if (error.response && error.response.status === 400) {
|
||||||
console.log(`Bad request for customer transactions: ${error.response.data?.detail || error.message}`);
|
console.log(`Bad request for customer transactions: ${error.response.data?.detail || error.message}`);
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
} else {
|
} else {
|
||||||
console.error("Error fetching transaction:", error);
|
console.error("Error fetching transaction:", error);
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getTypeColor(transactionType: number) {
|
|
||||||
|
const getTypeColor = (transactionType: number) => {
|
||||||
switch (transactionType) {
|
switch (transactionType) {
|
||||||
case 1: return 'text-blue-600'; // Auth
|
case 1: return 'text-blue-600'; // Auth
|
||||||
case 0: return 'text-orange-600'; // Charge
|
case 0: return 'text-orange-600'; // Charge
|
||||||
@@ -545,53 +552,55 @@ export default defineComponent({
|
|||||||
case 3: return 'text-green-600'; // Delivery/Other
|
case 3: return 'text-green-600'; // Delivery/Other
|
||||||
default: return 'text-gray-600';
|
default: return 'text-gray-600';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
CreateTransaction() {
|
|
||||||
const path = `${import.meta.env.VITE_MONEY_URL}/delivery/add/${this.deliveryOrder.id}`;
|
const CreateTransaction = () => {
|
||||||
|
const path = `${import.meta.env.VITE_MONEY_URL}/delivery/add/${deliveryOrder.value.id}`;
|
||||||
axios.post(path, {}, { withCredentials: true, headers: authHeader() })
|
axios.post(path, {}, { withCredentials: true, headers: authHeader() })
|
||||||
.then(() => notify({ title: "Success", text: "Accounting record created.", type: "success" }))
|
.then(() => notify({ title: "Success", text: "Accounting record created.", type: "success" }))
|
||||||
.catch(() => notify({ title: "Warning", text: "Could not create accounting record.", type: "warn" }));
|
.catch(() => notify({ title: "Warning", text: "Could not create accounting record.", type: "warn" }));
|
||||||
},
|
}
|
||||||
async onSubmit() {
|
|
||||||
if (Number(this.FinalizeOilOrderForm.gallons_delivered) <= 0) {
|
const onSubmit = async () => {
|
||||||
|
if (Number(FinalizeOilOrderForm.value.gallons_delivered) <= 0) {
|
||||||
notify({ title: "Validation Error", text: "Gallons delivered must be greater than zero.", type: "error" });
|
notify({ title: "Validation Error", text: "Gallons delivered must be greater than zero.", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isLoading = true;
|
isLoading.value = true;
|
||||||
const finalizePayload = {
|
const finalizePayload = {
|
||||||
gallons_delivered: this.FinalizeOilOrderForm.gallons_delivered,
|
gallons_delivered: FinalizeOilOrderForm.value.gallons_delivered,
|
||||||
fill_location: this.FinalizeOilOrderForm.fill_location,
|
fill_location: FinalizeOilOrderForm.value.fill_location,
|
||||||
cash_recieved: this.FinalizeOilOrderForm.cash_recieved,
|
cash_recieved: FinalizeOilOrderForm.value.cash_recieved,
|
||||||
check_number: this.FinalizeOilOrderForm.check_number,
|
check_number: FinalizeOilOrderForm.value.check_number,
|
||||||
};
|
};
|
||||||
const finalizePath = `${import.meta.env.VITE_BASE_URL}/deliverydata/finalize/${this.deliveryOrder.id}`;
|
const finalizePath = `${import.meta.env.VITE_BASE_URL}/deliverydata/finalize/${deliveryOrder.value.id}`;
|
||||||
try {
|
try {
|
||||||
const finalizeResponse = await axios.put(finalizePath, finalizePayload, { withCredentials: true, headers: authHeader() });
|
const finalizeResponse = await axios.put(finalizePath, finalizePayload, { withCredentials: true, headers: authHeader() });
|
||||||
if (!finalizeResponse.data.ok) {
|
if (!finalizeResponse.data.ok) {
|
||||||
throw new Error(finalizeResponse.data.error || "Failed to update delivery details.");
|
throw new Error(finalizeResponse.data.error || "Failed to update delivery details.");
|
||||||
}
|
}
|
||||||
this.CreateTransaction();
|
CreateTransaction();
|
||||||
notify({ title: "Success", text: "Ticket has been finalized.", type: "success" });
|
notify({ title: "Success", text: "Ticket has been finalized.", type: "success" });
|
||||||
|
|
||||||
// FIX: Wait for customer data to be loaded before redirecting
|
// FIX: Wait for customer data to be loaded before redirecting
|
||||||
await this.waitForCustomerData(this.deliveryOrder.customer_id);
|
await waitForCustomerData(deliveryOrder.value.customer_id);
|
||||||
|
|
||||||
// Updated redirect logic based on your requirements
|
// Updated redirect logic based on your requirements
|
||||||
await this.handleRedirect();
|
await handleRedirect();
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.detail || "An error occurred during finalization.";
|
const errorMessage = error.response?.data?.detail || "An error occurred during finalization.";
|
||||||
notify({ title: "Error", text: errorMessage, type: "error" });
|
notify({ title: "Error", text: errorMessage, type: "error" });
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// NEW: Wait for customer data to be loaded before redirecting
|
// NEW: Wait for customer data to be loaded before redirecting
|
||||||
async waitForCustomerData(customerId: number): Promise<void> {
|
const waitForCustomerData = async (customerId: number): Promise<void> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const checkCustomer = () => {
|
const checkCustomer = () => {
|
||||||
if (this.customer && this.customer.id && this.customer.id === customerId) {
|
if (customer.value && customer.value.id && customer.value.id === customerId) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(checkCustomer, 100);
|
setTimeout(checkCustomer, 100);
|
||||||
@@ -599,62 +608,60 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
checkCustomer();
|
checkCustomer();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// NEW: Updated redirect logic based on payment type and transaction status
|
// NEW: Updated redirect logic based on payment type and transaction status
|
||||||
async handleRedirect() {
|
const handleRedirect = async () => {
|
||||||
console.log('[DEBUG] Starting redirect logic...');
|
console.log('[DEBUG] Starting redirect logic...');
|
||||||
console.log('[DEBUG] payment_type:', this.deliveryOrder.payment_type);
|
console.log('[DEBUG] payment_type:', deliveryOrder.value.payment_type);
|
||||||
console.log('[DEBUG] transaction:', this.transaction);
|
console.log('[DEBUG] transaction:', transaction.value);
|
||||||
console.log('[DEBUG] customer:', this.customer);
|
console.log('[DEBUG] customer:', customer.value);
|
||||||
|
|
||||||
if (this.deliveryOrder.payment_type === 1) {
|
if (deliveryOrder.value.payment_type === 1) {
|
||||||
// payment_type 1: Manual charging - already charged, just redirect to profile
|
// payment_type 1: Manual charging - already charged, just redirect to profile
|
||||||
console.log('[DEBUG] payment_type 1 - redirecting to customer profile');
|
console.log('[DEBUG] payment_type 1 - redirecting to customer profile');
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
|
|
||||||
} else if (this.deliveryOrder.payment_type === 11) {
|
} else if (deliveryOrder.value.payment_type === 11) {
|
||||||
// payment_type 11: API charging - check transaction type
|
// payment_type 11: API charging - check transaction type
|
||||||
console.log('[DEBUG] payment_type 11 - checking transaction type');
|
console.log('[DEBUG] payment_type 11 - checking transaction type');
|
||||||
|
|
||||||
if (this.transaction) {
|
if (transaction.value) {
|
||||||
console.log('[DEBUG] Transaction found, type:', this.transaction.transaction_type);
|
console.log('[DEBUG] Transaction found, type:', transaction.value.transaction_type);
|
||||||
|
|
||||||
if (this.transaction.transaction_type === 0) {
|
if (transaction.value.transaction_type === 0) {
|
||||||
// Already charged (transaction_type = 0) - redirect to profile
|
// Already charged (transaction_type = 0) - redirect to profile
|
||||||
console.log('[DEBUG] Already charged - redirecting to customer profile');
|
console.log('[DEBUG] Already charged - redirecting to customer profile');
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
|
|
||||||
} else if (this.transaction.transaction_type === 1) {
|
} else if (transaction.value.transaction_type === 1) {
|
||||||
// Auth only (transaction_type = 1) - redirect to capture page
|
// Auth only (transaction_type = 1) - redirect to capture page
|
||||||
console.log('[DEBUG] Auth only - redirecting to capture page');
|
console.log('[DEBUG] Auth only - redirecting to capture page');
|
||||||
this.$router.push({ name: "captureAuthorize", params: { id: this.deliveryOrder.id } });
|
router.push({ name: "captureAuthorize", params: { id: deliveryOrder.value.id } });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Unknown transaction type - default to customer profile
|
// Unknown transaction type - default to customer profile
|
||||||
console.log('[DEBUG] Unknown transaction type - redirecting to customer profile');
|
console.log('[DEBUG] Unknown transaction type - redirecting to customer profile');
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No transaction found for payment_type 11 - redirect to customer profile
|
// No transaction found for payment_type 11 - redirect to customer profile
|
||||||
console.log('[DEBUG] No transaction found for payment_type 11 - redirecting to customer profile');
|
console.log('[DEBUG] No transaction found for payment_type 11 - redirecting to customer profile');
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ([2].includes(this.deliveryOrder.payment_type)) {
|
} else if ([2].includes(deliveryOrder.value.payment_type)) {
|
||||||
// payment_type 2: Credit Card & Cash - go to capture page for any remaining payment
|
// payment_type 2: Credit Card & Cash - go to capture page for any remaining payment
|
||||||
console.log('[DEBUG] payment_type 2 - redirecting to capture page');
|
console.log('[DEBUG] payment_type 2 - redirecting to capture page');
|
||||||
this.$router.push({ name: "captureAuthorize", params: { id: this.deliveryOrder.id } });
|
router.push({ name: "captureAuthorize", params: { id: deliveryOrder.value.id } });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Default case (cash, check, etc.) - redirect to customer profile
|
// Default case (cash, check, etc.) - redirect to customer profile
|
||||||
console.log('[DEBUG] Default payment type - redirecting to customer profile');
|
console.log('[DEBUG] Default payment type - redirecting to customer profile');
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="font-bold text-sm">Payment Method</div>
|
<div class="font-bold text-sm">Payment Method</div>
|
||||||
<div v-if="!userCardfound" class="text-warning text-sm">No card on file for this customer.</div>
|
<div v-if="!userCardfound" class="text-warning text-sm">No card on file for this customer.</div>
|
||||||
<div v-if="userCardfound" class="mt-2 p-3 rounded-lg border bg-primary/10 border-primary">
|
<div v-if="userCardfound && userCard" class="mt-2 p-3 rounded-lg border bg-primary/10 border-primary">
|
||||||
<div class="font-bold text-sm">{{ userCard.name_on_card }}</div>
|
<div class="font-bold text-sm">{{ userCard.name_on_card }}</div>
|
||||||
<div class="text-xs opacity-70">{{ userCard.type_of_card }}</div>
|
<div class="text-xs opacity-70">{{ userCard.type_of_card }}</div>
|
||||||
<div class="mt-1 text-sm font-mono tracking-wider">
|
<div class="mt-1 text-sm font-mono tracking-wider">
|
||||||
@@ -119,40 +119,34 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import authHeader from '../../../services/auth.header'
|
import axios from 'axios'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import authHeader from '../../../services/auth.header'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import useValidate from "@vuelidate/core";
|
||||||
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
|
||||||
|
// Route and router
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'finalizeTicketAuto',
|
const v$ = useValidate()
|
||||||
|
const loaded = ref(false)
|
||||||
components: {
|
const user = ref({
|
||||||
Header,
|
|
||||||
SideBar,
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
loaded: false,
|
|
||||||
user: {
|
|
||||||
id: 0
|
id: 0
|
||||||
},
|
})
|
||||||
userCardfound: false,
|
const userCardfound = ref(false)
|
||||||
deliveryStatus: [],
|
const deliveryStatus = ref([])
|
||||||
userCards: [],
|
const userCards = ref([])
|
||||||
deliveryNotesDriver: [],
|
const deliveryNotesDriver = ref([])
|
||||||
today_oil_price: 0,
|
const today_oil_price = ref(0)
|
||||||
|
|
||||||
FinalizeOilOrderForm: {
|
const FinalizeOilOrderForm = ref({
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
check_number: 0,
|
check_number: 0,
|
||||||
delivery_status: 10,
|
delivery_status: 10,
|
||||||
@@ -164,27 +158,27 @@
|
|||||||
prime: false,
|
prime: false,
|
||||||
same_day: false,
|
same_day: false,
|
||||||
emergency: false,
|
emergency: false,
|
||||||
},
|
})
|
||||||
CreateOilOrderForm: {
|
const CreateOilOrderForm = ref({
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
gallons_delivered: '',
|
gallons_delivered: '',
|
||||||
userCards: []
|
userCards: []
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
userCard: {
|
const userCard = ref<{
|
||||||
date_added: '',
|
date_added: string;
|
||||||
user_id: '',
|
user_id: string;
|
||||||
card_number: '',
|
card_number: string;
|
||||||
last_four_digits: '',
|
last_four_digits: string;
|
||||||
name_on_card: '',
|
name_on_card: string;
|
||||||
expiration_month: '',
|
expiration_month: string;
|
||||||
expiration_year: '',
|
expiration_year: string;
|
||||||
type_of_card: '',
|
type_of_card: string;
|
||||||
security_number: '',
|
security_number: string;
|
||||||
accepted_or_declined: '',
|
accepted_or_declined: string;
|
||||||
main_card: '',
|
main_card: string;
|
||||||
},
|
} | null>(null)
|
||||||
customer: {
|
const customer = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
@@ -196,17 +190,16 @@
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
})
|
||||||
customerDescription: {
|
const customerDescription = ref({
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
company_id: 0,
|
company_id: 0,
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
description: '',
|
description: '',
|
||||||
},
|
})
|
||||||
|
|
||||||
|
const autoTicket = ref({
|
||||||
autoTicket: {
|
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: '',
|
customer_id: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
@@ -229,9 +222,9 @@
|
|||||||
payment_status : '',
|
payment_status : '',
|
||||||
open_ticket_id: 0
|
open_ticket_id: 0
|
||||||
|
|
||||||
},
|
})
|
||||||
|
|
||||||
autoDelivery: {
|
const autoDelivery = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
@@ -250,29 +243,23 @@
|
|||||||
house_factor: 0,
|
house_factor: 0,
|
||||||
auto_status: 0,
|
auto_status: 0,
|
||||||
open_ticket_id: null,
|
open_ticket_id: null,
|
||||||
},
|
})
|
||||||
|
|
||||||
}
|
// Watchers
|
||||||
},
|
watch(() => route.params, () => {
|
||||||
|
today_price_oil()
|
||||||
|
getAutoTicket(route.params.id)
|
||||||
|
}, { immediate: false })
|
||||||
|
|
||||||
created() {
|
// Lifecycle
|
||||||
this.userStatus()
|
onMounted(() => {
|
||||||
},
|
userStatus()
|
||||||
watch: {
|
today_price_oil()
|
||||||
$route() {
|
getAutoTicket(route.params.id)
|
||||||
this.today_price_oil();
|
})
|
||||||
this.getAutoTicket(this.$route.params.id);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.today_price_oil();
|
|
||||||
this.getAutoTicket(this.$route.params.id);
|
|
||||||
|
|
||||||
|
// Functions
|
||||||
},
|
const userStatus = () => {
|
||||||
|
|
||||||
methods: {
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -282,14 +269,13 @@
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
this.user.id = response.data.user_id;
|
user.value.id = response.data.user_id;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const getPaymentCard = (card_id: any) => {
|
||||||
getPaymentCard(card_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -299,19 +285,20 @@
|
|||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
|
|
||||||
if (response.data.userCard.card_number === ''){
|
if (response.data.userCard.card_number === ''){
|
||||||
this.userCard === null;
|
userCard.value = null;
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
this.userCard = response.data;
|
userCard.value = response.data;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
}
|
}
|
||||||
this.FinalizeOilOrderForm.userCards = response.data.id
|
FinalizeOilOrderForm.value.userCards = response.data.id
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getPaymentCards(user_id: any) {
|
|
||||||
|
const getPaymentCards = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/cards/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/cards/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -319,16 +306,17 @@
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.userCards = response.data;
|
userCards.value = response.data;
|
||||||
if (this.userCards && this.userCards.length > 0) {
|
if (userCards.value && userCards.value.length > 0) {
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
this.userCard = this.userCards.find((card: any) => card.main_card) || this.userCards[0];
|
userCard.value = userCards.value.find((card: any) => card.main_card) || userCards.value[0];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomer(user_id: any) {
|
|
||||||
|
const getCustomer = (user_id: any) => {
|
||||||
if (!user_id || user_id === 'undefined') return;
|
if (!user_id || user_id === 'undefined') return;
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
@@ -337,9 +325,9 @@
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
if (this.customer.id > 0) {
|
if (customer.value.id > 0) {
|
||||||
this.getPaymentCards(this.customer.user_id || this.customer.id);
|
getPaymentCards(customer.value.user_id || customer.value.id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -348,10 +336,11 @@
|
|||||||
text: "Could not find customer",
|
text: "Could not find customer",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
this.customer = { 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: '' };
|
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: '' };
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomerDescription(user_id: any) {
|
|
||||||
|
const getCustomerDescription = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -359,8 +348,8 @@
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customerDescription = response.data;
|
customerDescription.value = response.data;
|
||||||
this.loaded = true
|
loaded.value = true
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -369,8 +358,9 @@
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getAutoTicket(delivery_id: any) {
|
|
||||||
|
const getAutoTicket = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -378,11 +368,11 @@
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.autoTicket = response.data;
|
autoTicket.value = response.data;
|
||||||
this.getCustomer(this.autoTicket.customer_id)
|
getCustomer(autoTicket.value.customer_id)
|
||||||
|
|
||||||
this.getAutoDelivery(this.autoTicket.id)
|
getAutoDelivery(autoTicket.value.id)
|
||||||
this.getCustomerDescription(this.autoTicket.customer_id)
|
getCustomerDescription(autoTicket.value.customer_id)
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -392,9 +382,9 @@
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getAutoDelivery(delivery_id: any) {
|
const getAutoDelivery = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/finddelivery/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/finddelivery/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -403,9 +393,9 @@
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
|
|
||||||
this.autoDelivery = response.data;
|
autoDelivery.value = response.data;
|
||||||
this.getCustomer(this.autoDelivery.customer_id)
|
getCustomer(autoDelivery.value.customer_id)
|
||||||
this.getCustomerDescription(this.autoDelivery.customer_id)
|
getCustomerDescription(autoDelivery.value.customer_id)
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -415,8 +405,9 @@
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
today_price_oil() {
|
|
||||||
|
const today_price_oil = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -425,13 +416,14 @@
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.today_oil_price = response.data.price_for_customer;
|
today_oil_price.value = response.data.price_for_customer;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
UpdateAuto(payload: {
|
|
||||||
|
const UpdateAuto = (payload: {
|
||||||
gallons: string,
|
gallons: string,
|
||||||
delivery_id: string,
|
delivery_id: string,
|
||||||
}) {
|
}) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/delivery"
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/delivery"
|
||||||
axios({
|
axios({
|
||||||
method: "put",
|
method: "put",
|
||||||
@@ -447,7 +439,7 @@
|
|||||||
type: 'postive',
|
type: 'postive',
|
||||||
title: 'top'
|
title: 'top'
|
||||||
})
|
})
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notify({
|
notify({
|
||||||
@@ -457,13 +449,12 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const ConfirmAuto = (payload: {
|
||||||
ConfirmAuto(payload: {
|
|
||||||
gallons_delivered: string,
|
gallons_delivered: string,
|
||||||
}) {
|
}) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/create/" + this.autoDelivery.id;
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/create/" + autoDelivery.value.id;
|
||||||
axios({
|
axios({
|
||||||
method: "post",
|
method: "post",
|
||||||
url: path,
|
url: path,
|
||||||
@@ -479,9 +470,9 @@
|
|||||||
text: "Auto Delivered",
|
text: "Auto Delivered",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.CreateTransaction(response.data['0']['auto_ticket_id'])
|
CreateTransaction(response.data['0']['auto_ticket_id'])
|
||||||
this.updateTransactionDelivery(this.autoDelivery.id, response.data['0']['auto_ticket_id'])
|
updateTransactionDelivery(autoDelivery.value.id, response.data['0']['auto_ticket_id'])
|
||||||
this.$router.push({ name: "payAutoCapture", params: { id: response.data['0']['auto_ticket_id'] } });
|
router.push({ name: "payAutoCapture", params: { id: response.data['0']['auto_ticket_id'] } });
|
||||||
|
|
||||||
}
|
}
|
||||||
if (response.data.error) {
|
if (response.data.error) {
|
||||||
@@ -490,13 +481,12 @@
|
|||||||
text: "Could not finalize auto",
|
text: "Could not finalize auto",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
this.$router.push("auto");
|
router.push("auto");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const closeTicket = (ticketId: number) => {
|
||||||
closeTicket(ticketId: number) {
|
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/close_ticket/" + ticketId;
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/close_ticket/" + ticketId;
|
||||||
axios({
|
axios({
|
||||||
method: "put",
|
method: "put",
|
||||||
@@ -507,13 +497,13 @@
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
// Ticket closed successfully
|
// Ticket closed successfully
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
UpdateDeliveredAuto(payload: {
|
const UpdateDeliveredAuto = (payload: {
|
||||||
gallons_delivered: string,
|
gallons_delivered: string,
|
||||||
}) {
|
}) => {
|
||||||
console.log(this.autoDelivery)
|
console.log(autoDelivery.value)
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/update/" + this.autoDelivery.id;
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/update/" + autoDelivery.value.id;
|
||||||
axios({
|
axios({
|
||||||
method: "put",
|
method: "put",
|
||||||
url: path,
|
url: path,
|
||||||
@@ -531,16 +521,16 @@
|
|||||||
// Removed redirect from here, will handle in onSubmit
|
// Removed redirect from here, will handle in onSubmit
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
updateTransactionDelivery(current_delivery_id: any, new_delivery_id: any) {
|
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}`;
|
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() })
|
axios.put(path, {}, { withCredentials: true, headers: authHeader() })
|
||||||
.then(() => console.log("Transaction auto_id updated"))
|
.then(() => console.log("Transaction auto_id updated"))
|
||||||
.catch(() => console.error("Error updating transaction auto_id"));
|
.catch(() => console.error("Error updating transaction auto_id"));
|
||||||
},
|
}
|
||||||
|
|
||||||
CreateTransaction(auto_ticket_id: string,) {
|
const CreateTransaction = (auto_ticket_id: string) => {
|
||||||
let path = import.meta.env.VITE_MONEY_URL + "/delivery/add/auto/" + auto_ticket_id;
|
let path = import.meta.env.VITE_MONEY_URL + "/delivery/add/auto/" + auto_ticket_id;
|
||||||
axios({
|
axios({
|
||||||
method: "post",
|
method: "post",
|
||||||
@@ -564,26 +554,23 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
onSubmit() {
|
|
||||||
|
const onSubmit = () => {
|
||||||
let payload = {
|
let payload = {
|
||||||
gallons_delivered: this.FinalizeOilOrderForm.gallons_delivered,
|
gallons_delivered: FinalizeOilOrderForm.value.gallons_delivered,
|
||||||
};
|
};
|
||||||
this.UpdateDeliveredAuto(payload);
|
UpdateDeliveredAuto(payload);
|
||||||
if (this.autoTicket.payment_status == '1') {
|
if (autoTicket.value.payment_status == '1') {
|
||||||
// Pre-authorized: redirect to capture page
|
// Pre-authorized: redirect to capture page
|
||||||
this.$router.push({ name: "payAutoCapture", params: { id: this.autoTicket.id } });
|
router.push({ name: "payAutoCapture", params: { id: autoTicket.value.id } });
|
||||||
} else {
|
} else {
|
||||||
// Fully charged: close ticket
|
// Fully charged: close ticket
|
||||||
if (this.autoDelivery.open_ticket_id) {
|
if (autoDelivery.value.open_ticket_id) {
|
||||||
this.closeTicket(this.autoDelivery.open_ticket_id);
|
closeTicket(autoDelivery.value.open_ticket_id);
|
||||||
}
|
}
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
}
|
}
|
||||||
},
|
</script>
|
||||||
|
|
||||||
},
|
<style scoped></style>
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="font-bold text-sm">Payment Method</div>
|
<div class="font-bold text-sm">Payment Method</div>
|
||||||
<div v-if="!userCardfound" class="text-warning text-sm">No card on file for this customer.</div>
|
<div v-if="!userCardfound" class="text-warning text-sm">No card on file for this customer.</div>
|
||||||
<div v-if="userCardfound" class="mt-2 p-3 rounded-lg border bg-primary/10 border-primary">
|
<div v-if="userCardfound && userCard" class="mt-2 p-3 rounded-lg border bg-primary/10 border-primary">
|
||||||
<div class="font-bold text-sm">{{ userCard.name_on_card }}</div>
|
<div class="font-bold text-sm">{{ userCard.name_on_card }}</div>
|
||||||
<div class="text-xs opacity-70">{{ userCard.type_of_card }}</div>
|
<div class="text-xs opacity-70">{{ userCard.type_of_card }}</div>
|
||||||
<div class="mt-1 text-sm font-mono tracking-wider">
|
<div class="mt-1 text-sm font-mono tracking-wider">
|
||||||
@@ -121,8 +121,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
@@ -131,30 +132,23 @@ import Footer from '../../../layouts/footers/footer.vue'
|
|||||||
import useValidate from "@vuelidate/core";
|
import useValidate from "@vuelidate/core";
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
|
||||||
|
// Route and router
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'finalizeTicketAuto',
|
const v$ = useValidate()
|
||||||
|
const loaded = ref(false)
|
||||||
components: {
|
const user = ref({
|
||||||
Header,
|
|
||||||
SideBar,
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
loaded: false,
|
|
||||||
user: {
|
|
||||||
id: 0
|
id: 0
|
||||||
},
|
})
|
||||||
userCardfound: false,
|
const userCardfound = ref(false)
|
||||||
deliveryStatus: [],
|
const deliveryStatus = ref([])
|
||||||
userCards: [],
|
const userCards = ref([])
|
||||||
deliveryNotesDriver: [],
|
const deliveryNotesDriver = ref([])
|
||||||
today_oil_price: 0,
|
const today_oil_price = ref(0)
|
||||||
|
|
||||||
FinalizeOilOrderForm: {
|
const FinalizeOilOrderForm = ref({
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
check_number: 0,
|
check_number: 0,
|
||||||
delivery_status: 10,
|
delivery_status: 10,
|
||||||
@@ -166,27 +160,27 @@ export default defineComponent({
|
|||||||
prime: false,
|
prime: false,
|
||||||
same_day: false,
|
same_day: false,
|
||||||
emergency: false,
|
emergency: false,
|
||||||
},
|
})
|
||||||
CreateOilOrderForm: {
|
const CreateOilOrderForm = ref({
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
gallons_delivered: '',
|
gallons_delivered: '',
|
||||||
userCards: []
|
userCards: []
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
userCard: {
|
const userCard = ref<{
|
||||||
date_added: '',
|
date_added: string;
|
||||||
user_id: '',
|
user_id: string;
|
||||||
card_number: '',
|
card_number: string;
|
||||||
last_four_digits: '',
|
last_four_digits: string;
|
||||||
name_on_card: '',
|
name_on_card: string;
|
||||||
expiration_month: '',
|
expiration_month: string;
|
||||||
expiration_year: '',
|
expiration_year: string;
|
||||||
type_of_card: '',
|
type_of_card: string;
|
||||||
security_number: '',
|
security_number: string;
|
||||||
accepted_or_declined: '',
|
accepted_or_declined: string;
|
||||||
main_card: '',
|
main_card: string;
|
||||||
},
|
} | null>(null)
|
||||||
customer: {
|
const customer = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
@@ -198,17 +192,16 @@ export default defineComponent({
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
})
|
||||||
customerDescription: {
|
const customerDescription = ref({
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
company_id: 0,
|
company_id: 0,
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
description: '',
|
description: '',
|
||||||
},
|
})
|
||||||
|
|
||||||
|
const autoTicket = ref({
|
||||||
autoTicket: {
|
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: '',
|
customer_id: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
@@ -231,9 +224,9 @@ export default defineComponent({
|
|||||||
payment_status: '',
|
payment_status: '',
|
||||||
open_ticket_id: 0
|
open_ticket_id: 0
|
||||||
|
|
||||||
},
|
})
|
||||||
|
|
||||||
autoDelivery: {
|
const autoDelivery = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
@@ -252,29 +245,23 @@ export default defineComponent({
|
|||||||
house_factor: 0,
|
house_factor: 0,
|
||||||
auto_status: 0,
|
auto_status: 0,
|
||||||
open_ticket_id: null,
|
open_ticket_id: null,
|
||||||
},
|
})
|
||||||
|
|
||||||
}
|
// Watchers
|
||||||
},
|
watch(() => route.params, () => {
|
||||||
|
today_price_oil()
|
||||||
|
getAutoTicket(route.params.id)
|
||||||
|
}, { immediate: false })
|
||||||
|
|
||||||
created() {
|
// Lifecycle
|
||||||
this.userStatus()
|
onMounted(() => {
|
||||||
},
|
userStatus()
|
||||||
watch: {
|
today_price_oil()
|
||||||
$route() {
|
getAutoDelivery(route.params.id)
|
||||||
this.today_price_oil();
|
})
|
||||||
this.getAutoTicket(this.$route.params.id);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.today_price_oil();
|
|
||||||
this.getAutoDelivery(this.$route.params.id);
|
|
||||||
|
|
||||||
|
// Functions
|
||||||
},
|
const userStatus = () => {
|
||||||
|
|
||||||
methods: {
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -284,14 +271,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
this.user.id = response.data.user_id;
|
user.value.id = response.data.user_id;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const getPaymentCard = (card_id: any) => {
|
||||||
getPaymentCard(card_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -301,19 +287,20 @@ export default defineComponent({
|
|||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
|
|
||||||
if (response.data.userCard.card_number === '') {
|
if (response.data.userCard.card_number === '') {
|
||||||
this.userCard === null;
|
userCard.value = null;
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.userCard = response.data;
|
userCard.value = response.data;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
}
|
}
|
||||||
this.FinalizeOilOrderForm.userCards = response.data.id
|
FinalizeOilOrderForm.value.userCards = response.data.id
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getPaymentCards(user_id: any) {
|
|
||||||
|
const getPaymentCards = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/cards/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/cards/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -321,40 +308,43 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.userCards = response.data;
|
userCards.value = response.data;
|
||||||
if (this.userCards && this.userCards.length > 0) {
|
if (userCards.value && userCards.value.length > 0) {
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
this.userCard = this.userCards.find((card: any) => card.main_card) || this.userCards[0];
|
userCard.value = userCards.value.find((card: any) => card.main_card) || userCards.value[0];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomer(userid: any) {
|
|
||||||
|
const getCustomer = (userid: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCreditCards(userid: any) {
|
|
||||||
|
const getCreditCards = (userid: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.userCards = response.data;
|
userCards.value = response.data;
|
||||||
if (this.userCards && this.userCards.length > 0) {
|
if (userCards.value && userCards.value.length > 0) {
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
this.userCard = this.userCards.find((card: any) => card.main_card) || this.userCards[0];
|
userCard.value = userCards.value.find((card: any) => card.main_card) || userCards.value[0];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCustomerDescription(user_id: any) {
|
|
||||||
|
const getCustomerDescription = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -362,8 +352,8 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customerDescription = response.data;
|
customerDescription.value = response.data;
|
||||||
this.loaded = true
|
loaded.value = true
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -372,8 +362,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getAutoTicket(delivery_id: any) {
|
|
||||||
|
const getAutoTicket = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -381,11 +372,11 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.autoTicket = response.data;
|
autoTicket.value = response.data;
|
||||||
this.getCustomer(this.autoTicket.customer_id)
|
getCustomer(autoTicket.value.customer_id)
|
||||||
|
|
||||||
this.getAutoDelivery(this.autoTicket.id)
|
getAutoDelivery(autoTicket.value.id)
|
||||||
this.getCustomerDescription(this.autoTicket.customer_id)
|
getCustomerDescription(autoTicket.value.customer_id)
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -395,9 +386,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getAutoDelivery(delivery_id: any) {
|
const getAutoDelivery = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/delivery/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/delivery/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -406,9 +397,9 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data && response.data.customer_id) {
|
if (response.data && response.data.customer_id) {
|
||||||
this.autoDelivery = response.data;
|
autoDelivery.value = response.data;
|
||||||
this.getCustomer(this.autoDelivery.customer_id)
|
getCustomer(autoDelivery.value.customer_id)
|
||||||
this.getCreditCards(this.autoDelivery.customer_id)
|
getCreditCards(autoDelivery.value.customer_id)
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch auto delivery data.");
|
console.error("API Error:", response.data.error || "Failed to fetch auto delivery data.");
|
||||||
}
|
}
|
||||||
@@ -421,8 +412,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
today_price_oil() {
|
|
||||||
|
const today_price_oil = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -431,13 +423,14 @@ export default defineComponent({
|
|||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.today_oil_price = response.data.price_for_customer;
|
today_oil_price.value = response.data.price_for_customer;
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
UpdateAuto(payload: {
|
|
||||||
|
const UpdateAuto = (payload: {
|
||||||
gallons: string,
|
gallons: string,
|
||||||
delivery_id: string,
|
delivery_id: string,
|
||||||
}) {
|
}) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/delivery"
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/delivery"
|
||||||
axios({
|
axios({
|
||||||
method: "put",
|
method: "put",
|
||||||
@@ -453,7 +446,7 @@ export default defineComponent({
|
|||||||
type: 'postive',
|
type: 'postive',
|
||||||
title: 'top'
|
title: 'top'
|
||||||
})
|
})
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notify({
|
notify({
|
||||||
@@ -463,9 +456,9 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
CreateTransaction(auto_ticket_id: string,) {
|
const CreateTransaction = (auto_ticket_id: string) => {
|
||||||
let path = import.meta.env.VITE_MONEY_URL + "/delivery/add/auto/" + auto_ticket_id;
|
let path = import.meta.env.VITE_MONEY_URL + "/delivery/add/auto/" + auto_ticket_id;
|
||||||
axios({
|
axios({
|
||||||
method: "post",
|
method: "post",
|
||||||
@@ -489,12 +482,12 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
ConfirmAuto(payload: {
|
const ConfirmAuto = (payload: {
|
||||||
gallons_delivered: string,
|
gallons_delivered: string,
|
||||||
}) {
|
}) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/create/nopreauth/" + this.autoDelivery.id;
|
let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/create/nopreauth/" + autoDelivery.value.id;
|
||||||
axios({
|
axios({
|
||||||
method: "post",
|
method: "post",
|
||||||
url: path,
|
url: path,
|
||||||
@@ -509,7 +502,7 @@ export default defineComponent({
|
|||||||
text: "Auto Delivered",
|
text: "Auto Delivered",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.CreateTransaction(response.data['0']['auto_ticket_id'])
|
CreateTransaction(response.data['0']['auto_ticket_id'])
|
||||||
}
|
}
|
||||||
if (response.data.error) {
|
if (response.data.error) {
|
||||||
notify({
|
notify({
|
||||||
@@ -517,22 +510,19 @@ export default defineComponent({
|
|||||||
text: "Could not finalize auto",
|
text: "Could not finalize auto",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
this.$router.push("auto");
|
router.push("auto");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
onSubmit() {
|
|
||||||
|
const onSubmit = () => {
|
||||||
let payload = {
|
let payload = {
|
||||||
gallons_delivered: this.FinalizeOilOrderForm.gallons_delivered,
|
gallons_delivered: FinalizeOilOrderForm.value.gallons_delivered,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ConfirmAuto(payload)
|
ConfirmAuto(payload)
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
|
}
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -71,43 +71,27 @@
|
|||||||
<Footer/>
|
<Footer/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
|
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
|
|
||||||
|
// Reactive data
|
||||||
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
|
const deliveries = ref([])
|
||||||
|
|
||||||
export default defineComponent({
|
// Lifecycle
|
||||||
name: 'deliveryTicketsMissing',
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
get_oil_orders()
|
||||||
|
})
|
||||||
|
|
||||||
components: {
|
// Functions
|
||||||
Header,
|
const userStatus = () => {
|
||||||
SideBar,
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.userStatus()
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.get_oil_orders()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -117,26 +101,24 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders() {
|
|
||||||
|
const get_oil_orders = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/pending';
|
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/pending';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.deliveries = response.data
|
deliveries.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -67,20 +67,20 @@
|
|||||||
<div class="font-bold text-sm">Current Status</div>
|
<div class="font-bold text-sm">Current Status</div>
|
||||||
<div class="badge badge-lg"
|
<div class="badge badge-lg"
|
||||||
:class="{
|
:class="{
|
||||||
'badge-success': [1, 10].includes(deliveryOrder.delivery_status),
|
'badge-success': [DELIVERY_STATUS.DELIVERED, DELIVERY_STATUS.FINALIZED].includes(deliveryOrder.delivery_status as any),
|
||||||
'badge-info': deliveryOrder.delivery_status == 2,
|
'badge-info': deliveryOrder.delivery_status == DELIVERY_STATUS.OUT_FOR_DELIVERY,
|
||||||
'badge-error': deliveryOrder.delivery_status == 5,
|
'badge-error': deliveryOrder.delivery_status == DELIVERY_STATUS.ISSUE,
|
||||||
'badge-warning': ![1, 10, 2, 5].includes(deliveryOrder.delivery_status)
|
'badge-warning': ![DELIVERY_STATUS.DELIVERED, DELIVERY_STATUS.FINALIZED, DELIVERY_STATUS.OUT_FOR_DELIVERY, DELIVERY_STATUS.ISSUE].includes(deliveryOrder.delivery_status as any)
|
||||||
}">
|
}">
|
||||||
<span v-if="deliveryOrder.delivery_status == 0">Waiting</span>
|
<span v-if="deliveryOrder.delivery_status == DELIVERY_STATUS.WAITING">Waiting</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 1">Delivered</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.DELIVERED">Delivered</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 2">Out_for_Delivery</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.OUT_FOR_DELIVERY">Out_for_Delivery</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 3">Tomorrow</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.TOMORROW">Tomorrow</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 4">Partial Delivery</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.PARTIAL_DELIVERY">Partial Delivery</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 5">Misdelivery</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.ISSUE">Misdelivery</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 6">Unknown</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.UNKNOWN">Unknown</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 9">Pending</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.PENDING_PAYMENT">Pending</span>
|
||||||
<span v-else-if="deliveryOrder.delivery_status == 10">Finalized</span>
|
<span v-else-if="deliveryOrder.delivery_status == DELIVERY_STATUS.FINALIZED">Finalized</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -354,54 +354,37 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { DELIVERY_STATUS, PAYMENT_STATUS, TRANSACTION_STATUS } from '../../constants/status'
|
||||||
interface UserCard {
|
import { CreditCard } from '../../types/models'
|
||||||
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 Header from '../../layouts/headers/headerauth.vue'
|
import Header from '../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import useValidate from "@vuelidate/core";
|
import useValidate from "@vuelidate/core";
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'deliveryOrder',
|
|
||||||
|
|
||||||
components: {
|
// Reactive data
|
||||||
Header,
|
const v$ = useValidate()
|
||||||
SideBar,
|
const user = ref({
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
user: {
|
|
||||||
user_id: 0
|
user_id: 0
|
||||||
},
|
})
|
||||||
priceprime: 0,
|
const priceprime = ref(0)
|
||||||
pricesameday: 0,
|
const pricesameday = ref(0)
|
||||||
priceemergency: 0,
|
const priceemergency = ref(0)
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
deliveryNotesDriver: [],
|
const deliveryNotesDriver = ref([])
|
||||||
userCardfound: false,
|
const userCardfound = ref(false)
|
||||||
userCard: {} as UserCard,
|
const userCard = ref({} as CreditCard)
|
||||||
customer: {
|
const customer = ref({
|
||||||
account_number: '',
|
account_number: '',
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
@@ -414,9 +397,9 @@ export default defineComponent({
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
})
|
||||||
|
|
||||||
deliveryMoney: {
|
const deliveryMoney = ref({
|
||||||
time_added: '',
|
time_added: '',
|
||||||
total_amount_oil: '',
|
total_amount_oil: '',
|
||||||
total_amount_emergency: '',
|
total_amount_emergency: '',
|
||||||
@@ -426,15 +409,15 @@ export default defineComponent({
|
|||||||
total_discount_amount: '',
|
total_discount_amount: '',
|
||||||
total_discount_total: '',
|
total_discount_total: '',
|
||||||
total_amount: '',
|
total_amount: '',
|
||||||
},
|
})
|
||||||
promo: {
|
const promo = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: '',
|
money_off_delivery: '',
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -442,8 +425,8 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
deliveryOrder: {
|
const deliveryOrder = ref({
|
||||||
id: '',
|
id: '',
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_name: '',
|
customer_name: '',
|
||||||
@@ -474,35 +457,38 @@ export default defineComponent({
|
|||||||
driver_first_name: '',
|
driver_first_name: '',
|
||||||
driver_last_name: '',
|
driver_last_name: '',
|
||||||
promo_id: 0,
|
promo_id: 0,
|
||||||
},
|
})
|
||||||
transaction: null as any,
|
const transaction = ref(null as any)
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Computed
|
||||||
this.userStatus()
|
const computedDELIVERY_STATUS = computed(() => DELIVERY_STATUS)
|
||||||
},
|
const computedPAYMENT_STATUS = computed(() => PAYMENT_STATUS)
|
||||||
watch: {
|
const computedTRANSACTION_STATUS = computed(() => TRANSACTION_STATUS)
|
||||||
$route() {
|
|
||||||
this.getOilOrder(this.$route.params.id);
|
|
||||||
this.getOilOrderMoney(this.$route.params.id);
|
|
||||||
this.sumdelivery(this.$route.params.id);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getOilOrder(this.$route.params.id);
|
|
||||||
this.getOilOrderMoney(this.$route.params.id);
|
|
||||||
this.sumdelivery(this.$route.params.id);
|
|
||||||
this.getOilPricing();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// Watchers
|
||||||
format_date(value: string) {
|
watch(route, () => {
|
||||||
|
getOilOrder(route.params.id);
|
||||||
|
getOilOrderMoney(route.params.id);
|
||||||
|
sumdelivery(route.params.id);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getOilOrder(route.params.id);
|
||||||
|
getOilOrderMoney(route.params.id);
|
||||||
|
sumdelivery(route.params.id);
|
||||||
|
getOilPricing();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
const format_date = (value: string) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
return moment(String(value)).format('LLLL')
|
return dayjs(String(value)).format('LLLL')
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getTypeColor(transactionType: number) {
|
|
||||||
|
const getTypeColor = (transactionType: number) => {
|
||||||
switch (transactionType) {
|
switch (transactionType) {
|
||||||
case 1: return 'text-blue-600'; // Auth
|
case 1: return 'text-blue-600'; // Auth
|
||||||
case 0: return 'text-orange-600'; // Charge
|
case 0: return 'text-orange-600'; // Charge
|
||||||
@@ -510,8 +496,9 @@ export default defineComponent({
|
|||||||
case 3: return 'text-purple-600'; // Delivery/Other
|
case 3: return 'text-purple-600'; // Delivery/Other
|
||||||
default: return 'text-gray-600';
|
default: return 'text-gray-600';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
deleteCall(delivery_id: any) {
|
|
||||||
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -524,7 +511,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.user_id } });
|
// Note: router.push would need to be imported and used
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -533,9 +520,10 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
cancelDelivery() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/cancel/' + this.deliveryOrder.id;
|
const cancelDelivery = () => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/cancel/' + deliveryOrder.value.id;
|
||||||
axios({
|
axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: path,
|
url: path,
|
||||||
@@ -548,7 +536,7 @@ export default defineComponent({
|
|||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
// Refresh the delivery data
|
// Refresh the delivery data
|
||||||
this.getOilOrder(this.deliveryOrder.id);
|
getOilOrder(deliveryOrder.value.id);
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -563,8 +551,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
userStatus() {
|
|
||||||
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -574,12 +563,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getOilPricing() {
|
|
||||||
|
const getOilPricing = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -587,7 +577,7 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.pricing = response.data;
|
pricing.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((_error: any) => {
|
.catch((_error: any) => {
|
||||||
notify({
|
notify({
|
||||||
@@ -596,8 +586,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomer(user_id: any) {
|
|
||||||
|
const getCustomer = (user_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -605,7 +596,7 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((_error: any) => {
|
.catch((_error: any) => {
|
||||||
notify({
|
notify({
|
||||||
@@ -614,9 +605,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPaymentCard(card_id: any) {
|
const getPaymentCard = (card_id: any) => {
|
||||||
if (card_id) {
|
if (card_id) {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
||||||
axios({
|
axios({
|
||||||
@@ -627,24 +618,24 @@ export default defineComponent({
|
|||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
// Check if we have valid card data
|
// Check if we have valid card data
|
||||||
if (response.data && response.data.card_number && response.data.card_number !== '') {
|
if (response.data && response.data.card_number && response.data.card_number !== '') {
|
||||||
this.userCard = response.data;
|
userCard.value = response.data;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
} else {
|
} else {
|
||||||
this.userCard = {} as UserCard;
|
userCard.value = {} as CreditCard;
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("Error fetching payment card:", error);
|
console.error("Error fetching payment card:", error);
|
||||||
this.userCard = {} as UserCard;
|
userCard.value = {} as CreditCard;
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilOrder(delivery_id: any) {
|
const getOilOrder = (delivery_id: any) => {
|
||||||
if (!delivery_id) { // Add a guard to prevent calls with an undefined ID
|
if (!delivery_id) { // Add a guard to prevent calls with an undefined ID
|
||||||
console.error("getOilOrder called with no ID.");
|
console.error("getOilOrder called with no ID.");
|
||||||
return;
|
return;
|
||||||
@@ -659,21 +650,21 @@ export default defineComponent({
|
|||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
// FIX: Check for the 'ok' flag and access the nested 'delivery' object
|
// FIX: Check for the 'ok' flag and access the nested 'delivery' object
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
this.deliveryOrder = response.data.delivery; // <-- THIS IS THE CRITICAL CHANGE
|
deliveryOrder.value = response.data.delivery; // <-- THIS IS THE CRITICAL CHANGE
|
||||||
|
|
||||||
// Now that this.deliveryOrder is the correct object, the rest of the logic will work.
|
// Now that deliveryOrder is the correct object, the rest of the logic will work.
|
||||||
this.getCustomer(this.deliveryOrder.customer_id);
|
getCustomer(deliveryOrder.value.customer_id);
|
||||||
|
|
||||||
if ([1, 2, 3].includes(this.deliveryOrder.payment_type)) {
|
if ([1, 2, 3].includes(deliveryOrder.value.payment_type)) {
|
||||||
this.getPaymentCard(this.deliveryOrder.payment_card_id);
|
getPaymentCard(deliveryOrder.value.payment_card_id);
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.promo_id != null) {
|
if (deliveryOrder.value.promo_id != null) {
|
||||||
this.getPromo(this.deliveryOrder.promo_id);
|
getPromo(deliveryOrder.value.promo_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only fetch transactions for Authorize.net payments
|
// Only fetch transactions for Authorize.net payments
|
||||||
if (this.deliveryOrder.payment_type == 11) {
|
if (deliveryOrder.value.payment_type == 11) {
|
||||||
this.getTransaction(delivery_id);
|
getTransaction(delivery_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -684,8 +675,9 @@ export default defineComponent({
|
|||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("Error fetching delivery order:", error);
|
console.error("Error fetching delivery order:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getOilOrderMoney(delivery_id: any) {
|
|
||||||
|
const getOilOrderMoney = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_MONEY_URL + "/delivery/order/money/" + delivery_id;
|
let path = import.meta.env.VITE_MONEY_URL + "/delivery/order/money/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -695,11 +687,12 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.deliveryMoney = response.data
|
deliveryMoney.value = response.data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
sumdelivery(delivery_id: any) {
|
|
||||||
|
const sumdelivery = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -708,53 +701,53 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
this.priceprime = response.data.priceprime || 0;
|
priceprime.value = response.data.priceprime || 0;
|
||||||
this.pricesameday = response.data.pricesameday || 0;
|
pricesameday.value = response.data.pricesameday || 0;
|
||||||
this.priceemergency = response.data.priceemergency || 0;
|
priceemergency.value = response.data.priceemergency || 0;
|
||||||
this.total_amount = parseFloat(response.data.total_amount) || 0;
|
total_amount.value = parseFloat(response.data.total_amount) || 0;
|
||||||
this.discount = parseFloat(response.data.discount) || 0;
|
discount.value = parseFloat(response.data.discount) || 0;
|
||||||
this.total_amount_after_discount = parseFloat(response.data.total_amount_after_discount) || 0;
|
total_amount_after_discount.value = parseFloat(response.data.total_amount_after_discount) || 0;
|
||||||
} else {
|
} else {
|
||||||
// Fallback calculation if API doesn't return expected data
|
// Fallback calculation if API doesn't return expected data
|
||||||
this.calculateFallbackTotal();
|
calculateFallbackTotal();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error("Error fetching delivery totals:", error);
|
console.error("Error fetching delivery totals:", error);
|
||||||
// Fallback calculation on error
|
// Fallback calculation on error
|
||||||
this.calculateFallbackTotal();
|
calculateFallbackTotal();
|
||||||
notify({
|
notify({
|
||||||
title: "Warning",
|
title: "Warning",
|
||||||
text: "Could not get delivery totals, using estimated calculation",
|
text: "Could not get delivery totals, using estimated calculation",
|
||||||
type: "warn",
|
type: "warn",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateFallbackTotal() {
|
const calculateFallbackTotal = () => {
|
||||||
// Fallback calculation using available data
|
// Fallback calculation using available data
|
||||||
if (this.deliveryOrder.gallons_ordered && this.pricing.price_for_customer) {
|
if (deliveryOrder.value.gallons_ordered && pricing.value.price_for_customer) {
|
||||||
const gallons = Number(this.deliveryOrder.gallons_ordered);
|
const gallons = Number(deliveryOrder.value.gallons_ordered);
|
||||||
const pricePerGallon = Number(this.pricing.price_for_customer);
|
const pricePerGallon = Number(pricing.value.price_for_customer);
|
||||||
let total = gallons * pricePerGallon;
|
let total = gallons * pricePerGallon;
|
||||||
|
|
||||||
if (this.deliveryOrder.prime == 1) {
|
if (deliveryOrder.value.prime == 1) {
|
||||||
total += Number(this.pricing.price_prime) || 0;
|
total += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.same_day == 1) {
|
if (deliveryOrder.value.same_day == 1) {
|
||||||
total += Number(this.pricing.price_same_day) || 0;
|
total += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.emergency == 1) {
|
if (deliveryOrder.value.emergency == 1) {
|
||||||
total += Number(this.pricing.price_emergency) || 0;
|
total += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.total_amount = total;
|
total_amount.value = total;
|
||||||
this.total_amount_after_discount = total; // No discount info available
|
total_amount_after_discount.value = total; // No discount info available
|
||||||
this.discount = 0;
|
discount.value = 0;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getPromo(promo_id: any) {
|
const getPromo = (promo_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -764,58 +757,59 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.promo = response.data;
|
promo.value = response.data;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.error('Error fetching promo:', error);
|
console.error('Error fetching promo:', error);
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateDeliveryTotal() {
|
const calculateDeliveryTotal = () => {
|
||||||
if (!this.deliveryOrder.gallons_delivered || !this.pricing.price_for_customer) {
|
if (!deliveryOrder.value.gallons_delivered || !pricing.value.price_for_customer) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
const gallons = Number(this.deliveryOrder.gallons_delivered);
|
const gallons = Number(deliveryOrder.value.gallons_delivered);
|
||||||
const pricePerGallon = Number(this.pricing.price_for_customer);
|
const pricePerGallon = Number(pricing.value.price_for_customer);
|
||||||
let total = gallons * pricePerGallon;
|
let total = gallons * pricePerGallon;
|
||||||
|
|
||||||
if (this.deliveryOrder.prime == 1) {
|
if (deliveryOrder.value.prime == 1) {
|
||||||
total += Number(this.pricing.price_prime) || 0;
|
total += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.same_day == 1) {
|
if (deliveryOrder.value.same_day == 1) {
|
||||||
total += Number(this.pricing.price_same_day) || 0;
|
total += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.emergency == 1) {
|
if (deliveryOrder.value.emergency == 1) {
|
||||||
total += Number(this.pricing.price_emergency) || 0;
|
total += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return total.toFixed(2);
|
return total.toFixed(2);
|
||||||
},
|
}
|
||||||
calculateEstimatedTotal() {
|
|
||||||
if (!this.deliveryOrder.gallons_ordered || !this.pricing.price_for_customer) {
|
const calculateEstimatedTotal = () => {
|
||||||
|
if (!deliveryOrder.value.gallons_ordered || !pricing.value.price_for_customer) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gallons = Number(this.deliveryOrder.gallons_ordered);
|
const gallons = Number(deliveryOrder.value.gallons_ordered);
|
||||||
const pricePerGallon = Number(this.pricing.price_for_customer);
|
const pricePerGallon = Number(pricing.value.price_for_customer);
|
||||||
let total = gallons * pricePerGallon;
|
let total = gallons * pricePerGallon;
|
||||||
|
|
||||||
if (this.deliveryOrder.prime == 1) {
|
if (deliveryOrder.value.prime == 1) {
|
||||||
total += Number(this.pricing.price_prime) || 0;
|
total += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.same_day == 1) {
|
if (deliveryOrder.value.same_day == 1) {
|
||||||
total += Number(this.pricing.price_same_day) || 0;
|
total += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder.emergency == 1) {
|
if (deliveryOrder.value.emergency == 1) {
|
||||||
total += Number(this.pricing.price_emergency) || 0;
|
total += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
},
|
}
|
||||||
|
|
||||||
getTransaction(delivery_id: any) {
|
const getTransaction = (delivery_id: any) => {
|
||||||
// Simple endpoint to get transaction directly by delivery_id
|
// Simple endpoint to get transaction directly by delivery_id
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/transaction/delivery/${delivery_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/transaction/delivery/${delivery_id}`;
|
||||||
axios.get(path, {
|
axios.get(path, {
|
||||||
@@ -823,19 +817,17 @@ export default defineComponent({
|
|||||||
headers: authHeader()
|
headers: authHeader()
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.transaction = response.data.transaction;
|
transaction.value = response.data.transaction;
|
||||||
console.log("Transaction loaded:", this.transaction);
|
console.log("Transaction loaded:", transaction.value);
|
||||||
} else {
|
} else {
|
||||||
console.log("No transaction found for delivery:", delivery_id);
|
console.log("No transaction found for delivery:", delivery_id);
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
}
|
}
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
console.error("Error fetching transaction:", error);
|
console.error("Error fetching transaction:", error);
|
||||||
this.transaction = null;
|
transaction.value = null;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -121,54 +121,38 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryCancelled',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const page = ref(1)
|
||||||
SideBar,
|
const perPage = ref(50)
|
||||||
Footer,
|
const recordsLength = ref(0)
|
||||||
},
|
const options = ref({
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
deliveries.value = [];
|
||||||
mounted() {
|
get_oil_orders(pageVal)
|
||||||
this.getPage(this.page)
|
}
|
||||||
},
|
|
||||||
methods: {
|
const userStatus = () => {
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -178,24 +162,25 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/issue/' + page;
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
axios({
|
try {
|
||||||
method: 'get',
|
const response = await deliveryService.getIssues(pageVal)
|
||||||
url: path,
|
deliveries.value = response.data || []
|
||||||
headers: authHeader(),
|
} catch (error) {
|
||||||
}).then((response: any) => {
|
console.error('Error fetching issue deliveries:', error)
|
||||||
this.deliveries = response.data
|
deliveries.value = []
|
||||||
})
|
}
|
||||||
},
|
}
|
||||||
deleteCall(delivery_id: any) {
|
|
||||||
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/cancelled/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/cancelled/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -208,7 +193,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -217,8 +202,12 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -121,54 +121,38 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryDelivered',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const page = ref(1)
|
||||||
SideBar,
|
const perPage = ref(50)
|
||||||
Footer,
|
const recordsLength = ref(0)
|
||||||
},
|
const options = ref({
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
deliveries.value = [];
|
||||||
mounted() {
|
get_oil_orders(pageVal)
|
||||||
this.getPage(this.page)
|
}
|
||||||
},
|
|
||||||
methods: {
|
const userStatus = () => {
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -178,25 +162,25 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
get_oil_orders(page: any) {
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delivered/' + page;
|
try {
|
||||||
axios({
|
const response = await deliveryService.getDelivered(pageVal)
|
||||||
method: 'get',
|
deliveries.value = response.data || []
|
||||||
url: path,
|
} catch (error) {
|
||||||
headers: authHeader(),
|
console.error('Error fetching delivered deliveries:', error)
|
||||||
}).then((response: any) => {
|
deliveries.value = []
|
||||||
this.deliveries = response.data
|
}
|
||||||
})
|
}
|
||||||
},
|
|
||||||
deleteCall(delivery_id: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -209,7 +193,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -218,8 +202,12 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -121,55 +121,38 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import { Delivery } from '../../../types/models'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryFinalized',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const page = ref(1)
|
||||||
SideBar,
|
const perPage = ref(50)
|
||||||
Footer,
|
const recordsLength = ref(0)
|
||||||
},
|
const options = ref({
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
deliveries.value = [];
|
||||||
mounted() {
|
get_oil_orders(pageVal)
|
||||||
this.getPage(this.page)
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -179,26 +162,25 @@
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
get_oil_orders(page: any) {
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/finalized/' + page;
|
try {
|
||||||
axios({
|
const response = await deliveryService.getFinalized(pageVal)
|
||||||
method: 'get',
|
deliveries.value = response.data || []
|
||||||
url: path,
|
} catch (error) {
|
||||||
headers: authHeader(),
|
console.error('Error fetching finalized deliveries:', error)
|
||||||
}).then((response: any) => {
|
deliveries.value = []
|
||||||
this.deliveries = response.data
|
}
|
||||||
})
|
}
|
||||||
},
|
|
||||||
|
|
||||||
deleteCall(delivery_id: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -211,7 +193,7 @@
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -220,10 +202,14 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
// Lifecycle
|
||||||
</script>
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
|||||||
@@ -122,54 +122,37 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryIssue',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const page = ref(1)
|
||||||
SideBar,
|
const perPage = ref(50)
|
||||||
Footer,
|
const recordsLength = ref(0)
|
||||||
},
|
const options = ref({
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
deliveries.value = [];
|
||||||
mounted() {
|
get_oil_orders(pageVal)
|
||||||
this.getPage(this.page)
|
}
|
||||||
},
|
|
||||||
methods: {
|
const userStatus = () => {
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -179,24 +162,26 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/issue/' + page;
|
const get_oil_orders = (pageVal: any) => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/issue/' + pageVal;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.deliveries = response.data
|
deliveries.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
deleteCall(delivery_id: any) {
|
|
||||||
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -209,7 +194,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -218,8 +203,12 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -157,52 +157,38 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import { Delivery } from '../../../types/models'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryPending',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const page = ref(1)
|
||||||
SideBar,
|
const perPage = ref(50)
|
||||||
Footer,
|
const recordsLength = ref(0)
|
||||||
},
|
const options = ref({
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
// Functions
|
||||||
created() {
|
const getPage = (pageVal: any) => {
|
||||||
this.userStatus()
|
deliveries.value = [];
|
||||||
},
|
get_oil_orders(pageVal)
|
||||||
mounted() {
|
}
|
||||||
this.getPage(this.page)
|
|
||||||
},
|
const userStatus = () => {
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -212,27 +198,25 @@
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
|
try {
|
||||||
|
const response = await deliveryService.getPending(pageVal)
|
||||||
|
deliveries.value = response.data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching pending deliveries:', error)
|
||||||
|
deliveries.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get_oil_orders(page: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/pending/' + page;
|
|
||||||
axios({
|
|
||||||
method: 'get',
|
|
||||||
url: path,
|
|
||||||
headers: authHeader(),
|
|
||||||
}).then((response: any) => {
|
|
||||||
this.deliveries = response.data
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCall(delivery_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -245,7 +229,7 @@
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -254,11 +238,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
},
|
onMounted(() => {
|
||||||
})
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
@@ -164,55 +164,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryOutForDelivery',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const totals = ref<{ town: string; gallons: number }[]>([])
|
||||||
SideBar,
|
const grand_total = ref(0)
|
||||||
Footer,
|
const page = ref(1)
|
||||||
},
|
const perPage = ref(50)
|
||||||
|
const recordsLength = ref(0)
|
||||||
data() {
|
const options = ref({
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
totals: [] as any[],
|
|
||||||
grand_total: 0,
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
// Functions
|
||||||
created() {
|
const getPage = (pageVal: any) => {
|
||||||
this.userStatus()
|
deliveries.value = [];
|
||||||
},
|
get_oil_orders(pageVal)
|
||||||
mounted() {
|
}
|
||||||
this.getPage(this.page)
|
|
||||||
this.get_totals()
|
const userStatus = () => {
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -222,46 +207,45 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const mod = (date: any) => new Date(date).getTime()
|
||||||
|
|
||||||
mod: (date: any) => new Date (date).getTime(),
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
get_oil_orders(page: any) {
|
try {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/outfordelivery/' + page;
|
const response = await deliveryService.getOutForDelivery(pageVal)
|
||||||
axios({
|
deliveries.value = response.data || []
|
||||||
method: 'get',
|
|
||||||
url: path,
|
|
||||||
headers: authHeader(),
|
|
||||||
}).then((response: any) => {
|
|
||||||
this.deliveries = response.data
|
|
||||||
// Sort deliveries by Delivery # (id) in descending order
|
// Sort deliveries by Delivery # (id) in descending order
|
||||||
this.deliveries.sort((a, b) => b.id - a.id);
|
deliveries.value.sort((a, b) => b.id - a.id);
|
||||||
})
|
} catch (error) {
|
||||||
},
|
console.error('Error fetching out for delivery:', error)
|
||||||
|
deliveries.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get_totals() {
|
const get_totals = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/today-totals';
|
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/today-totals';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.totals = response.data.totals || []
|
totals.value = response.data.totals || []
|
||||||
this.grand_total = response.data.grand_total || 0
|
grand_total.value = response.data.grand_total || 0
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
console.error('Error fetching totals:', error);
|
console.error('Error fetching totals:', error);
|
||||||
this.totals = []
|
totals.value = []
|
||||||
this.grand_total = 0
|
grand_total.value = 0
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteCall(delivery_id: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -274,7 +258,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -283,55 +267,13 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
// printtTicketAll() {
|
// Lifecycle
|
||||||
// let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/print_today';
|
onMounted(() => {
|
||||||
// axios({
|
userStatus()
|
||||||
// method: 'get',
|
getPage(page.value)
|
||||||
// url: path,
|
get_totals()
|
||||||
// headers: authHeader(),
|
|
||||||
// }).then((response: any) => {
|
|
||||||
// if (response.data.ok) {
|
|
||||||
// notify({
|
|
||||||
// title: "Success",
|
|
||||||
// text: "Sent to Printer",
|
|
||||||
// type: "success",
|
|
||||||
// });
|
|
||||||
// this.getPage(this.page)
|
|
||||||
// } else {
|
|
||||||
// notify({
|
|
||||||
// title: "Failure",
|
|
||||||
// text: "error printing",
|
|
||||||
// type: "success",
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// },
|
|
||||||
// printTicket(delivery_id: number) {
|
|
||||||
// let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/' + delivery_id;
|
|
||||||
// axios({
|
|
||||||
// method: 'options',
|
|
||||||
// url: path,
|
|
||||||
// headers: authHeader(),
|
|
||||||
// }).then((response: any) => {
|
|
||||||
// if (response.data.ok) {
|
|
||||||
// notify({
|
|
||||||
// title: "Success",
|
|
||||||
// text: "Sent to Printer",
|
|
||||||
// type: "success",
|
|
||||||
// });
|
|
||||||
// this.getPage(this.page)
|
|
||||||
// } else {
|
|
||||||
// notify({
|
|
||||||
// title: "Failure",
|
|
||||||
// text: "error printing",
|
|
||||||
// type: "success",
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -157,56 +157,44 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
interface TownTotal {
|
||||||
name: 'deliveryOutForDelivery',
|
town: string;
|
||||||
|
gallons: number;
|
||||||
|
}
|
||||||
|
|
||||||
components: {
|
// Reactive data
|
||||||
Header,
|
const token = ref(null)
|
||||||
SideBar,
|
const user = ref(null)
|
||||||
Footer,
|
const deliveries = ref<Delivery[]>([])
|
||||||
},
|
const totals = ref<TownTotal[]>([])
|
||||||
|
const grand_total = ref(0)
|
||||||
data() {
|
const page = ref(1)
|
||||||
return {
|
const perPage = ref(50)
|
||||||
token: null,
|
const recordsLength = ref(0)
|
||||||
user: null,
|
const options = ref({
|
||||||
deliveries: [] as any[],
|
|
||||||
totals: [] as any[],
|
|
||||||
grand_total: 0,
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus()
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getPage(this.page)
|
|
||||||
this.get_totals()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
|
|
||||||
userStatus() {
|
// Functions
|
||||||
|
const getPage = (pageVal: any) => {
|
||||||
|
deliveries.value = [];
|
||||||
|
get_oil_orders(pageVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -216,41 +204,42 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/tommorrow/' + page;
|
const get_oil_orders = (pageVal: any) => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/tommorrow/' + pageVal;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.deliveries = response.data
|
deliveries.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
get_totals() {
|
const get_totals = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/tomorrow-totals';
|
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/tomorrow-totals';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.totals = response.data.totals || []
|
totals.value = response.data.totals || []
|
||||||
this.grand_total = response.data.grand_total || 0
|
grand_total.value = response.data.grand_total || 0
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
console.error('Error fetching totals:', error);
|
console.error('Error fetching totals:', error);
|
||||||
this.totals = []
|
totals.value = []
|
||||||
this.grand_total = 0
|
grand_total.value = 0
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteCall(delivery_id: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -263,7 +252,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -272,10 +261,9 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const printtTicketAll = () => {
|
||||||
printtTicketAll() {
|
|
||||||
let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/print_tommorrow';
|
let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/print_tommorrow';
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -288,7 +276,7 @@ export default defineComponent({
|
|||||||
text: "Sent to Printer",
|
text: "Sent to Printer",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -297,9 +285,9 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
printTicket(delivery_id: number) {
|
const printTicket = (delivery_id: number) => {
|
||||||
let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/' + delivery_id;
|
let path = import.meta.env.VITE_PRINT_URL + '/command/printticket/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -312,7 +300,7 @@ export default defineComponent({
|
|||||||
text: "Sent to Printer",
|
text: "Sent to Printer",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -321,9 +309,12 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
|
get_totals()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -137,58 +137,40 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { deliveryService } from '../../../services/deliveryService'
|
||||||
|
import { Delivery } from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import PaginationComp from '../../../components/pagination.vue'
|
import PaginationComp from '../../../components/pagination.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification";
|
import { notify } from "@kyvg/vue3-notification";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'deliveryWaiting',
|
const token = ref(null)
|
||||||
|
const user = ref(null)
|
||||||
components: {
|
const deliveries = ref<Delivery[]>([])
|
||||||
Header,
|
const totals = ref<{ town: string; gallons: number }[]>([])
|
||||||
SideBar,
|
const grand_total = ref(0)
|
||||||
Footer,
|
const page = ref(1)
|
||||||
},
|
const perPage = ref(50)
|
||||||
|
const recordsLength = ref(0)
|
||||||
data() {
|
const options = ref({
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
user: null,
|
|
||||||
deliveries: [] as any[],
|
|
||||||
totals: [] as any[],
|
|
||||||
grand_total: 0,
|
|
||||||
page: 1,
|
|
||||||
perPage: 50,
|
|
||||||
recordsLength: 0,
|
|
||||||
options: {
|
|
||||||
edgeNavigation: false,
|
edgeNavigation: false,
|
||||||
format: false,
|
format: false,
|
||||||
template: PaginationComp
|
template: PaginationComp
|
||||||
}
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.userStatus()
|
const getPage = (pageVal: any) => {
|
||||||
},
|
deliveries.value = [];
|
||||||
mounted() {
|
get_oil_orders(pageVal)
|
||||||
this.getPage(this.page)
|
}
|
||||||
this.get_totals()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPage: function (page: any) {
|
|
||||||
// we simulate an api call that fetch the records from a backend
|
|
||||||
this.deliveries = [];
|
|
||||||
this.get_oil_orders(page)
|
|
||||||
},
|
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -198,40 +180,41 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get_oil_orders(page: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/waiting/' + page;
|
const get_oil_orders = async (pageVal: number) => {
|
||||||
axios({
|
try {
|
||||||
method: 'get',
|
const response = await deliveryService.getWaiting(pageVal)
|
||||||
url: path,
|
deliveries.value = response.data || []
|
||||||
headers: authHeader(),
|
} catch (error) {
|
||||||
}).then((response: any) => {
|
console.error('Error fetching waiting deliveries:', error)
|
||||||
this.deliveries = response.data
|
deliveries.value = []
|
||||||
})
|
}
|
||||||
},
|
}
|
||||||
get_totals() {
|
|
||||||
|
const get_totals = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/waiting-totals';
|
let path = import.meta.env.VITE_BASE_URL + '/deliverystatus/waiting-totals';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: any) => {
|
||||||
this.totals = response.data.totals || []
|
totals.value = response.data.totals || []
|
||||||
this.grand_total = response.data.grand_total || 0
|
grand_total.value = response.data.grand_total || 0
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
console.error('Error fetching totals:', error);
|
console.error('Error fetching totals:', error);
|
||||||
this.totals = []
|
totals.value = []
|
||||||
this.grand_total = 0
|
grand_total.value = 0
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteCall(delivery_id: any) {
|
const deleteCall = (delivery_id: any) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/delete/' + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@@ -244,7 +227,7 @@ export default defineComponent({
|
|||||||
text: "deleted delivery",
|
text: "deleted delivery",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
this.getPage(this.page)
|
getPage(page.value)
|
||||||
} else {
|
} else {
|
||||||
notify({
|
notify({
|
||||||
title: "Failure",
|
title: "Failure",
|
||||||
@@ -253,11 +236,13 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
},
|
userStatus()
|
||||||
|
getPage(page.value)
|
||||||
|
get_totals()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import useValidate from "@vuelidate/core";
|
|||||||
import { required, minLength, helpers } from "@vuelidate/validators";
|
import { required, minLength, helpers } from "@vuelidate/validators";
|
||||||
import Header from "../../layouts/headers/headerauth.vue";
|
import Header from "../../layouts/headers/headerauth.vue";
|
||||||
import authHeader from "../../services/auth.header";
|
import authHeader from "../../services/auth.header";
|
||||||
|
import {Employee} from '../../types/models';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "EmployeeChangePassword",
|
name: "EmployeeChangePassword",
|
||||||
@@ -74,7 +75,7 @@ export default defineComponent({
|
|||||||
user: null,
|
user: null,
|
||||||
user_admin: 0,
|
user_admin: 0,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
employee: null as any,
|
employee: {} as Employee,
|
||||||
ChangePasswordForm: {
|
ChangePasswordForm: {
|
||||||
new_password: "",
|
new_password: "",
|
||||||
password_confirm: "",
|
password_confirm: "",
|
||||||
|
|||||||
@@ -45,7 +45,8 @@
|
|||||||
<td>{{ person.employee_phone_number }}</td>
|
<td>{{ person.employee_phone_number }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<router-link :to="{ name: 'employeeEdit', params: { id: person.user_id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
<router-link :to="{ name: 'employeeEdit', params: { id: person.user_id || 0 } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||||
|
<router-link :to="{ name: 'employeeEdit', params: { id: person.user_id || 0 } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||||
<router-link :to="{ name: 'employeeProfile', params: { id: person.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
<router-link :to="{ name: 'employeeProfile', params: { id: person.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
||||||
<router-link v-if="user && user.user_admin === 0" :to="{ name: 'employeeChangePassword', params: { id: person.id } }" class="btn btn-sm btn-warning">Change Password</router-link>
|
<router-link v-if="user && user.user_admin === 0" :to="{ name: 'employeeChangePassword', params: { id: person.id } }" class="btn btn-sm btn-warning">Change Password</router-link>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
<p>{{ person.employee_phone_number }}</p>
|
<p>{{ person.employee_phone_number }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions justify-end flex-wrap gap-2 mt-2">
|
<div class="card-actions justify-end flex-wrap gap-2 mt-2">
|
||||||
<router-link :to="{ name: 'employeeEdit', params: { id: person.user_id } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
<router-link :to="{ name: 'employeeEdit', params: { id: person.user_id || 0 } }" class="btn btn-sm btn-secondary">Edit</router-link>
|
||||||
<router-link :to="{ name: 'employeeProfile', params: { id: person.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
<router-link :to="{ name: 'employeeProfile', params: { id: person.id } }" class="btn btn-sm btn-ghost">View</router-link>
|
||||||
<router-link v-if="user && user.user_admin === 0" :to="{ name: 'employeeChangePassword', params: { id: person.id } }" class="btn btn-sm btn-warning">Change Password</router-link>
|
<router-link v-if="user && user.user_admin === 0" :to="{ name: 'employeeChangePassword', params: { id: person.id } }" class="btn btn-sm btn-warning">Change Password</router-link>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,6 +98,7 @@ import axios from 'axios'
|
|||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
import PaginationComp from '../../components/pagination.vue'
|
import PaginationComp from '../../components/pagination.vue'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
|
import {Employee, User} from '../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'EmployeeHome',
|
name: 'EmployeeHome',
|
||||||
@@ -105,8 +107,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
user: {} as any,
|
user: {} as User,
|
||||||
employees: [] as any[],
|
employees: [] as Employee[],
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 50,
|
perPage: 50,
|
||||||
recordsLength: 0,
|
recordsLength: 0,
|
||||||
@@ -136,7 +138,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null;
|
this.user = {} as User;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// --- METHOD CORRECTED TO MATCH YOUR SIMPLE ARRAY API RESPONSE ---
|
// --- METHOD CORRECTED TO MATCH YOUR SIMPLE ARRAY API RESPONSE ---
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import EmployeeHome from '../employee/home.vue';
|
const EmployeeHome = () => import('../employee/home.vue');
|
||||||
import EmployeeCreate from "../employee/create.vue";
|
const EmployeeCreate = () => import("../employee/create.vue");
|
||||||
import EmployeeEdit from "../employee/edit.vue";
|
const EmployeeEdit = () => import("../employee/edit.vue");
|
||||||
import EmployeeProfile from "../employee/profile/home.vue";
|
const EmployeeProfile = () => import("../employee/profile/home.vue");
|
||||||
import EmployeeChangePassword from "../employee/changepassword.vue";
|
const EmployeeChangePassword = () => import("../employee/changepassword.vue");
|
||||||
|
|
||||||
const employeeRoutes = [
|
const employeeRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import MoneyYear from '../money/profit_year.vue';
|
const MoneyYear = () => import('../money/profit_year.vue');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -202,31 +202,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import type {
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
OilPricingResponse,
|
||||||
|
WhoAmIResponse,
|
||||||
|
AutoDeliveryData
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'AuthorizePrechargeAutho',
|
const router = useRouter()
|
||||||
|
|
||||||
data() {
|
// Reactive data
|
||||||
return {
|
const deliveryId = ref(route.params.id as string)
|
||||||
deliveryId: this.$route.params.id as string,
|
const loaded = ref(false)
|
||||||
loaded: false,
|
const chargeAmount = ref(0)
|
||||||
chargeAmount: 0,
|
const quickGallonAmounts = ref([100, 125, 150, 175, 200, 220])
|
||||||
quickGallonAmounts: [100, 125, 150, 175, 200, 220],
|
const loading = ref(false)
|
||||||
loading: false,
|
const action = ref('') // 'preauthorize' or 'charge'
|
||||||
action: '', // 'preauthorize' or 'charge'
|
const error = ref('')
|
||||||
error: '',
|
const success = ref('')
|
||||||
success: '',
|
const isChargeConfirmationModalVisible = ref(false)
|
||||||
isChargeConfirmationModalVisible: false,
|
const transactionId = ref(0)
|
||||||
transactionId: 0,
|
const user = ref({
|
||||||
user: {
|
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
},
|
})
|
||||||
autoDelivery: {
|
const autoDelivery = ref<AutoDeliveryData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_full_name: '',
|
customer_full_name: '',
|
||||||
@@ -236,10 +244,17 @@ export default defineComponent({
|
|||||||
customer_zip: '',
|
customer_zip: '',
|
||||||
tank_size: 0,
|
tank_size: 0,
|
||||||
estimated_gallons_left: 0,
|
estimated_gallons_left: 0,
|
||||||
|
estimated_gallons_left_prev_day: 0,
|
||||||
house_factor: 0,
|
house_factor: 0,
|
||||||
auto_status: 0,
|
auto_status: 0,
|
||||||
},
|
account_number: '',
|
||||||
credit_cards: [
|
last_fill: '',
|
||||||
|
days_since_last_fill: 0,
|
||||||
|
last_updated: '',
|
||||||
|
tank_height: '',
|
||||||
|
open_ticket_id: null,
|
||||||
|
})
|
||||||
|
const credit_cards = ref<CreditCardFormData[]>([
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name_on_card: '',
|
name_on_card: '',
|
||||||
@@ -250,10 +265,9 @@ export default defineComponent({
|
|||||||
last_four_digits: '',
|
last_four_digits: '',
|
||||||
expiration_year: '',
|
expiration_year: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
])
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -266,8 +280,8 @@ export default defineComponent({
|
|||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref<PricingData>({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -275,64 +289,56 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
currentOilPrice: 0,
|
const currentOilPrice = ref(0)
|
||||||
pricingTiers: [] as { gallons: number; price: number }[],
|
const pricingTiers = ref([] as { gallons: number; price: number }[])
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// Computed
|
||||||
selectedCard(): any {
|
const selectedCard = computed(() => {
|
||||||
return this.credit_cards.find((card: any) => card.main_card) || this.credit_cards[0]
|
return credit_cards.value.find((card) => card.main_card) || credit_cards.value[0]
|
||||||
},
|
})
|
||||||
customerStateName(): string {
|
|
||||||
|
const customerStateName = computed(() => {
|
||||||
const states: Record<number, string> = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
const states: Record<number, string> = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' };
|
||||||
return states[this.customer.customer_state] || 'Unknown state';
|
return states[customer.value.customer_state] || 'Unknown state';
|
||||||
},
|
})
|
||||||
selectedGallonAmount(): number | null {
|
|
||||||
return this.quickGallonAmounts.find(gal => this.calculatePriceForGallons(gal).toFixed(2) === this.chargeAmount.toFixed(2)) || null;
|
const selectedGallonAmount = computed(() => {
|
||||||
|
return quickGallonAmounts.value.find(gal => calculatePriceForGallons(gal).toFixed(2) === chargeAmount.value.toFixed(2)) || null;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
watch(() => route.params.id, (newId) => {
|
||||||
|
if (newId !== deliveryId.value) {
|
||||||
|
resetState()
|
||||||
|
deliveryId.value = newId as string
|
||||||
|
loadData(newId as string)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
this.loadData(this.deliveryId)
|
onMounted(() => {
|
||||||
this.getPricingTiers()
|
loadData(deliveryId.value)
|
||||||
},
|
getPricingTiers()
|
||||||
|
})
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.watchRoute()
|
const resetState = () => {
|
||||||
},
|
loading.value = false
|
||||||
|
action.value = ''
|
||||||
|
error.value = ''
|
||||||
|
success.value = ''
|
||||||
|
chargeAmount.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
methods: {
|
const loadData = (deliveryId: string) => {
|
||||||
watchRoute() {
|
userStatus()
|
||||||
watch(
|
getAutoDelivery(deliveryId)
|
||||||
() => this.$route.params.id,
|
getOilPricing()
|
||||||
(newId) => {
|
getCurrentOilPrice()
|
||||||
if (newId !== this.deliveryId) {
|
}
|
||||||
this.resetState()
|
|
||||||
this.deliveryId = newId as string
|
|
||||||
this.loadData(newId as string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
resetState() {
|
const getCurrentOilPrice = () => {
|
||||||
this.loading = false
|
|
||||||
this.action = ''
|
|
||||||
this.error = ''
|
|
||||||
this.success = ''
|
|
||||||
this.chargeAmount = 0
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData(deliveryId: string) {
|
|
||||||
this.userStatus()
|
|
||||||
this.getAutoDelivery(deliveryId)
|
|
||||||
this.getOilPricing()
|
|
||||||
this.getCurrentOilPrice()
|
|
||||||
},
|
|
||||||
|
|
||||||
getCurrentOilPrice() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
let path = import.meta.env.VITE_BASE_URL + '/info/price/oil'
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -340,9 +346,9 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<OilPricingResponse>) => {
|
||||||
this.currentOilPrice = response.data.price_for_customer;
|
currentOilPrice.value = response.data.price_for_customer;
|
||||||
this.calculateDefaultChargeAmount()
|
calculateDefaultChargeAmount()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -351,13 +357,13 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateDefaultChargeAmount() {
|
const calculateDefaultChargeAmount = () => {
|
||||||
this.chargeAmount = this.calculateTotalAsNumber()
|
chargeAmount.value = calculateTotalAsNumber()
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -365,23 +371,23 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilPricing() {
|
const getOilPricing = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<OilPricingResponse>) => {
|
||||||
this.pricing = response.data;
|
pricing.value = response.data;
|
||||||
this.calculateDefaultChargeAmount()
|
calculateDefaultChargeAmount()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -390,57 +396,57 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getAutoDelivery(delivery_id: any) {
|
const getAutoDelivery = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/delivery/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/delivery/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<AutoDeliveryData>) => {
|
||||||
if (response.data && response.data.customer_id) {
|
if (response.data && response.data.customer_id) {
|
||||||
this.autoDelivery = response.data;
|
autoDelivery.value = response.data;
|
||||||
this.getCustomer(this.autoDelivery.customer_id)
|
getCustomer(autoDelivery.value.customer_id)
|
||||||
this.getCreditCards(this.autoDelivery.customer_id)
|
getCreditCards(autoDelivery.value.customer_id)
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch auto delivery data.");
|
console.error("API Error: Failed to fetch auto delivery data.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((err: Error) => {
|
||||||
console.error("API Error in getAutoDelivery:", error);
|
console.error("API Error in getAutoDelivery:", err);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: "Could not get automatic delivery",
|
text: "Could not get automatic delivery",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getCreditCards(user_id: any) {
|
const getCreditCards = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CreditCardFormData[]>) => {
|
||||||
this.credit_cards = response.data
|
credit_cards.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomer(userid: any) {
|
const getCustomer = (userid: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CustomerFormData>) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getPricingTiers() {
|
const getPricingTiers = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers";
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -448,8 +454,8 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader()
|
headers: authHeader()
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<Record<string, number>>) => {
|
||||||
this.pricingTiers = Object.entries(response.data).map(([gallons, price]) => ({ gallons: parseInt(gallons, 10), price: Number(price) }));
|
pricingTiers.value = Object.entries(response.data).map(([gallons, price]) => ({ gallons: parseInt(gallons, 10), price: Number(price) }));
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -458,16 +464,16 @@ export default defineComponent({
|
|||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
isPricingTierSelected(tierGallons: number | string): boolean {
|
const isPricingTierSelected = (tierGallons: number | string): boolean => {
|
||||||
const calculated = this.calculateGallonsToFill()
|
const calculated = calculateGallonsToFill()
|
||||||
return calculated == Number(tierGallons)
|
return calculated == Number(tierGallons)
|
||||||
},
|
}
|
||||||
|
|
||||||
calculatePriceForGallons(gallons: number): number {
|
const calculatePriceForGallons = (gallons: number): number => {
|
||||||
let priceForGallons = 0;
|
let priceForGallons = 0;
|
||||||
const sortedTiers = [...this.pricingTiers].sort((a, b) => Number(a.gallons) - Number(b.gallons));
|
const sortedTiers = [...pricingTiers.value].sort((a, b) => Number(a.gallons) - Number(b.gallons));
|
||||||
|
|
||||||
// Find the highest tier that is less than or equal to the gallons ordered
|
// Find the highest tier that is less than or equal to the gallons ordered
|
||||||
let applicableTier = sortedTiers.filter(t => gallons >= Number(t.gallons)).pop();
|
let applicableTier = sortedTiers.filter(t => gallons >= Number(t.gallons)).pop();
|
||||||
@@ -483,79 +489,79 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return priceForGallons;
|
return priceForGallons;
|
||||||
},
|
}
|
||||||
|
|
||||||
setGallons(gallons: number) {
|
const setGallons = (gallons: number) => {
|
||||||
this.chargeAmount = this.calculatePriceForGallons(gallons);
|
chargeAmount.value = calculatePriceForGallons(gallons);
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateGallonsToFill() {
|
const calculateGallonsToFill = () => {
|
||||||
return this.autoDelivery.tank_size - this.autoDelivery.estimated_gallons_left
|
return autoDelivery.value.tank_size - autoDelivery.value.estimated_gallons_left
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateSubtotal() {
|
const calculateSubtotal = () => {
|
||||||
const gallons = this.calculateGallonsToFill()
|
const gallons = calculateGallonsToFill()
|
||||||
const pricePerGallon = this.pricing.price_for_customer || this.currentOilPrice
|
const pricePerGallon = pricing.value.price_for_customer || currentOilPrice.value
|
||||||
return (gallons * pricePerGallon).toFixed(2)
|
return (gallons * pricePerGallon).toFixed(2)
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAmount() {
|
const calculateTotalAmount = () => {
|
||||||
const subtotal = parseFloat(this.calculateSubtotal())
|
const subtotal = parseFloat(calculateSubtotal())
|
||||||
let total = subtotal
|
let total = subtotal
|
||||||
|
|
||||||
// No additional fees for auto preauthorization
|
// No additional fees for auto preauthorization
|
||||||
|
|
||||||
return total.toFixed(2)
|
return total.toFixed(2)
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAsNumber() {
|
const calculateTotalAsNumber = () => {
|
||||||
return parseFloat(this.calculateTotalAmount())
|
return parseFloat(calculateTotalAmount())
|
||||||
},
|
}
|
||||||
|
|
||||||
async handlePreauthorize() {
|
const handlePreauthorize = async () => {
|
||||||
await this.processPayment('preauthorize')
|
await processPayment('preauthorize')
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleChargeNow() {
|
const handleChargeNow = async () => {
|
||||||
this.loading = true
|
loading.value = true
|
||||||
this.isChargeConfirmationModalVisible = true
|
isChargeConfirmationModalVisible.value = true
|
||||||
},
|
}
|
||||||
|
|
||||||
async proceedWithCharge() {
|
const proceedWithCharge = async () => {
|
||||||
this.isChargeConfirmationModalVisible = false
|
isChargeConfirmationModalVisible.value = false
|
||||||
await this.processPayment('charge')
|
await processPayment('charge')
|
||||||
},
|
}
|
||||||
|
|
||||||
cancelCharge() {
|
const cancelCharge = () => {
|
||||||
this.isChargeConfirmationModalVisible = false
|
isChargeConfirmationModalVisible.value = false
|
||||||
},
|
}
|
||||||
|
|
||||||
async processPayment(actionType: string) {
|
const processPayment = async (actionType: string) => {
|
||||||
if (!this.selectedCard) {
|
if (!selectedCard.value) {
|
||||||
this.error = 'No credit card found for this customer'
|
error.value = 'No credit card found for this customer'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
loading.value = true
|
||||||
this.action = actionType
|
action.value = actionType
|
||||||
this.error = ''
|
error.value = ''
|
||||||
this.success = ''
|
success.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 2: If payment method is credit, perform the pre-authorization
|
// Step 2: If payment method is credit, perform the pre-authorization
|
||||||
if (actionType === 'preauthorize') {
|
if (actionType === 'preauthorize') {
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Pre-authorization amount must be greater than zero.");
|
throw new Error("Pre-authorization amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const authPayload = {
|
const authPayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
preauthorize_amount: this.chargeAmount.toFixed(2),
|
preauthorize_amount: chargeAmount.value.toFixed(2),
|
||||||
delivery_id: null,
|
delivery_id: null,
|
||||||
auto_id: this.deliveryId,
|
auto_id: deliveryId.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${this.customer.id}`;
|
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
@@ -563,50 +569,50 @@ export default defineComponent({
|
|||||||
throw new Error("Failed transaction: No Authorize.net transaction ID received");
|
throw new Error("Failed transaction: No Authorize.net transaction ID received");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.transactionId = response.data.id;
|
transactionId.value = response.data.id;
|
||||||
|
|
||||||
// Update auto_delivery status to 3
|
// Update auto_delivery status to 3
|
||||||
await axios.put(`${import.meta.env.VITE_AUTO_URL}/delivery/update_status/${this.deliveryId}`, {}, { withCredentials: true, headers: authHeader() });
|
await axios.put(`${import.meta.env.VITE_AUTO_URL}/delivery/update_status/${deliveryId.value}`, {}, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Create Tickets_Auto_Delivery after successful preauthorize
|
// Create Tickets_Auto_Delivery after successful preauthorize
|
||||||
const ticketPayload = {
|
const ticketPayload = {
|
||||||
gallons_delivered: 0,
|
gallons_delivered: 0,
|
||||||
payment_type: 11, // 11 for preauthorize, 1 for charge
|
payment_type: 11, // 11 for preauthorize, 1 for charge
|
||||||
payment_card_id: this.selectedCard.id,
|
payment_card_id: selectedCard.value!.id,
|
||||||
payment_status: 1 // Pre-authorized status (ready for capture)
|
payment_status: 1 // Pre-authorized status (ready for capture)
|
||||||
};
|
};
|
||||||
const ticketUrl = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/create/${this.deliveryId}`;
|
const ticketUrl = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/create/${deliveryId.value}`;
|
||||||
const ticketResponse = await axios.post(ticketUrl, ticketPayload, { withCredentials: true, headers: authHeader() });
|
const ticketResponse = await axios.post(ticketUrl, ticketPayload, { withCredentials: true, headers: authHeader() });
|
||||||
console.log('Ticket response data:', ticketResponse.data, 'type:', typeof ticketResponse.data, 'keys:', ticketResponse.data ? Object.keys(ticketResponse.data) : 'no data');
|
console.log('Ticket response data:', ticketResponse.data, 'type:', typeof ticketResponse.data, 'keys:', ticketResponse.data ? Object.keys(ticketResponse.data) : 'no data');
|
||||||
|
|
||||||
// Update transaction auto_id to ticket ID
|
// Update transaction auto_id to ticket ID
|
||||||
if (this.transactionId && ticketResponse.data) {
|
if (transactionId.value && ticketResponse.data) {
|
||||||
const data = Array.isArray(ticketResponse.data) ? ticketResponse.data[0] : ticketResponse.data;
|
const data = Array.isArray(ticketResponse.data) ? ticketResponse.data[0] : ticketResponse.data;
|
||||||
if (data && data.auto_ticket_id !== undefined) {
|
if (data && data.auto_ticket_id !== undefined) {
|
||||||
await axios.put(`${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/${this.transactionId}/update_auto_id/${data.auto_ticket_id}`, {}, { withCredentials: true, headers: authHeader() });
|
await axios.put(`${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/${transactionId.value}/update_auto_id/${data.auto_ticket_id}`, {}, { withCredentials: true, headers: authHeader() });
|
||||||
} else {
|
} else {
|
||||||
console.error('auto_ticket_id is undefined in ticket response');
|
console.error('auto_ticket_id is undefined in ticket response');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On successful authorization, show success and redirect
|
// On successful authorization, show success and redirect
|
||||||
this.success = `Preauthorization successful! Transaction ID: ${response.data.auth_net_transaction_id}`;
|
success.value = `Preauthorization successful! Transaction ID: ${response.data.auth_net_transaction_id}`;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
else { // Handle 'charge' action
|
else { // Handle 'charge' action
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Charge amount must be greater than zero.");
|
throw new Error("Charge amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const chargePayload = {
|
const chargePayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
charge_amount: this.chargeAmount.toFixed(2),
|
charge_amount: chargeAmount.value.toFixed(2),
|
||||||
delivery_id: this.deliveryId,
|
delivery_id: deliveryId.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${this.customer.id}`;
|
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
console.log('=== DEBUG: Charge payload ===');
|
console.log('=== DEBUG: Charge payload ===');
|
||||||
console.log('Calling endpoint:', chargePath);
|
console.log('Calling endpoint:', chargePath);
|
||||||
@@ -615,50 +621,49 @@ export default defineComponent({
|
|||||||
const response = await axios.post(chargePath, chargePayload, { withCredentials: true, headers: authHeader() });
|
const response = await axios.post(chargePath, chargePayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Update auto_delivery status to 3
|
// Update auto_delivery status to 3
|
||||||
await axios.put(`${import.meta.env.VITE_AUTO_URL}/delivery/update_status/${this.deliveryId}`, {}, { withCredentials: true, headers: authHeader() });
|
await axios.put(`${import.meta.env.VITE_AUTO_URL}/delivery/update_status/${deliveryId.value}`, {}, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
||||||
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
||||||
if (!response.data.auth_net_transaction_id || response.data.auth_net_transaction_id.trim() === '') {
|
if (!response.data.auth_net_transaction_id || response.data.auth_net_transaction_id.trim() === '') {
|
||||||
throw new Error("Failed transaction: No Authorize.net transaction ID received");
|
throw new Error("Failed transaction: No Authorize.net transaction ID received");
|
||||||
}
|
}
|
||||||
this.transactionId = response.data.id;
|
transactionId.value = response.data.id;
|
||||||
this.success = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id}`;
|
success.value = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id}`;
|
||||||
|
|
||||||
// Create Tickets_Auto_Delivery after successful charge
|
// Create Tickets_Auto_Delivery after successful charge
|
||||||
const ticketPayload = {
|
const ticketPayload = {
|
||||||
gallons_delivered: 0,
|
gallons_delivered: 0,
|
||||||
payment_type: 11, // 11 for Authorize charge
|
payment_type: 11, // 11 for Authorize charge
|
||||||
payment_card_id: this.selectedCard.id,
|
payment_card_id: selectedCard.value!.id,
|
||||||
payment_status: response.data.status // 0 = APPROVED
|
payment_status: response.data.status // 0 = APPROVED
|
||||||
};
|
};
|
||||||
const ticketUrl = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/create/${this.deliveryId}`;
|
const ticketUrl = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/create/${deliveryId.value}`;
|
||||||
console.log("POOOPP!")
|
console.log("POOOPP!")
|
||||||
await axios.post(ticketUrl, ticketPayload, { withCredentials: true, headers: authHeader() });
|
await axios.post(ticketUrl, ticketPayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "auto" });
|
router.push({ name: "auto" });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
// The error message from your backend will be more specific now
|
// The error message from your backend will be more specific now
|
||||||
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
console.log(error)
|
const axiosErr = err as AxiosError<{ detail?: string }>;
|
||||||
this.error = error.response?.data?.detail || `Failed to ${actionType} payment`
|
console.log(err)
|
||||||
|
error.value = axiosErr.response?.data?.detail || `Failed to ${actionType} payment`
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: this.error,
|
text: error.value,
|
||||||
type: "error",
|
type: "error",
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
loading.value = false
|
||||||
this.action = ''
|
action.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -237,28 +237,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import type {
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
CustomerDescriptionData,
|
||||||
|
AutoTicketData,
|
||||||
|
AutoDeliveryData,
|
||||||
|
PaymentCardResponse,
|
||||||
|
AuthorizeNetTransactionResponse
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'captureAuthorizeAuto',
|
const router = useRouter()
|
||||||
|
|
||||||
data() {
|
// Reactive data
|
||||||
return {
|
const loading = ref(false)
|
||||||
loading: false,
|
const userCardfound = ref(false)
|
||||||
userCardfound: false,
|
const gallonsDelivered = ref('')
|
||||||
gallonsDelivered: '',
|
const captureAmount = ref(0)
|
||||||
captureAmount: 0,
|
const preAuthAmount = ref(0)
|
||||||
preAuthAmount: 0,
|
const transaction = ref<AuthorizeNetTransactionResponse | null>(null)
|
||||||
transaction: null as any,
|
const showPaymentModal = ref(false)
|
||||||
showPaymentModal: false,
|
const modalStep = ref(0)
|
||||||
modalStep: 0,
|
const modalCapturedAmount = ref(0)
|
||||||
modalCapturedAmount: 0,
|
|
||||||
|
|
||||||
userCard: {
|
const userCard = ref<CreditCardFormData>({
|
||||||
|
id: 0,
|
||||||
date_added: '',
|
date_added: '',
|
||||||
user_id: '',
|
user_id: '',
|
||||||
card_number: '',
|
card_number: '',
|
||||||
@@ -269,16 +280,16 @@ export default defineComponent({
|
|||||||
type_of_card: '',
|
type_of_card: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
accepted_or_declined: '',
|
accepted_or_declined: '',
|
||||||
main_card: '',
|
main_card: false,
|
||||||
},
|
})
|
||||||
customerDescription: {
|
const customerDescription = ref<CustomerDescriptionData>({
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
company_id: 0,
|
company_id: 0,
|
||||||
fill_location: 0,
|
fill_location: 0,
|
||||||
description: '',
|
description: '',
|
||||||
},
|
})
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
@@ -290,8 +301,9 @@ export default defineComponent({
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
account_number: '',
|
||||||
autoDelivery: {
|
})
|
||||||
|
const autoDelivery = ref<AutoDeliveryData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
account_number: '',
|
account_number: '',
|
||||||
@@ -310,33 +322,28 @@ export default defineComponent({
|
|||||||
house_factor: 0,
|
house_factor: 0,
|
||||||
auto_status: 0,
|
auto_status: 0,
|
||||||
open_ticket_id: null,
|
open_ticket_id: null,
|
||||||
},
|
})
|
||||||
autoTicket: {
|
const autoTicket = ref<AutoTicketData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: '',
|
customer_id: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
|
customer_town: '',
|
||||||
customer_town : '',
|
customer_state: '',
|
||||||
customer_state : '',
|
customer_address: '',
|
||||||
customer_address : '',
|
|
||||||
customer_zip: '',
|
customer_zip: '',
|
||||||
customer_full_name : '',
|
customer_full_name: '',
|
||||||
|
oil_prices_id: '',
|
||||||
oil_prices_id : '',
|
fill_date: '',
|
||||||
fill_date : '',
|
gallons_delivered: '',
|
||||||
gallons_delivered : '',
|
price_per_gallon: '',
|
||||||
price_per_gallon : '',
|
total_amount_customer: '',
|
||||||
|
payment_type: '',
|
||||||
total_amount_customer : '',
|
payment_card_id: '',
|
||||||
|
payment_status: 0,
|
||||||
payment_type : '',
|
|
||||||
payment_card_id : '',
|
|
||||||
payment_status : 0,
|
|
||||||
open_ticket_id: 0
|
open_ticket_id: 0
|
||||||
|
})
|
||||||
|
|
||||||
},
|
const pricing = ref<PricingData>({
|
||||||
|
|
||||||
pricing: {
|
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -344,38 +351,37 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
|
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
this.getAutoTicket(this.$route.params.id)
|
onMounted(() => {
|
||||||
this.getTransaction()
|
getAutoTicket(route.params.id)
|
||||||
},
|
getTransaction()
|
||||||
|
})
|
||||||
|
|
||||||
methods: {
|
// Functions
|
||||||
getAutoTicket(delivery_id: any) {
|
const getAutoTicket = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/autoticket/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<AutoTicketData>) => {
|
||||||
this.autoTicket = response.data;
|
autoTicket.value = response.data;
|
||||||
console.log(this.autoTicket)
|
console.log(autoTicket.value)
|
||||||
this.gallonsDelivered = this.autoTicket.gallons_delivered;
|
gallonsDelivered.value = autoTicket.value.gallons_delivered;
|
||||||
this.captureAmount = parseFloat(this.autoTicket.total_amount_customer || '0');
|
captureAmount.value = parseFloat(autoTicket.value.total_amount_customer || '0');
|
||||||
this.getCustomer(this.autoTicket.customer_id)
|
getCustomer(autoTicket.value.customer_id)
|
||||||
|
|
||||||
this.getAutoDelivery(this.autoTicket.id)
|
getAutoDelivery(autoTicket.value.id)
|
||||||
this.getCustomerDescription(this.autoTicket.customer_id)
|
getCustomerDescription(autoTicket.value.customer_id)
|
||||||
if (this.autoTicket.payment_card_id) {
|
if (autoTicket.value.payment_card_id) {
|
||||||
this.getPaymentCard(this.autoTicket.payment_card_id)
|
getPaymentCard(autoTicket.value.payment_card_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -386,20 +392,20 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getAutoDelivery(delivery_id: any) {
|
const getAutoDelivery = (delivery_id: number) => {
|
||||||
let path = import.meta.env.VITE_AUTO_URL + "/delivery/finddelivery/" + delivery_id;
|
let path = import.meta.env.VITE_AUTO_URL + "/delivery/finddelivery/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<AutoDeliveryData>) => {
|
||||||
|
|
||||||
this.autoDelivery = response.data;
|
autoDelivery.value = response.data;
|
||||||
this.getCustomer(this.autoDelivery.customer_id)
|
getCustomer(autoDelivery.value.customer_id)
|
||||||
this.getCustomerDescription(this.autoDelivery.customer_id)
|
getCustomerDescription(autoDelivery.value.customer_id)
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -409,48 +415,50 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPaymentCard(card_id: any) {
|
const getPaymentCard = (card_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
let path = import.meta.env.VITE_BASE_URL + "/payment/card/" + card_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<PaymentCardResponse>) => {
|
||||||
if (response.data.userCard.card_number === ''){
|
if (response.data.userCard.card_number === ''){
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
this.userCard = response.data;
|
userCard.value = response.data.userCard as CreditCardFormData;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomer(user_id: any) {
|
|
||||||
|
const getCustomer = (user_id: number | string) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get<CustomerFormData>(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
notify({ title: "Error", text: "Could not find customer", type: "error" });
|
notify({ title: "Error", text: "Could not find customer", type: "error" });
|
||||||
console.error("Error fetching customer:", error);
|
console.error("Error fetching customer:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCustomerDescription(user_id: any) {
|
|
||||||
|
const getCustomerDescription = (user_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
let path = import.meta.env.VITE_BASE_URL + "/customer/description/" + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<CustomerDescriptionData>) => {
|
||||||
this.customerDescription = response.data;
|
customerDescription.value = response.data;
|
||||||
this.loading = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -459,46 +467,47 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getTransaction() {
|
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/auto/transaction/delivery/${this.$route.params.id}`;
|
const getTransaction = () => {
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/auto/transaction/delivery/${route.params.id}`;
|
||||||
.then((response: any) => {
|
axios.get<AuthorizeNetTransactionResponse>(path, { withCredentials: true, headers: authHeader() })
|
||||||
this.transaction = response.data;
|
.then((response) => {
|
||||||
this.preAuthAmount = parseFloat(response.data.preauthorize_amount || 0);
|
transaction.value = response.data;
|
||||||
|
preAuthAmount.value = parseFloat(String(response.data.preauthorize_amount) || '0');
|
||||||
if (response.data.status !== 0) { // Not approved
|
if (response.data.status !== 0) { // Not approved
|
||||||
this.preAuthAmount = 0;
|
preAuthAmount.value = 0;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: AxiosError) => {
|
||||||
if (error.response && error.response.status === 404) {
|
if (error.response && error.response.status === 404) {
|
||||||
notify({ title: "Info", text: "No pre-authorization found. Redirecting to customer profile to update payment method.", type: "info" });
|
notify({ title: "Info", text: "No pre-authorization found. Redirecting to customer profile to update payment method.", type: "info" });
|
||||||
console.log("No transaction found for Automatic - redirecting to customer profile");
|
console.log("No transaction found for Automatic - redirecting to customer profile");
|
||||||
this.$router.push({ name: 'customerProfile', params: { id: this.customer.id } });
|
router.push({ name: 'customerProfile', params: { id: customer.value.id } });
|
||||||
} else {
|
} else {
|
||||||
notify({ title: "Error", text: "No pre-authorized transaction found", type: "error" });
|
notify({ title: "Error", text: "No pre-authorized transaction found", type: "error" });
|
||||||
this.$router.push({ name: 'finalizeTicketAuto', params: { id: this.$route.params.id } });
|
router.push({ name: 'finalizeTicketAuto', params: { id: route.params.id } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
async capturePayment() {
|
const capturePayment = async () => {
|
||||||
if (this.autoTicket.payment_status !== 1) {
|
if (autoTicket.value.payment_status !== 1) {
|
||||||
notify({ title: "Error", text: "Payment already captured or not ready for capture", type: "error" });
|
notify({ title: "Error", text: "Payment already captured or not ready for capture", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.transaction || !this.captureAmount) {
|
if (!transaction.value || !captureAmount.value) {
|
||||||
notify({ title: "Error", text: "Invalid capture amount or transaction data", type: "error" });
|
notify({ title: "Error", text: "Invalid capture amount or transaction data", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
charge_amount: this.captureAmount,
|
charge_amount: captureAmount.value,
|
||||||
auth_net_transaction_id: this.transaction.auth_net_transaction_id
|
auth_net_transaction_id: transaction.value.auth_net_transaction_id
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = `${import.meta.env.VITE_AUTHORIZE_URL}/api/capture/`;
|
const url = `${import.meta.env.VITE_AUTHORIZE_URL}/api/capture/`;
|
||||||
@@ -510,13 +519,13 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data && response.data.status === 0) {
|
if (response.data && response.data.status === 0) {
|
||||||
this.autoTicket.payment_status = 3; // Update local status immediately
|
autoTicket.value.payment_status = 3; // Update local status immediately
|
||||||
this.modalCapturedAmount = this.captureAmount;
|
modalCapturedAmount.value = captureAmount.value;
|
||||||
this.showPaymentModal = true;
|
showPaymentModal.value = true;
|
||||||
// Close the ticket and unassign from delivery
|
// Close the ticket and unassign from delivery
|
||||||
this.closeTicket(this.autoTicket.id);
|
closeTicket(autoTicket.value.id);
|
||||||
setTimeout(() => { this.modalStep = 1 }, 2000);
|
setTimeout(() => { modalStep.value = 1 }, 2000);
|
||||||
setTimeout(() => { this.showPaymentModal = false; this.$router.push({ name: 'auto' }) }, 4000);
|
setTimeout(() => { showPaymentModal.value = false; router.push({ name: 'auto' }) }, 4000);
|
||||||
} else if (response.data && response.data.status === 1) {
|
} else if (response.data && response.data.status === 1) {
|
||||||
const reason = response.data.rejection_reason || "The payment was declined by the gateway.";
|
const reason = response.data.rejection_reason || "The payment was declined by the gateway.";
|
||||||
notify({
|
notify({
|
||||||
@@ -528,7 +537,8 @@ export default defineComponent({
|
|||||||
throw new Error("Invalid response from server during capture.");
|
throw new Error("Invalid response from server during capture.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
|
const error = err as AxiosError<{ detail?: string }>;
|
||||||
const detail = error.response?.data?.detail || "Failed to capture payment due to a server error.";
|
const detail = error.response?.data?.detail || "Failed to capture payment due to a server error.";
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -537,21 +547,21 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
console.error("Capture Payment Error:", error);
|
console.error("Capture Payment Error:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
cancelCapture() {
|
const cancelCapture = () => {
|
||||||
this.$router.push({ name: 'finalizeTicketAuto', params: { id: this.$route.params.id } });
|
router.push({ name: 'finalizeTicketAuto', params: { id: route.params.id } });
|
||||||
},
|
}
|
||||||
|
|
||||||
async closeTicket(ticket_id: number) {
|
const closeTicket = async (ticket_id: number) => {
|
||||||
const path = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/close_ticket/${ticket_id}`;
|
const path = `${import.meta.env.VITE_AUTO_URL}/confirm/auto/close_ticket/${ticket_id}`;
|
||||||
axios.put(path, {}, { withCredentials: true })
|
axios.put(path, {}, { withCredentials: true })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Ticket closed successfully");
|
console.log("Ticket closed successfully");
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
notify({
|
notify({
|
||||||
title: "Warning",
|
title: "Warning",
|
||||||
text: "Payment captured, but failed to close ticket. Check manually.",
|
text: "Payment captured, but failed to close ticket. Check manually.",
|
||||||
@@ -559,9 +569,9 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
console.error("Error closing ticket:", error);
|
console.error("Error closing ticket:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getTypeColor(transactionType: number) {
|
const getTypeColor = (transactionType: number) => {
|
||||||
switch (transactionType) {
|
switch (transactionType) {
|
||||||
case 1: return 'text-blue-600'; // Auth
|
case 1: return 'text-blue-600'; // Auth
|
||||||
case 0: return 'text-orange-600'; // Charge
|
case 0: return 'text-orange-600'; // Charge
|
||||||
@@ -569,9 +579,7 @@ export default defineComponent({
|
|||||||
case 3: return 'text-green-600'; // Delivery/Other
|
case 3: return 'text-green-600'; // Delivery/Other
|
||||||
default: return 'text-gray-600';
|
default: return 'text-gray-600';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -172,29 +172,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import type {
|
||||||
|
DeliveryFormData,
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
PromoData,
|
||||||
|
DeliveryOrderResponse,
|
||||||
|
DeliveryTotalResponse,
|
||||||
|
OilPricingResponse,
|
||||||
|
PromoResponse,
|
||||||
|
WhoAmIResponse,
|
||||||
|
UpdateStatusResponse
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
// Router and route
|
||||||
name: 'AuthorizePreauthCharge',
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
data() {
|
// Reactive data
|
||||||
return {
|
const deliveryId = ref(route.params.id as string)
|
||||||
deliveryId: this.$route.params.id as string,
|
const loaded = ref(false)
|
||||||
loaded: false,
|
const chargeAmount = ref(0)
|
||||||
chargeAmount: 0,
|
const loading = ref(false)
|
||||||
loading: false,
|
const action = ref('') // 'preauthorize' or 'charge'
|
||||||
action: '', // 'preauthorize' or 'charge'
|
const error = ref('')
|
||||||
error: '',
|
const success = ref('')
|
||||||
success: '',
|
const isChargeConfirmationModalVisible = ref(false)
|
||||||
isChargeConfirmationModalVisible: false,
|
const user = ref({
|
||||||
user: {
|
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
},
|
})
|
||||||
delivery: {
|
const delivery = ref<DeliveryFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_name: '',
|
customer_name: '',
|
||||||
@@ -228,8 +242,8 @@ export default defineComponent({
|
|||||||
pre_charge_amount: 0,
|
pre_charge_amount: 0,
|
||||||
total_price: 0,
|
total_price: 0,
|
||||||
service_id: null,
|
service_id: null,
|
||||||
},
|
})
|
||||||
credit_cards: [
|
const credit_cards = ref<CreditCardFormData[]>([
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name_on_card: '',
|
name_on_card: '',
|
||||||
@@ -240,10 +254,9 @@ export default defineComponent({
|
|||||||
last_four_digits: '',
|
last_four_digits: '',
|
||||||
expiration_year: '',
|
expiration_year: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
])
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -256,8 +269,8 @@ export default defineComponent({
|
|||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref<PricingData>({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -265,115 +278,105 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
promo_active: false,
|
const promo_active = ref(false)
|
||||||
promo: {
|
const promo = ref<PromoData>({
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: 0,
|
money_off_delivery: 0,
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const selectedCard = computed(() => {
|
||||||
|
return credit_cards.value.find((card) => card.id === delivery.value.payment_card_id)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
loadData(deliveryId.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
watch(() => route.params.id, (newId) => {
|
||||||
|
if (newId !== deliveryId.value) {
|
||||||
|
resetState()
|
||||||
|
deliveryId.value = newId as string
|
||||||
|
loadData(newId as string)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
|
||||||
computed: {
|
// Functions
|
||||||
selectedCard(): any {
|
const resetState = () => {
|
||||||
return this.credit_cards.find((card: any) => card.id === this.delivery.payment_card_id)
|
loading.value = false
|
||||||
}
|
action.value = ''
|
||||||
},
|
error.value = ''
|
||||||
|
success.value = ''
|
||||||
|
chargeAmount.value = 0
|
||||||
|
promo_active.value = false
|
||||||
|
total_amount.value = 0
|
||||||
|
discount.value = 0
|
||||||
|
total_amount_after_discount.value = 0
|
||||||
|
deliveryId.value = route.params.id as string
|
||||||
|
}
|
||||||
|
|
||||||
mounted() {
|
const loadData = (deliveryId: string) => {
|
||||||
this.loadData(this.deliveryId)
|
userStatus()
|
||||||
},
|
getOilOrder(deliveryId)
|
||||||
|
sumdelivery(deliveryId)
|
||||||
|
getOilPricing()
|
||||||
|
updatestatus()
|
||||||
|
}
|
||||||
|
|
||||||
created() {
|
const updatestatus = () => {
|
||||||
this.watchRoute()
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
watchRoute() {
|
|
||||||
watch(
|
|
||||||
() => this.$route.params.id,
|
|
||||||
(newId) => {
|
|
||||||
if (newId !== this.deliveryId) {
|
|
||||||
this.resetState()
|
|
||||||
this.deliveryId = newId as string
|
|
||||||
this.loadData(newId as string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
resetState() {
|
|
||||||
this.loading = false
|
|
||||||
this.action = ''
|
|
||||||
this.error = ''
|
|
||||||
this.success = ''
|
|
||||||
this.chargeAmount = 0
|
|
||||||
this.promo_active = false
|
|
||||||
this.total_amount = 0
|
|
||||||
this.discount = 0
|
|
||||||
this.total_amount_after_discount = 0
|
|
||||||
this.deliveryId = this.$route.params.id as string
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData(deliveryId: string) {
|
|
||||||
this.userStatus()
|
|
||||||
this.getOilOrder(deliveryId)
|
|
||||||
this.sumdelivery(deliveryId)
|
|
||||||
this.getOilPricing()
|
|
||||||
this.updatestatus()
|
|
||||||
},
|
|
||||||
|
|
||||||
updatestatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<UpdateStatusResponse>) => {
|
||||||
if (response.data.update)
|
if (response.data.update)
|
||||||
console.log("Updated Status of Deliveries")
|
console.log("Updated Status of Deliveries")
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
updateChargeAmount() {
|
const updateChargeAmount = () => {
|
||||||
// Only update if we have all necessary data
|
// Only update if we have all necessary data
|
||||||
if (this.total_amount_after_discount > 0 &&
|
if (total_amount_after_discount.value > 0 &&
|
||||||
this.pricing.price_prime !== undefined &&
|
pricing.value.price_prime !== undefined &&
|
||||||
this.pricing.price_same_day !== undefined &&
|
pricing.value.price_same_day !== undefined &&
|
||||||
this.pricing.price_emergency !== undefined) {
|
pricing.value.price_emergency !== undefined) {
|
||||||
this.chargeAmount = this.calculateTotalAsNumber();
|
chargeAmount.value = calculateTotalAsNumber();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
sumdelivery(delivery_id: any) {
|
const sumdelivery = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<DeliveryTotalResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.total_amount = parseFloat(response.data.total_amount) || 0;
|
total_amount.value = parseFloat(String(response.data.total_amount)) || 0;
|
||||||
this.discount = parseFloat(response.data.discount) || 0;
|
discount.value = parseFloat(String(response.data.discount)) || 0;
|
||||||
this.total_amount_after_discount = parseFloat(response.data.total_amount_after_discount) || 0;
|
total_amount_after_discount.value = parseFloat(String(response.data.total_amount_after_discount)) || 0;
|
||||||
|
|
||||||
// Try to update charge amount with complete pricing
|
// Try to update charge amount with complete pricing
|
||||||
const updated = this.updateChargeAmount();
|
const updated = updateChargeAmount();
|
||||||
|
|
||||||
// Fallback only if pricing not loaded yet and calculation didn't run
|
// Fallback only if pricing not loaded yet and calculation didn't run
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
if (this.promo_active) {
|
if (promo_active.value) {
|
||||||
this.chargeAmount = this.total_amount_after_discount;
|
chargeAmount.value = total_amount_after_discount.value;
|
||||||
} else {
|
} else {
|
||||||
this.chargeAmount = this.total_amount;
|
chargeAmount.value = total_amount.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,9 +388,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPromo(promo_id: any) {
|
const getPromo = (promo_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -395,18 +398,18 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<PromoResponse>) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.promo = response.data
|
promo.value = response.data
|
||||||
this.promo_active = true
|
promo_active.value = true
|
||||||
|
|
||||||
// Trigger a charge amount update if all data is available
|
// Trigger a charge amount update if all data is available
|
||||||
this.updateChargeAmount();
|
updateChargeAmount();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -414,24 +417,24 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilPricing() {
|
const getOilPricing = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<OilPricingResponse>) => {
|
||||||
this.pricing = response.data;
|
pricing.value = response.data;
|
||||||
// Try to update charge amount when pricing is loaded
|
// Try to update charge amount when pricing is loaded
|
||||||
this.updateChargeAmount();
|
updateChargeAmount();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -440,29 +443,29 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilOrder(delivery_id: any) {
|
const getOilOrder = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/order/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/order/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<DeliveryOrderResponse>) => {
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
this.delivery = response.data.delivery;
|
delivery.value = response.data.delivery as DeliveryFormData;
|
||||||
this.getCustomer(this.delivery.customer_id)
|
getCustomer(delivery.value.customer_id)
|
||||||
this.getCreditCards(this.delivery.customer_id)
|
getCreditCards(delivery.value.customer_id)
|
||||||
if (this.delivery.promo_id != null) {
|
if (delivery.value.promo_id != null) {
|
||||||
this.getPromo(this.delivery.promo_id);
|
getPromo(delivery.value.promo_id);
|
||||||
this.promo_active = true;
|
promo_active.value = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
console.error("API Error in getOilOrder:", error);
|
console.error("API Error in getOilOrder:", error);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -470,163 +473,163 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getCreditCards(user_id: any) {
|
const getCreditCards = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CreditCardFormData[]>) => {
|
||||||
this.credit_cards = response.data
|
credit_cards.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomer(userid: any) {
|
const getCustomer = (userid: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CustomerFormData>) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateSubtotal() {
|
const calculateSubtotal = () => {
|
||||||
const gallons = this.delivery.gallons_ordered || 0
|
const gallons = delivery.value.gallons_ordered || 0
|
||||||
const pricePerGallon = this.delivery.customer_price || 0
|
const pricePerGallon = delivery.value.customer_price || 0
|
||||||
return (gallons * pricePerGallon).toFixed(2)
|
return (gallons * pricePerGallon).toFixed(2)
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAmount() {
|
const calculateTotalAmount = () => {
|
||||||
if (this.total_amount_after_discount == null || this.total_amount_after_discount === undefined) {
|
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalNum = Number(this.total_amount_after_discount);
|
let totalNum = Number(total_amount_after_discount.value);
|
||||||
if (isNaN(totalNum)) {
|
if (isNaN(totalNum)) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.delivery && this.delivery.prime == 1 && this.pricing && this.pricing.price_prime) {
|
if (delivery.value && delivery.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||||||
totalNum += Number(this.pricing.price_prime) || 0;
|
totalNum += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.same_day == 1 && this.pricing && this.pricing.price_same_day) {
|
if (delivery.value && delivery.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||||||
totalNum += Number(this.pricing.price_same_day) || 0;
|
totalNum += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.emergency == 1 && this.pricing && this.pricing.price_emergency) {
|
if (delivery.value && delivery.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||||||
totalNum += Number(this.pricing.price_emergency) || 0;
|
totalNum += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalNum.toFixed(2);
|
return totalNum.toFixed(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAsNumber() {
|
const calculateTotalAsNumber = () => {
|
||||||
if (this.total_amount_after_discount == null || this.total_amount_after_discount === undefined) {
|
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalNum = Number(this.total_amount_after_discount);
|
let totalNum = Number(total_amount_after_discount.value);
|
||||||
if (isNaN(totalNum)) {
|
if (isNaN(totalNum)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.delivery && this.delivery.prime == 1 && this.pricing && this.pricing.price_prime) {
|
if (delivery.value && delivery.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||||||
totalNum += Number(this.pricing.price_prime) || 0;
|
totalNum += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.same_day == 1 && this.pricing && this.pricing.price_same_day) {
|
if (delivery.value && delivery.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||||||
totalNum += Number(this.pricing.price_same_day) || 0;
|
totalNum += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.emergency == 1 && this.pricing && this.pricing.price_emergency) {
|
if (delivery.value && delivery.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||||||
totalNum += Number(this.pricing.price_emergency) || 0;
|
totalNum += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalNum;
|
return totalNum;
|
||||||
},
|
}
|
||||||
|
|
||||||
async handlePreauthorize() {
|
const handlePreauthorize = async () => {
|
||||||
await this.processPayment('preauthorize')
|
await processPayment('preauthorize')
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleChargeNow() {
|
const handleChargeNow = async () => {
|
||||||
if (!this.selectedCard) {
|
if (!selectedCard.value) {
|
||||||
this.error = 'No credit card found for this customer'
|
error.value = 'No credit card found for this customer'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
this.error = 'Please enter a valid charge amount'
|
error.value = 'Please enter a valid charge amount'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.isChargeConfirmationModalVisible = true
|
isChargeConfirmationModalVisible.value = true
|
||||||
},
|
}
|
||||||
|
|
||||||
async proceedWithCharge() {
|
const proceedWithCharge = async () => {
|
||||||
this.isChargeConfirmationModalVisible = false
|
isChargeConfirmationModalVisible.value = false
|
||||||
await this.processPayment('charge')
|
await processPayment('charge')
|
||||||
},
|
}
|
||||||
|
|
||||||
cancelCharge() {
|
const cancelCharge = () => {
|
||||||
this.isChargeConfirmationModalVisible = false
|
isChargeConfirmationModalVisible.value = false
|
||||||
},
|
}
|
||||||
|
|
||||||
async processPayment(actionType: string) {
|
const processPayment = async (actionType: string) => {
|
||||||
if (!this.selectedCard) {
|
if (!selectedCard.value) {
|
||||||
this.error = 'No credit card found for this customer'
|
error.value = 'No credit card found for this customer'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
loading.value = true
|
||||||
this.action = actionType
|
action.value = actionType
|
||||||
this.error = ''
|
error.value = ''
|
||||||
this.success = ''
|
success.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 2: If payment method is credit, perform the pre-authorization
|
// Step 2: If payment method is credit, perform the pre-authorization
|
||||||
if (actionType === 'preauthorize') {
|
if (actionType === 'preauthorize') {
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Pre-authorization amount must be greater than zero.");
|
throw new Error("Pre-authorization amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const authPayload = {
|
const authPayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
preauthorize_amount: this.chargeAmount.toFixed(2),
|
preauthorize_amount: chargeAmount.value.toFixed(2),
|
||||||
delivery_id: this.delivery.id,
|
delivery_id: delivery.value.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${this.customer.id}`;
|
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Update payment type to 11 after successful preauthorization
|
// Update payment type to 11 after successful preauthorization
|
||||||
try {
|
try {
|
||||||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${this.delivery.id}`, {}, { headers: authHeader() });
|
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${delivery.value.id}`, {}, { headers: authHeader() });
|
||||||
} catch (updateError) {
|
} catch (updateError) {
|
||||||
console.error('Failed to update payment type after preauthorization:', updateError);
|
console.error('Failed to update payment type after preauthorization:', updateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On successful authorization, show success and redirect
|
// On successful authorization, show success and redirect
|
||||||
this.success = `Preauthorization successful! Transaction ID: ${response.data?.auth_net_transaction_id || 'N/A'}`;
|
success.value = `Preauthorization successful! Transaction ID: ${response.data?.auth_net_transaction_id || 'N/A'}`;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else { // Handle 'charge' action
|
} else { // Handle 'charge' action
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Charge amount must be greater than zero.");
|
throw new Error("Charge amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a payload that matches the backend's TransactionCreateByCardID schema
|
// Create a payload that matches the backend's TransactionCreateByCardID schema
|
||||||
const chargePayload = {
|
const chargePayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
charge_amount: this.chargeAmount.toFixed(2),
|
charge_amount: chargeAmount.value.toFixed(2),
|
||||||
delivery_id: this.delivery.id,
|
delivery_id: delivery.value.id,
|
||||||
service_id: this.delivery.service_id || null,
|
service_id: delivery.value.service_id || null,
|
||||||
// You can add other fields here if your schema requires them
|
// You can add other fields here if your schema requires them
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the correct endpoint for charging a saved card
|
// Use the correct endpoint for charging a saved card
|
||||||
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${this.customer.id}`;
|
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
console.log('=== DEBUG: Charge payload ===');
|
console.log('=== DEBUG: Charge payload ===');
|
||||||
console.log('Calling endpoint:', chargePath);
|
console.log('Calling endpoint:', chargePath);
|
||||||
@@ -636,37 +639,36 @@ export default defineComponent({
|
|||||||
|
|
||||||
// Update payment type to 11 after successful charge
|
// Update payment type to 11 after successful charge
|
||||||
try {
|
try {
|
||||||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${this.delivery.id}`, {}, { headers: authHeader() });
|
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/${delivery.value.id}`, {}, { headers: authHeader() });
|
||||||
} catch (updateError) {
|
} catch (updateError) {
|
||||||
console.error('Failed to update payment type after charge:', updateError);
|
console.error('Failed to update payment type after charge:', updateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
||||||
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
||||||
this.success = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`;
|
success.value = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
// The error message from your backend will be more specific now
|
// The error message from your backend will be more specific now
|
||||||
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
console.log(error)
|
const axiosErr = err as AxiosError<{ detail?: string }>;
|
||||||
this.error = error.response?.data?.detail || `Failed to ${actionType} payment`
|
console.log(err)
|
||||||
|
error.value = axiosErr.response?.data?.detail || `Failed to ${actionType} payment`
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: this.error,
|
text: error.value,
|
||||||
type: "error",
|
type: "error",
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
loading.value = false
|
||||||
this.action = ''
|
action.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -278,37 +278,44 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import type {
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
PromoData,
|
||||||
|
DeliveryOrderResponse,
|
||||||
|
DeliveryTotalResponse,
|
||||||
|
OilPricingResponse,
|
||||||
|
PromoResponse,
|
||||||
|
PaymentCardResponse,
|
||||||
|
AuthorizeNetTransactionResponse
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'captureAuthorize',
|
const router = useRouter()
|
||||||
|
|
||||||
components: {
|
// Reactive data
|
||||||
Header,
|
const loading = ref(false)
|
||||||
SideBar,
|
const userCardfound = ref(false)
|
||||||
Footer,
|
const gallonsDelivered = ref('')
|
||||||
},
|
const captureAmount = ref(0)
|
||||||
|
const preAuthAmount = ref(0)
|
||||||
|
const transaction = ref<AuthorizeNetTransactionResponse | null>(null)
|
||||||
|
const showPaymentModal = ref(false)
|
||||||
|
const modalStep = ref(0)
|
||||||
|
const modalCapturedAmount = ref(0)
|
||||||
|
|
||||||
data() {
|
const userCard = ref<CreditCardFormData>({
|
||||||
return {
|
id: 0,
|
||||||
loading: false,
|
|
||||||
userCardfound: false,
|
|
||||||
gallonsDelivered: '',
|
|
||||||
captureAmount: 0,
|
|
||||||
preAuthAmount: 0,
|
|
||||||
transaction: null as any,
|
|
||||||
showPaymentModal: false,
|
|
||||||
modalStep: 0,
|
|
||||||
modalCapturedAmount: 0,
|
|
||||||
|
|
||||||
userCard: {
|
|
||||||
date_added: '',
|
date_added: '',
|
||||||
user_id: '',
|
user_id: '',
|
||||||
card_number: '',
|
card_number: '',
|
||||||
@@ -319,9 +326,9 @@ export default defineComponent({
|
|||||||
type_of_card: '',
|
type_of_card: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
accepted_or_declined: '',
|
accepted_or_declined: '',
|
||||||
main_card: '',
|
main_card: false,
|
||||||
},
|
})
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
@@ -333,8 +340,9 @@ export default defineComponent({
|
|||||||
customer_apt: '',
|
customer_apt: '',
|
||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
},
|
account_number: '',
|
||||||
deliveryOrder: {
|
})
|
||||||
|
const deliveryOrder = ref({
|
||||||
id: '',
|
id: '',
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_name: '',
|
customer_name: '',
|
||||||
@@ -366,8 +374,8 @@ export default defineComponent({
|
|||||||
driver_first_name: '',
|
driver_first_name: '',
|
||||||
driver_last_name: '',
|
driver_last_name: '',
|
||||||
total_price: 0,
|
total_price: 0,
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref<PricingData>({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -375,42 +383,41 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
promo_active: false,
|
const promo_active = ref(false)
|
||||||
promo: {
|
const promo = ref<PromoData>({
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: 0,
|
money_off_delivery: 0,
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
this.getOilOrder(this.$route.params.id)
|
onMounted(() => {
|
||||||
this.getOilPricing()
|
getOilOrder(route.params.id)
|
||||||
this.getTransaction()
|
getOilPricing()
|
||||||
},
|
getTransaction()
|
||||||
|
})
|
||||||
|
|
||||||
methods: {
|
// Functions
|
||||||
sumdelivery(delivery_id: any) {
|
const sumdelivery = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<DeliveryTotalResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.total_amount = parseFloat(response.data.total_amount) || 0;
|
total_amount.value = parseFloat(String(response.data.total_amount)) || 0;
|
||||||
this.discount = parseFloat(response.data.discount) || 0;
|
discount.value = parseFloat(String(response.data.discount)) || 0;
|
||||||
this.total_amount_after_discount = parseFloat(response.data.total_amount_after_discount) || 0;
|
total_amount_after_discount.value = parseFloat(String(response.data.total_amount_after_discount)) || 0;
|
||||||
|
|
||||||
// Set capture amount to the calculated total including fees and discount
|
// Set capture amount to the calculated total including fees and discount
|
||||||
this.captureAmount = this.calculateTotalAsNumber();
|
captureAmount.value = calculateTotalAsNumber();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -420,9 +427,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPromo(promo_id: any) {
|
const getPromo = (promo_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -430,114 +437,114 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<PromoResponse>) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.promo = response.data
|
promo.value = response.data
|
||||||
this.promo_active = true
|
promo_active.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilOrder(delivery_id: any) {
|
const getOilOrder = (delivery_id: number | string) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/delivery/order/${delivery_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/delivery/order/${delivery_id}`;
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get<DeliveryOrderResponse>(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
this.deliveryOrder = response.data.delivery;
|
deliveryOrder.value = response.data.delivery as typeof deliveryOrder.value;
|
||||||
this.gallonsDelivered = this.deliveryOrder.gallons_delivered;
|
gallonsDelivered.value = deliveryOrder.value.gallons_delivered;
|
||||||
this.getCustomer(this.deliveryOrder.customer_id);
|
getCustomer(deliveryOrder.value.customer_id);
|
||||||
this.sumdelivery(delivery_id);
|
sumdelivery(delivery_id);
|
||||||
|
|
||||||
if ([1, 2, 3].includes(this.deliveryOrder.payment_type)) {
|
if ([1, 2, 3].includes(deliveryOrder.value.payment_type)) {
|
||||||
this.getPaymentCard(this.deliveryOrder.payment_card_id);
|
getPaymentCard(deliveryOrder.value.payment_card_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.deliveryOrder.promo_id != null) {
|
if (deliveryOrder.value.promo_id != null) {
|
||||||
this.getPromo(this.deliveryOrder.promo_id);
|
getPromo(deliveryOrder.value.promo_id);
|
||||||
this.promo_active = true;
|
promo_active.value = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => console.error("Error fetching oil order:", error));
|
.catch((error: Error) => console.error("Error fetching oil order:", error));
|
||||||
},
|
}
|
||||||
|
|
||||||
getPaymentCard(card_id: any) {
|
const getPaymentCard = (card_id: number | string) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/card/${card_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/card/${card_id}`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get<PaymentCardResponse>(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
if (response.data.userCard && response.data.userCard.card_number !== '') {
|
if (response.data.userCard && response.data.userCard.card_number !== '') {
|
||||||
this.userCard = response.data;
|
userCard.value = response.data.userCard as CreditCardFormData;
|
||||||
this.userCardfound = true;
|
userCardfound.value = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
this.userCardfound = false;
|
userCardfound.value = false;
|
||||||
console.error("Error fetching payment card:", error);
|
console.error("Error fetching payment card:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomer(user_id: any) {
|
const getCustomer = (user_id: number) => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${user_id}`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get<CustomerFormData>(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
notify({ title: "Error", text: "Could not find customer", type: "error" });
|
notify({ title: "Error", text: "Could not find customer", type: "error" });
|
||||||
console.error("Error fetching customer:", error);
|
console.error("Error fetching customer:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getOilPricing() {
|
const getOilPricing = () => {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/info/price/oil/table`;
|
const path = `${import.meta.env.VITE_BASE_URL}/info/price/oil/table`;
|
||||||
axios.get(path, { withCredentials: true })
|
axios.get<OilPricingResponse>(path, { withCredentials: true })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
this.pricing = response.data;
|
pricing.value = response.data;
|
||||||
// Calculate capture amount if delivery order is already loaded
|
// Calculate capture amount if delivery order is already loaded
|
||||||
this.calculateCaptureAmount();
|
calculateCaptureAmount();
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
notify({ title: "Error", text: "Could not get oil pricing", type: "error" });
|
notify({ title: "Error", text: "Could not get oil pricing", type: "error" });
|
||||||
console.error("Error fetching oil pricing:", error);
|
console.error("Error fetching oil pricing:", error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getTransaction() {
|
const getTransaction = () => {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/delivery/${this.$route.params.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/api/transaction/delivery/${route.params.id}`;
|
||||||
axios.get(path, { withCredentials: true, headers: authHeader() })
|
axios.get<AuthorizeNetTransactionResponse>(path, { withCredentials: true, headers: authHeader() })
|
||||||
.then((response: any) => {
|
.then((response) => {
|
||||||
this.transaction = response.data;
|
transaction.value = response.data;
|
||||||
this.preAuthAmount = parseFloat(response.data.preauthorize_amount || 0);
|
preAuthAmount.value = parseFloat(String(response.data.preauthorize_amount) || '0');
|
||||||
if (response.data.status !== 0) { // Not approved
|
if (response.data.status !== 0) { // Not approved
|
||||||
this.preAuthAmount = 0;
|
preAuthAmount.value = 0;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: AxiosError) => {
|
||||||
if (error.response && error.response.status === 404) {
|
if (error.response && error.response.status === 404) {
|
||||||
notify({ title: "Info", text: "No pre-authorization found. Redirecting to customer profile to update payment method.", type: "info" });
|
notify({ title: "Info", text: "No pre-authorization found. Redirecting to customer profile to update payment method.", type: "info" });
|
||||||
console.log("No transaction found for delivery - redirecting to customer profile");
|
console.log("No transaction found for delivery - redirecting to customer profile");
|
||||||
this.$router.push({ name: 'customerProfile', params: { id: this.customer.id } });
|
router.push({ name: 'customerProfile', params: { id: customer.value.id } });
|
||||||
} else {
|
} else {
|
||||||
notify({ title: "Error", text: "No pre-authorized transaction found", type: "error" });
|
notify({ title: "Error", text: "No pre-authorized transaction found", type: "error" });
|
||||||
this.$router.push({ name: 'finalizeTicket', params: { id: this.$route.params.id } });
|
router.push({ name: 'finalizeTicket', params: { id: route.params.id } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
async capturePayment() {
|
const capturePayment = async () => {
|
||||||
if (!this.transaction || !this.captureAmount) {
|
if (!transaction.value || !captureAmount.value) {
|
||||||
notify({ title: "Error", text: "Invalid capture amount or transaction data", type: "error" });
|
notify({ title: "Error", text: "Invalid capture amount or transaction data", type: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
charge_amount: this.captureAmount, // FastAPI handles string/number conversion
|
charge_amount: captureAmount.value, // FastAPI handles string/number conversion
|
||||||
auth_net_transaction_id: this.transaction.auth_net_transaction_id
|
auth_net_transaction_id: transaction.value.auth_net_transaction_id
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ FIX: Cleaned up URL, removing the unnecessary customer_id query parameter.
|
// ✅ FIX: Cleaned up URL, removing the unnecessary customer_id query parameter.
|
||||||
@@ -552,10 +559,10 @@ export default defineComponent({
|
|||||||
// ✅ FIX: Improved logic to handle both success and declines properly.
|
// ✅ FIX: Improved logic to handle both success and declines properly.
|
||||||
if (response.data && response.data.status === 0) {
|
if (response.data && response.data.status === 0) {
|
||||||
// This is the APPROVED case
|
// This is the APPROVED case
|
||||||
this.modalCapturedAmount = this.captureAmount;
|
modalCapturedAmount.value = captureAmount.value;
|
||||||
this.showPaymentModal = true;
|
showPaymentModal.value = true;
|
||||||
setTimeout(() => { this.modalStep = 1 }, 2000);
|
setTimeout(() => { modalStep.value = 1 }, 2000);
|
||||||
setTimeout(() => { this.showPaymentModal = false; this.$router.push({ name: 'deliveryOrder', params: { id: this.$route.params.id } }) }, 4000);
|
setTimeout(() => { showPaymentModal.value = false; router.push({ name: 'deliveryOrder', params: { id: route.params.id } }) }, 4000);
|
||||||
|
|
||||||
} else if (response.data && response.data.status === 1) {
|
} else if (response.data && response.data.status === 1) {
|
||||||
// This is the DECLINED case
|
// This is the DECLINED case
|
||||||
@@ -571,8 +578,9 @@ export default defineComponent({
|
|||||||
throw new Error("Invalid response from server during capture.");
|
throw new Error("Invalid response from server during capture.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
// This 'catch' block now only handles network errors or server crashes (500 errors).
|
// This 'catch' block now only handles network errors or server crashes (500 errors).
|
||||||
|
const error = err as AxiosError<{ detail?: string }>;
|
||||||
const detail = error.response?.data?.detail || "Failed to capture payment due to a server error.";
|
const detail = error.response?.data?.detail || "Failed to capture payment due to a server error.";
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -581,90 +589,90 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
console.error("Capture Payment Error:", error);
|
console.error("Capture Payment Error:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateSubtotal() {
|
const calculateSubtotal = () => {
|
||||||
const gallons = parseFloat(this.gallonsDelivered || '0') || 0;
|
const gallons = parseFloat(gallonsDelivered.value || '0') || 0;
|
||||||
const pricePerGallon = typeof this.deliveryOrder.customer_price === 'string' ? parseFloat(this.deliveryOrder.customer_price) : Number(this.deliveryOrder.customer_price) || 0;
|
const pricePerGallon = typeof deliveryOrder.value.customer_price === 'string' ? parseFloat(deliveryOrder.value.customer_price) : Number(deliveryOrder.value.customer_price) || 0;
|
||||||
return (gallons * pricePerGallon).toFixed(2);
|
return (gallons * pricePerGallon).toFixed(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAmount() {
|
const calculateTotalAmount = () => {
|
||||||
if (this.total_amount_after_discount == null || this.total_amount_after_discount === undefined) {
|
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalNum = Number(this.total_amount_after_discount);
|
let totalNum = Number(total_amount_after_discount.value);
|
||||||
if (isNaN(totalNum)) {
|
if (isNaN(totalNum)) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.deliveryOrder && this.deliveryOrder.prime == 1 && this.pricing && this.pricing.price_prime) {
|
if (deliveryOrder.value && deliveryOrder.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||||||
totalNum += Number(this.pricing.price_prime) || 0;
|
totalNum += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder && this.deliveryOrder.same_day == 1 && this.pricing && this.pricing.price_same_day) {
|
if (deliveryOrder.value && deliveryOrder.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||||||
totalNum += Number(this.pricing.price_same_day) || 0;
|
totalNum += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder && this.deliveryOrder.emergency == 1 && this.pricing && this.pricing.price_emergency) {
|
if (deliveryOrder.value && deliveryOrder.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||||||
totalNum += Number(this.pricing.price_emergency) || 0;
|
totalNum += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalNum.toFixed(2);
|
return totalNum.toFixed(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateTotalAsNumber() {
|
const calculateTotalAsNumber = () => {
|
||||||
if (this.total_amount_after_discount == null || this.total_amount_after_discount === undefined) {
|
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalNum = Number(this.total_amount_after_discount);
|
let totalNum = Number(total_amount_after_discount.value);
|
||||||
if (isNaN(totalNum)) {
|
if (isNaN(totalNum)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.deliveryOrder && this.deliveryOrder.prime == 1 && this.pricing && this.pricing.price_prime) {
|
if (deliveryOrder.value && deliveryOrder.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||||||
totalNum += Number(this.pricing.price_prime) || 0;
|
totalNum += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder && this.deliveryOrder.same_day == 1 && this.pricing && this.pricing.price_same_day) {
|
if (deliveryOrder.value && deliveryOrder.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||||||
totalNum += Number(this.pricing.price_same_day) || 0;
|
totalNum += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.deliveryOrder && this.deliveryOrder.emergency == 1 && this.pricing && this.pricing.price_emergency) {
|
if (deliveryOrder.value && deliveryOrder.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||||||
totalNum += Number(this.pricing.price_emergency) || 0;
|
totalNum += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalNum;
|
return totalNum;
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateCaptureAmount() {
|
const calculateCaptureAmount = () => {
|
||||||
// Only calculate if we have both delivery order and pricing data
|
// Only calculate if we have both delivery order and pricing data
|
||||||
if (this.deliveryOrder.id && this.pricing.price_for_customer) {
|
if (deliveryOrder.value.id && pricing.value.price_for_customer) {
|
||||||
const gallons = typeof this.gallonsDelivered === 'string' ? parseFloat(this.gallonsDelivered) : Number(this.gallonsDelivered) || 0;
|
const gallons = typeof gallonsDelivered.value === 'string' ? parseFloat(gallonsDelivered.value) : Number(gallonsDelivered.value) || 0;
|
||||||
const pricePerGallon = typeof this.deliveryOrder.customer_price === 'string' ? parseFloat(this.deliveryOrder.customer_price) : Number(this.deliveryOrder.customer_price) || 0;
|
const pricePerGallon = typeof deliveryOrder.value.customer_price === 'string' ? parseFloat(deliveryOrder.value.customer_price) : Number(deliveryOrder.value.customer_price) || 0;
|
||||||
let total = gallons * pricePerGallon;
|
let total = gallons * pricePerGallon;
|
||||||
|
|
||||||
// Add prime fee if applicable
|
// Add prime fee if applicable
|
||||||
if (this.deliveryOrder.prime == 1) {
|
if (deliveryOrder.value.prime == 1) {
|
||||||
const primeFee = typeof this.pricing.price_prime === 'string' ? parseFloat(this.pricing.price_prime) : Number(this.pricing.price_prime) || 0;
|
const primeFee = typeof pricing.value.price_prime === 'string' ? parseFloat(pricing.value.price_prime) : Number(pricing.value.price_prime) || 0;
|
||||||
total += primeFee;
|
total += primeFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add same day fee if applicable
|
// Add same day fee if applicable
|
||||||
if (this.deliveryOrder.same_day === 1) {
|
if (deliveryOrder.value.same_day === 1) {
|
||||||
const sameDayFee = typeof this.pricing.price_same_day === 'string' ? parseFloat(this.pricing.price_same_day) : Number(this.pricing.price_same_day) || 0;
|
const sameDayFee = typeof pricing.value.price_same_day === 'string' ? parseFloat(pricing.value.price_same_day) : Number(pricing.value.price_same_day) || 0;
|
||||||
total += sameDayFee;
|
total += sameDayFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.captureAmount = total;
|
captureAmount.value = total;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
cancelCapture() {
|
const cancelCapture = () => {
|
||||||
this.$router.push({ name: 'finalizeTicket', params: { id: this.$route.params.id } });
|
router.push({ name: 'finalizeTicket', params: { id: route.params.id } });
|
||||||
},
|
}
|
||||||
|
|
||||||
getTypeColor(transactionType: number) {
|
const getTypeColor = (transactionType: number) => {
|
||||||
switch (transactionType) {
|
switch (transactionType) {
|
||||||
case 1: return 'text-blue-600'; // Auth
|
case 1: return 'text-blue-600'; // Auth
|
||||||
case 0: return 'text-orange-600'; // Charge
|
case 0: return 'text-orange-600'; // Charge
|
||||||
@@ -672,9 +680,7 @@ export default defineComponent({
|
|||||||
case 3: return 'text-green-600'; // Delivery/Other
|
case 3: return 'text-green-600'; // Delivery/Other
|
||||||
default: return 'text-gray-600';
|
default: return 'text-gray-600';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -335,35 +335,44 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
|
import type {
|
||||||
|
DeliveryFormData,
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
PricingData,
|
||||||
|
PromoData,
|
||||||
|
DeliveryOrderResponse,
|
||||||
|
DeliveryTotalResponse,
|
||||||
|
OilPricingResponse,
|
||||||
|
PromoResponse,
|
||||||
|
WhoAmIResponse,
|
||||||
|
AuthorizeCheckResponse,
|
||||||
|
CreateAuthorizeAccountResponse,
|
||||||
|
CardsOnFileResponse,
|
||||||
|
UpdateStatusResponse
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
import useValidate from "@vuelidate/core";
|
import { useVuelidate } from "@vuelidate/core";
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
import { minLength, required } from "@vuelidate/validators";
|
import { minLength, required } from "@vuelidate/validators";
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'PayOil',
|
const router = useRouter()
|
||||||
|
|
||||||
components: {
|
// Reactive data
|
||||||
Header,
|
const loaded = ref(false)
|
||||||
SideBar,
|
const user = ref({
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
loaded: false,
|
|
||||||
user: {
|
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
},
|
})
|
||||||
delivery: {
|
const delivery = ref<DeliveryFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_name: '',
|
customer_name: '',
|
||||||
@@ -396,9 +405,8 @@ export default defineComponent({
|
|||||||
driver_last_name: '',
|
driver_last_name: '',
|
||||||
pre_charge_amount: 0,
|
pre_charge_amount: 0,
|
||||||
total_price: 0,
|
total_price: 0,
|
||||||
|
})
|
||||||
},
|
const credit_cards = ref<CreditCardFormData[]>([
|
||||||
credit_cards: [
|
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name_on_card: '',
|
name_on_card: '',
|
||||||
@@ -409,12 +417,11 @@ export default defineComponent({
|
|||||||
last_four_digits: '',
|
last_four_digits: '',
|
||||||
expiration_year: '',
|
expiration_year: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
])
|
||||||
|
|
||||||
stripe: null,
|
const stripe = ref(null)
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -428,8 +435,8 @@ export default defineComponent({
|
|||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
auth_net_profile_id: null,
|
auth_net_profile_id: null,
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref<PricingData>({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -437,87 +444,78 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
promo_active: false,
|
const promo_active = ref(false)
|
||||||
promo: {
|
const promo = ref<PromoData>({
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: 0,
|
money_off_delivery: 0,
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
priceprime: 0,
|
const priceprime = ref(0)
|
||||||
pricesameday: 0,
|
const pricesameday = ref(0)
|
||||||
priceemergency: 0,
|
const priceemergency = ref(0)
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
credit_cards_count: 0,
|
const credit_cards_count = ref(0)
|
||||||
isLoadingAuthorize: true,
|
const isLoadingAuthorize = ref(true)
|
||||||
authorizeCheck: { profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false },
|
const authorizeCheck = ref({ profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false })
|
||||||
isDeleteAccountModalVisible: false,
|
const isDeleteAccountModalVisible = ref(false)
|
||||||
isCreateAccountModalVisible: false,
|
const isCreateAccountModalVisible = ref(false)
|
||||||
isCreatingAccount: false,
|
const isCreatingAccount = ref(false)
|
||||||
createdProfileId: '',
|
const createdProfileId = ref('')
|
||||||
isDuplicateErrorModalVisible: false,
|
const isDuplicateErrorModalVisible = ref(false)
|
||||||
}
|
|
||||||
},
|
// Validations
|
||||||
validations() {
|
const validations = {
|
||||||
return {
|
|
||||||
CreateOilOrderForm: {
|
CreateOilOrderForm: {
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
gallons_ordered: { required, minLength: minLength(1) },
|
gallons_ordered: { required, minLength: minLength(1) },
|
||||||
expected_delivery_date: { required },
|
expected_delivery_date: { required },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus()
|
|
||||||
|
|
||||||
},
|
const v$ = useVuelidate(validations)
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
|
|
||||||
},
|
// Lifecycle
|
||||||
},
|
onMounted(() => {
|
||||||
mounted() {
|
userStatus()
|
||||||
this.getOilOrder(this.$route.params.id)
|
getOilOrder(route.params.id)
|
||||||
this.sumdelivery(this.$route.params.id);
|
sumdelivery(route.params.id);
|
||||||
this.getOilPricing()
|
getOilPricing()
|
||||||
this.updatestatus();
|
updatestatus();
|
||||||
|
})
|
||||||
|
|
||||||
},
|
// Functions
|
||||||
|
const updatestatus = () => {
|
||||||
methods: {
|
|
||||||
updatestatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
let path = import.meta.env.VITE_BASE_URL + '/delivery/updatestatus';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<UpdateStatusResponse>) => {
|
||||||
if (response.data.update)
|
if (response.data.update)
|
||||||
console.log("Updated Status of Deliveries")
|
console.log("Updated Status of Deliveries")
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
sumdelivery(delivery_id: any) {
|
|
||||||
|
const sumdelivery = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/total/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<DeliveryTotalResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
|
priceprime.value = response.data.priceprime;
|
||||||
|
pricesameday.value = response.data.pricesameday;
|
||||||
this.priceprime = response.data.priceprime;
|
priceemergency.value = response.data.priceemergency;
|
||||||
this.pricesameday = response.data.pricesameday;
|
total_amount.value = response.data.total_amount;
|
||||||
this.priceemergency = response.data.priceemergency;
|
discount.value = response.data.discount;
|
||||||
this.total_amount = response.data.total_amount;
|
total_amount_after_discount.value = response.data.total_amount_after_discount;
|
||||||
this.discount = response.data.discount;
|
|
||||||
this.total_amount_after_discount = response.data.total_amount_after_discount;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -527,8 +525,9 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getPromo(promo_id: any) {
|
|
||||||
|
const getPromo = (promo_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
let path = import.meta.env.VITE_BASE_URL + "/promo/" + promo_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -536,14 +535,14 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<PromoResponse>) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.promo = response.data
|
promo.value = response.data
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
userStatus() {
|
|
||||||
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -551,22 +550,22 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
},
|
const getOilPricing = () => {
|
||||||
getOilPricing() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/table";
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<OilPricingResponse>) => {
|
||||||
this.pricing = response.data;
|
pricing.value = response.data;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
notify({
|
notify({
|
||||||
@@ -575,34 +574,35 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getOilOrder(delivery_id: any) {
|
|
||||||
|
const getOilOrder = (delivery_id: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/order/" + delivery_id;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/order/" + delivery_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: path,
|
url: path,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<DeliveryOrderResponse>) => {
|
||||||
console.log("=== DEBUG: API Response:", response.data);
|
console.log("=== DEBUG: API Response:", response.data);
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
console.log("=== DEBUG: Delivery data from API:", response.data.delivery);
|
console.log("=== DEBUG: Delivery data from API:", response.data.delivery);
|
||||||
console.log("=== DEBUG: Delivery ID from API:", response.data.delivery?.id);
|
console.log("=== DEBUG: Delivery ID from API:", response.data.delivery?.id);
|
||||||
this.delivery = response.data.delivery;
|
delivery.value = response.data.delivery as DeliveryFormData;
|
||||||
console.log("=== DEBUG: Delivery object after assignment:", this.delivery);
|
console.log("=== DEBUG: Delivery object after assignment:", delivery.value);
|
||||||
console.log("=== DEBUG: Delivery ID after assignment:", this.delivery.id);
|
console.log("=== DEBUG: Delivery ID after assignment:", delivery.value.id);
|
||||||
this.getCustomer(this.delivery.customer_id)
|
getCustomer(delivery.value.customer_id)
|
||||||
this.getCreditCards(this.delivery.customer_id)
|
getCreditCards(delivery.value.customer_id)
|
||||||
this.getCreditCardsCount(this.delivery.customer_id)
|
getCreditCardsCount(delivery.value.customer_id)
|
||||||
if (this.delivery.promo_id != null) {
|
if (delivery.value.promo_id != null) {
|
||||||
this.getPromo(this.delivery.promo_id);
|
getPromo(delivery.value.promo_id);
|
||||||
this.promo_active = true;
|
promo_active.value = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
console.error("API Error:", response.data.error || "Failed to fetch delivery data.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
console.error("=== DEBUG: API Error in getOilOrder:", error);
|
console.error("=== DEBUG: API Error in getOilOrder:", error);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -610,69 +610,72 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCreditCards(user_id: any) {
|
|
||||||
|
const getCreditCards = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CreditCardFormData[]>) => {
|
||||||
|
credit_cards.value = response.data
|
||||||
this.credit_cards = response.data
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCreditCardsCount(user_id: any) {
|
|
||||||
|
const getCreditCardsCount = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/onfile/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/onfile/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CardsOnFileResponse>) => {
|
||||||
this.credit_cards_count = response.data.cards
|
credit_cards_count.value = response.data.cards
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCustomer(userid: any) {
|
|
||||||
|
const getCustomer = (userid: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CustomerFormData>) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
this.checkAuthorizeAccount();
|
checkAuthorizeAccount();
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
calculateTotalAmount() {
|
|
||||||
if (this.total_amount_after_discount == null || this.total_amount_after_discount === undefined) {
|
const calculateTotalAmount = () => {
|
||||||
|
if (total_amount_after_discount.value == null || total_amount_after_discount.value === undefined) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalNum = Number(this.total_amount_after_discount);
|
let totalNum = Number(total_amount_after_discount.value);
|
||||||
if (isNaN(totalNum)) {
|
if (isNaN(totalNum)) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.delivery && this.delivery.prime == 1 && this.pricing && this.pricing.price_prime) {
|
if (delivery.value && delivery.value.prime == 1 && pricing.value && pricing.value.price_prime) {
|
||||||
totalNum += Number(this.pricing.price_prime) || 0;
|
totalNum += Number(pricing.value.price_prime) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.same_day == 1 && this.pricing && this.pricing.price_same_day) {
|
if (delivery.value && delivery.value.same_day == 1 && pricing.value && pricing.value.price_same_day) {
|
||||||
totalNum += Number(this.pricing.price_same_day) || 0;
|
totalNum += Number(pricing.value.price_same_day) || 0;
|
||||||
}
|
}
|
||||||
if (this.delivery && this.delivery.emergency == 1 && this.pricing && this.pricing.price_emergency) {
|
if (delivery.value && delivery.value.emergency == 1 && pricing.value && pricing.value.price_emergency) {
|
||||||
totalNum += Number(this.pricing.price_emergency) || 0;
|
totalNum += Number(pricing.value.price_emergency) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalNum.toFixed(2);
|
return totalNum.toFixed(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
checkoutOilUpdatePayment(payment_type: number) {
|
const checkoutOilUpdatePayment = (payment_type: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/delivery/cash/" + this.delivery.id + '/' + payment_type;
|
let path = import.meta.env.VITE_BASE_URL + "/delivery/cash/" + delivery.value.id + '/' + payment_type;
|
||||||
axios({
|
axios({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
url: path,
|
url: path,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<{ ok: boolean }>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
if (payment_type == 0) {
|
if (payment_type == 0) {
|
||||||
notify({
|
notify({
|
||||||
@@ -710,7 +713,7 @@ export default defineComponent({
|
|||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.$router.push({ name: "deliveryOrder", params: { id: this.delivery.id } });
|
router.push({ name: "deliveryOrder", params: { id: delivery.value.id } });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -720,67 +723,69 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
async checkAuthorizeAccount() {
|
|
||||||
if (!this.customer.id) return;
|
|
||||||
|
|
||||||
this.isLoadingAuthorize = true;
|
const checkAuthorizeAccount = async () => {
|
||||||
|
if (!customer.value.id) return;
|
||||||
|
|
||||||
|
isLoadingAuthorize.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${customer.value.id}`;
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
const response = await axios.get(path, { headers: authHeader() });
|
||||||
this.authorizeCheck = response.data;
|
authorizeCheck.value = response.data;
|
||||||
|
|
||||||
// Check if the API returned an error in the response body
|
// Check if the API returned an error in the response body
|
||||||
if (this.authorizeCheck.missing_components && this.authorizeCheck.missing_components.includes('api_error')) {
|
if (authorizeCheck.value.missing_components && authorizeCheck.value.missing_components.includes('api_error')) {
|
||||||
console.log("API error detected in response, calling cleanup for customer:", this.customer.id);
|
console.log("API error detected in response, calling cleanup for customer:", customer.value.id);
|
||||||
this.cleanupAuthorizeData();
|
cleanupAuthorizeData();
|
||||||
return; // Don't set loading to false yet, let cleanup handle it
|
return; // Don't set loading to false yet, let cleanup handle it
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check authorize account:", error);
|
console.error("Failed to check authorize account:", error);
|
||||||
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
||||||
// Set default error state
|
// Set default error state
|
||||||
this.authorizeCheck = {
|
authorizeCheck.value = {
|
||||||
profile_exists: false,
|
profile_exists: false,
|
||||||
has_payment_methods: false,
|
has_payment_methods: false,
|
||||||
missing_components: ['api_error'],
|
missing_components: ['api_error'],
|
||||||
valid_for_charging: false
|
valid_for_charging: false
|
||||||
};
|
};
|
||||||
// Automatically cleanup the local Authorize.Net data on API error
|
// Automatically cleanup the local Authorize.Net data on API error
|
||||||
console.log("Calling cleanupAuthorizedData for customer:", this.customer.id);
|
console.log("Calling cleanupAuthorizedData for customer:", customer.value.id);
|
||||||
this.cleanupAuthorizeData();
|
cleanupAuthorizeData();
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingAuthorize = false;
|
isLoadingAuthorize.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async createAuthorizeAccount() {
|
|
||||||
|
const createAuthorizeAccount = async () => {
|
||||||
// Show the creating account modal
|
// Show the creating account modal
|
||||||
this.isCreatingAccount = true;
|
isCreatingAccount.value = true;
|
||||||
this.isCreateAccountModalVisible = true;
|
isCreateAccountModalVisible.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/create-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/create-account/${customer.value.id}`;
|
||||||
const response = await axios.post(path, {}, { headers: authHeader() });
|
const response = await axios.post(path, {}, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
// Update local state
|
// Update local state
|
||||||
this.customer.auth_net_profile_id = response.data.profile_id;
|
customer.value.auth_net_profile_id = response.data.profile_id;
|
||||||
this.authorizeCheck.valid_for_charging = true;
|
authorizeCheck.value.valid_for_charging = true;
|
||||||
// this.authorizeCheck.profile_exists = true;
|
// authorizeCheck.value.profile_exists = true;
|
||||||
this.authorizeCheck.has_payment_methods = true;
|
authorizeCheck.value.has_payment_methods = true;
|
||||||
this.authorizeCheck.missing_components = [];
|
authorizeCheck.value.missing_components = [];
|
||||||
this.createdProfileId = response.data.profile_id;
|
createdProfileId.value = response.data.profile_id;
|
||||||
|
|
||||||
// Refresh credit cards to get updated payment profile IDs
|
// Refresh credit cards to get updated payment profile IDs
|
||||||
await this.getCreditCards(this.customer.id);
|
await getCreditCards(customer.value.id);
|
||||||
|
|
||||||
// Switch modal to success view and close after delay
|
// Switch modal to success view and close after delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.isCreatingAccount = false;
|
isCreatingAccount.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
this.createdProfileId = '';
|
createdProfileId.value = '';
|
||||||
|
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
@@ -792,7 +797,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Hide modal on error
|
// Hide modal on error
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
|
|
||||||
// Check for E00039 duplicate error
|
// Check for E00039 duplicate error
|
||||||
const errorMessage = response.data.message || response.data.error_detail || "Failed to create Authorize.net account";
|
const errorMessage = response.data.message || response.data.error_detail || "Failed to create Authorize.net account";
|
||||||
@@ -800,7 +805,7 @@ export default defineComponent({
|
|||||||
if (response.data.is_duplicate || errorMessage.includes("E00039")) {
|
if (response.data.is_duplicate || errorMessage.includes("E00039")) {
|
||||||
// Show duplicate account popup
|
// Show duplicate account popup
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showDuplicateErrorModal();
|
showDuplicateErrorModal();
|
||||||
}, 300);
|
}, 300);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -812,10 +817,11 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
|
const error = err as { response?: { data?: { error_detail?: string; detail?: string; message?: string; is_duplicate?: boolean } }; message?: string };
|
||||||
console.error("Failed to create account:", error);
|
console.error("Failed to create account:", error);
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
this.isCreatingAccount = false;
|
isCreatingAccount.value = false;
|
||||||
|
|
||||||
// Check for E00039 duplicate error
|
// Check for E00039 duplicate error
|
||||||
const errorMessage = error.response?.data?.error_detail ||
|
const errorMessage = error.response?.data?.error_detail ||
|
||||||
@@ -826,7 +832,7 @@ export default defineComponent({
|
|||||||
if (error.response?.data?.is_duplicate || errorMessage.includes("E00039")) {
|
if (error.response?.data?.is_duplicate || errorMessage.includes("E00039")) {
|
||||||
// Show duplicate account popup
|
// Show duplicate account popup
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showDuplicateErrorModal();
|
showDuplicateErrorModal();
|
||||||
}, 300);
|
}, 300);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -838,36 +844,41 @@ export default defineComponent({
|
|||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
showDeleteAccountModal() {
|
|
||||||
this.isDeleteAccountModalVisible = true;
|
const showDeleteAccountModal = () => {
|
||||||
},
|
isDeleteAccountModalVisible.value = true;
|
||||||
showDuplicateErrorModal() {
|
}
|
||||||
this.isDuplicateErrorModalVisible = true;
|
|
||||||
},
|
const showDuplicateErrorModal = () => {
|
||||||
hideDuplicateErrorModal() {
|
isDuplicateErrorModalVisible.value = true;
|
||||||
this.isDuplicateErrorModalVisible = false;
|
}
|
||||||
},
|
|
||||||
addCreditCard() {
|
const hideDuplicateErrorModal = () => {
|
||||||
|
isDuplicateErrorModalVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addCreditCard = () => {
|
||||||
// Redirect to add card page
|
// Redirect to add card page
|
||||||
this.$router.push({ name: 'cardadd', params: { customerId: this.customer.id } });
|
router.push({ name: 'cardadd', params: { customerId: customer.value.id } });
|
||||||
},
|
}
|
||||||
async deleteAccount() {
|
|
||||||
this.isDeleteAccountModalVisible = false;
|
const deleteAccount = async () => {
|
||||||
|
isDeleteAccountModalVisible.value = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/delete-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/delete-account/${customer.value.id}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader() });
|
const response = await axios.delete(path, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
// Update local state
|
// Update local state
|
||||||
this.customer.auth_net_profile_id = null;
|
customer.value.auth_net_profile_id = null;
|
||||||
this.authorizeCheck.valid_for_charging = false;
|
authorizeCheck.value.valid_for_charging = false;
|
||||||
this.authorizeCheck.profile_exists = false;
|
authorizeCheck.value.profile_exists = false;
|
||||||
this.authorizeCheck.has_payment_methods = false;
|
authorizeCheck.value.has_payment_methods = false;
|
||||||
|
|
||||||
// Refresh credit cards list (IDs should now be null)
|
// Refresh credit cards list (IDs should now be null)
|
||||||
this.getCreditCards(this.customer.id);
|
getCreditCards(customer.value.id);
|
||||||
|
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
@@ -881,7 +892,7 @@ export default defineComponent({
|
|||||||
type: "warning"
|
type: "warning"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error("Failed to delete account:", error);
|
console.error("Failed to delete account:", error);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -889,21 +900,22 @@ export default defineComponent({
|
|||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async cleanupAuthorizeData() {
|
|
||||||
|
const cleanupAuthorizeData = async () => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/authorize/cleanup/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/authorize/cleanup/${customer.value.id}`;
|
||||||
const response = await axios.post(path, {}, { headers: authHeader() });
|
const response = await axios.post(path, {}, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
// Update local state to reflect cleanup
|
// Update local state to reflect cleanup
|
||||||
this.customer.auth_net_profile_id = null;
|
customer.value.auth_net_profile_id = null;
|
||||||
this.authorizeCheck.valid_for_charging = false;
|
authorizeCheck.value.valid_for_charging = false;
|
||||||
this.authorizeCheck.profile_exists = false;
|
authorizeCheck.value.profile_exists = false;
|
||||||
this.authorizeCheck.has_payment_methods = false;
|
authorizeCheck.value.has_payment_methods = false;
|
||||||
|
|
||||||
// Refresh credit cards to reflect null payment profile IDs
|
// Refresh credit cards to reflect null payment profile IDs
|
||||||
this.getCreditCards(this.customer.id);
|
getCreditCards(customer.value.id);
|
||||||
|
|
||||||
console.log("Successfully cleaned up Authorize.Net data:", response.data.message);
|
console.log("Successfully cleaned up Authorize.Net data:", response.data.message);
|
||||||
} else {
|
} else {
|
||||||
@@ -912,13 +924,14 @@ export default defineComponent({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error during cleanup:", error);
|
console.error("Error during cleanup:", error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getAccountStatusMessage(): string {
|
|
||||||
if (!this.authorizeCheck || !this.authorizeCheck.missing_components) {
|
const getAccountStatusMessage = (): string => {
|
||||||
|
if (!authorizeCheck.value || !authorizeCheck.value.missing_components) {
|
||||||
return 'Account setup incomplete';
|
return 'Account setup incomplete';
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = this.authorizeCheck.missing_components;
|
const missing = authorizeCheck.value.missing_components;
|
||||||
if (missing.includes('customer_not_found')) {
|
if (missing.includes('customer_not_found')) {
|
||||||
return 'Customer not found in Authorize.net';
|
return 'Customer not found in Authorize.net';
|
||||||
} else if (missing.includes('authorize_net_profile')) {
|
} else if (missing.includes('authorize_net_profile')) {
|
||||||
@@ -932,10 +945,7 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'Account requires setup';
|
return 'Account requires setup';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
import PayOil from './oil/pay_oil.vue';
|
const PayOil = () => import('./oil/pay_oil.vue');
|
||||||
import AuthorizePreauthCharge from './oil/authorize_preauthcharge.vue';
|
const AuthorizePreauthCharge = () => import('./oil/authorize_preauthcharge.vue');
|
||||||
import CaptureAuthorize from './oil/capture_authorize.vue';
|
const CaptureAuthorize = () => import('./oil/capture_authorize.vue');
|
||||||
import PayService from './service/pay_service.vue';
|
const PayService = () => import('./service/pay_service.vue');
|
||||||
import AuthorizeServicePreauthCharge from './service/authorize_preauthcharge.vue';
|
const AuthorizeServicePreauthCharge = () => import('./service/authorize_preauthcharge.vue');
|
||||||
import ChargeServiceAuthorize from './service/capture_authorize.vue';
|
const ChargeServiceAuthorize = () => import('./service/capture_authorize.vue');
|
||||||
import AuthorizePrechargeAutho from './auto/authorize_precharge_autho.vue';
|
const AuthorizePrechargeAutho = () => import('./auto/authorize_precharge_autho.vue');
|
||||||
import CaptureAuthorizeAutho from './auto/capture_authorize_autho.vue';
|
const CaptureAuthorizeAutho = () => import('./auto/capture_authorize_autho.vue');
|
||||||
|
|
||||||
const payRoutes = [
|
const payRoutes = [
|
||||||
// This is for oil delivery
|
// This is for oil delivery
|
||||||
|
|||||||
@@ -154,29 +154,47 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
|
import type {
|
||||||
|
CustomerFormData,
|
||||||
|
CreditCardFormData,
|
||||||
|
WhoAmIResponse
|
||||||
|
} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
const route = useRoute()
|
||||||
name: 'AuthorizeServicePreauthCharge',
|
const router = useRouter()
|
||||||
|
|
||||||
data() {
|
interface ServiceFormData {
|
||||||
return {
|
id: number;
|
||||||
serviceId: this.$route.params.id as string,
|
scheduled_date: string;
|
||||||
loaded: false,
|
customer_id: number;
|
||||||
chargeAmount: 0,
|
customer_name: string;
|
||||||
loading: false,
|
customer_address: string;
|
||||||
action: '', // 'preauthorize' or 'charge'
|
customer_town: string;
|
||||||
error: '',
|
type_service_call: number;
|
||||||
success: '',
|
description: string;
|
||||||
selectedCardId: null as number | null, // Track which card is selected
|
service_cost: string;
|
||||||
user: {
|
payment_card_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reactive data
|
||||||
|
const serviceId = ref(route.params.id as string)
|
||||||
|
const loaded = ref(false)
|
||||||
|
const chargeAmount = ref(0)
|
||||||
|
const loading = ref(false)
|
||||||
|
const action = ref('') // 'preauthorize' or 'charge'
|
||||||
|
const error = ref('')
|
||||||
|
const success = ref('')
|
||||||
|
const selectedCardId = ref(null as number | null) // Track which card is selected
|
||||||
|
const user = ref({
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
},
|
})
|
||||||
service: {
|
const service = ref<ServiceFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
scheduled_date: '',
|
scheduled_date: '',
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
@@ -187,8 +205,8 @@ export default defineComponent({
|
|||||||
description: '',
|
description: '',
|
||||||
service_cost: '',
|
service_cost: '',
|
||||||
payment_card_id: 0,
|
payment_card_id: 0,
|
||||||
},
|
})
|
||||||
credit_cards: [
|
const credit_cards = ref<CreditCardFormData[]>([
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name_on_card: '',
|
name_on_card: '',
|
||||||
@@ -199,10 +217,9 @@ export default defineComponent({
|
|||||||
last_four_digits: '',
|
last_four_digits: '',
|
||||||
expiration_year: '',
|
expiration_year: '',
|
||||||
security_number: '',
|
security_number: '',
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
])
|
||||||
customer: {
|
const customer = ref<CustomerFormData>({
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -215,66 +232,55 @@ export default defineComponent({
|
|||||||
customer_home_type: 0,
|
customer_home_type: 0,
|
||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
},
|
})
|
||||||
|
|
||||||
}
|
// Computed
|
||||||
},
|
const selectedCard = computed(() => {
|
||||||
|
|
||||||
computed: {
|
|
||||||
selectedCard(): any {
|
|
||||||
// If user has selected a card manually, use that
|
// If user has selected a card manually, use that
|
||||||
if (this.selectedCardId) {
|
if (selectedCardId.value) {
|
||||||
return this.credit_cards.find((card: any) => card.id === this.selectedCardId)
|
return credit_cards.value.find((card) => card.id === selectedCardId.value)
|
||||||
}
|
}
|
||||||
// Otherwise use automatic selection logic
|
// Otherwise use automatic selection logic
|
||||||
// First try to find payment_card_id from service
|
// First try to find payment_card_id from service
|
||||||
if (this.service.payment_card_id && this.service.payment_card_id > 0) {
|
if (service.value.payment_card_id && service.value.payment_card_id > 0) {
|
||||||
return this.credit_cards.find((card: any) => card.id === this.service.payment_card_id)
|
return credit_cards.value.find((card) => card.id === service.value.payment_card_id)
|
||||||
}
|
}
|
||||||
// Otherwise return the primary card (main_card = true)
|
// Otherwise return the primary card (main_card = true)
|
||||||
return this.credit_cards.find((card: any) => card.main_card === true) ||
|
return credit_cards.value.find((card) => card.main_card === true) ||
|
||||||
this.credit_cards.find((card: any) => card.id > 0) || // Any card if no primary
|
credit_cards.value.find((card) => card.id > 0) || // Any card if no primary
|
||||||
null
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
watch(() => route.params.id, (newId) => {
|
||||||
|
if (newId !== serviceId.value) {
|
||||||
|
resetState()
|
||||||
|
serviceId.value = newId as string
|
||||||
|
loadData(newId as string)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
|
||||||
mounted() {
|
// Lifecycle
|
||||||
this.loadData(this.serviceId)
|
onMounted(() => {
|
||||||
},
|
loadData(serviceId.value)
|
||||||
|
})
|
||||||
|
|
||||||
created() {
|
// Functions
|
||||||
this.watchRoute()
|
const resetState = () => {
|
||||||
},
|
loading.value = false
|
||||||
|
action.value = ''
|
||||||
|
error.value = ''
|
||||||
|
success.value = ''
|
||||||
|
chargeAmount.value = 0
|
||||||
|
serviceId.value = route.params.id as string
|
||||||
|
}
|
||||||
|
|
||||||
methods: {
|
const loadData = (serviceId: string) => {
|
||||||
watchRoute() {
|
userStatus()
|
||||||
watch(
|
getServiceOrder(serviceId)
|
||||||
() => this.$route.params.id,
|
}
|
||||||
(newId) => {
|
|
||||||
if (newId !== this.serviceId) {
|
|
||||||
this.resetState()
|
|
||||||
this.serviceId = newId as string
|
|
||||||
this.loadData(newId as string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
resetState() {
|
const userStatus = () => {
|
||||||
this.loading = false
|
|
||||||
this.action = ''
|
|
||||||
this.error = ''
|
|
||||||
this.success = ''
|
|
||||||
this.chargeAmount = 0
|
|
||||||
this.serviceId = this.$route.params.id as string
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData(serviceId: string) {
|
|
||||||
this.userStatus()
|
|
||||||
this.getServiceOrder(serviceId)
|
|
||||||
},
|
|
||||||
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -282,14 +288,14 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceOrder(serviceId: any) {
|
const getServiceOrder = (serviceId: number | string) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/service/" + serviceId;
|
let path = import.meta.env.VITE_BASE_URL + "/service/" + serviceId;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -297,21 +303,21 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<{ ok?: boolean; service?: ServiceFormData } | ServiceFormData[] | ServiceFormData>) => {
|
||||||
let serviceData;
|
let serviceData: ServiceFormData | undefined;
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
// Handle different API response structures
|
// Handle different API response structures
|
||||||
if (response.data.service) {
|
if ('service' in response.data && response.data.service) {
|
||||||
// API returns {ok: true, service: {...}} structure
|
// API returns {ok: true, service: {...}} structure
|
||||||
serviceData = response.data.service;
|
serviceData = response.data.service;
|
||||||
} else if (Array.isArray(response.data)) {
|
} else if (Array.isArray(response.data)) {
|
||||||
serviceData = response.data[0]; // Array response
|
serviceData = response.data[0]; // Array response
|
||||||
} else {
|
} else if ('id' in response.data) {
|
||||||
serviceData = response.data; // Direct object response
|
serviceData = response.data as ServiceFormData; // Direct object response
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceData && serviceData.id) {
|
if (serviceData && serviceData.id) {
|
||||||
this.service = {
|
service.value = {
|
||||||
id: serviceData.id,
|
id: serviceData.id,
|
||||||
scheduled_date: serviceData.scheduled_date,
|
scheduled_date: serviceData.scheduled_date,
|
||||||
customer_id: serviceData.customer_id,
|
customer_id: serviceData.customer_id,
|
||||||
@@ -325,8 +331,8 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Fetch related data
|
// Fetch related data
|
||||||
this.getCustomer(this.service.customer_id);
|
getCustomer(service.value.customer_id);
|
||||||
this.getCreditCards(this.service.customer_id);
|
getCreditCards(service.value.customer_id);
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error: Invalid service data received:", serviceData);
|
console.error("API Error: Invalid service data received:", serviceData);
|
||||||
notify({
|
notify({
|
||||||
@@ -344,114 +350,110 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((err: Error) => {
|
||||||
console.error("API Error in getServiceOrder:", error);
|
console.error("API Error in getServiceOrder:", err);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: "Could not get service data",
|
text: "Could not get service data",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const getCreditCards = (user_id: number) => {
|
||||||
|
|
||||||
getCreditCards(user_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CreditCardFormData[]>) => {
|
||||||
console.log('Credit cards loaded:', response.data?.length || 0, 'cards');
|
console.log('Credit cards loaded:', response.data?.length || 0, 'cards');
|
||||||
this.credit_cards = response.data || [];
|
credit_cards.value = response.data || [];
|
||||||
}).catch((error: any) => {
|
}).catch((err: Error) => {
|
||||||
console.error('Failed to load credit cards:', error);
|
console.error('Failed to load credit cards:', err);
|
||||||
this.credit_cards = [];
|
credit_cards.value = [];
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getCustomer(userid: any) {
|
const getCustomer = (userid: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CustomerFormData>) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const handlePreauthorize = async () => {
|
||||||
|
await processPayment('preauthorize')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChargeNow = async () => {
|
||||||
|
await processPayment('charge')
|
||||||
|
}
|
||||||
|
|
||||||
async handlePreauthorize() {
|
const processPayment = async (actionType: string) => {
|
||||||
await this.processPayment('preauthorize')
|
if (!selectedCard.value) {
|
||||||
},
|
error.value = 'No credit card found for this customer'
|
||||||
|
|
||||||
async handleChargeNow() {
|
|
||||||
await this.processPayment('charge')
|
|
||||||
},
|
|
||||||
|
|
||||||
async processPayment(actionType: string) {
|
|
||||||
if (!this.selectedCard) {
|
|
||||||
this.error = 'No credit card found for this customer'
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
loading.value = true
|
||||||
this.action = actionType
|
action.value = actionType
|
||||||
this.error = ''
|
error.value = ''
|
||||||
this.success = ''
|
success.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 2: If payment method is credit, perform the pre-authorization
|
// Step 2: If payment method is credit, perform the pre-authorization
|
||||||
if (actionType === 'preauthorize') {
|
if (actionType === 'preauthorize') {
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Pre-authorization amount must be greater than zero.");
|
throw new Error("Pre-authorization amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const authPayload = {
|
const authPayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
preauthorize_amount: this.chargeAmount.toFixed(2),
|
preauthorize_amount: chargeAmount.value.toFixed(2),
|
||||||
service_id: this.service.id,
|
service_id: service.value.id,
|
||||||
delivery_id: null, // No delivery for services
|
delivery_id: null, // No delivery for services
|
||||||
};
|
};
|
||||||
|
|
||||||
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${this.customer.id}`;
|
const authPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/authorize/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
const response = await axios.post(authPath, authPayload, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Update payment type to 11 after successful preauthorization
|
// Update payment type to 11 after successful preauthorization
|
||||||
try {
|
try {
|
||||||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/service/${this.service.id}`, {
|
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/authorize/service/${service.value.id}`, {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
status: actionType === 'preauthorize' ? 1 : 3
|
status: actionType === 'preauthorize' ? 1 : 3
|
||||||
}, { headers: authHeader() });
|
}, { headers: authHeader() });
|
||||||
} catch (updateError) {
|
} catch (updateError) {
|
||||||
console.error('Failed to update payment type after preauthorization:', updateError);
|
console.error('Failed to update payment type after preauthorization:', updateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On successful authorization, show success and redirect
|
// On successful authorization, show success and redirect
|
||||||
this.success = `Preauthorization successful! Transaction ID: ${response.data?.auth_net_transaction_id || 'N/A'}`;
|
success.value = `Preauthorization successful! Transaction ID: ${response.data?.auth_net_transaction_id || 'N/A'}`;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else { // Handle 'charge' action
|
} else { // Handle 'charge' action
|
||||||
if (!this.chargeAmount || this.chargeAmount <= 0) {
|
if (!chargeAmount.value || chargeAmount.value <= 0) {
|
||||||
throw new Error("Charge amount must be greater than zero.");
|
throw new Error("Charge amount must be greater than zero.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a payload that matches the backend's TransactionCreateByCardID schema
|
// Create a payload that matches the backend's TransactionCreateByCardID schema
|
||||||
const chargePayload = {
|
const chargePayload = {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
charge_amount: this.chargeAmount.toFixed(2),
|
charge_amount: chargeAmount.value.toFixed(2),
|
||||||
service_id: this.service.id,
|
service_id: service.value.id,
|
||||||
delivery_id: null, // No delivery for services
|
delivery_id: null, // No delivery for services
|
||||||
// You can add other fields here if your schema requires them
|
// You can add other fields here if your schema requires them
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the correct endpoint for charging a saved card
|
// Use the correct endpoint for charging a saved card
|
||||||
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${this.customer.id}`;
|
const chargePath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/charge/saved-card/${customer.value.id}`;
|
||||||
|
|
||||||
console.log('=== DEBUG: Charge payload ===');
|
console.log('=== DEBUG: Charge payload ===');
|
||||||
console.log('Calling endpoint:', chargePath);
|
console.log('Calling endpoint:', chargePath);
|
||||||
@@ -462,19 +464,19 @@ export default defineComponent({
|
|||||||
// Update service cost to the charged amount using new dedicated API
|
// Update service cost to the charged amount using new dedicated API
|
||||||
try {
|
try {
|
||||||
await axios.put(
|
await axios.put(
|
||||||
`${import.meta.env.VITE_BASE_URL}/service/update-cost/${this.service.id}`,
|
`${import.meta.env.VITE_BASE_URL}/service/update-cost/${service.value.id}`,
|
||||||
{ service_cost: this.chargeAmount },
|
{ service_cost: chargeAmount.value },
|
||||||
{ headers: authHeader(), withCredentials: true }
|
{ headers: authHeader(), withCredentials: true }
|
||||||
);
|
);
|
||||||
console.log(`✅ Updated service cost to ${this.chargeAmount} for service ${this.service.id}`);
|
console.log(`✅ Updated service cost to ${chargeAmount.value} for service ${service.value.id}`);
|
||||||
} catch (costError) {
|
} catch (costError) {
|
||||||
console.error('❌ Failed to update service cost:', costError);
|
console.error('❌ Failed to update service cost:', costError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update payment status after successful charge
|
// Update payment status after successful charge
|
||||||
try {
|
try {
|
||||||
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/capture/service/${this.service.id}`, {
|
await axios.put(`${import.meta.env.VITE_BASE_URL}/payment/capture/service/${service.value.id}`, {
|
||||||
card_id: (this.selectedCard as any).id,
|
card_id: selectedCard.value!.id,
|
||||||
status: 3 // Approved status
|
status: 3 // Approved status
|
||||||
}, {
|
}, {
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
@@ -486,51 +488,48 @@ export default defineComponent({
|
|||||||
|
|
||||||
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
// Status codes: 0 = APPROVED, 1 = DECLINED (based on backend TransactionStatus enum)
|
||||||
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
if (response.data && response.data.status === 0) { // 0 = APPROVED
|
||||||
this.success = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`;
|
success.value = `Charge successful! Transaction ID: ${response.data.auth_net_transaction_id || 'N/A'}`;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$router.push({ name: "customerProfile", params: { id: this.customer.id } });
|
router.push({ name: "customerProfile", params: { id: customer.value.id } });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
// The error message from your backend will be more specific now
|
// The error message from your backend will be more specific now
|
||||||
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
throw new Error(`Payment charge failed: ${response.data?.rejection_reason || 'Unknown error'}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
console.log(error)
|
const axiosErr = err as AxiosError<{ detail?: string }>;
|
||||||
this.error = error.response?.data?.detail || `Failed to ${actionType} payment`
|
console.log(err)
|
||||||
|
error.value = axiosErr.response?.data?.detail || `Failed to ${actionType} payment`
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: this.error,
|
text: error.value,
|
||||||
type: "error",
|
type: "error",
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
loading.value = false
|
||||||
this.action = ''
|
action.value = ''
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeName(typeId: number): string {
|
const getServiceTypeName = (typeId: number): string => {
|
||||||
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
||||||
return typeMap[typeId] || 'Unknown';
|
return typeMap[typeId] || 'Unknown';
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeColor(typeId: number): string {
|
const getServiceTypeColor = (typeId: number): string => {
|
||||||
const colorMap: { [key: number]: string } = { 0: 'primary', 1: 'error', 2: 'warning', 3: 'info', 4: 'neutral' };
|
const colorMap: { [key: number]: string } = { 0: 'primary', 1: 'error', 2: 'warning', 3: 'info', 4: 'neutral' };
|
||||||
return `badge-${colorMap[typeId] || 'neutral'}`;
|
return `badge-${colorMap[typeId] || 'neutral'}`;
|
||||||
},
|
}
|
||||||
|
|
||||||
selectCard(cardId: number) {
|
const selectCard = (cardId: number) => {
|
||||||
this.selectedCardId = cardId;
|
selectedCardId.value = cardId;
|
||||||
},
|
}
|
||||||
|
|
||||||
|
const formatScheduledDate = (dateString: string): string => {
|
||||||
|
|
||||||
formatScheduledDate(dateString: string): string {
|
|
||||||
if (!dateString) return 'Not scheduled';
|
if (!dateString) return 'Not scheduled';
|
||||||
return dateString; // Could format with dayjs if needed
|
return dateString; // Could format with dayjs if needed
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ const service = ref<Service | null>(null);
|
|||||||
const preAuthCard = ref<UserCard | null>(null);
|
const preAuthCard = ref<UserCard | null>(null);
|
||||||
const selectedCard = ref<UserCard | null>(null);
|
const selectedCard = ref<UserCard | null>(null);
|
||||||
const chargeAmount = ref<number>(0);
|
const chargeAmount = ref<number>(0);
|
||||||
const transaction = ref(null as any);
|
const transaction = ref<ServiceTransaction | null>(null);
|
||||||
const preAuthAmount = ref<number>(0);
|
const preAuthAmount = ref<number>(0);
|
||||||
const serviceTransactions = ref<ServiceTransaction[]>([]);
|
const serviceTransactions = ref<ServiceTransaction[]>([]);
|
||||||
const showPaymentModal = ref(false);
|
const showPaymentModal = ref(false);
|
||||||
@@ -356,7 +356,8 @@ const updateServiceCost = async (serviceId: number, newCost: number): Promise<bo
|
|||||||
console.error(`❌ SERVICE COST UPDATE FAILED:`, response.data);
|
console.error(`❌ SERVICE COST UPDATE FAILED:`, response.data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
|
const error = err as { response?: { data?: unknown }; message?: string };
|
||||||
console.error(`💥 ERROR UPDATING SERVICE COST:`, error.response?.data || error.message);
|
console.error(`💥 ERROR UPDATING SERVICE COST:`, error.response?.data || error.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -459,7 +460,8 @@ const chargeService = async () => {
|
|||||||
throw new Error("Invalid response from server during capture.");
|
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.";
|
const detail = error.response?.data?.detail || "Failed to process payment due to a server error.";
|
||||||
notify({ title: "Error", text: detail, type: "error" });
|
notify({ title: "Error", text: detail, type: "error" });
|
||||||
console.error("Charge/Capture Service Error:", error);
|
console.error("Charge/Capture Service Error:", error);
|
||||||
@@ -489,7 +491,7 @@ const getTransaction = async () => {
|
|||||||
selectedCard.value = cardResponse.data;
|
selectedCard.value = cardResponse.data;
|
||||||
chargeAmount.value = preAuthAmount.value;
|
chargeAmount.value = preAuthAmount.value;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error("No pre-authorized transaction found for service:", error);
|
console.error("No pre-authorized transaction found for service:", error);
|
||||||
preAuthAmount.value = service.value?.service_cost || 0;
|
preAuthAmount.value = service.value?.service_cost || 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,10 +271,18 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import axios, { AxiosResponse, AxiosError } from 'axios'
|
||||||
import authHeader from '../../../services/auth.header'
|
import authHeader from '../../../services/auth.header'
|
||||||
|
import { ServiceCall, ServicePart, CreditCard, Customer } from '../../../types/models'
|
||||||
|
import type {
|
||||||
|
WhoAmIResponse,
|
||||||
|
CardsOnFileResponse,
|
||||||
|
AuthorizeCheckResponse,
|
||||||
|
CreateAuthorizeAccountResponse
|
||||||
|
} from '../../../types/models'
|
||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
@@ -283,52 +291,35 @@ import useValidate from "@vuelidate/core";
|
|||||||
import { notify } from "@kyvg/vue3-notification"
|
import { notify } from "@kyvg/vue3-notification"
|
||||||
import { required } from "@vuelidate/validators";
|
import { required } from "@vuelidate/validators";
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'PayService',
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
components: {
|
const loaded = ref(false)
|
||||||
Header,
|
const user = ref({
|
||||||
SideBar,
|
|
||||||
Footer,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
v$: useValidate(),
|
|
||||||
loaded: false,
|
|
||||||
user: {
|
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
},
|
})
|
||||||
service: {
|
const service = ref<ServiceCall>({
|
||||||
id: 0,
|
id: 0,
|
||||||
scheduled_date: '',
|
|
||||||
customer_id: 0,
|
customer_id: 0,
|
||||||
customer_name: '',
|
customer_name: '',
|
||||||
customer_address: '',
|
customer_address: '',
|
||||||
customer_town: '',
|
customer_town: '',
|
||||||
|
customer_state: '',
|
||||||
|
customer_zip: '',
|
||||||
type_service_call: 0,
|
type_service_call: 0,
|
||||||
|
when_ordered: '',
|
||||||
|
scheduled_date: '',
|
||||||
description: '',
|
description: '',
|
||||||
service_cost: '',
|
service_cost: '0',
|
||||||
|
payment_type: 0,
|
||||||
payment_card_id: 0,
|
payment_card_id: 0,
|
||||||
},
|
payment_status: 0,
|
||||||
serviceParts: null as any,
|
})
|
||||||
credit_cards: [
|
const serviceParts = ref<ServicePart[] | null>(null)
|
||||||
{
|
const credit_cards = ref<CreditCard[]>([])
|
||||||
id: 0,
|
|
||||||
name_on_card: '',
|
|
||||||
main_card: false,
|
|
||||||
card_number: '',
|
|
||||||
expiration_month: '',
|
|
||||||
type_of_card: '',
|
|
||||||
last_four_digits: '',
|
|
||||||
expiration_year: '',
|
|
||||||
security_number: '',
|
|
||||||
|
|
||||||
}
|
const stripe = ref(null)
|
||||||
],
|
const customer = ref({
|
||||||
|
|
||||||
stripe: null,
|
|
||||||
customer: {
|
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
customer_first_name: '',
|
customer_first_name: '',
|
||||||
@@ -342,8 +333,8 @@ export default defineComponent({
|
|||||||
customer_phone_number: '',
|
customer_phone_number: '',
|
||||||
account_number: '',
|
account_number: '',
|
||||||
auth_net_profile_id: null,
|
auth_net_profile_id: null,
|
||||||
},
|
})
|
||||||
pricing: {
|
const pricing = ref({
|
||||||
price_from_supplier: 0,
|
price_from_supplier: 0,
|
||||||
price_for_customer: 0,
|
price_for_customer: 0,
|
||||||
price_for_employee: 0,
|
price_for_employee: 0,
|
||||||
@@ -351,57 +342,44 @@ export default defineComponent({
|
|||||||
price_prime: 0,
|
price_prime: 0,
|
||||||
price_emergency: 0,
|
price_emergency: 0,
|
||||||
date: "",
|
date: "",
|
||||||
},
|
})
|
||||||
promo_active: false,
|
const promo_active = ref(false)
|
||||||
promo: {
|
const promo = ref({
|
||||||
name_of_promotion: '',
|
name_of_promotion: '',
|
||||||
description: '',
|
description: '',
|
||||||
money_off_delivery: 0,
|
money_off_delivery: 0,
|
||||||
text_on_ticket: ''
|
text_on_ticket: ''
|
||||||
},
|
})
|
||||||
priceprime: 0,
|
const priceprime = ref(0)
|
||||||
pricesameday: 0,
|
const pricesameday = ref(0)
|
||||||
priceemergency: 0,
|
const priceemergency = ref(0)
|
||||||
total_amount: 0,
|
const total_amount = ref(0)
|
||||||
discount: 0,
|
const discount = ref(0)
|
||||||
total_amount_after_discount: 0,
|
const total_amount_after_discount = ref(0)
|
||||||
credit_cards_count: 0,
|
const credit_cards_count = ref(0)
|
||||||
isLoadingAuthorize: true,
|
const isLoadingAuthorize = ref(true)
|
||||||
authorizeCheck: { profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false },
|
const authorizeCheck = ref({ profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false })
|
||||||
isDeleteAccountModalVisible: false,
|
const isDeleteAccountModalVisible = ref(false)
|
||||||
isCreateAccountModalVisible: false,
|
const isCreateAccountModalVisible = ref(false)
|
||||||
isCreatingAccount: false,
|
const isCreatingAccount = ref(false)
|
||||||
createdProfileId: '',
|
const createdProfileId = ref('')
|
||||||
isDuplicateErrorModalVisible: false,
|
const isDuplicateErrorModalVisible = ref(false)
|
||||||
}
|
|
||||||
},
|
// Validation rules
|
||||||
validations() {
|
const rules = {
|
||||||
return {
|
|
||||||
CreateServiceOrderForm: {
|
CreateServiceOrderForm: {
|
||||||
basicInfo: {
|
basicInfo: {
|
||||||
description: { required },
|
description: { required },
|
||||||
service_cost: { required },
|
service_cost: { required },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus()
|
|
||||||
|
|
||||||
},
|
// Vuelidate instance
|
||||||
watch: {
|
const v$ = useValidate(rules, {})
|
||||||
$route() {
|
|
||||||
|
|
||||||
},
|
// Functions
|
||||||
},
|
const userStatus = () => {
|
||||||
mounted() {
|
|
||||||
this.getServiceOrder(this.$route.params.id)
|
|
||||||
this.getServicePartsForCustomer();
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
userStatus() {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -409,14 +387,14 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<WhoAmIResponse>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
},
|
const getServiceOrder = (service_id: number | string) => {
|
||||||
getServiceOrder(service_id: any) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/service/" + service_id;
|
let path = import.meta.env.VITE_BASE_URL + "/service/" + service_id;
|
||||||
axios({
|
axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
@@ -424,21 +402,21 @@ export default defineComponent({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<{ ok?: boolean; service?: ServiceCall } | ServiceCall[] | ServiceCall>) => {
|
||||||
let serviceData;
|
let serviceData: ServiceCall | undefined;
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
// Handle different API response structures
|
// Handle different API response structures
|
||||||
if (response.data.service) {
|
if ('service' in response.data && response.data.service) {
|
||||||
// API returns {ok: true, service: {...}} structure
|
// API returns {ok: true, service: {...}} structure
|
||||||
serviceData = response.data.service;
|
serviceData = response.data.service;
|
||||||
} else if (Array.isArray(response.data)) {
|
} else if (Array.isArray(response.data)) {
|
||||||
serviceData = response.data[0]; // Array response
|
serviceData = response.data[0]; // Array response
|
||||||
} else {
|
} else if ('id' in response.data) {
|
||||||
serviceData = response.data; // Direct object response
|
serviceData = response.data as ServiceCall; // Direct object response
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceData && serviceData.id) {
|
if (serviceData && serviceData.id) {
|
||||||
this.service = {
|
service.value = {
|
||||||
id: serviceData.id,
|
id: serviceData.id,
|
||||||
scheduled_date: serviceData.scheduled_date,
|
scheduled_date: serviceData.scheduled_date,
|
||||||
customer_id: serviceData.customer_id,
|
customer_id: serviceData.customer_id,
|
||||||
@@ -452,10 +430,10 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Fetch related data
|
// Fetch related data
|
||||||
this.getCustomer(this.service.customer_id);
|
getCustomer(service.value.customer_id);
|
||||||
this.getCreditCards(this.service.customer_id);
|
getCreditCards(service.value.customer_id);
|
||||||
this.getCreditCardsCount(this.service.customer_id);
|
getCreditCardsCount(service.value.customer_id);
|
||||||
this.getServicePartsForCustomer();
|
getServicePartsForCustomer();
|
||||||
} else {
|
} else {
|
||||||
console.error("API Error: Invalid service data received:", serviceData);
|
console.error("API Error: Invalid service data received:", serviceData);
|
||||||
notify({
|
notify({
|
||||||
@@ -473,7 +451,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: AxiosError) => {
|
||||||
console.error("API Error in getServiceOrder:", error);
|
console.error("API Error in getServiceOrder:", error);
|
||||||
console.error("Error details:", error.response?.data || error.message);
|
console.error("Error details:", error.response?.data || error.message);
|
||||||
notify({
|
notify({
|
||||||
@@ -482,59 +460,63 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getServicePartsForCustomer() {
|
|
||||||
if (!this.service.customer_id) return;
|
|
||||||
|
|
||||||
let path = `${import.meta.env.VITE_BASE_URL}/service/parts/customer/${this.service.customer_id}`;
|
const getServicePartsForCustomer = () => {
|
||||||
axios.get(path, { headers: authHeader() })
|
if (!service.value.customer_id) return;
|
||||||
.then((response: any) => {
|
|
||||||
this.serviceParts = response.data;
|
let path = `${import.meta.env.VITE_BASE_URL}/service/parts/customer/${service.value.customer_id}`;
|
||||||
|
axios.get<ServicePart[]>(path, { headers: authHeader() })
|
||||||
|
.then((response) => {
|
||||||
|
serviceParts.value = response.data;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: Error) => {
|
||||||
console.error("Failed to fetch service parts:", error);
|
console.error("Failed to fetch service parts:", error);
|
||||||
this.serviceParts = null;
|
serviceParts.value = null;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
getCreditCards(user_id: any) {
|
|
||||||
|
const getCreditCards = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CreditCard[]>) => {
|
||||||
|
credit_cards.value = response.data
|
||||||
this.credit_cards = response.data
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCreditCardsCount(user_id: any) {
|
|
||||||
|
const getCreditCardsCount = (user_id: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/onfile/' + user_id;
|
let path = import.meta.env.VITE_BASE_URL + '/payment/cards/onfile/' + user_id;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<CardsOnFileResponse>) => {
|
||||||
this.credit_cards_count = response.data.cards
|
credit_cards_count.value = response.data.cards
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
getCustomer(userid: any) {
|
|
||||||
|
const getCustomer = (userid: number) => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + userid;
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: path,
|
url: path,
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
}).then((response: any) => {
|
}).then((response: AxiosResponse<typeof customer.value>) => {
|
||||||
this.customer = response.data
|
customer.value = response.data
|
||||||
this.checkAuthorizeAccount();
|
checkAuthorizeAccount();
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
processServicePayment(payment_type: number) {
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + "/payment/service/payment/" + this.service.id + '/' + payment_type;
|
const processServicePayment = (payment_type: number) => {
|
||||||
|
let path = import.meta.env.VITE_BASE_URL + "/payment/service/payment/" + service.value.id + '/' + payment_type;
|
||||||
axios({
|
axios({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
url: path,
|
url: path,
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: AxiosResponse<{ ok: boolean }>) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
if (payment_type == 0) {
|
if (payment_type == 0) {
|
||||||
notify({
|
notify({
|
||||||
@@ -564,7 +546,7 @@ export default defineComponent({
|
|||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.$router.push({ name: "ServiceHome" });
|
router.push({ name: "ServiceHome" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -574,67 +556,69 @@ export default defineComponent({
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
async checkAuthorizeAccount() {
|
|
||||||
if (!this.customer.id) return;
|
|
||||||
|
|
||||||
this.isLoadingAuthorize = true;
|
const checkAuthorizeAccount = async () => {
|
||||||
|
if (!customer.value.id) return;
|
||||||
|
|
||||||
|
isLoadingAuthorize.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${customer.value.id}`;
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
const response = await axios.get(path, { headers: authHeader() });
|
||||||
this.authorizeCheck = response.data;
|
authorizeCheck.value = response.data;
|
||||||
|
|
||||||
// Check if the API returned an error in the response body
|
// Check if the API returned an error in the response body
|
||||||
if (this.authorizeCheck.missing_components && this.authorizeCheck.missing_components.includes('api_error')) {
|
if (authorizeCheck.value.missing_components && authorizeCheck.value.missing_components.includes('api_error')) {
|
||||||
console.log("API error detected in response, calling cleanup for customer:", this.customer.id);
|
console.log("API error detected in response, calling cleanup for customer:", customer.value.id);
|
||||||
this.cleanupAuthorizeData();
|
cleanupAuthorizeData();
|
||||||
return; // Don't set loading to false yet, let cleanup handle it
|
return; // Don't set loading to false yet, let cleanup handle it
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check authorize account:", error);
|
console.error("Failed to check authorize account:", error);
|
||||||
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
|
||||||
// Set default error state
|
// Set default error state
|
||||||
this.authorizeCheck = {
|
authorizeCheck.value = {
|
||||||
profile_exists: false,
|
profile_exists: false,
|
||||||
has_payment_methods: false,
|
has_payment_methods: false,
|
||||||
missing_components: ['api_error'],
|
missing_components: ['api_error'],
|
||||||
valid_for_charging: false
|
valid_for_charging: false
|
||||||
};
|
};
|
||||||
// Automatically cleanup the local Authorize.Net data on API error
|
// Automatically cleanup the local Authorize.Net data on API error
|
||||||
console.log("Calling cleanupAuthorizedData for customer:", this.customer.id);
|
console.log("Calling cleanupAuthorizedData for customer:", customer.value.id);
|
||||||
this.cleanupAuthorizeData();
|
cleanupAuthorizeData();
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingAuthorize = false;
|
isLoadingAuthorize.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async createAuthorizeAccount() {
|
|
||||||
|
const createAuthorizeAccount = async () => {
|
||||||
// Show the creating account modal
|
// Show the creating account modal
|
||||||
this.isCreatingAccount = true;
|
isCreatingAccount.value = true;
|
||||||
this.isCreateAccountModalVisible = true;
|
isCreateAccountModalVisible.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/create-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/create-account/${customer.value.id}`;
|
||||||
const response = await axios.post(path, {}, { headers: authHeader() });
|
const response = await axios.post(path, {}, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
// Update local state
|
// Update local state
|
||||||
this.customer.auth_net_profile_id = response.data.profile_id;
|
customer.value.auth_net_profile_id = response.data.profile_id;
|
||||||
this.authorizeCheck.valid_for_charging = true;
|
authorizeCheck.value.valid_for_charging = true;
|
||||||
this.authorizeCheck.profile_exists = true;
|
authorizeCheck.value.profile_exists = true;
|
||||||
this.authorizeCheck.has_payment_methods = true;
|
authorizeCheck.value.has_payment_methods = true;
|
||||||
this.authorizeCheck.missing_components = [];
|
authorizeCheck.value.missing_components = [];
|
||||||
this.createdProfileId = response.data.profile_id;
|
createdProfileId.value = response.data.profile_id;
|
||||||
|
|
||||||
// Refresh credit cards to get updated payment profile IDs
|
// Refresh credit cards to get updated payment profile IDs
|
||||||
await this.getCreditCards(this.customer.id);
|
await getCreditCards(customer.value.id);
|
||||||
|
|
||||||
// Switch modal to success view and close after delay
|
// Switch modal to success view and close after delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.isCreatingAccount = false;
|
isCreatingAccount.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
this.createdProfileId = '';
|
createdProfileId.value = '';
|
||||||
|
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
@@ -646,7 +630,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Hide modal on error
|
// Hide modal on error
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
|
|
||||||
// Check for E00039 duplicate error
|
// Check for E00039 duplicate error
|
||||||
const errorMessage = response.data.message || response.data.error_detail || "Failed to create Authorize.net account";
|
const errorMessage = response.data.message || response.data.error_detail || "Failed to create Authorize.net account";
|
||||||
@@ -654,7 +638,7 @@ export default defineComponent({
|
|||||||
if (response.data.is_duplicate || errorMessage.includes("E00039")) {
|
if (response.data.is_duplicate || errorMessage.includes("E00039")) {
|
||||||
// Show duplicate account popup
|
// Show duplicate account popup
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showDuplicateErrorModal();
|
showDuplicateErrorModal();
|
||||||
}, 300);
|
}, 300);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -666,10 +650,11 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (err: unknown) {
|
||||||
|
const error = err as AxiosError<{ error_detail?: string; detail?: string; message?: string; is_duplicate?: boolean }>;
|
||||||
console.error("Failed to create account:", error);
|
console.error("Failed to create account:", error);
|
||||||
this.isCreateAccountModalVisible = false;
|
isCreateAccountModalVisible.value = false;
|
||||||
this.isCreatingAccount = false;
|
isCreatingAccount.value = false;
|
||||||
|
|
||||||
// Check for E00039 duplicate error
|
// Check for E00039 duplicate error
|
||||||
const errorMessage = error.response?.data?.error_detail ||
|
const errorMessage = error.response?.data?.error_detail ||
|
||||||
@@ -680,7 +665,7 @@ export default defineComponent({
|
|||||||
if (error.response?.data?.is_duplicate || errorMessage.includes("E00039")) {
|
if (error.response?.data?.is_duplicate || errorMessage.includes("E00039")) {
|
||||||
// Show duplicate account popup
|
// Show duplicate account popup
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showDuplicateErrorModal();
|
showDuplicateErrorModal();
|
||||||
}, 300);
|
}, 300);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -692,36 +677,41 @@ export default defineComponent({
|
|||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
showDeleteAccountModal() {
|
|
||||||
this.isDeleteAccountModalVisible = true;
|
const showDeleteAccountModal = () => {
|
||||||
},
|
isDeleteAccountModalVisible.value = true;
|
||||||
showDuplicateErrorModal() {
|
}
|
||||||
this.isDuplicateErrorModalVisible = true;
|
|
||||||
},
|
const showDuplicateErrorModal = () => {
|
||||||
hideDuplicateErrorModal() {
|
isDuplicateErrorModalVisible.value = true;
|
||||||
this.isDuplicateErrorModalVisible = false;
|
}
|
||||||
},
|
|
||||||
addCreditCard() {
|
const hideDuplicateErrorModal = () => {
|
||||||
|
isDuplicateErrorModalVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addCreditCard = () => {
|
||||||
// Redirect to add card page
|
// Redirect to add card page
|
||||||
this.$router.push({ name: 'cardadd', params: { customerId: this.customer.id } });
|
router.push({ name: 'cardadd', params: { customerId: customer.value.id } });
|
||||||
},
|
}
|
||||||
async deleteAccount() {
|
|
||||||
this.isDeleteAccountModalVisible = false;
|
const deleteAccount = async () => {
|
||||||
|
isDeleteAccountModalVisible.value = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/delete-account/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/delete-account/${customer.value.id}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader() });
|
const response = await axios.delete(path, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
// Update local state
|
// Update local state
|
||||||
this.customer.auth_net_profile_id = null;
|
customer.value.auth_net_profile_id = null;
|
||||||
this.authorizeCheck.valid_for_charging = false;
|
authorizeCheck.value.valid_for_charging = false;
|
||||||
this.authorizeCheck.profile_exists = false;
|
authorizeCheck.value.profile_exists = false;
|
||||||
this.authorizeCheck.has_payment_methods = false;
|
authorizeCheck.value.has_payment_methods = false;
|
||||||
|
|
||||||
// Refresh credit cards list (IDs should now be null)
|
// Refresh credit cards list (IDs should now be null)
|
||||||
this.getCreditCards(this.customer.id);
|
getCreditCards(customer.value.id);
|
||||||
|
|
||||||
notify({
|
notify({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
@@ -735,7 +725,7 @@ export default defineComponent({
|
|||||||
type: "warning"
|
type: "warning"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error("Failed to delete account:", error);
|
console.error("Failed to delete account:", error);
|
||||||
notify({
|
notify({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
@@ -743,21 +733,22 @@ export default defineComponent({
|
|||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async cleanupAuthorizeData() {
|
|
||||||
|
const cleanupAuthorizeData = async () => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/payment/authorize/cleanup/${this.customer.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/payment/authorize/cleanup/${customer.value.id}`;
|
||||||
const response = await axios.post(path, {}, { headers: authHeader() });
|
const response = await axios.post(path, {}, { headers: authHeader() });
|
||||||
|
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
// Update local state to reflect cleanup
|
// Update local state to reflect cleanup
|
||||||
this.customer.auth_net_profile_id = null;
|
customer.value.auth_net_profile_id = null;
|
||||||
this.authorizeCheck.valid_for_charging = false;
|
authorizeCheck.value.valid_for_charging = false;
|
||||||
this.authorizeCheck.profile_exists = false;
|
authorizeCheck.value.profile_exists = false;
|
||||||
this.authorizeCheck.has_payment_methods = false;
|
authorizeCheck.value.has_payment_methods = false;
|
||||||
|
|
||||||
// Refresh credit cards to reflect null payment profile IDs
|
// Refresh credit cards to reflect null payment profile IDs
|
||||||
this.getCreditCards(this.customer.id);
|
getCreditCards(customer.value.id);
|
||||||
|
|
||||||
console.log("Successfully cleaned up Authorize.Net data:", response.data.message);
|
console.log("Successfully cleaned up Authorize.Net data:", response.data.message);
|
||||||
} else {
|
} else {
|
||||||
@@ -766,13 +757,14 @@ export default defineComponent({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error during cleanup:", error);
|
console.error("Error during cleanup:", error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getAccountStatusMessage(): string {
|
|
||||||
if (!this.authorizeCheck || !this.authorizeCheck.missing_components) {
|
const getAccountStatusMessage = (): string => {
|
||||||
|
if (!authorizeCheck.value || !authorizeCheck.value.missing_components) {
|
||||||
return 'Account setup incomplete';
|
return 'Account setup incomplete';
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = this.authorizeCheck.missing_components;
|
const missing = authorizeCheck.value.missing_components;
|
||||||
if (missing.includes('customer_not_found')) {
|
if (missing.includes('customer_not_found')) {
|
||||||
return 'Customer not found in Authorize.net';
|
return 'Customer not found in Authorize.net';
|
||||||
} else if (missing.includes('authorize_net_profile')) {
|
} else if (missing.includes('authorize_net_profile')) {
|
||||||
@@ -786,22 +778,30 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'Account requires setup';
|
return 'Account requires setup';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getServiceTypeName(typeId: number): string {
|
|
||||||
|
const getServiceTypeName = (typeId: number): string => {
|
||||||
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
||||||
return typeMap[typeId] || 'Unknown';
|
return typeMap[typeId] || 'Unknown';
|
||||||
},
|
}
|
||||||
getServiceTypeColor(typeId: number): string {
|
|
||||||
|
const getServiceTypeColor = (typeId: number): string => {
|
||||||
const colorMap: { [key: number]: string } = { 0: 'primary', 1: 'error', 2: 'warning', 3: 'info', 4: 'neutral' };
|
const colorMap: { [key: number]: string } = { 0: 'primary', 1: 'error', 2: 'warning', 3: 'info', 4: 'neutral' };
|
||||||
return `badge-${colorMap[typeId] || 'neutral'}`;
|
return `badge-${colorMap[typeId] || 'neutral'}`;
|
||||||
},
|
}
|
||||||
formatScheduledDate(dateString: string): string {
|
|
||||||
|
const formatScheduledDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'Not scheduled';
|
if (!dateString) return 'Not scheduled';
|
||||||
return dateString; // Could format with dayjs if needed
|
return dateString; // Could format with dayjs if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
userStatus()
|
||||||
|
getServiceOrder(route.params.id)
|
||||||
|
getServicePartsForCustomer();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -35,8 +35,8 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import Header from '../../layouts/headers/headerauth.vue';
|
import Header from '../../layouts/headers/headerauth.vue';
|
||||||
import SideBar from '../../layouts/sidebar/sidebar.vue';
|
import SideBar from '../../layouts/sidebar/sidebar.vue';
|
||||||
import Footer from '../../layouts/footers/footer.vue';
|
import Footer from '../../layouts/footers/footer.vue';
|
||||||
@@ -60,43 +60,18 @@ interface ServiceCall {
|
|||||||
service_cost: string;
|
service_cost: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
// Reactive data
|
||||||
name: 'ServiceCalendar',
|
const user = ref(null)
|
||||||
components: { Header, SideBar, Footer, FullCalendar, ServiceEditModal },
|
const selectedServiceForEdit = ref(null as Partial<ServiceCall> | null)
|
||||||
data() {
|
const fullCalendar = ref()
|
||||||
return {
|
|
||||||
user: null,
|
|
||||||
selectedServiceForEdit: null as Partial<ServiceCall> | null,
|
|
||||||
calendarOptions: {
|
|
||||||
plugins: [dayGridPlugin, interactionPlugin],
|
|
||||||
initialView: 'dayGridMonth',
|
|
||||||
weekends: true,
|
|
||||||
// Instead of a static array, we use a function source.
|
|
||||||
// This is the standard way FullCalendar fetches events.
|
|
||||||
events: `${import.meta.env.VITE_BASE_URL}/service/all`,
|
|
||||||
eventClick: this.handleEventClick,
|
|
||||||
// Add headers for authentication if needed by your API
|
|
||||||
eventSourceSuccess: (content) => {
|
|
||||||
// This is where you could transform data if needed
|
|
||||||
return content;
|
|
||||||
},
|
|
||||||
eventSourceFailure: (error) => {
|
|
||||||
console.error("Failed to fetch calendar events:", error);
|
|
||||||
}
|
|
||||||
} as CalendarOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus();
|
|
||||||
// We no longer need to call fetchEvents() here because FullCalendar does it automatically.
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// We can remove the fetchEvents method as FullCalendar now handles it.
|
|
||||||
// async fetchEvents(): Promise<void> { ... }
|
|
||||||
|
|
||||||
handleEventClick(clickInfo: EventClickArg): void {
|
// Functions
|
||||||
|
// We can remove the fetchEvents method as FullCalendar now handles it.
|
||||||
|
// async fetchEvents(): Promise<void> { ... }
|
||||||
|
|
||||||
|
const handleEventClick = (clickInfo: EventClickArg): void => {
|
||||||
// This logic remains the same, as it correctly pulls data from extendedProps
|
// This logic remains the same, as it correctly pulls data from extendedProps
|
||||||
this.selectedServiceForEdit = {
|
selectedServiceForEdit.value = {
|
||||||
id: parseInt(clickInfo.event.id),
|
id: parseInt(clickInfo.event.id),
|
||||||
scheduled_date: clickInfo.event.startStr,
|
scheduled_date: clickInfo.event.startStr,
|
||||||
customer_name: clickInfo.event.title.split(': ')[1] || 'Unknown Customer',
|
customer_name: clickInfo.event.title.split(': ')[1] || 'Unknown Customer',
|
||||||
@@ -105,52 +80,77 @@ export default defineComponent({
|
|||||||
description: clickInfo.event.extendedProps.description,
|
description: clickInfo.event.extendedProps.description,
|
||||||
service_cost: clickInfo.event.extendedProps.service_cost,
|
service_cost: clickInfo.event.extendedProps.service_cost,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
closeEditModal() {
|
// Calendar options
|
||||||
this.selectedServiceForEdit = null;
|
const calendarOptions = ref({
|
||||||
|
plugins: [dayGridPlugin, interactionPlugin],
|
||||||
|
initialView: 'dayGridMonth',
|
||||||
|
weekends: true,
|
||||||
|
// Instead of a static array, we use a function source.
|
||||||
|
// This is the standard way FullCalendar fetches events.
|
||||||
|
events: `${import.meta.env.VITE_BASE_URL}/service/all`,
|
||||||
|
eventClick: handleEventClick,
|
||||||
|
// Add headers for authentication if needed by your API
|
||||||
|
eventSourceSuccess: (content) => {
|
||||||
|
// This is where you could transform data if needed
|
||||||
|
return content;
|
||||||
},
|
},
|
||||||
|
eventSourceFailure: (error) => {
|
||||||
|
console.error("Failed to fetch calendar events:", error);
|
||||||
|
}
|
||||||
|
} as CalendarOptions)
|
||||||
|
|
||||||
// =================== THIS IS THE CORRECTED SECTION ===================
|
// Lifecycle
|
||||||
async handleSaveChanges(updatedService: ServiceCall) {
|
onMounted(() => {
|
||||||
|
userStatus();
|
||||||
|
// We no longer need to call fetchEvents() here because FullCalendar does it automatically.
|
||||||
|
})
|
||||||
|
|
||||||
|
const closeEditModal = () => {
|
||||||
|
selectedServiceForEdit.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================== THIS IS THE CORRECTED SECTION ===================
|
||||||
|
const handleSaveChanges = async (updatedService: ServiceCall) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
||||||
await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
||||||
|
|
||||||
// Get the FullCalendar component instance from the ref
|
// Get the FullCalendar component instance from the ref
|
||||||
const calendarApi = (this.$refs.fullCalendar as any).getApi();
|
const calendarApi = (fullCalendar.value as any).getApi();
|
||||||
if (calendarApi) {
|
if (calendarApi) {
|
||||||
// Tell FullCalendar to re-fetch its events from the source.
|
// Tell FullCalendar to re-fetch its events from the source.
|
||||||
// This is the most reliable way to refresh the view immediately.
|
// This is the most reliable way to refresh the view immediately.
|
||||||
calendarApi.refetchEvents();
|
calendarApi.refetchEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// =================== END OF CORRECTED SECTION ===================
|
// =================== END OF CORRECTED SECTION ===================
|
||||||
|
|
||||||
async handleDeleteService(serviceId: number) {
|
const handleDeleteService = async (serviceId: number) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
||||||
await axios.delete(path, { withCredentials: true, headers: authHeader() });
|
await axios.delete(path, { withCredentials: true, headers: authHeader() });
|
||||||
|
|
||||||
// Also refresh the calendar after a delete
|
// Also refresh the calendar after a delete
|
||||||
const calendarApi = (this.$refs.fullCalendar as any).getApi();
|
const calendarApi = (fullCalendar.value as any).getApi();
|
||||||
if (calendarApi) {
|
if (calendarApi) {
|
||||||
calendarApi.refetchEvents();
|
calendarApi.refetchEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting event:", error);
|
console.error("Error deleting event:", error);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -160,13 +160,11 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
@@ -99,8 +99,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import authHeader from '../../services/auth.header';
|
import authHeader from '../../services/auth.header';
|
||||||
@@ -111,88 +111,94 @@ interface EditableService extends Omit<ServiceCall, 'scheduled_date'> { date: st
|
|||||||
interface Customer { id: number; account_number: string; customer_first_name: string; customer_last_name: string; customer_address: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; }
|
interface Customer { id: number; account_number: string; customer_first_name: string; customer_last_name: string; customer_address: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; }
|
||||||
interface ServiceParts { customer_id: number; oil_filter: string; oil_filter_2: string; oil_nozzle: string; oil_nozzle_2: string; }
|
interface ServiceParts { customer_id: number; oil_filter: string; oil_filter_2: string; oil_nozzle: string; oil_nozzle_2: string; }
|
||||||
|
|
||||||
export default defineComponent({
|
// Props and Emits
|
||||||
name: 'ServiceEditModal',
|
const props = defineProps<{
|
||||||
props: { service: { type: Object as PropType<Partial<ServiceCall>>, required: true } },
|
service: Partial<ServiceCall>
|
||||||
data() {
|
}>()
|
||||||
return {
|
|
||||||
editableService: {} as Partial<EditableService>,
|
const emit = defineEmits<{
|
||||||
customer: null as Customer | null,
|
'close-modal': []
|
||||||
serviceParts: null as ServiceParts | null,
|
'save-changes': [service: ServiceCall]
|
||||||
isLoadingParts: true,
|
'delete-service': [serviceId: number]
|
||||||
serviceOptions: [
|
}>()
|
||||||
|
|
||||||
|
// Reactive data
|
||||||
|
const editableService = ref({} as Partial<EditableService>)
|
||||||
|
const customer = ref(null as Customer | null)
|
||||||
|
const serviceParts = ref(null as ServiceParts | null)
|
||||||
|
const isLoadingParts = ref(true)
|
||||||
|
const serviceOptions = ref([
|
||||||
{ text: 'Tune-up', value: 0 }, { text: 'No Heat', value: 1 }, { text: 'Fix', value: 2 },
|
{ text: 'Tune-up', value: 0 }, { text: 'No Heat', value: 1 }, { text: 'Fix', value: 2 },
|
||||||
{ text: 'Tank Install', value: 3 }, { text: 'Other', value: 4 },
|
{ text: 'Tank Install', value: 3 }, { text: 'Other', value: 4 },
|
||||||
],
|
])
|
||||||
};
|
|
||||||
},
|
// Watchers
|
||||||
watch: {
|
watch(() => props.service, (newVal) => {
|
||||||
service: {
|
|
||||||
handler(newVal) {
|
|
||||||
if (!newVal) return;
|
if (!newVal) return;
|
||||||
const scheduled = dayjs(newVal.scheduled_date || new Date());
|
const scheduled = dayjs(newVal.scheduled_date || new Date());
|
||||||
this.editableService = { ...newVal, date: scheduled.format('YYYY-MM-DD'), time: scheduled.hour() };
|
editableService.value = { ...newVal, date: scheduled.format('YYYY-MM-DD'), time: scheduled.hour() };
|
||||||
if (newVal.customer_id) {
|
if (newVal.customer_id) {
|
||||||
this.getCustomer(newVal.customer_id);
|
getCustomer(newVal.customer_id);
|
||||||
this.getServiceParts(newVal.customer_id);
|
getServiceParts(newVal.customer_id);
|
||||||
}
|
}
|
||||||
},
|
}, { immediate: true, deep: true })
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
// Functions
|
||||||
},
|
const getCustomer = (customerId: number) => {
|
||||||
},
|
customer.value = null;
|
||||||
methods: {
|
|
||||||
getCustomer(customerId: number) {
|
|
||||||
this.customer = null;
|
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/customer/' + customerId;
|
let path = import.meta.env.VITE_BASE_URL + '/customer/' + customerId;
|
||||||
axios.get(path, { headers: authHeader() })
|
axios.get(path, { headers: authHeader() })
|
||||||
.then((response: any) => { this.customer = response.data; })
|
.then((response: any) => { customer.value = response.data; })
|
||||||
.catch((error: any) => { console.error("Failed to fetch customer details for modal:", error); });
|
.catch((error: any) => { console.error("Failed to fetch customer details for modal:", error); });
|
||||||
},
|
}
|
||||||
getServiceParts(customerId: number) {
|
|
||||||
this.isLoadingParts = true;
|
const getServiceParts = (customerId: number) => {
|
||||||
this.serviceParts = null;
|
isLoadingParts.value = true;
|
||||||
|
serviceParts.value = null;
|
||||||
let path = `${import.meta.env.VITE_BASE_URL}/service/parts/customer/${customerId}`;
|
let path = `${import.meta.env.VITE_BASE_URL}/service/parts/customer/${customerId}`;
|
||||||
axios.get(path, { headers: authHeader() })
|
axios.get(path, { headers: authHeader() })
|
||||||
.then((response: any) => { this.serviceParts = response.data; })
|
.then((response: any) => { serviceParts.value = response.data; })
|
||||||
.catch((error: any) => { console.error("Failed to fetch service parts:", error); })
|
.catch((error: any) => { console.error("Failed to fetch service parts:", error); })
|
||||||
.finally(() => { this.isLoadingParts = false; });
|
.finally(() => { isLoadingParts.value = false; });
|
||||||
},
|
}
|
||||||
async saveChanges() {
|
|
||||||
const date = this.editableService.date;
|
const saveChanges = async () => {
|
||||||
const time = this.editableService.time || 0;
|
const date = editableService.value.date;
|
||||||
|
const time = editableService.value.time || 0;
|
||||||
const combinedDateTime = dayjs(`${date} ${time}:00`).format('YYYY-MM-DDTHH:mm:ss');
|
const combinedDateTime = dayjs(`${date} ${time}:00`).format('YYYY-MM-DDTHH:mm:ss');
|
||||||
const finalPayload = { ...this.service, ...this.editableService, scheduled_date: combinedDateTime };
|
const finalPayload = { ...props.service, ...editableService.value, scheduled_date: combinedDateTime };
|
||||||
|
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${finalPayload.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${finalPayload.id}`;
|
||||||
try {
|
try {
|
||||||
await axios.put(path, finalPayload, { headers: authHeader(), withCredentials: true });
|
await axios.put(path, finalPayload, { headers: authHeader(), withCredentials: true });
|
||||||
this.$emit('save-changes', finalPayload);
|
emit('save-changes', finalPayload as ServiceCall);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
confirmDelete() {
|
|
||||||
if (this.service.id && window.confirm(`Are you sure you want to delete this service call?`)) {
|
const confirmDelete = () => {
|
||||||
this.$emit('delete-service', this.service.id);
|
if (props.service.id && window.confirm(`Are you sure you want to delete this service call?`)) {
|
||||||
|
emit('delete-service', props.service.id);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getServiceTypeName(typeId: number | undefined | null): string {
|
|
||||||
|
const getServiceTypeName = (typeId: number | undefined | null): string => {
|
||||||
if (typeId === undefined || typeId === null) return 'Unknown';
|
if (typeId === undefined || typeId === null) return 'Unknown';
|
||||||
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
const typeMap: { [key: number]: string } = { 0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other' };
|
||||||
return typeMap[typeId] || 'Unknown';
|
return typeMap[typeId] || 'Unknown';
|
||||||
},
|
}
|
||||||
getServiceTypeColor(typeId: number | undefined | null): string {
|
|
||||||
|
const getServiceTypeColor = (typeId: number | undefined | null): string => {
|
||||||
if (typeId === undefined || typeId === null) return 'gray';
|
if (typeId === undefined || typeId === null) return 'gray';
|
||||||
const colorMap: { [key: number]: string } = { 0: 'blue', 1: 'red', 2: 'green', 3: '#B58900', 4: 'black' };
|
const colorMap: { [key: number]: string } = { 0: 'blue', 1: 'red', 2: 'green', 3: '#B58900', 4: 'black' };
|
||||||
return colorMap[typeId] || 'gray';
|
return colorMap[typeId] || 'gray';
|
||||||
},
|
}
|
||||||
getStateAbbrev(stateId: number | undefined | null): string {
|
|
||||||
|
const getStateAbbrev = (stateId: number | undefined | null): string => {
|
||||||
if (stateId === undefined || stateId === null) return 'Unknown';
|
if (stateId === undefined || stateId === null) return 'Unknown';
|
||||||
const stateMap: { [key: number]: string } = { 0: 'MA', 1: 'RI', 2: 'NH', 3: 'ME', 4: 'VT', 5: 'CT', 6: 'NY' };
|
const stateMap: { [key: number]: string } = { 0: 'MA', 1: 'RI', 2: 'NH', 3: 'ME', 4: 'VT', 5: 'CT', 6: 'NY' };
|
||||||
return stateMap[stateId] || 'Unknown';
|
return stateMap[stateId] || 'Unknown';
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -156,63 +156,48 @@
|
|||||||
@delete-service="handleDeleteService"
|
@delete-service="handleDeleteService"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { ServiceCall } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import ServiceEditModal from './ServiceEditModal.vue'
|
import ServiceEditModal from './ServiceEditModal.vue'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface ServiceCall {
|
// Reactive data
|
||||||
id: number;
|
const user = ref(null)
|
||||||
scheduled_date: string;
|
const services = ref<ServiceCall[]>([])
|
||||||
customer_id: number;
|
const isLoading = ref(true)
|
||||||
customer_name: string;
|
const selectedServiceForEdit = ref<ServiceCall | null>(null)
|
||||||
customer_address: string;
|
// --- ADDITIONS FOR TRUNCATION ---
|
||||||
customer_town: string;
|
const wordLimit = ref(50)
|
||||||
type_service_call: number;
|
const expandedIds = ref<number[]>([])
|
||||||
description: string;
|
|
||||||
service_cost: string;
|
|
||||||
payment_status?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
// Lifecycle
|
||||||
name: 'ServiceHome',
|
onMounted(() => {
|
||||||
components: { Footer, ServiceEditModal },
|
userStatus();
|
||||||
data() {
|
fetchUpcomingServices();
|
||||||
return {
|
})
|
||||||
user: null,
|
|
||||||
services: [] as ServiceCall[],
|
// Functions
|
||||||
isLoading: true,
|
const fetchUpcomingServices = async (): Promise<void> => {
|
||||||
selectedServiceForEdit: null as ServiceCall | null,
|
isLoading.value = true;
|
||||||
// --- ADDITIONS FOR TRUNCATION ---
|
|
||||||
wordLimit: 50,
|
|
||||||
expandedIds: [] as number[],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus();
|
|
||||||
this.fetchUpcomingServices();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async fetchUpcomingServices(): Promise<void> {
|
|
||||||
this.isLoading = true;
|
|
||||||
try {
|
try {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/service/upcoming';
|
const path = import.meta.env.VITE_BASE_URL + '/service/upcoming';
|
||||||
const response = await axios.get(path, {
|
const response = await axios.get(path, {
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
this.services = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
services.value = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch upcoming service calls:", error);
|
console.error("Failed to fetch upcoming service calls:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -222,83 +207,88 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
openEditModal(service: ServiceCall) {
|
const openEditModal = (service: ServiceCall) => {
|
||||||
this.selectedServiceForEdit = service;
|
selectedServiceForEdit.value = service;
|
||||||
},
|
}
|
||||||
|
|
||||||
closeEditModal() {
|
const closeEditModal = () => {
|
||||||
this.selectedServiceForEdit = null;
|
selectedServiceForEdit.value = null;
|
||||||
},
|
}
|
||||||
isLongDescription(text: string): boolean {
|
|
||||||
|
const isLongDescription = (text: string): boolean => {
|
||||||
if (!text) return false;
|
if (!text) return false;
|
||||||
return text.split(/\s+/).length > this.wordLimit;
|
return text.split(/\s+/).length > wordLimit.value;
|
||||||
},
|
}
|
||||||
truncateDescription(text: string): string {
|
|
||||||
if (!this.isLongDescription(text)) return text;
|
const truncateDescription = (text: string): string => {
|
||||||
|
if (!isLongDescription(text)) return text;
|
||||||
const words = text.split(/\s+/);
|
const words = text.split(/\s+/);
|
||||||
return words.slice(0, this.wordLimit).join(' ') + '...';
|
return words.slice(0, wordLimit.value).join(' ') + '...';
|
||||||
},
|
}
|
||||||
isExpanded(id: number): boolean {
|
|
||||||
return this.expandedIds.includes(id);
|
const isExpanded = (id: number): boolean => {
|
||||||
},
|
return expandedIds.value.includes(id);
|
||||||
toggleExpand(id: number): void {
|
}
|
||||||
const index = this.expandedIds.indexOf(id);
|
|
||||||
|
const toggleExpand = (id: number): void => {
|
||||||
|
const index = expandedIds.value.indexOf(id);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
this.expandedIds.push(id);
|
expandedIds.value.push(id);
|
||||||
} else {
|
} else {
|
||||||
this.expandedIds.splice(index, 1);
|
expandedIds.value.splice(index, 1);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
async handleSaveChanges(updatedService: ServiceCall) {
|
|
||||||
|
const handleSaveChanges = async (updatedService: ServiceCall) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
||||||
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
const index = this.services.findIndex(s => s.id === updatedService.id);
|
const index = services.value.findIndex(s => s.id === updatedService.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.services[index] = response.data.service;
|
services.value[index] = response.data.service;
|
||||||
}
|
}
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleDeleteService(serviceId: number) {
|
const handleDeleteService = async (serviceId: number) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.services = this.services.filter(s => s.id !== serviceId);
|
services.value = services.value.filter(s => s.id !== serviceId);
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete service call:", error);
|
console.error("Failed to delete service call:", error);
|
||||||
alert("An error occurred while deleting. Please check the console.");
|
alert("An error occurred while deleting. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
const formatDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('MMMM D, YYYY');
|
return dayjs(dateString).format('MMMM D, YYYY');
|
||||||
},
|
}
|
||||||
|
|
||||||
formatTime(dateString: string): string {
|
const formatTime = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('h:mm A');
|
return dayjs(dateString).format('h:mm A');
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeName(typeId: number): string {
|
const getServiceTypeName = (typeId: number): string => {
|
||||||
const typeMap: { [key: number]: string } = {
|
const typeMap: { [key: number]: string } = {
|
||||||
0: 'Tune-up',
|
0: 'Tune-up',
|
||||||
1: 'No Heat',
|
1: 'No Heat',
|
||||||
@@ -307,9 +297,9 @@ export default defineComponent({
|
|||||||
4: 'Other',
|
4: 'Other',
|
||||||
};
|
};
|
||||||
return typeMap[typeId] || 'Unknown Service';
|
return typeMap[typeId] || 'Unknown Service';
|
||||||
},
|
}
|
||||||
// --- ADD THIS METHOD ---
|
|
||||||
formatCurrency(value: string | number): string {
|
const formatCurrency = (value: string | number): string => {
|
||||||
if (value === null || value === undefined || value === '') return '$0.00';
|
if (value === null || value === undefined || value === '') return '$0.00';
|
||||||
const numberValue = Number(value);
|
const numberValue = Number(value);
|
||||||
if (isNaN(numberValue)) return '$0.00';
|
if (isNaN(numberValue)) return '$0.00';
|
||||||
@@ -318,8 +308,9 @@ export default defineComponent({
|
|||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
}).format(numberValue);
|
}).format(numberValue);
|
||||||
},
|
}
|
||||||
getServiceTypeColor(typeId: number): string {
|
|
||||||
|
const getServiceTypeColor = (typeId: number): string => {
|
||||||
const colorMap: { [key: number]: string } = {
|
const colorMap: { [key: number]: string } = {
|
||||||
0: 'blue',
|
0: 'blue',
|
||||||
1: 'red',
|
1: 'red',
|
||||||
@@ -328,15 +319,13 @@ export default defineComponent({
|
|||||||
4: 'black',
|
4: 'black',
|
||||||
};
|
};
|
||||||
return colorMap[typeId] || 'gray';
|
return colorMap[typeId] || 'gray';
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowChargeButton(service: any): boolean {
|
const shouldShowChargeButton = (service: any): boolean => {
|
||||||
return service.payment_status === null || service.payment_status === undefined;
|
return service.payment_status === null || service.payment_status === undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowCaptureButton(service: any): boolean {
|
const shouldShowCaptureButton = (service: any): boolean => {
|
||||||
return service.payment_status === 1;
|
return service.payment_status === 1;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -163,86 +163,72 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { ServiceCall } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import ServiceEditModal from './ServiceEditModal.vue'
|
import ServiceEditModal from './ServiceEditModal.vue'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface ServiceCall {
|
// Reactive data
|
||||||
id: number;
|
const user = ref(null)
|
||||||
scheduled_date: string;
|
const services = ref<ServiceCall[]>([])
|
||||||
customer_id: number;
|
const isLoading = ref(true)
|
||||||
customer_name: string;
|
const selectedServiceForEdit = ref<ServiceCall | null>(null)
|
||||||
customer_address: string;
|
// --- ADDITIONS FOR TRUNCATION ---
|
||||||
customer_town: string;
|
const wordLimit = ref(50)
|
||||||
type_service_call: number;
|
const expandedIds = ref<number[]>([])
|
||||||
description: string;
|
|
||||||
service_cost: string;
|
// Lifecycle
|
||||||
payment_status?: number;
|
onMounted(() => {
|
||||||
|
userStatus();
|
||||||
|
fetchPastServices();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
const isLongDescription = (text: string): boolean => {
|
||||||
|
if (!text) return false;
|
||||||
|
return text.split(/\s+/).length > wordLimit.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
const truncateDescription = (text: string): string => {
|
||||||
name: 'ServiceHPast',
|
if (!isLongDescription(text)) return text;
|
||||||
components: { Footer, ServiceEditModal },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
user: null,
|
|
||||||
services: [] as ServiceCall[],
|
|
||||||
isLoading: true,
|
|
||||||
selectedServiceForEdit: null as ServiceCall | null,
|
|
||||||
// --- ADDITIONS FOR TRUNCATION ---
|
|
||||||
wordLimit: 50,
|
|
||||||
expandedIds: [] as number[],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus();
|
|
||||||
this.fetchPastServices();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// --- NEW METHODS FOR TRUNCATION ---
|
|
||||||
isLongDescription(text: string): boolean {
|
|
||||||
if (!text) return false;
|
|
||||||
return text.split(/\s+/).length > this.wordLimit;
|
|
||||||
},
|
|
||||||
truncateDescription(text: string): string {
|
|
||||||
if (!this.isLongDescription(text)) return text;
|
|
||||||
const words = text.split(/\s+/);
|
const words = text.split(/\s+/);
|
||||||
return words.slice(0, this.wordLimit).join(' ') + '...';
|
return words.slice(0, wordLimit.value).join(' ') + '...';
|
||||||
},
|
}
|
||||||
isExpanded(id: number): boolean {
|
|
||||||
return this.expandedIds.includes(id);
|
|
||||||
},
|
|
||||||
toggleExpand(id: number): void {
|
|
||||||
const index = this.expandedIds.indexOf(id);
|
|
||||||
if (index === -1) {
|
|
||||||
this.expandedIds.push(id);
|
|
||||||
} else {
|
|
||||||
this.expandedIds.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// --- API and Data Handling Methods ---
|
const isExpanded = (id: number): boolean => {
|
||||||
async fetchPastServices(): Promise<void> {
|
return expandedIds.value.includes(id);
|
||||||
this.isLoading = true;
|
}
|
||||||
|
|
||||||
|
const toggleExpand = (id: number): void => {
|
||||||
|
const index = expandedIds.value.indexOf(id);
|
||||||
|
if (index === -1) {
|
||||||
|
expandedIds.value.push(id);
|
||||||
|
} else {
|
||||||
|
expandedIds.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPastServices = async (): Promise<void> => {
|
||||||
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/service/past';
|
const path = import.meta.env.VITE_BASE_URL + '/service/past';
|
||||||
const response = await axios.get(path, {
|
const response = await axios.get(path, {
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
this.services = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
services.value = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch past service calls:", error);
|
console.error("Failed to fetch past service calls:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -252,55 +238,54 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
openEditModal(service: ServiceCall) {
|
const openEditModal = (service: ServiceCall) => {
|
||||||
this.selectedServiceForEdit = service;
|
selectedServiceForEdit.value = service;
|
||||||
},
|
}
|
||||||
|
|
||||||
closeEditModal() {
|
const closeEditModal = () => {
|
||||||
this.selectedServiceForEdit = null;
|
selectedServiceForEdit.value = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleSaveChanges(updatedService: ServiceCall) {
|
const handleSaveChanges = async (updatedService: ServiceCall) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
||||||
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
const index = this.services.findIndex(s => s.id === updatedService.id);
|
const index = services.value.findIndex(s => s.id === updatedService.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.services[index] = response.data.service;
|
services.value[index] = response.data.service;
|
||||||
}
|
}
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleDeleteService(serviceId: number) {
|
const handleDeleteService = async (serviceId: number) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.services = this.services.filter(s => s.id !== serviceId);
|
services.value = services.value.filter(s => s.id !== serviceId);
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete service call:", error);
|
console.error("Failed to delete service call:", error);
|
||||||
alert("An error occurred while deleting. Please check the console.");
|
alert("An error occurred while deleting. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// --- Formatting and Display Methods ---
|
const formatCurrency = (value: string | number): string => {
|
||||||
formatCurrency(value: string | number): string {
|
|
||||||
if (value === null || value === undefined || value === '') return '$0.00';
|
if (value === null || value === undefined || value === '') return '$0.00';
|
||||||
const numberValue = Number(value);
|
const numberValue = Number(value);
|
||||||
if (isNaN(numberValue)) return '$0.00';
|
if (isNaN(numberValue)) return '$0.00';
|
||||||
@@ -309,19 +294,19 @@ export default defineComponent({
|
|||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
}).format(numberValue);
|
}).format(numberValue);
|
||||||
},
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
const formatDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('MMMM D, YYYY');
|
return dayjs(dateString).format('MMMM D, YYYY');
|
||||||
},
|
}
|
||||||
|
|
||||||
formatTime(dateString: string): string {
|
const formatTime = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('h:mm A');
|
return dayjs(dateString).format('h:mm A');
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeName(typeId: number): string {
|
const getServiceTypeName = (typeId: number): string => {
|
||||||
const typeMap: { [key: number]: string } = {
|
const typeMap: { [key: number]: string } = {
|
||||||
0: 'Tune-up',
|
0: 'Tune-up',
|
||||||
1: 'No Heat',
|
1: 'No Heat',
|
||||||
@@ -330,9 +315,9 @@ export default defineComponent({
|
|||||||
4: 'Other',
|
4: 'Other',
|
||||||
};
|
};
|
||||||
return typeMap[typeId] || 'Unknown Service';
|
return typeMap[typeId] || 'Unknown Service';
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeColor(typeId: number): string {
|
const getServiceTypeColor = (typeId: number): string => {
|
||||||
const colorMap: { [key: number]: string } = {
|
const colorMap: { [key: number]: string } = {
|
||||||
0: 'blue',
|
0: 'blue',
|
||||||
1: 'red',
|
1: 'red',
|
||||||
@@ -341,15 +326,13 @@ export default defineComponent({
|
|||||||
4: 'black',
|
4: 'black',
|
||||||
};
|
};
|
||||||
return colorMap[typeId] || 'gray';
|
return colorMap[typeId] || 'gray';
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowChargeButton(service: any): boolean {
|
const shouldShowChargeButton = (service: any): boolean => {
|
||||||
return service.payment_status === null || service.payment_status === undefined;
|
return service.payment_status === null || service.payment_status === undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowCaptureButton(service: any): boolean {
|
const shouldShowCaptureButton = (service: any): boolean => {
|
||||||
return service.payment_status === 1;
|
return service.payment_status === 1;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -123,56 +123,43 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { ServicePlan } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface ServicePlan {
|
// Reactive data
|
||||||
id: number;
|
const user = ref(null)
|
||||||
customer_id: number;
|
const servicePlans = ref<ServicePlan[]>([])
|
||||||
customer_name: string;
|
const isLoading = ref(true)
|
||||||
customer_address: string;
|
|
||||||
customer_town: string;
|
|
||||||
contract_plan: number;
|
|
||||||
contract_years: number;
|
|
||||||
contract_start_date: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
// Lifecycle
|
||||||
name: 'ServicePlans',
|
onMounted(() => {
|
||||||
components: { Footer },
|
userStatus();
|
||||||
data() {
|
fetchServicePlans();
|
||||||
return {
|
})
|
||||||
user: null,
|
|
||||||
servicePlans: [] as ServicePlan[],
|
// Functions
|
||||||
isLoading: true,
|
const fetchServicePlans = async (): Promise<void> => {
|
||||||
}
|
isLoading.value = true;
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus();
|
|
||||||
this.fetchServicePlans();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async fetchServicePlans(): Promise<void> {
|
|
||||||
this.isLoading = true;
|
|
||||||
try {
|
try {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/service/plans/active';
|
const path = import.meta.env.VITE_BASE_URL + '/service/plans/active';
|
||||||
const response = await axios.get(path, {
|
const response = await axios.get(path, {
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
this.servicePlans = response.data;
|
servicePlans.value = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch service plans:", error);
|
console.error("Failed to fetch service plans:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -182,41 +169,41 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
getPlanName(planType: number): string {
|
const getPlanName = (planType: number): string => {
|
||||||
const planNames: { [key: number]: string } = {
|
const planNames: { [key: number]: string } = {
|
||||||
1: 'Standard',
|
1: 'Standard',
|
||||||
2: 'Premium'
|
2: 'Premium'
|
||||||
};
|
};
|
||||||
return planNames[planType] || 'Unknown';
|
return planNames[planType] || 'Unknown';
|
||||||
},
|
}
|
||||||
|
|
||||||
getPlanColor(planType: number): string {
|
const getPlanColor = (planType: number): string => {
|
||||||
const planColors: { [key: number]: string } = {
|
const planColors: { [key: number]: string } = {
|
||||||
1: 'blue',
|
1: 'blue',
|
||||||
2: 'gold'
|
2: 'gold'
|
||||||
};
|
};
|
||||||
return planColors[planType] || 'gray';
|
return planColors[planType] || 'gray';
|
||||||
},
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
const formatDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('MMM D, YYYY');
|
return dayjs(dateString).format('MMM D, YYYY');
|
||||||
},
|
}
|
||||||
|
|
||||||
formatEndDate(startDate: string, years: number): string {
|
const formatEndDate = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'N/A';
|
if (!startDate) return 'N/A';
|
||||||
return dayjs(startDate).add(years, 'year').format('MMM D, YYYY');
|
return dayjs(startDate).add(years, 'year').format('MMM D, YYYY');
|
||||||
},
|
}
|
||||||
|
|
||||||
getStatusText(startDate: string, years: number): string {
|
const getStatusText = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'Unknown';
|
if (!startDate) return 'Unknown';
|
||||||
const endDate = dayjs(startDate).add(years, 'year');
|
const endDate = dayjs(startDate).add(years, 'year');
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
@@ -227,9 +214,9 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'Active';
|
return 'Active';
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getStatusBadge(startDate: string, years: number): string {
|
const getStatusBadge = (startDate: string, years: number): string => {
|
||||||
if (!startDate) return 'badge-ghost';
|
if (!startDate) return 'badge-ghost';
|
||||||
const endDate = dayjs(startDate).add(years, 'year');
|
const endDate = dayjs(startDate).add(years, 'year');
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
@@ -240,7 +227,5 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
return 'badge-success';
|
return 'badge-success';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -163,86 +163,72 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../../services/auth.header'
|
import authHeader from '../../services/auth.header'
|
||||||
|
import { ServiceCall } from '../../types/models'
|
||||||
import Footer from '../../layouts/footers/footer.vue'
|
import Footer from '../../layouts/footers/footer.vue'
|
||||||
import ServiceEditModal from './ServiceEditModal.vue'
|
import ServiceEditModal from './ServiceEditModal.vue'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface ServiceCall {
|
// Reactive data
|
||||||
id: number;
|
const user = ref(null)
|
||||||
scheduled_date: string;
|
const services = ref<ServiceCall[]>([])
|
||||||
customer_id: number;
|
const isLoading = ref(true)
|
||||||
customer_name: string;
|
const selectedServiceForEdit = ref<ServiceCall | null>(null)
|
||||||
customer_address: string;
|
// --- ADDITIONS FOR TRUNCATION ---
|
||||||
customer_town: string;
|
const wordLimit = ref(50)
|
||||||
type_service_call: number;
|
const expandedIds = ref<number[]>([])
|
||||||
description: string;
|
|
||||||
service_cost: string;
|
// Lifecycle
|
||||||
payment_status?: number;
|
onMounted(() => {
|
||||||
|
userStatus();
|
||||||
|
fetchTodayServices();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
const isLongDescription = (text: string): boolean => {
|
||||||
|
if (!text) return false;
|
||||||
|
return text.split(/\s+/).length > wordLimit.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
const truncateDescription = (text: string): string => {
|
||||||
name: 'ServiceToday',
|
if (!isLongDescription(text)) return text;
|
||||||
components: { Footer, ServiceEditModal },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
user: null,
|
|
||||||
services: [] as ServiceCall[],
|
|
||||||
isLoading: true,
|
|
||||||
selectedServiceForEdit: null as ServiceCall | null,
|
|
||||||
// --- ADDITIONS FOR TRUNCATION ---
|
|
||||||
wordLimit: 50,
|
|
||||||
expandedIds: [] as number[],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.userStatus();
|
|
||||||
this.fetchTodayServices();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// --- NEW METHODS FOR TRUNCATION ---
|
|
||||||
isLongDescription(text: string): boolean {
|
|
||||||
if (!text) return false;
|
|
||||||
return text.split(/\s+/).length > this.wordLimit;
|
|
||||||
},
|
|
||||||
truncateDescription(text: string): string {
|
|
||||||
if (!this.isLongDescription(text)) return text;
|
|
||||||
const words = text.split(/\s+/);
|
const words = text.split(/\s+/);
|
||||||
return words.slice(0, this.wordLimit).join(' ') + '...';
|
return words.slice(0, wordLimit.value).join(' ') + '...';
|
||||||
},
|
}
|
||||||
isExpanded(id: number): boolean {
|
|
||||||
return this.expandedIds.includes(id);
|
|
||||||
},
|
|
||||||
toggleExpand(id: number): void {
|
|
||||||
const index = this.expandedIds.indexOf(id);
|
|
||||||
if (index === -1) {
|
|
||||||
this.expandedIds.push(id);
|
|
||||||
} else {
|
|
||||||
this.expandedIds.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// --- API and Data Handling Methods ---
|
const isExpanded = (id: number): boolean => {
|
||||||
async fetchTodayServices(): Promise<void> {
|
return expandedIds.value.includes(id);
|
||||||
this.isLoading = true;
|
}
|
||||||
|
|
||||||
|
const toggleExpand = (id: number): void => {
|
||||||
|
const index = expandedIds.value.indexOf(id);
|
||||||
|
if (index === -1) {
|
||||||
|
expandedIds.value.push(id);
|
||||||
|
} else {
|
||||||
|
expandedIds.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchTodayServices = async (): Promise<void> => {
|
||||||
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const path = import.meta.env.VITE_BASE_URL + '/service/today';
|
const path = import.meta.env.VITE_BASE_URL + '/service/today';
|
||||||
const response = await axios.get(path, {
|
const response = await axios.get(path, {
|
||||||
headers: authHeader(),
|
headers: authHeader(),
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
this.services = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
services.value = response.data.sort((a: ServiceCall, b: ServiceCall) => b.id - a.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch today's service calls:", error);
|
console.error("Failed to fetch today's service calls:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
userStatus() {
|
const userStatus = () => {
|
||||||
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -252,55 +238,54 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.user = response.data.user;
|
user.value = response.data.user;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.user = null
|
user.value = null
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
openEditModal(service: ServiceCall) {
|
const openEditModal = (service: ServiceCall) => {
|
||||||
this.selectedServiceForEdit = service;
|
selectedServiceForEdit.value = service;
|
||||||
},
|
}
|
||||||
|
|
||||||
closeEditModal() {
|
const closeEditModal = () => {
|
||||||
this.selectedServiceForEdit = null;
|
selectedServiceForEdit.value = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleSaveChanges(updatedService: ServiceCall) {
|
const handleSaveChanges = async (updatedService: ServiceCall) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
||||||
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
const response = await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
const index = this.services.findIndex(s => s.id === updatedService.id);
|
const index = services.value.findIndex(s => s.id === updatedService.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.services[index] = response.data.service;
|
services.value[index] = response.data.service;
|
||||||
}
|
}
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async handleDeleteService(serviceId: number) {
|
const handleDeleteService = async (serviceId: number) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
||||||
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
const response = await axios.delete(path, { headers: authHeader(), withCredentials: true });
|
||||||
if (response.data.ok) {
|
if (response.data.ok) {
|
||||||
this.services = this.services.filter(s => s.id !== serviceId);
|
services.value = services.value.filter(s => s.id !== serviceId);
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete service call:", error);
|
console.error("Failed to delete service call:", error);
|
||||||
alert("An error occurred while deleting. Please check the console.");
|
alert("An error occurred while deleting. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// --- Formatting and Display Methods ---
|
const formatCurrency = (value: string | number): string => {
|
||||||
formatCurrency(value: string | number): string {
|
|
||||||
if (value === null || value === undefined || value === '') return '$0.00';
|
if (value === null || value === undefined || value === '') return '$0.00';
|
||||||
const numberValue = Number(value);
|
const numberValue = Number(value);
|
||||||
if (isNaN(numberValue)) return '$0.00';
|
if (isNaN(numberValue)) return '$0.00';
|
||||||
@@ -309,19 +294,19 @@ export default defineComponent({
|
|||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
}).format(numberValue);
|
}).format(numberValue);
|
||||||
},
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
const formatDate = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('MMMM D, YYYY');
|
return dayjs(dateString).format('MMMM D, YYYY');
|
||||||
},
|
}
|
||||||
|
|
||||||
formatTime(dateString: string): string {
|
const formatTime = (dateString: string): string => {
|
||||||
if (!dateString) return 'N/A';
|
if (!dateString) return 'N/A';
|
||||||
return dayjs(dateString).format('h:mm A');
|
return dayjs(dateString).format('h:mm A');
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeName(typeId: number): string {
|
const getServiceTypeName = (typeId: number): string => {
|
||||||
const typeMap: { [key: number]: string } = {
|
const typeMap: { [key: number]: string } = {
|
||||||
0: 'Tune-up',
|
0: 'Tune-up',
|
||||||
1: 'No Heat',
|
1: 'No Heat',
|
||||||
@@ -330,9 +315,9 @@ export default defineComponent({
|
|||||||
4: 'Other',
|
4: 'Other',
|
||||||
};
|
};
|
||||||
return typeMap[typeId] || 'Unknown Service';
|
return typeMap[typeId] || 'Unknown Service';
|
||||||
},
|
}
|
||||||
|
|
||||||
getServiceTypeColor(typeId: number): string {
|
const getServiceTypeColor = (typeId: number): string => {
|
||||||
const colorMap: { [key: number]: string } = {
|
const colorMap: { [key: number]: string } = {
|
||||||
0: 'blue',
|
0: 'blue',
|
||||||
1: 'red',
|
1: 'red',
|
||||||
@@ -341,15 +326,13 @@ export default defineComponent({
|
|||||||
4: 'black',
|
4: 'black',
|
||||||
};
|
};
|
||||||
return colorMap[typeId] || 'gray';
|
return colorMap[typeId] || 'gray';
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowChargeButton(service: any): boolean {
|
const shouldShowChargeButton = (service: any): boolean => {
|
||||||
return service.payment_status === null || service.payment_status === undefined;
|
return service.payment_status === null || service.payment_status === undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldShowCaptureButton(service: any): boolean {
|
const shouldShowCaptureButton = (service: any): boolean => {
|
||||||
return service.payment_status === 1;
|
return service.payment_status === 1;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -55,8 +55,9 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import Header from '../../../layouts/headers/headerauth.vue';
|
import Header from '../../../layouts/headers/headerauth.vue';
|
||||||
import FullCalendar from '@fullcalendar/vue3';
|
import FullCalendar from '@fullcalendar/vue3';
|
||||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||||
@@ -70,45 +71,18 @@ import authHeader from '../../../services/auth.header';
|
|||||||
interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; service_cost: string;}
|
interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; service_cost: string;}
|
||||||
interface Customer { id: number; customer_last_name: string; customer_first_name: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; customer_address: string; customer_home_type: number; customer_apt: string; }
|
interface Customer { id: number; customer_last_name: string; customer_first_name: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; customer_address: string; customer_home_type: number; customer_apt: string; }
|
||||||
|
|
||||||
export default defineComponent({
|
// Route
|
||||||
name: 'CalendarCustomer',
|
const route = useRoute();
|
||||||
components: { Header, FullCalendar, EventSidebar, ServiceEditModal },
|
|
||||||
data() {
|
// Functions declared first (needed for calendarOptions)
|
||||||
return {
|
const handleEventClick = (clickInfo: EventClickArg): void => {
|
||||||
isLoading: false,
|
const events = (calendarOptions.value.events as any[]) || [];
|
||||||
selectedServiceForEdit: null as Partial<ServiceCall> | null,
|
|
||||||
calendarOptions: {
|
|
||||||
plugins: [dayGridPlugin, interactionPlugin],
|
|
||||||
initialView: 'dayGridMonth',
|
|
||||||
weekends: true,
|
|
||||||
events: [] as any[],
|
|
||||||
eventClick: this.handleEventClick,
|
|
||||||
} as CalendarOptions,
|
|
||||||
customer: null as Customer | null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route.params.id': {
|
|
||||||
handler(newId) {
|
|
||||||
if (newId) this.getCustomer(newId as string);
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fetchEvents();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// --- THIS IS THE FIX ---
|
|
||||||
// The logic from ServiceCalendar.vue is now correctly applied here.
|
|
||||||
handleEventClick(clickInfo: EventClickArg): void {
|
|
||||||
const events = (this.calendarOptions.events as any[]) || [];
|
|
||||||
const originalEvent = events.find(e => e.id == clickInfo.event.id);
|
const originalEvent = events.find(e => e.id == clickInfo.event.id);
|
||||||
|
|
||||||
if (originalEvent) {
|
if (originalEvent) {
|
||||||
// We "flatten" the nested object from the calendar into the simple,
|
// We "flatten" the nested object from the calendar into the simple,
|
||||||
// flat structure that the modal expects, ensuring customer_id is included.
|
// flat structure that the modal expects, ensuring customer_id is included.
|
||||||
this.selectedServiceForEdit = {
|
selectedServiceForEdit.value = {
|
||||||
id: originalEvent.id,
|
id: originalEvent.id,
|
||||||
scheduled_date: originalEvent.start,
|
scheduled_date: originalEvent.start,
|
||||||
customer_id: originalEvent.customer_id, // This was the missing piece
|
customer_id: originalEvent.customer_id, // This was the missing piece
|
||||||
@@ -120,91 +94,113 @@ export default defineComponent({
|
|||||||
customer_town: '',
|
customer_town: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
closeEditModal() {
|
// Reactive data
|
||||||
this.selectedServiceForEdit = null;
|
const isLoading = ref(false);
|
||||||
},
|
const selectedServiceForEdit = ref<Partial<ServiceCall> | null>(null);
|
||||||
|
const calendarOptions = ref<CalendarOptions>({
|
||||||
|
plugins: [dayGridPlugin, interactionPlugin],
|
||||||
|
initialView: 'dayGridMonth',
|
||||||
|
weekends: true,
|
||||||
|
events: [] as any[],
|
||||||
|
eventClick: handleEventClick,
|
||||||
|
});
|
||||||
|
const customer = ref<Customer | null>(null);
|
||||||
|
|
||||||
async handleSaveChanges(updatedService: ServiceCall) {
|
// Watchers
|
||||||
|
watch(() => route.params.id, (newId) => {
|
||||||
|
if (newId) getCustomer(newId as string);
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
onMounted(() => {
|
||||||
|
fetchEvents();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
const closeEditModal = () => {
|
||||||
|
selectedServiceForEdit.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveChanges = async (updatedService: ServiceCall) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`;
|
||||||
await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true });
|
||||||
await this.fetchEvents();
|
await fetchEvents();
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save changes:", error);
|
console.error("Failed to save changes:", error);
|
||||||
alert("An error occurred while saving. Please check the console.");
|
alert("An error occurred while saving. Please check the console.");
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async handleDeleteService(serviceId: number) {
|
const handleDeleteService = async (serviceId: number) => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`;
|
||||||
const response = await axios.delete(path, { withCredentials: true, headers: authHeader() });
|
const response = await axios.delete(path, { withCredentials: true, headers: authHeader() });
|
||||||
if (response.data.ok === true) {
|
if (response.data.ok === true) {
|
||||||
await this.fetchEvents();
|
await fetchEvents();
|
||||||
this.closeEditModal();
|
closeEditModal();
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to delete event:", response.data.error);
|
console.error("Failed to delete event:", response.data.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting event:", error);
|
console.error("Error deleting event:", error);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async getCustomer(customerId: string): Promise<void> {
|
const getCustomer = async (customerId: string): Promise<void> => {
|
||||||
this.isLoading = true;
|
isLoading.value = true;
|
||||||
this.customer = null;
|
customer.value = null;
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId}`;
|
const path = `${import.meta.env.VITE_BASE_URL}/customer/${customerId}`;
|
||||||
const response = await axios.get(path, { withCredentials: true, headers: authHeader() });
|
const response = await axios.get(path, { withCredentials: true, headers: authHeader() });
|
||||||
if (response.data && response.data.id) {
|
if (response.data && response.data.id) {
|
||||||
this.customer = response.data;
|
customer.value = response.data;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("API call to get customer FAILED:", error);
|
console.error("API call to get customer FAILED:", error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async fetchEvents(): Promise<void> {
|
const fetchEvents = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/service/all`;
|
const path = `${import.meta.env.VITE_BASE_URL}/service/all`;
|
||||||
const response = await axios.get(path, { headers: authHeader(), withCredentials: true });
|
const response = await axios.get(path, { headers: authHeader(), withCredentials: true });
|
||||||
this.calendarOptions.events = response.data;
|
calendarOptions.value.events = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching all calendar events:", error);
|
console.error("Error fetching all calendar events:", error);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async handleEventScheduled(eventData: any): Promise<void> {
|
const handleEventScheduled = async (eventData: any): Promise<void> => {
|
||||||
if (!this.customer) {
|
if (!customer.value) {
|
||||||
alert("Error: A customer must be loaded in the sidebar to create a new event.");
|
alert("Error: A customer must be loaded in the sidebar to create a new event.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
expected_delivery_date: eventData.start, type_service_call: eventData.type_service_call,
|
expected_delivery_date: eventData.start, type_service_call: eventData.type_service_call,
|
||||||
customer_id: this.customer.id, description: eventData.extendedProps.description,
|
customer_id: customer.value.id, description: eventData.extendedProps.description,
|
||||||
};
|
};
|
||||||
const path = import.meta.env.VITE_BASE_URL + "/service/create";
|
const path = import.meta.env.VITE_BASE_URL + "/service/create";
|
||||||
const response = await axios.post(path, payload, { withCredentials: true, headers: authHeader() });
|
const response = await axios.post(path, payload, { withCredentials: true, headers: authHeader() });
|
||||||
if (response.data.ok === true) {
|
if (response.data.ok === true) {
|
||||||
await this.fetchEvents();
|
await fetchEvents();
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create event:", response.data.error);
|
console.error("Failed to create event:", response.data.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating event:", error);
|
console.error("Error creating event:", error);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async handleEventDelete(eventId: string): Promise<void> {
|
const handleEventDelete = async (eventId: string): Promise<void> => {
|
||||||
// This is a simple alias now, as handleDeleteService is more specific
|
// This is a simple alias now, as handleDeleteService is more specific
|
||||||
await this.handleDeleteService(Number(eventId));
|
await handleDeleteService(Number(eventId));
|
||||||
},
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
@@ -77,8 +77,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface Customer {
|
interface Customer {
|
||||||
@@ -94,80 +94,79 @@ interface Customer {
|
|||||||
customer_apt: string;
|
customer_apt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
// Props
|
||||||
name: 'EventSidebar',
|
const props = defineProps<{
|
||||||
props: {
|
customer: Customer | null;
|
||||||
customer: {
|
}>();
|
||||||
type: Object as PropType<Customer | null>,
|
|
||||||
required: true,
|
// Emits
|
||||||
},
|
const emit = defineEmits<{
|
||||||
},
|
'event-scheduled': [eventData: any];
|
||||||
data() {
|
}>();
|
||||||
return {
|
|
||||||
selectedService: '' as string | number,
|
// Reactive data
|
||||||
serviceOptions: [
|
const selectedService = ref<string | number>('');
|
||||||
|
const serviceOptions = ref([
|
||||||
{ text: 'Tune-up', value: 0 },
|
{ text: 'Tune-up', value: 0 },
|
||||||
{ text: 'No Heat', value: 1 },
|
{ text: 'No Heat', value: 1 },
|
||||||
{ text: 'Fix', value: 2 },
|
{ text: 'Fix', value: 2 },
|
||||||
{ text: 'Tank Install', value: 3 },
|
{ text: 'Tank Install', value: 3 },
|
||||||
{ text: 'Other', value: 4 },
|
{ text: 'Other', value: 4 },
|
||||||
],
|
]);
|
||||||
event: {
|
const event = ref({
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
date: dayjs().format('YYYY-MM-DD'),
|
date: dayjs().format('YYYY-MM-DD'),
|
||||||
endDate: '',
|
endDate: '',
|
||||||
time: 12,
|
time: 12,
|
||||||
},
|
});
|
||||||
};
|
|
||||||
},
|
// Computed properties
|
||||||
computed: {
|
const customerStateName = computed((): string => {
|
||||||
customerStateName(): string {
|
if (!props.customer) return '';
|
||||||
if (!this.customer) return '';
|
|
||||||
const stateMap: { [key: number]: string } = {
|
const stateMap: { [key: number]: string } = {
|
||||||
0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire',
|
0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire',
|
||||||
3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York',
|
3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York',
|
||||||
};
|
};
|
||||||
return stateMap[this.customer.customer_state] || 'Unknown';
|
return stateMap[props.customer.customer_state] || 'Unknown';
|
||||||
},
|
});
|
||||||
customerHomeType(): string {
|
|
||||||
if (!this.customer) return '';
|
const customerHomeType = computed((): string => {
|
||||||
|
if (!props.customer) return '';
|
||||||
const homeTypeMap: { [key: number]: string } = {
|
const homeTypeMap: { [key: number]: string } = {
|
||||||
0: 'Residential', 1: 'Apartment', 2: 'Condo', 3: 'Commercial',
|
0: 'Residential', 1: 'Apartment', 2: 'Condo', 3: 'Commercial',
|
||||||
4: 'Business', 5: 'Construction', 6: 'Container',
|
4: 'Business', 5: 'Construction', 6: 'Container',
|
||||||
};
|
};
|
||||||
return homeTypeMap[this.customer.customer_home_type] || 'Unknown';
|
return homeTypeMap[props.customer.customer_home_type] || 'Unknown';
|
||||||
}
|
});
|
||||||
},
|
|
||||||
methods: {
|
// Functions
|
||||||
submitEvent() {
|
const submitEvent = () => {
|
||||||
if (!this.customer) {
|
if (!props.customer) {
|
||||||
alert("Cannot submit: No customer data is loaded.");
|
alert("Cannot submit: No customer data is loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDateTime = dayjs(`${this.event.date} ${this.event.time}:00`).format('YYYY-MM-DDTHH:mm:ss');
|
const startDateTime = dayjs(`${event.value.date} ${event.value.time}:00`).format('YYYY-MM-DDTHH:mm:ss');
|
||||||
const endDateTime = this.event.endDate ? dayjs(this.event.endDate).add(1, 'day').format('YYYY-MM-DD') : undefined;
|
const endDateTime = event.value.endDate ? dayjs(event.value.endDate).add(1, 'day').format('YYYY-MM-DD') : undefined;
|
||||||
|
|
||||||
const eventPayload = {
|
const eventPayload = {
|
||||||
title: this.event.title,
|
title: event.value.title,
|
||||||
start: startDateTime,
|
start: startDateTime,
|
||||||
type_service_call: this.selectedService,
|
type_service_call: selectedService.value,
|
||||||
end: endDateTime,
|
end: endDateTime,
|
||||||
extendedProps: {
|
extendedProps: {
|
||||||
description: this.event.description,
|
description: event.value.description,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$emit('event-scheduled', eventPayload);
|
emit('event-scheduled', eventPayload);
|
||||||
|
|
||||||
this.event.title = '';
|
event.value.title = '';
|
||||||
this.selectedService = '';
|
selectedService.value = '';
|
||||||
this.event.description = '';
|
event.value.description = '';
|
||||||
this.event.endDate = '';
|
event.value.endDate = '';
|
||||||
this.event.date = dayjs().format('YYYY-MM-DD');
|
event.value.date = dayjs().format('YYYY-MM-DD');
|
||||||
this.event.time = 12;
|
event.value.time = 12;
|
||||||
},
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Import the new component at the top
|
// Import the new component at the top
|
||||||
import ServiceHome from './ServiceHome.vue'
|
const ServiceHome = () => import('./ServiceHome.vue')
|
||||||
import ServicePast from './ServicePast.vue'
|
const ServicePast = () => import('./ServicePast.vue')
|
||||||
import CalendarCustomer from './calender/CalendarCustomer.vue'
|
const CalendarCustomer = () => import('./calender/CalendarCustomer.vue')
|
||||||
import ServiceCalendar from './ServiceCalendar.vue'
|
const ServiceCalendar = () => import('./ServiceCalendar.vue')
|
||||||
import ServiceToday from './ServiceToday.vue'
|
const ServiceToday = () => import('./ServiceToday.vue')
|
||||||
import ServicePlans from './ServicePlans.vue'
|
const ServicePlans = () => import('./ServicePlans.vue')
|
||||||
|
|
||||||
const serviceRoutes = [
|
const serviceRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
import Ticket from "../ticket/ticket.vue";
|
const Ticket = () => import("../ticket/ticket.vue");
|
||||||
import TicketAuto from "../ticket/ticketauto.vue";
|
const TicketAuto = () => import("../ticket/ticketauto.vue");
|
||||||
|
|
||||||
|
|
||||||
const ticketRoutes = [
|
const ticketRoutes = [
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ import authHeader from '../../../services/auth.header'
|
|||||||
import Header from '../../../layouts/headers/headerauth.vue'
|
import Header from '../../../layouts/headers/headerauth.vue'
|
||||||
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
import SideBar from '../../../layouts/sidebar/sidebar.vue'
|
||||||
import Footer from '../../../layouts/footers/footer.vue'
|
import Footer from '../../../layouts/footers/footer.vue'
|
||||||
|
import {AuthorizeTransaction} from '../../../types/models'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'transactionsAuthorize',
|
name: 'transactionsAuthorize',
|
||||||
@@ -182,7 +183,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
transactions: [] as any[],
|
transactions: [] as AuthorizeTransaction[],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -208,7 +209,7 @@ export default defineComponent({
|
|||||||
getStatusText(status: number) {
|
getStatusText(status: number) {
|
||||||
return status === 0 ? 'Approved' : 'Declined'
|
return status === 0 ? 'Approved' : 'Declined'
|
||||||
},
|
},
|
||||||
getSourceText(transaction: any) {
|
getSourceText(transaction: AuthorizeTransaction) {
|
||||||
if (transaction.auto_id) {
|
if (transaction.auto_id) {
|
||||||
return 'Automatic'
|
return 'Automatic'
|
||||||
} else if (transaction.delivery_id) {
|
} else if (transaction.delivery_id) {
|
||||||
@@ -222,7 +223,7 @@ export default defineComponent({
|
|||||||
formatDate(dateStr: string) {
|
formatDate(dateStr: string) {
|
||||||
return dateStr.split('T')[0]; // YYYY-MM-DD
|
return dateStr.split('T')[0]; // YYYY-MM-DD
|
||||||
},
|
},
|
||||||
getCaptureRoute(transaction: any) {
|
getCaptureRoute(transaction: AuthorizeTransaction) {
|
||||||
if (transaction.service_id) {
|
if (transaction.service_id) {
|
||||||
return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } };
|
return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } };
|
||||||
} else if (transaction.delivery_id) {
|
} else if (transaction.delivery_id) {
|
||||||
@@ -230,7 +231,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return {}; // fallback, though condition should prevent this
|
return {}; // fallback, though condition should prevent this
|
||||||
},
|
},
|
||||||
getPreauthRoute(transaction: any) {
|
getPreauthRoute(transaction: AuthorizeTransaction) {
|
||||||
if (transaction.service_id) {
|
if (transaction.service_id) {
|
||||||
return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } };
|
return { name: 'chargeServiceAuthorize', params: { id: transaction.service_id } };
|
||||||
} else if (transaction.delivery_id) {
|
} else if (transaction.delivery_id) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import AuthorizePage from './authorize/index.vue';
|
const AuthorizePage = () => import('./authorize/index.vue');
|
||||||
|
|
||||||
const transactionsRoutes = [
|
const transactionsRoutes = [
|
||||||
{
|
{
|
||||||
|
|||||||
139
src/services/adminService.ts
Normal file
139
src/services/adminService.ts
Normal file
@@ -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;
|
||||||
51
src/services/api.ts
Normal file
51
src/services/api.ts
Normal file
@@ -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;
|
||||||
40
src/services/authService.ts
Normal file
40
src/services/authService.ts
Normal file
@@ -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;
|
||||||
56
src/services/customerService.ts
Normal file
56
src/services/customerService.ts
Normal file
@@ -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<CustomerListResponse> =>
|
||||||
|
api.get(`/customer/all/${page}`),
|
||||||
|
|
||||||
|
getById: (id: number): Promise<ApiResponse<Customer>> =>
|
||||||
|
api.get(`/customer/${id}`),
|
||||||
|
|
||||||
|
create: (data: CreateCustomerRequest): Promise<ApiResponse<Customer>> =>
|
||||||
|
api.post('/customer/create', data),
|
||||||
|
|
||||||
|
update: (id: number, data: UpdateCustomerRequest): Promise<ApiResponse<Customer>> =>
|
||||||
|
api.put(`/customer/edit/${id}`, data),
|
||||||
|
|
||||||
|
delete: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.delete(`/customer/delete/${id}`),
|
||||||
|
|
||||||
|
getCount: (): Promise<ApiResponse<{ count: number }>> =>
|
||||||
|
api.get('/customer/count'),
|
||||||
|
|
||||||
|
// Profile & details
|
||||||
|
getDescription: (id: number): Promise<ApiResponse<CustomerDescription>> =>
|
||||||
|
api.get(`/customer/description/${id}`),
|
||||||
|
|
||||||
|
// Tank information
|
||||||
|
getTank: (id: number): Promise<ApiResponse<TankInspection>> =>
|
||||||
|
api.get(`/customer/tank/${id}`),
|
||||||
|
|
||||||
|
updateTank: (id: number, data: Partial<TankInspection>): Promise<ApiResponse<void>> =>
|
||||||
|
api.put(`/customer/edit/tank/${id}`, data),
|
||||||
|
|
||||||
|
// Automatic delivery
|
||||||
|
getAutomaticStatus: (id: number): Promise<ApiResponse<{ status: number }>> =>
|
||||||
|
api.get(`/customer/automatic/status/${id}`),
|
||||||
|
|
||||||
|
assignAutomatic: (id: number, data: { status: number }): Promise<ApiResponse<{ status: number }>> =>
|
||||||
|
api.put(`/customer/automatic/assign/${id}`, data),
|
||||||
|
|
||||||
|
// Search
|
||||||
|
search: (query: string): Promise<ApiResponse<Customer[]>> =>
|
||||||
|
api.get(`/search/customer?q=${encodeURIComponent(query)}`),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default customerService;
|
||||||
128
src/services/deliveryService.ts
Normal file
128
src/services/deliveryService.ts
Normal file
@@ -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<ApiResponse<Delivery>> =>
|
||||||
|
api.post(`/delivery/create/${customerId}`, data),
|
||||||
|
|
||||||
|
getById: (id: number): Promise<ApiResponse<Delivery>> =>
|
||||||
|
api.get(`/delivery/${id}`),
|
||||||
|
|
||||||
|
getOrder: (id: number): Promise<ApiResponse<Delivery>> =>
|
||||||
|
api.get(`/delivery/order/${id}`),
|
||||||
|
|
||||||
|
update: (id: number, data: UpdateDeliveryRequest): Promise<ApiResponse<Delivery>> =>
|
||||||
|
api.put(`/delivery/edit/${id}`, data),
|
||||||
|
|
||||||
|
delete: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.delete(`/delivery/delete/${id}`),
|
||||||
|
|
||||||
|
cancel: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.put(`/delivery/cancel/${id}`),
|
||||||
|
|
||||||
|
markCancelled: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.put(`/delivery/cancelled/${id}`),
|
||||||
|
|
||||||
|
// List operations
|
||||||
|
getAll: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/all/${page}`),
|
||||||
|
|
||||||
|
getByCustomer: (customerId: number, page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/customer/${customerId}/${page}`),
|
||||||
|
|
||||||
|
getPast1: (customerId: number): Promise<ApiResponse<Delivery[]>> =>
|
||||||
|
api.get(`/delivery/past1/${customerId}`),
|
||||||
|
|
||||||
|
getPast2: (customerId: number): Promise<ApiResponse<Delivery[]>> =>
|
||||||
|
api.get(`/delivery/past2/${customerId}`),
|
||||||
|
|
||||||
|
// Status-based lists
|
||||||
|
getWaiting: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/waiting/${page}`),
|
||||||
|
|
||||||
|
getTomorrow: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/tommorrow/${page}`),
|
||||||
|
|
||||||
|
getOutForDelivery: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/outfordelivery/${page}`),
|
||||||
|
|
||||||
|
getDelivered: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/delivered/${page}`),
|
||||||
|
|
||||||
|
getFinalized: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/finalized/${page}`),
|
||||||
|
|
||||||
|
getPending: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/pending/${page}`),
|
||||||
|
|
||||||
|
getIssues: (page: number = 1): Promise<DeliveryListResponse> =>
|
||||||
|
api.get(`/delivery/issue/${page}`),
|
||||||
|
|
||||||
|
// Status & totals
|
||||||
|
updateStatus: (data: { id: number; status: number }): Promise<ApiResponse<void>> =>
|
||||||
|
api.put('/delivery/updatestatus', data),
|
||||||
|
|
||||||
|
getTotal: (id: number): Promise<ApiResponse<{ total: number }>> =>
|
||||||
|
api.get(`/delivery/total/${id}`),
|
||||||
|
|
||||||
|
// Cash handling
|
||||||
|
handleCash: (id: number, type: string): Promise<ApiResponse<{ amount: number }>> =>
|
||||||
|
api.get(`/delivery/cash/${id}/${type}`),
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
finalize: (id: number, data?: { final_price: number }): Promise<ApiResponse<void>> =>
|
||||||
|
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;
|
||||||
111
src/services/paymentService.ts
Normal file
111
src/services/paymentService.ts
Normal file
@@ -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<ApiResponse<CreditCard>> =>
|
||||||
|
api.get(`/payment/card/${id}`),
|
||||||
|
|
||||||
|
getCards: (customerId: number): Promise<ApiResponse<CreditCard[]>> =>
|
||||||
|
api.get(`/payment/cards/${customerId}`),
|
||||||
|
|
||||||
|
getCardsOnFile: (customerId: number): Promise<ApiResponse<CreditCard[]>> =>
|
||||||
|
api.get(`/payment/cards/onfile/${customerId}`),
|
||||||
|
|
||||||
|
getAllCards: (page: number = 1): Promise<CardListResponse> =>
|
||||||
|
api.get(`/payment/cards/all/${page}`),
|
||||||
|
|
||||||
|
createCard: (customerId: number, data: CreateCardRequest): Promise<ApiResponse<CreditCard>> =>
|
||||||
|
api.post(`/payment/card/create/${customerId}`, data),
|
||||||
|
|
||||||
|
updateCard: (id: number, data: Partial<CreditCard>): Promise<ApiResponse<CreditCard>> =>
|
||||||
|
api.put(`/payment/card/edit/${id}`, data),
|
||||||
|
|
||||||
|
removeCard: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.delete(`/payment/card/remove/${id}`),
|
||||||
|
|
||||||
|
removeCardAlt: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.delete(`/payment/cards/remove/${id}`),
|
||||||
|
|
||||||
|
updatePaymentProfile: (id: number, data: { profile_id: string }): Promise<ApiResponse<void>> =>
|
||||||
|
api.put(`/payment/card/update_payment_profile/${id}`, data),
|
||||||
|
|
||||||
|
// Authorization & capture (Main API)
|
||||||
|
authorizeDelivery: (id: number, data: PaymentRequest): Promise<ApiResponse<PaymentTransaction>> =>
|
||||||
|
api.put(`/payment/authorize/${id}`, data),
|
||||||
|
|
||||||
|
authorizeService: (id: number, data: PaymentRequest): Promise<ApiResponse<PaymentTransaction>> =>
|
||||||
|
api.put(`/payment/authorize/service/${id}`, data),
|
||||||
|
|
||||||
|
cleanupAuthorization: (id: number): Promise<ApiResponse<void>> =>
|
||||||
|
api.put(`/payment/authorize/cleanup/${id}`),
|
||||||
|
|
||||||
|
captureServicePayment: (id: number, data: { amount: number }): Promise<ApiResponse<PaymentTransaction>> =>
|
||||||
|
api.put(`/payment/capture/service/${id}`, data),
|
||||||
|
|
||||||
|
// Service payment
|
||||||
|
getServicePayment: (id: number, type: string): Promise<ApiResponse<{ amount: number }>> =>
|
||||||
|
api.get(`/payment/service/payment/${id}/${type}`),
|
||||||
|
|
||||||
|
// Transactions
|
||||||
|
getDeliveryTransaction: (id: number): Promise<ApiResponse<PaymentTransaction>> =>
|
||||||
|
api.get(`/payment/transaction/delivery/${id}`),
|
||||||
|
|
||||||
|
getAuthorizeTransactions: (): Promise<ApiResponse<AuthorizeTransaction[]>> =>
|
||||||
|
api.get('/payment/transactions/authorize/1'),
|
||||||
|
|
||||||
|
getCustomerTransactions: (customerId: number, page: number = 1): Promise<TransactionListResponse> =>
|
||||||
|
api.get(`/payment/transactions/customer/${customerId}/${page}`),
|
||||||
|
|
||||||
|
getServiceTransactions: (serviceId: number): Promise<ApiResponse<PaymentTransaction[]>> =>
|
||||||
|
api.get(`/payment/transactions/service/${serviceId}`),
|
||||||
|
|
||||||
|
// Authorize.net endpoints
|
||||||
|
authorize: {
|
||||||
|
tokenizeCard: (customerId: number, data: TokenizeCardRequest): Promise<ApiResponse<CreditCard>> =>
|
||||||
|
authorizeApi.post(`/api/payments/customers/${customerId}/cards`, data),
|
||||||
|
|
||||||
|
updateTokenizedCard: (customerId: number, cardId: number, data: UpdateTokenizedCardRequest): Promise<ApiResponse<CreditCard>> =>
|
||||||
|
authorizeApi.put(`/api/payments/customers/${customerId}/cards/${cardId}`, data),
|
||||||
|
|
||||||
|
authorizeSavedCard: (customerId: number, data: PreauthorizeSavedCardRequest): Promise<ApiResponse<AuthorizeNetTransactionResponse>> =>
|
||||||
|
authorizeApi.post(`/api/payments/authorize/saved-card/${customerId}`, data),
|
||||||
|
|
||||||
|
chargeSavedCard: (customerId: number, data: ChargeSavedCardRequest): Promise<ApiResponse<AuthorizeNetTransactionResponse>> =>
|
||||||
|
authorizeApi.post(`/api/payments/charge/saved-card/${customerId}`, data),
|
||||||
|
|
||||||
|
charge: (customerId: number, data: ChargeDirectRequest): Promise<ApiResponse<AuthorizeNetTransactionResponse>> =>
|
||||||
|
authorizeApi.post(`/api/charge/${customerId}`, data),
|
||||||
|
|
||||||
|
capture: (data: CaptureRequest): Promise<ApiResponse<AuthorizeNetTransactionResponse>> =>
|
||||||
|
authorizeApi.post('/api/capture/', data),
|
||||||
|
|
||||||
|
// Auto transaction endpoints
|
||||||
|
getAutoTransaction: (deliveryId: number): Promise<ApiResponse<AuthorizeNetTransactionResponse>> =>
|
||||||
|
authorizeApi.get(`/api/auto/transaction/delivery/${deliveryId}`),
|
||||||
|
|
||||||
|
updateAutoTransactionId: (deliveryId: number, newId: number, data?: Record<string, unknown>): Promise<ApiResponse<void>> =>
|
||||||
|
authorizeApi.put(`/api/auto/transaction/delivery/${deliveryId}/update/${newId}`, data ?? {}),
|
||||||
|
|
||||||
|
linkTransactionToAuto: (transactionId: number, autoId: number, data?: Record<string, unknown>): Promise<ApiResponse<void>> =>
|
||||||
|
authorizeApi.put(`/api/transaction/${transactionId}/update_auto_id/${autoId}`, data ?? {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default paymentService;
|
||||||
31
src/services/queryService.ts
Normal file
31
src/services/queryService.ts
Normal file
@@ -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;
|
||||||
63
src/services/serviceService.ts
Normal file
63
src/services/serviceService.ts
Normal file
@@ -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;
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import authHeader from '../services/auth.header' // Adjust path if needed
|
import { authService } from '../services/authService'
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
user_name: string;
|
user_name: string;
|
||||||
@@ -60,9 +60,8 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
return false // No token, definitely not authenticated
|
return false // No token, definitely not authenticated
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Use your existing endpoint to verify the token
|
// Use the centralized auth service
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/auth/whoami`;
|
const response = await authService.whoami();
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
user.value = response.data.user
|
user.value = response.data.user
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import axios from 'axios'
|
import { adminService } from '../services/adminService'
|
||||||
import authHeader from '../services/auth.header' // Adjust path if needed
|
|
||||||
|
|
||||||
export const useCountsStore = defineStore('counts', () => {
|
export const useCountsStore = defineStore('counts', () => {
|
||||||
// --- STATE ---
|
// --- STATE ---
|
||||||
@@ -21,8 +20,7 @@ export const useCountsStore = defineStore('counts', () => {
|
|||||||
// A single action to fetch ALL counts from our new, efficient endpoint.
|
// A single action to fetch ALL counts from our new, efficient endpoint.
|
||||||
async function fetchSidebarCounts() {
|
async function fetchSidebarCounts() {
|
||||||
try {
|
try {
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/deliverystatus/stats/sidebar-counts`;
|
const response = await adminService.stats.sidebarCounts();
|
||||||
const response = await axios.get(path, { headers: authHeader() });
|
|
||||||
|
|
||||||
if (response.data && response.data.ok) {
|
if (response.data && response.data.ok) {
|
||||||
const counts = response.data.counts;
|
const counts = response.data.counts;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import axios from 'axios'
|
import { customerService } from '../services/customerService'
|
||||||
|
|
||||||
// Define a type for what a search result looks like.
|
// Define a type for what a search result looks like.
|
||||||
interface CustomerSearchResult {
|
interface CustomerSearchResult {
|
||||||
@@ -38,9 +38,7 @@ export const useSearchStore = defineStore('search', () => {
|
|||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
// NOTE: Make sure this URL is correct. You may need to add your VITE_BASE_URL
|
const response = await customerService.search(searchTerm.value);
|
||||||
const path = `${import.meta.env.VITE_BASE_URL}/search/customer?q=${searchTerm.value}`;
|
|
||||||
const response = await axios.get(path);
|
|
||||||
searchResults.value = response.data;
|
searchResults.value = response.data;
|
||||||
} catch { // No `error` parameter as requested
|
} catch { // No `error` parameter as requested
|
||||||
searchResults.value = [];
|
searchResults.value = [];
|
||||||
|
|||||||
725
src/types/models.ts
Normal file
725
src/types/models.ts
Normal file
@@ -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<T> {
|
||||||
|
ok: boolean;
|
||||||
|
data?: T;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaginatedResponse<T> {
|
||||||
|
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<T> {
|
||||||
|
data: T;
|
||||||
|
status: number;
|
||||||
|
statusText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility types
|
||||||
|
export type CustomerListResponse = PaginatedResponse<Customer>;
|
||||||
|
export type DeliveryListResponse = PaginatedResponse<Delivery>;
|
||||||
|
export type TransactionListResponse = PaginatedResponse<PaymentTransaction>;
|
||||||
|
export type CardListResponse = PaginatedResponse<CreditCard>;
|
||||||
|
export type ServiceListResponse = PaginatedResponse<ServiceCall>;
|
||||||
|
export type EmployeeListResponse = PaginatedResponse<Employee>;
|
||||||
Reference in New Issue
Block a user