added cancel button

This commit is contained in:
2025-10-20 12:01:16 -04:00
parent 78e959883d
commit e70ae887f3
10 changed files with 136 additions and 66 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -15,9 +15,10 @@
<div class="">Spring Rebuilders - (508) 799-9342</div>
</nav>
<nav>
<h6 class="footer-title">Other</h6>
<a class="link link-hover"></a>
<a class="link link-hover"></a>
<h6 class="footer-title">Google Review link / qrcode</h6>
<a class="link link-hover">https://g.page/r/CZHnPQ85LsMUEBM/review</a>
<button @click="copyReviewLink" class="btn btn-outline btn-sm ml-2">Copy Link</button>
<h6 class="link link-hover"> <img src="../../assets/images/googlereview.png" alt="Company Logo" class="h-10 w-auto" /></h6>
<a class="link link-hover"></a>
</nav>
</footer>
@@ -36,7 +37,17 @@ export default defineComponent({
},
mounted() { },
methods: {},
methods: {
async copyReviewLink() {
try {
await navigator.clipboard.writeText('https://g.page/r/CZHnPQ85LsMUEBM/review')
alert('Link copied to clipboard!')
} catch (err) {
console.error('Failed to copy text: ', err)
alert('Failed to copy link. Please try again.')
}
},
},
})
</script>
<style></style>
<style></style>

View File

