Hello from MCP server
<template>
<BaseLayout title="Technician - View Job">
<!-- Toolbar at top of screen -->
<Toolbar :force-step2="true" @help-clicked="openInfoModal" />
<div class="view-job-container">
<h2 class="view-job-title">Is this the job you're working on?</h2>
<div class="yes-no-buttons">
<YesButton :disabled="!isButtonEnabled" @click="handleSelectJob" />
<button
:disabled="!isButtonEnabled"
@click="handleNo"
class="no-button"
>
No
</button>
</div>
<Divider />
<div class="content-section">
<h3 class="job-name">{{ problem?.name || "Loading..." }}</h3>
<div
class="description"
v-html="formatDescriptionWithHashtags(problem?.description)"
></div>
</div>
<!-- Action Bar -->
<ActionBar title="Check Steps" :flash="true" :disabled="true" @next="handleSelectJob" />
</div>
<!-- Info Modal -->
<ion-modal :is-open="isInfoModalOpen" @didDismiss="closeInfoModal">
<ion-header>
<ion-toolbar>
<ion-title>Session Store</ion-title>
<ion-buttons slot="end">
<ion-button @click="closeInfoModal">Close</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="info-content">
<pre>{{ JSON.stringify(sessionStore.$state, null, 2) }}</pre>
</div>
</ion-content>
</ion-modal>
</BaseLayout>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
IonIcon,
IonModal,
IonHeader,
IonToolbar,
IonTitle,
IonButtons,
IonButton,
IonContent,
} from "@ionic/vue";
import { arrowBack, helpCircleOutline, construct } from "ionicons/icons";
import BaseLayout from "@/components/BaseLayout.vue";
import Toolbar from "@/components/Toolbar.vue";
import ActionBar from "@/components/ActionBar.vue";
import Divider from "@/components/Divider.vue";
import YesButton from "@/components/YesButton.vue";
import { getDb } from "@/dataAccess/getDb";
import { getApi } from "@/dataAccess/getApi";
import { useSessionStore } from "@/stores/session";
import type { ProblemsRecord } from "@/pocketbase-types";
const route = useRoute();
const router = useRouter();
const sessionStore = useSessionStore();
const problem = ref<ProblemsRecord | null>(null);
const isInfoModalOpen = ref(false);
const isButtonEnabled = ref(false);
let enableButtonTimeout: ReturnType<typeof setTimeout> | null = null;
// Find the job in the session that matches this problem
const currentJob = computed(() => {
if (!problem.value) return null;
return sessionStore.jobs.find((job) => job.problem?.id === problem.value?.id);
});
onMounted(async () => {
const problemId = route.params.problemId as string;
const db = await getDb();
if (db && problemId) {
problem.value = await db.problems.byId(problemId);
}
// Load session first to ensure we have the latest data
await sessionStore.load();
// Set step1 to true when viewing a job
sessionStore.appProgress.step1 = true;
await sessionStore.save();
// Enable button after 1.5 seconds
isButtonEnabled.value = false;
enableButtonTimeout = setTimeout(() => {
isButtonEnabled.value = true;
}, 1500);
});
onUnmounted(() => {
// Clear timeout when navigating away
if (enableButtonTimeout) {
clearTimeout(enableButtonTimeout);
enableButtonTimeout = null;
}
});
const goBack = () => {
router.back();
};
const openInfoModal = () => {
isInfoModalOpen.value = true;
};
const closeInfoModal = () => {
isInfoModalOpen.value = false;
};
const formatDescriptionWithHashtags = (description: string | undefined) => {
if (!description) return "";
// Split into lines and process each one
const lines = description.split("\n");
const processedLines = lines.map((line) => {
if (line.includes(":")) {
return '<span class="colon-line">' + line + "</span>";
}
return line;
});
// Join lines with <br> tags
let formatted = processedLines.join("<br>");
// Find the first # and style everything from there onwards
const hashIndex = formatted.indexOf("#");
if (hashIndex === -1) return formatted;
const beforeHash = formatted.substring(0, hashIndex);
const afterHash = formatted.substring(hashIndex);
return beforeHash + '<span class="hashtag">' + afterHash + "</span>";
};
const handleSelectJob = async () => {
if (problem.value) {
// Set step2 to true when check steps is clicked
if (!sessionStore.appProgress.step2) {
sessionStore.appProgress.step2 = true;
await sessionStore.save();
}
// Navigate to check-steps with problem ID
router.push(`/check-steps/${problem.value.id}`);
}
};
const handleNo = async () => {
// Set step2 to false
sessionStore.appProgress.step2 = false;
await sessionStore.save();
// Navigate back to find-job (step 1)
router.push('/find-job');
};
</script>
<style scoped>
:deep(.ion-page) {
animation: none !important;
transform: none !important;
transition: none !important;
}
.view-job-container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
animation: none !important;
transform: none !important;
transition: none !important;
}
.view-job-title {
margin: 0 0 32px 0;
color: #ffffff;
font-size: 32px;
font-weight: 600;
text-align: center;
font-variant: small-caps;
}
.job-name {
margin: 0 0 16px 0;
color: #ffffff;
font-size: 24px;
font-weight: 600;
}
.header-section {
margin-bottom: 32px;
}
.title-container {
position: relative;
margin-bottom: 24px;
}
.title-icons {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
display: flex;
gap: 16px;
}
.title-icons-right {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
display: flex;
gap: 16px;
}
.back-icon-wrapper {
position: relative;
display: inline-block;
background-color: var(--ion-color-tertiary);
padding: 8px;
border-radius: 4px;
}
.title-icons ion-icon {
font-size: 48px;
}
.title-icons-right ion-icon {
font-size: 48px;
}
.clickable-icon {
cursor: pointer;
transition:
transform 0.2s ease,
color 0.2s ease;
}
.clickable-icon:hover {
transform: scale(1.1);
color: var(--ion-color-primary-shade);
}
.clickable-icon:active {
transform: scale(0.95);
}
.page-title {
margin: 0;
color: #ffffff;
font-size: 48px;
font-weight: 700;
text-align: center;
font-variant: small-caps;
padding-left: 80px;
padding-right: 80px;
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
}
.construction-icon {
width: 48px;
height: 48px;
object-fit: contain;
}
.content-section {
padding: 0 20px 100px 20px;
}
.description {
color: #ffffff;
font-size: 18px;
line-height: 1.6;
max-width: 1000px;
margin: 0 auto;
}
.description :deep(.hashtag) {
color: var(--ion-color-medium);
font-size: 16px;
}
.description :deep(.colon-line) {
display: inline-block;
margin-top: 16px;
}
.yes-no-buttons {
display: flex;
justify-content: center;
gap: 16px;
margin-bottom: 24px;
}
.no-button {
display: inline-block;
padding: 16px 48px;
font-size: 20px;
font-weight: 600;
color: #ffffff;
background-color: var(--ion-color-secondary);
border: none;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s ease, background-color 0.2s ease;
}
.no-button:hover:not(:disabled) {
transform: scale(1.05);
background-color: var(--ion-color-secondary-shade);
}
.no-button:active:not(:disabled) {
transform: scale(0.95);
}
.no-button:disabled {
background-color: var(--ion-background-color);
border: 3px solid var(--ion-color-secondary);
color: var(--ion-color-secondary);
opacity: 0.6;
cursor: not-allowed;
}
.no-button:disabled:hover {
background-color: var(--ion-background-color);
transform: none;
}
/* Mobile adjustments */
@media (max-width: 767px) {
.view-job-container {
padding: 16px;
}
.page-title {
font-size: 36px;
}
.description {
font-size: 16px;
}
.no-button {
font-size: 18px;
padding: 12px 36px;
}
}
/* Info Modal Styles */
.info-content {
color: var(--ion-color-dark);
}
.info-content pre {
background: var(--ion-color-light);
padding: 16px;
border-radius: 8px;
overflow-x: auto;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
}
</style>