added cancel button
This commit is contained in:
BIN
src/assets/images/googlereview.png
Normal file
BIN
src/assets/images/googlereview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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!' });
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user