Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
<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>