Hello from MCP server
<template>
<div class="toolbar">
<div class="toolbar-content">
<div class="toolbar-left">
<ion-icon :icon="arrowUndo" @click="goBack" class="clickable-icon" size="large"></ion-icon>
<ion-icon v-if="showClearIcon" :icon="closeCircleOutline" @click="handleClearClick" class="clickable-icon" size="large"></ion-icon>
</div>
<div v-if="!hideProgress" class="toolbar-icons">
<ion-icon
v-for="(iconData, index) in progressIcons"
:key="index"
:icon="iconData.icon"
:color="iconData.color"
size="large"
></ion-icon>
</div>
<div class="toolbar-right">
<div class="percent-button clickable-icon" :class="{ 'percent-active': discountActive }" @click="handlePercentClick">%</div>
<ion-icon :icon="helpCircleOutline" @click="handleHelpClick" class="clickable-icon" size="large"></ion-icon>
<ion-icon :icon="arrowRedo" @click="goForward" class="clickable-icon" size="large"></ion-icon>
</div>
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { IonIcon } from '@ionic/vue';
import { ellipse, ellipseOutline, arrowUndo, arrowRedo, helpCircleOutline, closeCircleOutline } from 'ionicons/icons';
import { useSessionStore } from '@/stores/session';
// Customer Toolbar component - for customer-facing pages
const sessionStore = useSessionStore();
const router = useRouter();
// Props
const props = withDefaults(defineProps<{
forceStep1?: boolean;
forceStep2?: boolean;
forceStep3?: boolean;
forceStep4?: boolean;
forceStep5?: boolean;
currentStep?: number; // The current step index (0-based)
discountActive?: boolean; // Whether the SA discount is active
showClearIcon?: boolean; // Whether to show the clear selections icon
hideProgress?: boolean; // Whether to hide the progress bar
}>(), {
forceStep1: false,
forceStep2: false,
forceStep3: false,
forceStep4: false,
forceStep5: false,
currentStep: -1,
discountActive: false,
showClearIcon: false,
hideProgress: false
});
// Define emits for parent components to listen to
const emit = defineEmits(['help-clicked', 'percent-clicked', 'clear-clicked']);
// Handle help icon click
const handleHelpClick = () => {
emit('help-clicked');
};
// Handle percent button click
const handlePercentClick = () => {
emit('percent-clicked');
};
// Handle clear icon click
const handleClearClick = () => {
emit('clear-clicked');
};
// Handle back arrow click
const goBack = () => {
router.back();
};
// Handle forward arrow click - navigate to next step
const goForward = () => {
if (props.currentStep < 0) {
router.forward();
return;
}
const numJobs = sessionStore.jobs.length;
const totalSteps = numJobs + 2; // N menus + review + payment
const nextStep = props.currentStep + 1;
if (nextStep >= totalSteps) {
// Already at last step
return;
}
// Sort jobs by price (highest first)
const sortedJobs = [...sessionStore.jobs].sort((a, b) => {
const getPriceA = a.selectedPrice ? parseFloat(a.selectedPrice) : ((a.baseHours || 0) + (a.extraTime || 0));
const getPriceB = b.selectedPrice ? parseFloat(b.selectedPrice) : ((b.baseHours || 0) + (b.extraTime || 0));
return getPriceB - getPriceA;
});
if (nextStep < numJobs) {
// Navigate to next job menu
const nextJob = sortedJobs[nextStep];
router.push(`/job/${nextJob.id}/show/legacy`);
} else if (nextStep === numJobs) {
// Navigate to review/cart
router.push('/cart/');
} else if (nextStep === numJobs + 1) {
// Navigate to payment (when implemented)
// router.push('/payment');
}
};
const progressIcons = computed(() => {
const icons = [];
// Calculate number of steps dynamically
// Number of jobs (menus) + 2 (review + payment)
const numJobs = sessionStore.jobs.length;
const totalSteps = numJobs + 2; // N menus + review + payment
// Sort jobs by price (highest first) to match menu order
const sortedJobs = [...sessionStore.jobs].sort((a, b) => {
const getPriceA = a.selectedPrice ? parseFloat(a.selectedPrice) : ((a.baseHours || 0) + (a.extraTime || 0));
const getPriceB = b.selectedPrice ? parseFloat(b.selectedPrice) : ((b.baseHours || 0) + (b.extraTime || 0));
return getPriceB - getPriceA;
});
const getIconData = (stepIndex: number, forced: boolean = false) => {
// Check if this step is completed in the customerProgress array
const progressArray = sessionStore.customerProgress as any;
const stepValue = progressArray[stepIndex];
if (stepValue === "pending") {
return { icon: ellipse, color: "warning" };
}
// For menu steps (0 to numJobs-1), check if the corresponding job has a selected offer
let isComplete = forced || stepValue === true;
if (stepIndex < numJobs) {
const job = sortedJobs[stepIndex];
if (job) {
// A menu is complete if it has a selected offer
isComplete = !!(job.selectedOffer && job.selectedPrice);
}
}
// If this is the current step, show filled success icon
if (stepIndex === props.currentStep) {
return { icon: ellipse, color: "success" };
}
// If complete (has item in cart), show filled success icon
// If not complete, show outline success icon
return { icon: isComplete ? ellipse : ellipseOutline, color: "success" };
};
// Generate icons for each step dynamically
for (let i = 0; i < totalSteps; i++) {
// Check if this step should be forced
let forced = false;
if (i === 0 && props.forceStep1) forced = true;
if (i === 1 && props.forceStep2) forced = true;
if (i === 2 && props.forceStep3) forced = true;
if (i === 3 && props.forceStep4) forced = true;
if (i === 4 && props.forceStep5) forced = true;
icons.push(getIconData(i, forced));
}
return icons;
});
</script>
<style scoped>
.toolbar {
position: relative;
background-color: var(--ion-background-color);
padding: 20px;
border-bottom: 3px solid var(--ion-color-primary);
}
.toolbar-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
}
.toolbar-left {
display: flex;
align-items: center;
gap: 16px;
}
.toolbar-left ion-icon {
color: var(--ion-color-primary);
}
.clickable-icon {
cursor: pointer;
}
.toolbar-right {
display: flex;
align-items: center;
gap: 16px;
}
.toolbar-right ion-icon {
color: var(--ion-color-primary);
}
.toolbar-icons {
display: flex;
gap: 16px;
align-items: center;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.toolbar-icons ion-icon {
color: var(--ion-color-primary);
}
.percent-button {
color: var(--ion-color-medium);
font-size: 32px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
min-width: 32px;
transition: color 0.2s ease;
}
.percent-active {
color: var(--ion-color-primary);
}
</style>