Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
<template>
  <BaseLayout :title="'Confirm Your Selection'">
    <div class="confirmation-container" v-if="selectedTier">
      <div class="content-section">
        <div class="tier-card" :class="getTierClass(selectedTier.name)">
          <!-- Tier Badge -->
          <div class="tier-badge" :class="getTierBadgeClass(selectedTier.name)">
            <span class="tier-name">{{ selectedTier.name }}</span>
          </div>

          <!-- Tier Content -->
          <div class="tier-content">
            <h3 class="tier-title">{{ selectedTier.title?.toUpperCase() || selectedTier.name }}</h3>

            <!-- Features List -->
            <div class="features-section">
              <template v-if="selectedTier.menuCopy">
                <div
                  v-for="copy in selectedTier.menuCopy"
                  :key="copy.id"
                  class="feature-item"
                >
                  <div
                    v-for="item in copy.contentItems"
                    :key="item.id"
                    class="feature-text"
                  >
                    <ion-icon :icon="checkmarkCircle" class="feature-icon"></ion-icon>
                    <span>{{ item.content }}</span>
                  </div>
                </div>
              </template>
              <template v-else-if="selectedTier.contentItems">
                <div
                  v-for="item in selectedTier.contentItems"
                  :key="item.id"
                  v-show="!item.refId?.includes('_title')"
                  class="feature-text"
                >
                  <ion-icon :icon="checkmarkCircle" class="feature-icon"></ion-icon>
                  <span>{{ item.content }}</span>
                </div>
              </template>
            </div>

            <!-- Pricing Section -->
            <div class="pricing-section">
              <div class="price-amount">
                <show-currency :currencyIn="selectedTier.price" />
              </div>
              <div class="warranty-text" v-if="selectedTier.warranty">
                {{ selectedTier.warranty }}
              </div>
            </div>
          </div>
        </div>

        <!-- Action Buttons -->
        <div class="actions-section">
          <ion-button
            expand="block"
            color="success"
            size="large"
            @click="confirmSelection"
            v-if="isInSession"
          >
            <ion-icon :icon="checkmarkCircle" slot="start"></ion-icon>
            Confirm Selection
          </ion-button>
          <ion-button
            expand="block"
            fill="outline"
            color="primary"
            size="large"
            @click="goBack"
          >
            <ion-icon :icon="arrowBack" slot="start"></ion-icon>
            Back to Menu
          </ion-button>
          <div v-if="!isInSession" class="browse-message">
            <p>This is a preview. Start a session to make selections.</p>
          </div>
        </div>
      </div>
    </div>
  </BaseLayout>
</template>

<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { getDb } from "@/dataAccess/getDb";
import BaseLayout from "@/components/BaseLayout.vue";
import ShowCurrency from "@/components/ShowCurrency.vue";
import calculatePrice from "@/framework/calculatePrice";
import legacyFormula from "@/lib/legacyFormula";
import { useSessionStore } from "@/stores/session";
import { useOrganizationStore } from "@/stores/organization";
import {
  IonIcon,
  IonButton,
} from "@ionic/vue";
import { checkmarkCircle, arrowBack } from "ionicons/icons";

const sessionStore = useSessionStore();
const orgStore = useOrganizationStore();
const route = useRoute();
const router = useRouter();
const selectedTier = ref<any>(null);

const isInSession = computed(() => {
  return (route.name as string) === "offers.confirm.job";
});

function getTierClass(tierName: string): string {
  const name = tierName?.toLowerCase() || "";
  if (name.includes("platinum")) return "tier-platinum";
  if (name.includes("gold")) return "tier-gold";
  if (name.includes("silver")) return "tier-silver";
  if (name.includes("bronze")) return "tier-bronze";
  if (name.includes("band")) return "tier-bandaid";
  return "";
}

function getTierBadgeClass(tierName: string): string {
  const name = tierName?.toLowerCase() || "";
  if (name.includes("platinum")) return "badge-platinum";
  if (name.includes("gold")) return "badge-gold";
  if (name.includes("silver")) return "badge-silver";
  if (name.includes("bronze")) return "badge-bronze";
  if (name.includes("band")) return "badge-bandaid";
  return "";
}

function populateFormulaVars(f: any, offer: any, extraCostMultiplier: number) {
  f.vars.hourlyFee = orgStore.hourlyFee;
  f.vars.serviceCallFee = orgStore.serviceCallFee;
  f.vars.saDiscount = orgStore.saDiscount;
  f.vars.salesTax = orgStore.saDiscount;
  f.vars.multiplier = parseFloat(offer.multiplier);
  f.vars.extraCostMultiplier = extraCostMultiplier;
  return f;
}

async function confirmSelection() {
  if (!isInSession.value || !selectedTier.value) return;

  const jobId = route.params.jobId as string;

  // Calculate the tier's base hours from costsTime
  const tierBaseHours = selectedTier.value.offer?.costsTime?.reduce(
    (sum: number, cost: any) => sum + (cost.hours || 0),
    0
  ) || 0;

  await sessionStore.setSelectedOffer(
    jobId,
    selectedTier.value.offer,
    selectedTier.value.price.toString(),
    selectedTier.value.name,
    selectedTier.value.title,
    tierBaseHours
  );

  router.push('/cart');
}

function goBack() {
  router.back();
}

