Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
import { getDb } from "@/dataAccess/getDb";
import { convertToPrefs, type ConvertedValue } from "@/framework/currencies";

// ============================================================================
// Tech Handbook Types and Functions (moved from composables/useTechHandbook.ts)
// ============================================================================

export interface TechHandbookItem {
  content: string;
}

export interface TierTechHandbook {
  tierName: string;
  offerId: string;
  items: string[];
}

/**
 * Load tech handbook content for a single offer by ID
 * Returns an array of content strings
 */
export async function loadTechHandbookForOffer(offerId: string): Promise<string[]> {
  const db = await getDb();
  if (!db || !offerId) return [];

  const fullOffer = await db.offers.byOfferId(offerId);
  if (!fullOffer?.techHandbookExpanded || fullOffer.techHandbookExpanded.length === 0) {
    return [];
  }

  // Extract content from each contentItem and reverse to get correct order
  return fullOffer.techHandbookExpanded
    .map((item: TechHandbookItem) => item.content)
    .filter((content: string) => content)
    .reverse();
}

/**
 * Load tech handbook content for multiple tiers
 * Returns an array of tier names with their handbook items
 */
export async function loadTechHandbookForTiers(tiers: any[]): Promise<TierTechHandbook[]> {
  const result: TierTechHandbook[] = [];

  for (const tier of tiers || []) {
    const tierName = tier.name || tier.title || 'Unknown Tier';
    const offerId = tier.offer?.id;

    if (!offerId) continue;

    const items = await loadTechHandbookForOffer(offerId);

    if (items.length > 0) {
      result.push({ tierName, offerId, items });
    }
  }

  return result;
}

// ============================================================================
// Job Content Hierarchy Types
// ============================================================================

export interface ContentItem {
  id: string;
  name: string;
  refId: string;
  content: string;
}

export interface CostTime {
  id: string;
  name: string;
  refId: string;
  hours: number;
}

export interface CostMaterial {
  id: string;
  name: string;
  refId: string;
  quantity: number;
  quantityConverted: ConvertedValue | null;
}

export interface Offer {
  id: string;
  name: string;
  refId: string;
  costsTime: CostTime[];
  costsMaterial: CostMaterial[];
  techHandbook: ContentItem[];
}

export interface Tier {
  id: string;
  name: string;
  refId: string;
  rank: number;
  warrantyCopy: string | null;
}

export interface MenuCopy {
  id: string;
  name: string;
  refId: string;
  contentItems: ContentItem[];
}

export interface MenuTier {
  id: string;
  refId: string;
  tier: Tier;
  offer: Offer;
  menuCopy: MenuCopy[];
  contentItems: ContentItem[];
}

export interface Menu {
  id: string;
  name: string;
  refId: string;
  tiers: MenuTier[];
}

export interface Problem {
  id: string;
  name: string;
  description: string;
  refId: string;
}

export interface JobContentHierarchy {
  problem: Problem;
  menus: Menu[];
}

// ============================================================================
// Job Content Loading Function
// ============================================================================

/**
 * Given a problem ID, returns a hierarchical object with:
 * - problem (with name, description, refId)
 * - menus[] (each with tiers)
 *   - tiers[] (each with tier info, offer, menuCopy, contentItems)
 *     - offer (with costsTime, costsMaterial, techHandbook)
 */
export async function loadJobContentByProblemId(problemId: string): Promise<JobContentHierarchy | null> {
  const db = await getDb();
  if (!db || !problemId) return null;

  // Get the problem
  const problem = await db.problems.byId(problemId);
  if (!problem) return null;

  const result: JobContentHierarchy = {
    problem: {
      id: problem.id,
      name: problem.name,
      description: problem.description,
      refId: problem.refId,
    },
    menus: [],
  };

  // Get menus for this problem
  const menuIds = problem.menus || [];

  for (const menuId of menuIds) {
    // Use the existing byMenuId which already builds the hierarchy
    const menuData = await db.menus.byMenuId(menuId);
    if (!menuData) continue;

    const menu: Menu = {
      id: menuData.id,
      name: menuData.name,
      refId: menuData.refId,
      tiers: [],
    };

    // Transform tiers to our structure
    for (const tierData of menuData.tiers || []) {
      const menuTier: MenuTier = {
        id: tierData.id,
        refId: tierData.refId,
        tier: {
          id: tierData.id,
          name: tierData.name,
          refId: tierData.refId,
          rank: tierData.rank,
          warrantyCopy: tierData.warrantyCopy || null,
        },
        offer: {
          id: tierData.offer?.id || '',
          name: tierData.offer?.name || '',
          refId: tierData.offer?.refId || '',
          costsTime: (tierData.offer?.costsTime || []).map((c: any) => ({
            id: c.id,
            name: c.name,
            refId: c.refId,
            hours: c.hours || 0,
          })),
          costsMaterial: await Promise.all((tierData.offer?.costsMaterial || []).map(async (c: any) => ({
            id: c.id,
            name: c.name,
            refId: c.refId,
            quantity: c.quantity || 0,
            quantityConverted: await convertToPrefs(c.quantity || 0),
          }))),
          techHandbook: (tierData.offer?.techHandbookExpanded || []).map((c: any) => ({
            id: c.id,
            name: c.name,
            refId: c.refId,
            content: c.content || '',
          })),
        },
        menuCopy: (tierData.menuCopy || []).map((mc: any) => ({
          id: mc.id,
          name: mc.name,
          refId: mc.refId,
          contentItems: (mc.contentItems || []).map((c: any) => ({
            id: c.id,
            name: c.name,
            refId: c.refId,
            content: c.content || '',
          })),
        })),
        contentItems: (tierData.contentItems || []).map((c: any) => ({
          id: c.id,
          name: c.name,
          refId: c.refId,
          content: c.content || '',
        })),
      };

      menu.tiers.push(menuTier);
    }

    result.menus.push(menu);
  }

  return result;
}

/**
 * Given a problem refId (e.g., "PA1_problem"), returns a hierarchical object
 * with problem, menus, tiers, and offers.
 */
export async function loadJobContentByProblemRefId(refId: string): Promise<JobContentHierarchy | null> {
  const db = await getDb();
  if (!db || !refId) return null;

  // Get the problem by refId
  const problem = await db.problems.byRefId(refId);
  if (!problem) return null;

  // Use the existing function to load the full hierarchy
  return loadJobContentByProblemId(problem.id);
}