@@ -137,7 +137,28 @@
@edit-card="editCard"
@remove-card="removeCard"
/>
<!-- Automatic Delivery Actions Box -->
<div v-if="automatic_status === 1 && autodeliveries.length > 0" class="bg-base-100 rounded-lg p-4 border">
<h3 class="font-semibold mb-4">Automatic Delivery Actions</h3>
<div class="flex flex-wrap gap-2">
<router-link v-if="autodeliveries[0].auto_status != 3"
:to="{ name: 'payAutoAuthorize', params: { id: autodeliveries[0].id } }">
<button class="btn btn-primary btn-sm">Preauthorize</button>
</router-link>
<router-link :to="{ name: 'finalizeTicketAutoNocc', params: { id: autodeliveries[0].id } }">
<button class="btn btn-secondary btn-sm">Finalize</button>
</router-link>
<router-link v-if="autodeliveries[0].auto_status == 3"
:to="{ name: 'finalizeTicketAuto', params: { id: autodeliveries[0].open_ticket_id || autodeliveries[0].id } }">
<button class="btn btn-secondary btn-sm">Finalize</button>
</router-link>
<router-link :to="{ name: 'TicketAuto', params: { id: autodeliveries[0].id } }">
<button class="btn btn-success btn-sm">Print Ticket</button>
</router-link>
</div>
</div>
</div>
</div>
</div>
@@ -305,6 +326,8 @@ interface AutomaticDelivery {
customer_full_name: string;
gallons_delivered: number | string;
fill_date: string;
auto_status: number | string;
open_ticket_id: number | string;
}
interface CreditCard {
@@ -600,13 +623,14 @@ export default defineComponent({
})
},
getCustomerAutoDelivery(userid: any) {
let path = import.meta.env.VITE_AUTO_URL + '/delivery/all/profile/profile/' + userid ;
let path = import.meta.env.VITE_AUTO_URL + '/delivery/auto/customer/' + userid ;
axios({
method: 'get',
url: path,
headers: authHeader(),
}).then((response: any) => {
this.autodeliveries = response.data
// Handle the case where response.data might be null (no auto delivery found)
this.autodeliveries = response.data ? [response.data] : []
})
},
getCustomerDelivery(userid: any, delivery_page: any) {

View File

@@ -16,15 +16,16 @@
</tr>
</thead>
<tbody>
<tr v-for="auto in deliveries" :key="auto.id" class="hover">
<td>{{ auto.id }}</td>
<td>{{ auto.customer_full_name }}</td>
<td>{{ auto.gallons_delivered }}</td>
<td>{{ auto.fill_date }}</td>
<tr v-for="autoticket in deliveries" :key="autoticket.id" class="hover">
<td>{{ autoticket.id }}</td>
<td>{{ autoticket.customer_full_name }}</td>
<td>{{ autoticket.gallons_delivered }}</td>
<td>{{ autoticket.fill_date }}</td>
<td>
<router-link :to="{ name: 'automaticView', params: { id: auto.id } }">
<router-link :to="{ name: 'automaticView', params: { id: autoticket.id } }">
<button class="btn btn-xs btn-primary">View</button>
</router-link>
</td>
</tr>
</tbody>
@@ -40,6 +41,8 @@ interface AutomaticDelivery {
id: number;
customer_full_name: string;
gallons_delivered: number | string;
auto_status: number | string;
open_ticket_id: number | string;
fill_date: string;
}

View File

@@ -34,6 +34,7 @@
<router-link :to="{ name: 'deliveryOrder', params: { id: oil.id } }" class="btn btn-xs btn-ghost">View</router-link>
<router-link :to="{ name: 'deliveryEdit', params: { id: oil.id } }" class="btn btn-xs btn-secondary">Edit</router-link>
<router-link :to="{ name: 'Ticket', params: { id: oil.id } }" class="btn btn-xs btn-success">Print</router-link>
<router-link :to="{ name: 'finalizeTicket', params: { id: oil.id } }" v-if="oil.delivery_status != 10" class="btn btn-xs btn-accent">Finalize</router-link>
</div>
</td>
</tr>

View File

@@ -26,32 +26,7 @@
Automatic Deliveries
</a>
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-4" v-show="activeTab === 'automatic'">
<div class="overflow-x-auto">
<table class="table table-sm w-full">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Gallons</th>
<th>Date</th>
<th>View</th>
</tr>
</thead>
<tbody>
<tr v-for="auto in autodeliveries" :key="auto.id" class="hover">
<td>{{ auto.id }}</td>
<td>{{ auto.customer_full_name }}</td>
<td>{{ auto.gallons_delivered }}</td>
<td>{{ auto.fill_date }}</td>
<td>
<router-link :to="{ name: 'automaticView', params: { id: auto.id } }">
<button class="btn btn-xs btn-primary">View</button>
</router-link>
</td>
</tr>
</tbody>
</table>
</div>
<AutomaticDeliveries :deliveries="autodeliveries" />
</div>
</div>
</template>
@@ -61,6 +36,7 @@ import { ref } from 'vue';
import DeliveriesTable from './DeliveriesTable.vue';
import ServiceCallsTable from './ServiceCallsTable.vue';
import TransactionsTable from './TransactionsTable.vue';
import AutomaticDeliveries from './AutomaticDeliveries.vue';
// 1. Define the interfaces for the data this component receives and passes down.
// These should match the interfaces in the child components.
@@ -78,6 +54,8 @@ interface AutomaticDelivery {
id: number;
customer_full_name: string;
gallons_delivered: number | string;
auto_status: number | string;
open_ticket_id: number | string;
fill_date: string;
}

View File

@@ -377,6 +377,7 @@ export default defineComponent({
return {
v$: useValidate(),
user: null as any,
customer: {} as any,
isLoading: false, // For main form submission
isCardSaving: false, // For quick add card form
quickGallonAmounts: [100, 125, 150, 175, 200, 220],
@@ -384,6 +385,8 @@ export default defineComponent({
promos: [] as Promo[],
truckDriversList: [] as Driver[],
pricingTiers: [] as PricingTier[],
isLoadingAuthorize: true,
authorizeCheck: { profile_exists: false, has_payment_methods: false, missing_components: [] as string[], valid_for_charging: false },
isConfirmationModalVisible: false,
formDelivery: {
gallons_ordered: '',
@@ -410,7 +413,7 @@ export default defineComponent({
card_name: '',
type_of_card: '',
} as CardFormData,
customer: {} as Customer,
}
},
validations() {
@@ -622,7 +625,29 @@ export default defineComponent({
this.isLoading = false;
}
},
async checkAuthorizeAccount() {
if (!this.customer.id) return;
this.isLoadingAuthorize = true;
try {
const path = `${import.meta.env.VITE_AUTHORIZE_URL}/user/check-authorize-account/${this.customer.id}`;
const response = await axios.get(path, { headers: authHeader() });
this.authorizeCheck = response.data;
} catch (error) {
console.error("Failed to check authorize account:", error);
notify({ title: "Error", text: "Could not check payment account status.", type: "error" });
// Set default error state
this.authorizeCheck = {
profile_exists: false,
has_payment_methods: false,
missing_components: ['api_error'],
valid_for_charging: false
};
} finally {
this.isLoadingAuthorize = false;
}
},
async onCardSubmit() {
this.v$.formCard.$validate();
if (this.v$.formCard.$error) {
@@ -643,13 +668,7 @@ export default defineComponent({
name_on_card: this.formCard.card_name, // Map card_name to name_on_card for Flask
};
// Payload for FastAPI backend (it only needs the essentials for Authorize.Net)
const fastapiPayload = {
card_number: this.formCard.card_number.replace(/\s/g, ''),
expiration_date: `${this.formCard.expiration_year}-${this.formCard.expiration_month}`,
cvv: this.formCard.security_number, // Map security_number to cvv for FastAPI
main_card: false, // Send this to FastAPI as well
};
// --- STEP 2: CRITICAL CALL - SAVE CARD TO LOCAL DATABASE VIA FLASK ---
try {
@@ -671,6 +690,15 @@ export default defineComponent({
}
// --- STEP 3: BEST-EFFORT CALL - TOKENIZE CARD VIA FASTAPI ---
if (this.authorizeCheck.profile_exists) {
// Payload for FastAPI backend (it only needs the essentials for Authorize.Net)
const fastapiPayload = {
card_number: this.formCard.card_number.replace(/\s/g, ''),
expiration_date: `${this.formCard.expiration_year}-${this.formCard.expiration_month}`,
cvv: this.formCard.security_number, // Map security_number to cvv for FastAPI
main_card: false, // Send this to FastAPI as well
};
try {
const fastapiPath = `${import.meta.env.VITE_AUTHORIZE_URL}/api/payments/customers/${this.customer.id}/cards`;
console.log("Attempting to tokenize card with Authorize.Net via FastAPI:", fastapiPath);
@@ -680,7 +708,9 @@ export default defineComponent({
// 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);
}
}else{
console.log("Skipping Authorize.Net tokenization as no profile exists for customer.");
}
// --- STEP 4: ALWAYS SHOW SUCCESS, REFRESH CARDS, STAY ON PAGE ---
// This code runs as long as the first (Flask) call was successful.
notify({ type: 'success', title: 'Card Saved!' });

View File

@@ -339,6 +339,13 @@
<router-link :to="{ name: 'Ticket', params: { id: deliveryOrder.id } }">
<button class="btn btn-success btn-sm">Print Ticket</button>
</router-link>
<button
class="btn btn-error btn-sm"
@click="cancelDelivery"
v-if="[0, 2, 3, 4, 5, 9].includes(deliveryOrder.delivery_status)"
>
Cancel
</button>
</div>
</div>
</div>
@@ -527,6 +534,36 @@ export default defineComponent({
}
})
},
cancelDelivery() {
let path = import.meta.env.VITE_BASE_URL + '/delivery/cancel/' + this.deliveryOrder.id;
axios({
method: 'post',
url: path,
headers: authHeader(),
}).then((response: any) => {
if (response.data.ok) {
notify({
title: "Success",
text: "Delivery cancelled",
type: "success",
});
// Refresh the delivery data
this.getOilOrder(this.deliveryOrder.id);
} else {
notify({
title: "Failure",
text: "Failed to cancel delivery",
type: "error",
});
}
}).catch(() => {
notify({
title: "Error",
text: "Error cancelling delivery",
type: "error",
});
});
},
userStatus() {
let path = import.meta.env.VITE_BASE_URL + '/auth/whoami';
axios({

View File

@@ -35,8 +35,6 @@
<th>Status</th>
<th>Town / Address</th>
<th>Gallons</th>
<th>Options</th>
<th>Payment</th>
<th class="text-right">Actions</th>
</tr>
</thead>
@@ -79,13 +77,7 @@
<span v-if="oil.emergency" class="badge badge-error badge-xs">EMERGENCY</span>
</div>
</td>
<td>
<span v-if="oil.payment_type == 0">Cash</span>
<span v-else-if="oil.payment_type == 1">CC</span>
<span v-else-if="oil.payment_type == 2">Cash/CC</span>
<span v-else-if="oil.payment_type == 3">Check</span>
<span v-else-if="oil.payment_type == 4">Other</span>
</td>
<td class="text-right">
<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>
@@ -137,13 +129,7 @@
<span v-if="oil.customer_asked_for_fill" class="badge badge-info badge-xs">FILL</span>
<span v-else>{{ oil.gallons_ordered }}</span>
</p>
<p><strong class="font-semibold">Payment:</strong>
<span v-if="oil.payment_type == 0">Cash</span>
<span v-else-if="oil.payment_type == 1">CC</span>
<span v-else-if="oil.payment_type == 2">Cash/CC</span>
<span v-else-if="oil.payment_type == 3">Check</span>
<span v-else-if="oil.payment_type == 4">Other</span>
</p>
</div>
<div class="card-actions justify-end flex-wrap gap-2 mt-2">

View File

@@ -29,7 +29,7 @@
<th>Status</th>
<th>Town / Address</th>
<th>Gallons</th>
<th>Options</th>
<th class="text-right">Actions</th>
</tr>
</thead>