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 :icon="star" @click="openBookmarksModal" class="clickable-icon" size="large"></ion-icon>
<ion-icon v-if="!isOnServiceCall" :icon="home" @click="goToServiceCall" class="clickable-icon" size="large"></ion-icon>
</div>
<div class="toolbar-icons">
<div
v-for="(iconData, index) in progressIcons"
:key="index"
class="progress-icon-wrapper"
>
<ion-icon
:icon="iconData.icon"
:color="iconData.color"
size="large"
></ion-icon>
<ion-icon
v-if="iconData.showCheckmark"
:icon="checkmark"
class="checkmark-overlay"
color="light"
></ion-icon>
</div>
</div>
<div class="toolbar-right">
<ion-icon v-if="showClear" :icon="closeCircle" @click="handleClearClick" class="clickable-icon" color="danger" size="large"></ion-icon>
<ion-icon :icon="happyOutline" @click="handleAgentClick" class="clickable-icon" size="large"></ion-icon>
<ion-icon :icon="helpCircleOutline" @click="handleHelpClick" class="clickable-icon" size="large"></ion-icon>
</div>
<slot></slot>
</div>
</div>
<!-- Bookmarks Modal -->
<ion-modal :is-open="isBookmarksModalOpen" @didDismiss="closeBookmarksModal">
<ion-header>
<ion-toolbar>
<ion-title>Bookmarks</ion-title>
<ion-buttons slot="end">
<ion-button @click="closeBookmarksModal">Close</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="bookmarks-content">
<div class="service-call-link" @click="goToServiceCall">
<ion-icon :icon="home" class="home-icon"></ion-icon>
<span>Go to Service Call</span>
</div>
<div class="bookmarks-list">
<div
v-for="job in bookmarkedJobs"
:key="job.id"
@click="selectBookmarkedJob(job)"
class="bookmark-item"
>
<ion-icon :icon="star" color="secondary" class="bookmark-star"></ion-icon>
<div class="bookmark-details">
<h3 class="bookmark-title">{{ job.name }}</h3>
<p class="bookmark-description">{{ job.description }}</p>
</div>
</div>
</div>
</div>
</ion-content>
</ion-modal>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
import {
IonIcon,
IonModal,
IonHeader,
IonToolbar,
IonTitle,
IonButtons,
IonButton,
IonContent
} from '@ionic/vue';
import { ellipse, ellipseOutline, checkmark, arrowUndo, star, helpCircleOutline, happyOutline, closeCircle, home } from 'ionicons/icons';
import { useSessionStore } from '@/stores/session';
import type { ProblemsRecord } from '@/pocketbase-types';
// Toolbar component - reusable across multiple views
const sessionStore = useSessionStore();
const router = useRouter();
// Check if we're on service-call page to hide home icon
const isOnServiceCall = computed(() => {
return router.currentRoute.value.path === '/service-call';
});
// Props
const props = withDefaults(defineProps<{
showClear?: boolean;
forceStep1?: boolean;
forceStep2?: boolean;
forceStep3?: boolean;
forceStep4?: boolean;
forceStep5?: boolean;
}>(), {
showClear: false,
forceStep1: false,
forceStep2: false,
forceStep3: false,
forceStep4: false,
forceStep5: false
});
// Define emits for parent components to listen to
const emit = defineEmits(['help-clicked', 'clear-clicked', 'agent-clicked']);
const NUM_CIRCLES = 5;
// Handle help icon click
const handleHelpClick = () => {
emit('help-clicked');
};
// Handle clear icon click
const handleClearClick = () => {
emit('clear-clicked');
};
// Handle agent (smiley) icon click
const handleAgentClick = () => {
emit('agent-clicked');
};
// Handle back arrow click
const goBack = () => {
router.back();
};
const progressIcons = computed(() => {
const icons = [];
const progress = sessionStore.appProgress;
const getIconData = (stepValue: string | boolean, forced: boolean = false, isCurrent: boolean = false) => {
if (stepValue === "pending") {
return { icon: ellipse, color: "secondary", showCheckmark: false };
}
const isComplete = stepValue === true;
const isForcedCurrent = forced && !isComplete;
// Complete step: filled circle with checkmark
if (isComplete) {
return { icon: ellipse, color: "success", showCheckmark: true };
}
// Current step (forced): filled circle without checkmark
if (isForcedCurrent) {
return { icon: ellipse, color: "success", showCheckmark: false };
}
// Incomplete step: outline circle
return { icon: ellipseOutline, color: "primary", showCheckmark: false };
};
// Map each step to filled or outline icon with appropriate color
// Steps 1-5 can be forced on via prop
icons.push(getIconData(progress.step1, props.forceStep1));
icons.push(getIconData(progress.step2, props.forceStep2));
icons.push(getIconData(progress.step3, props.forceStep3));
icons.push(getIconData(progress.step4, props.forceStep4));
icons.push(getIconData(progress.step5, props.forceStep5));
return icons;
});
// Bookmarks functionality
const isBookmarksModalOpen = ref(false);
const openBookmarksModal = () => {
isBookmarksModalOpen.value = true;
};
const closeBookmarksModal = () => {
isBookmarksModalOpen.value = false;
};
// Get 5 random jobs as bookmarks (mock data)
const bookmarkedJobs = computed(() => {
if (sessionStore.problems.length === 0) return [];
// Create a copy and shuffle
const shuffled = [...sessionStore.problems].sort(() => 0.5 - Math.random());
// Return first 5
return shuffled.slice(0, 5);
});
// Select a bookmarked job
const selectBookmarkedJob = (problem: ProblemsRecord) => {
closeBookmarksModal();
router.push(`/confirm-job/${problem.id}`);
};
// Navigate to service call
const goToServiceCall = () => {
if (isBookmarksModalOpen.value) {
closeBookmarksModal();
}
router.push('/service-call');
};
</script>
<style scoped>
.toolbar {
position: relative;
background-color: var(--ion-background-color-step-50);
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%);
}
.progress-icon-wrapper {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.checkmark-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
font-size: 20px;
font-weight: 900;
stroke: currentColor;
stroke-width: 2px;
}
.toolbar-icons ion-icon {
color: var(--ion-color-primary);
}
/* Bookmarks modal styles */
.bookmarks-content {
padding: 20px;
}
.service-call-link {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background-color: var(--ion-color-dark);
color: var(--ion-color-primary);
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
margin-bottom: 24px;
font-weight: 600;
font-size: 16px;
}
.service-call-link:hover {
background-color: var(--ion-color-dark-shade);
}
.home-icon {
font-size: 24px;
color: var(--ion-color-primary);
}
.bookmarks-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.bookmark-item {
display: flex;
align-items: flex-start;
gap: 16px;
padding: 16px;
background-color: var(--ion-color-light);
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
}
.bookmark-item:hover {
background-color: var(--ion-color-light-shade);
}
.bookmark-star {
font-size: 32px;
flex-shrink: 0;
}
.bookmark-details {
flex: 1;
}
.bookmark-title {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
color: var(--ion-color-dark);
}
.bookmark-description {
margin: 0;
font-size: 14px;
color: var(--ion-color-medium);
line-height: 1.4;
}
</style>