onMounted(async () => {
  try {
    const db = await getDb();
    await orgStore.loadVariables();
    await sessionStore.load();

    const tierId = route.params.tierId as string;
    const jobId = route.params.jobId as string;
    let menuId = route.params.menuId as string;

    let job = null;
    if (isInSession.value && jobId) {
      job = sessionStore.jobs.find((e) => e.id === jobId);
      if (job && job.problem && (job.problem as any).menus) {
        menuId = (job.problem as any).menus[0];
      }
    }

    if (!menuId) {
      console.error("No menuId found");
      return;
    }

    if (db) {
      const menuResponse = await db.menus.byMenuId(menuId);
      if (menuResponse) {
        const tier = menuResponse.tiers.find((t: any) => t.id === tierId);
        if (tier) {
          selectedTier.value = tier;

          let extraCostMultiplier = 1;
          if (job) {
            extraCostMultiplier = job.extraTime * job.extraMaterial;
          }

          // TODO: we really need to extract and re-use this logic...
          const f = populateFormulaVars(
            legacyFormula(),
            tier.offer,
            extraCostMultiplier,
          );
          const result = await calculatePrice(
            tier.offer.costsMaterial,
            tier.offer.costsTime,
            f,
          );
          selectedTier.value.price = result.finalPrice;
          selectedTier.value.priceConverted = result.finalPriceConverted;

          const titleItem = tier.contentItems?.find((e: any) =>
            e.refId?.includes("_title"),
          );
          if (titleItem) {
            selectedTier.value.title = titleItem.content;
          }

          const tierName = tier.name?.toLowerCase() || "";
          if (tierName.includes("platinum")) {
            selectedTier.value.warranty = "2 year limited warranty";
          } else if (tierName.includes("gold")) {
            selectedTier.value.warranty = "18 month limited warranty";
          } else if (tierName.includes("silver")) {
            selectedTier.value.warranty = "1 year limited warranty";
          } else if (tierName.includes("bronze")) {
            selectedTier.value.warranty = "6 month limited warranty";
          } else if (tierName.includes("band")) {
            selectedTier.value.warranty = "30 day limited warranty";
          }
        }
      }
    }
  } catch (error) {
    console.error("Error loading tier:", error);
  }
});
</script>

<style scoped>
.confirmation-container {
  padding: 20px;
  max-width: 800px;
  margin: 0 auto;
}

.content-section {
  padding: 0 20px 20px 20px;
}

.tier-card {
  background-color: var(--ion-color-dark);
  border-radius: 12px;
  overflow: hidden;
  border: 2px solid transparent;
  display: flex;
  flex-direction: column;
  margin-bottom: 24px;
}

/* Tier-specific border colors */
.tier-platinum {
  border-color: var(--menu-color-platinum, #3db4d6);
}

.tier-gold {
  border-color: var(--menu-color-gold, #ffd83b);
}

.tier-silver {
  border-color: var(--menu-color-silver, #bfbfbf);
}

.tier-bronze {
  border-color: var(--menu-color-bronze, #ffad2b);
}

.tier-bandaid {
  border-color: var(--menu-color-bandaid, #ff8073);
}

.tier-badge {
  padding: 16px 20px;
  text-align: center;
  font-weight: 700;
  font-size: 24px;
  text-transform: uppercase;
  color: #000;
}

.badge-platinum {
  background: linear-gradient(135deg, var(--menu-color-platinum, #3db4d6) 0%, var(--menu-color-platinum-tint, #5ec3dd) 100%);
}

.badge-gold {
  background: linear-gradient(135deg, var(--menu-color-gold, #ffd83b) 0%, var(--menu-color-gold-tint, #ffe066) 100%);
}

.badge-silver {
  background: linear-gradient(135deg, var(--menu-color-silver, #bfbfbf) 0%, var(--menu-color-silver-tint, #d4d4d4) 100%);
}

.badge-bronze {
  background: linear-gradient(135deg, var(--menu-color-bronze, #ffad2b) 0%, var(--menu-color-bronze-tint, #ffc04d) 100%);
}

.badge-bandaid {
  background: linear-gradient(135deg, var(--menu-color-bandaid, #ff8073) 0%, var(--menu-color-bandaid-tint, #ff9a8f) 100%);
}

.tier-name {
  display: block;
  line-height: 1.2;
}

.tier-content {
  padding: 24px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.tier-title {
  margin: 0 0 20px 0;
  color: #ffffff;
  font-size: 20px;
  font-weight: 600;
  text-align: center;
  line-height: 1.3;
}

.features-section {
  flex: 1;
  margin-bottom: 24px;
}

.feature-item {
  margin-bottom: 8px;
}

.feature-text {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
  color: var(--ion-color-light);
  font-size: 15px;
  line-height: 1.5;
}

.feature-icon {
  color: var(--ion-color-primary);
  font-size: 20px;
  flex-shrink: 0;
  margin-top: 2px;
}

.pricing-section {
  text-align: center;
  margin-bottom: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--ion-color-medium);
}

.price-amount {
  color: #ffffff;
  font-size: 32px;
  font-weight: 700;
  margin-bottom: 12px;
}

.warranty-text {
  color: var(--ion-color-medium);
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.actions-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.browse-message {
  text-align: center;
  margin-top: 8px;
  color: var(--ion-color-medium);
  font-size: 14px;
}

.browse-message p {
  margin: 0;
}

/* Mobile adjustments */
@media (max-width: 767px) {
  .confirmation-container {
    padding: 16px;
  }

  .tier-badge {
    font-size: 20px;
    padding: 12px 16px;
  }

  .tier-title {
    font-size: 18px;
  }

  .feature-text {
    font-size: 14px;
  }

  .price-amount {
    font-size: 28px;
  }
}
</style>