Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
<template>
  <BaseLayout title="Problem Details">
    <ion-grid :fixed="true">
      <ion-row>
        <ion-col>
          <ion-button fill="clear" @click="goBack">
            <ion-icon :icon="arrowBackOutline" slot="start" />
            Back to Browse
          </ion-button>
        </ion-col>
      </ion-row>

      <ion-row v-if="problem">
        <ion-col>
          <ion-card>
            <ion-card-header>
              <ion-card-title>{{ problem.name }}</ion-card-title>
              <ion-card-subtitle>Problem ID: {{ problem.refId }}</ion-card-subtitle>
            </ion-card-header>
            <ion-card-content>
              <ion-grid>
                <ion-row>
                  <ion-col size="12" size-md="8">
                    <div class="problem-info">
                      <div class="description-header">
                        <h3>Description</h3>
                        <ion-button 
                          fill="clear" 
                          size="small" 
                          @click="toggleDescriptionEdit"
                        >
                          <ion-icon :icon="isEditingDescription ? closeOutline : pencilOutline" slot="start" />
                          {{ isEditingDescription ? 'Cancel' : 'Edit' }}
                        </ion-button>
                      </div>
                      
                      <div v-if="!isEditingDescription" class="description-display">
                        <p class="description">
                          {{ problem.description || 'Description will go here' }}
                        </p>
                      </div>

                      <div v-if="isEditingDescription" class="description-edit">
                        <ion-textarea
                          v-model="editingDescription"
                          placeholder="Enter problem description..."
                          :rows="4"
                          fill="outline"
                        ></ion-textarea>
                        <div class="description-actions">
                          <ion-button 
                            @click="saveDescription"
                            color="primary"
                            size="small"
                            :disabled="isSavingDescription"
                          >
                            <ion-spinner v-if="isSavingDescription" />
                            <span v-else>Save</span>
                          </ion-button>
                          <ion-button 
                            @click="cancelDescriptionEdit"
                            fill="clear"
                            size="small"
                            :disabled="isSavingDescription"
                          >
                            Cancel
                          </ion-button>
                        </div>
                      </div>
                      <p class="problem-id">
                        <strong>Reference ID:</strong> {{ problem.refId }}
                      </p>
                      <p class="org-info">
                        <strong>Organization:</strong> {{ problem.org }}
                      </p>
                      <div class="creation-info">
                        <p v-if="problem.created">
                          <strong>Created:</strong> {{ formatDate(problem.created) }}
                        </p>
                        <p v-if="problem.updated">
                          <strong>Last Updated:</strong> {{ formatDate(problem.updated) }}
                        </p>
                      </div>
                    </div>
                  </ion-col>
                  <ion-col size="12" size-md="4">
                    <div class="problem-metadata">
                      <div class="tags-header">
                        <h3>Tags</h3>
                        <ion-button 
                          fill="clear" 
                          size="small" 
                          @click="toggleTagManagement"
                        >
                          <ion-icon :icon="isManagingTags ? closeOutline : addOutline" slot="start" />
                          {{ isManagingTags ? 'Done' : 'Manage' }}
                        </ion-button>
                      </div>
                      
                      <div class="tags-section">
                        <!-- Current Tags Display -->
                        <div v-if="problemTags.length > 0" class="tags-container">
                          <span
                            v-for="tag in problemTags"
                            :key="tag.id"
                            class="tag"
                            :class="{ removable: isManagingTags }"
                            @click="isManagingTags && removeTagFromProblem(tag.id)"
                          >
                            #{{ tag.name }}
                            <ion-icon 
                              v-if="isManagingTags" 
                              :icon="closeCircleOutline" 
                              class="remove-icon"
                            />
                          </span>
                        </div>
                        <p v-else class="no-tags">No tags assigned</p>

                        <!-- Tag Management Section -->
                        <div v-if="isManagingTags" class="tag-management">
                          <div class="add-existing-tag">
                            <h4>Add Existing Tags</h4>
                            
                            <!-- Search input -->
                            <ion-searchbar
                              v-model="tagSearchQuery"
                              placeholder="Search tags..."
                              @ionInput="onTagSearchInput"
                              class="tag-search"
                            ></ion-searchbar>
                            
                            <!-- Tag selection area -->
                            <div class="tag-selection-container">
                              <div v-if="filteredAvailableTags.length > 0" class="available-tags-list">
                                <div 
                                  v-for="tag in filteredAvailableTags" 
                                  :key="tag.id"
                                  class="tag-selection-item"
                                >
                                  <ion-checkbox
                                    :checked="selectedTagsToAdd.includes(tag.id)"
                                    @ionChange="toggleTagSelection(tag.id, $event)"
                                  ></ion-checkbox>
                                  <span class="tag-name">{{ tag.name }}</span>
                                </div>
                              </div>
                              <div v-else class="no-tags-found">
                                <p v-if="tagSearchQuery.trim()">No tags found matching "{{ tagSearchQuery }}"</p>
                                <p v-else>No tags available to add</p>
                              </div>
                            </div>
                            
                            <!-- Selected tags display -->
                            <div v-if="selectedTagsToAdd.length > 0" class="selected-tags-preview">
                              <h5>Selected Tags ({{ selectedTagsToAdd.length }}):</h5>
                              <div class="selected-tags-chips">
                                <span
                                  v-for="tagId in selectedTagsToAdd"
                                  :key="tagId"
                                  class="selected-tag-chip"
                                  @click="removeFromSelection(tagId)"
                                >
                                  {{ getTagName(tagId) }}
                                  <ion-icon :icon="closeCircleOutline" class="remove-selected-icon" />
                                </span>
                              </div>
                            </div>
                            
                            <ion-button 
                              v-if="selectedTagsToAdd.length > 0"
                              @click="addSelectedTags"
                              size="small"
                              :disabled="isAddingTags"
                              class="add-tags-button"
                            >
                              <ion-spinner v-if="isAddingTags" />
                              <span v-else>Add {{ selectedTagsToAdd.length }} Tag{{ selectedTagsToAdd.length > 1 ? 's' : '' }}</span>
                            </ion-button>
                          </div>

                          <div class="create-new-tag">
                            <h4>Create New Tag</h4>
                            <div class="new-tag-form">
                              <ion-input
                                v-model="newTagName"
                                placeholder="Enter tag name..."
                                @keyup.enter="createNewTag"
                              ></ion-input>
                              <ion-button 
                                @click="createNewTag"
                                :disabled="!newTagName.trim() || isCreatingTag"
                                size="small"
                              >
                                <ion-spinner v-if="isCreatingTag" />
                                <span v-else>Create</span>
                              </ion-button>
                            </div>
                          </div>
                        </div>
                      </div>

                      <h3>Associated Menus</h3>
                      <div class="menus-section">
                        <div v-if="associatedMenus.length > 0">
                          <ion-chip 
                            v-for="menu in associatedMenus" 
                            :key="menu.id"
                            color="secondary"
                            outline
                          >
                            <ion-label>{{ menu.name || 'Unnamed Menu' }}</ion-label>
                          </ion-chip>
                        </div>
                        <p v-else class="no-menus">No associated menus</p>
                      </div>
                    </div>
                  </ion-col>
                </ion-row>
              </ion-grid>
            </ion-card-content>
          </ion-card>
        </ion-col>
      </ion-row>


      <!-- Loading State -->
      <ion-row v-if="!problem && !loading">
        <ion-col>
          <ion-card>
            <ion-card-content>
              <p class="error-message">Problem not found.</p>
              <ion-button @click="goBack" fill="clear">
                <ion-icon :icon="arrowBackOutline" slot="start" />
                Back to Browse
              </ion-button>
            </ion-card-content>
          </ion-card>
        </ion-col>
      </ion-row>

      <!-- Loading Spinner -->
      <ion-row v-if="loading">
        <ion-col class="ion-text-center">
          <ion-spinner></ion-spinner>
          <p>Loading problem details...</p>
        </ion-col>
      </ion-row>
    </ion-grid>
  </BaseLayout>
</template>

<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
import { useRouter, useRoute } from "vue-router";
import BaseLayout from "@/components/BaseLayout.vue";
import type { ProblemsRecord, ProblemTagsRecord, MenusRecord } from "@/pocketbase-types.ts";
import { getDb } from "@/dataAccess/getDb";
import { getApi } from "@/dataAccess/getApi";
import { ChangesetProcessor, type Change } from "@/dataAccess/changeset-processor";
import {
  IonButton,
  IonGrid,
  IonRow,
  IonCol,
  IonCard,
  IonCardHeader,
  IonCardTitle,
  IonCardSubtitle,
  IonCardContent,
  IonIcon,
  IonChip,
  IonLabel,
  IonSpinner,
  IonSelect,
  IonSelectOption,
  IonInput,
  IonTextarea,
  IonSearchbar,
  IonCheckbox,
  alertController,
  toastController,
} from "@ionic/vue";
import {
  arrowBackOutline,
  addOutline,
  closeOutline,
  closeCircleOutline,
  pencilOutline,
} from "ionicons/icons";

const router = useRouter();
const route = useRoute();
const problem = ref<ProblemsRecord | null>(null);
const problemTags = ref<ProblemTagsRecord[]>([]);
const allTags = ref<ProblemTagsRecord[]>([]);
const associatedMenus = ref<MenusRecord[]>([]);
const loading = ref(true);

// Tag management states
const isManagingTags = ref(false);
const selectedTagsToAdd = ref<string[]>([]);
const newTagName = ref("");
const isCreatingTag = ref(false);
const isAddingTags = ref(false);
const tagSearchQuery = ref("");

// Description editing states
const isEditingDescription = ref(false);
const editingDescription = ref("");
const isSavingDescription = ref(false);

// Computed properties
const availableTagsToAdd = computed(() => {
  const currentTagIds = problemTags.value.map(tag => tag.id);
  return allTags.value
    .filter(tag => !currentTagIds.includes(tag.id))
    .sort((a, b) => (a.name || '').localeCompare(b.name || ''));
});

const filteredAvailableTags = computed(() => {
  const searchTerm = tagSearchQuery.value.toLowerCase().trim();
  if (!searchTerm) {
    return availableTagsToAdd.value;
  }
  return availableTagsToAdd.value.filter(tag => 
    tag.name?.toLowerCase().includes(searchTerm)
  );
});

// Helper functions
const formatDate = (dateString: string): string => {
  try {
    return new Date(dateString).toLocaleDateString('en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      hour: '2-digit',
      minute: '2-digit'
    });
  } catch {
    return dateString;
  }
};

const getProblemTagIds = (problem: ProblemsRecord): string[] => {
  if (!problem.problemTags) return [];
  
  if (typeof problem.problemTags === 'string') {
    try {
      return JSON.parse(problem.problemTags);
    } catch (e) {
      return [];
    }
  } else if (Array.isArray(problem.problemTags)) {
    return problem.problemTags;
  }
  
  return [];
};

const getMenuIds = (problem: ProblemsRecord): string[] => {
  if (!problem.menus) return [];
  
  if (typeof problem.menus === 'string') {
    try {
      return JSON.parse(problem.menus);
    } catch (e) {
      return [];
    }
  } else if (Array.isArray(problem.menus)) {
    return problem.menus;
  }
  
  return [];
};

// Helper function to publish changes both to API and apply locally
const publishAndApplyChanges = async (changes: Change[], orgId: string) => {
  const { pb } = await getApi();
  const db = await getDb();
  
  if (!db) {
    throw new Error('Database not available');
  }
  
  console.log('Publishing changes for orgId:', orgId);
  console.log('Publishing changes:', changes);

  // For book lookup, always use the user's activeOrg since clientApp is a special case
  // that maps to the user's actual organization for changeset purposes
  const activeOrgId = pb.authStore.record?.expand?.activeOrg?.id || pb.authStore.record?.activeOrg;
  if (!activeOrgId) {
    throw new Error('No active organization found for user');
  }

  const books = await pb.collection("books").getFullList({
    filter: `org = '${activeOrgId}'`,
    sort: "created"
  });
  
  if (books.length === 0) {
    throw new Error(`No books found for active organization: ${activeOrgId}`);
  }

  const bookId = books[0].id;
  console.log('Using activeOrg book:', bookId, 'for changeset, but processing in orgId:', orgId);

  // Apply changes locally first for immediate UI feedback
  // We need to access the SQLite connection directly
  const { getSQLite } = await import("@/dataAccess/getSQLite");
  const sql = await getSQLite();
  const processor = new ChangesetProcessor(sql.dbConn);
  
  console.log('Applying changes locally with orgId:', orgId);
  // Pass false for useTransaction to avoid nested transaction conflict
  await processor.processChangeset(changes, orgId, false);
  await sql.saveDb();

  // Then publish to API for server sync
  console.log('Publishing to API...');
  await pb.collection("pricebookChanges").create({
    org: activeOrgId,  // Use activeOrg for API changeset
    book: bookId,
    changeset: changes,
  });

  console.log('Changes applied successfully');
};

// Description editing functions
const toggleDescriptionEdit = () => {
  if (isEditingDescription.value) {
    cancelDescriptionEdit();
  } else {
    isEditingDescription.value = true;
    editingDescription.value = problem.value?.description || "";
  }
};

const saveDescription = async () => {
  if (!problem.value) return;
  
  isSavingDescription.value = true;

  try {
    const changes: Change[] = [
      {
        collection: "problems",
        operation: "update",
        data: {
          refId: problem.value.refId,
          description: editingDescription.value.trim(),
        },
      },
    ];

    console.log('Saving description:', editingDescription.value.trim());

    // Publish changes to API and apply locally using problem's org
    await publishAndApplyChanges(changes, problem.value.org || 'clientApp');

    // Update local problem data
    problem.value.description = editingDescription.value.trim();
    
    // Exit edit mode
    isEditingDescription.value = false;

    const toast = await toastController.create({
      message: 'Description saved successfully',
      duration: 2000,
      color: 'success'
    });
    await toast.present();

  } catch (error) {
    console.error("Error saving description:", error);
    const toast = await toastController.create({
      message: 'Failed to save description',
      duration: 3000,
      color: 'danger'
    });
    await toast.present();
  } finally {
    isSavingDescription.value = false;
  }
};

const cancelDescriptionEdit = () => {
  isEditingDescription.value = false;
  editingDescription.value = "";
};

// Tag management functions
const toggleTagManagement = () => {
  isManagingTags.value = !isManagingTags.value;
  selectedTagsToAdd.value = [];
  newTagName.value = "";
  tagSearchQuery.value = "";
};

const generateTagRefId = (name: string): string => {
  return name.toLowerCase().replace(/[^a-z0-9]/g, '_').replace(/_{2,}/g, '_').replace(/^_|_$/g, '');
};

const createNewTag = async () => {
  const tagName = newTagName.value.trim().toLowerCase();
  if (!tagName || !problem.value) return;

  console.log('Creating new tag:', tagName);

  // Check if tag name already exists
  if (allTags.value.some(tag => tag.name?.toLowerCase() === tagName.toLowerCase())) {
    const toast = await toastController.create({
      message: 'A tag with this name already exists',
      duration: 3000,
      color: 'warning'
    });
    await toast.present();
    return;
  }

  isCreatingTag.value = true;

  try {
    const refId = generateTagRefId(tagName);
    console.log('Generated refId:', refId);

    const changes: Change[] = [
      {
        collection: "problemTags",
        operation: "create",
        data: {
          name: tagName,
          refId: refId,
        },
      },
    ];

    // Publish changes to API and apply locally using problem's org
    await publishAndApplyChanges(changes, problem.value.org || 'clientApp');

    // Refresh local data to see the new tag
    await loadAllTags();
    console.log('All tags after creation:', allTags.value);

    // Find the newly created tag and add it to the problem
    const newTag = allTags.value.find(tag => tag.refId === refId);
    console.log('Found new tag:', newTag);
    
    if (newTag) {
      await addTagToProblem(newTag.id);
    } else {
      console.error('Could not find newly created tag with refId:', refId);
    }

    newTagName.value = "";

    const toast = await toastController.create({
      message: `Tag "${tagName}" created successfully`,
      duration: 2000,
      color: 'success'
    });
    await toast.present();

  } catch (error) {
    console.error("Error creating tag:", error);
    const toast = await toastController.create({
      message: 'Failed to create tag',
      duration: 3000,
      color: 'danger'
    });
    await toast.present();
  } finally {
    isCreatingTag.value = false;
  }
};

const onTagSearchInput = (event: CustomEvent) => {
  tagSearchQuery.value = event.detail.value;
};

const toggleTagSelection = (tagId: string, event: CustomEvent) => {
  const isChecked = event.detail.checked;
  if (isChecked) {
    if (!selectedTagsToAdd.value.includes(tagId)) {
      selectedTagsToAdd.value.push(tagId);
    }
  } else {
    selectedTagsToAdd.value = selectedTagsToAdd.value.filter(id => id !== tagId);
  }
  console.log('Selected tags to add:', selectedTagsToAdd.value);
};

const removeFromSelection = (tagId: string) => {
  selectedTagsToAdd.value = selectedTagsToAdd.value.filter(id => id !== tagId);
};

const getTagName = (tagId: string): string => {
  const tag = allTags.value.find(t => t.id === tagId);
  return tag?.name || 'Unknown Tag';
};

const addSelectedTags = async () => {
  if (selectedTagsToAdd.value.length === 0) return;
  
  isAddingTags.value = true;
  
  try {
    console.log('Adding multiple tags:', selectedTagsToAdd.value);
    
    // Store count before clearing
    const addedCount = selectedTagsToAdd.value.length;
    
    // Use the more efficient batch add function
    await addMultipleTagsToProblem(selectedTagsToAdd.value);
    
    // Clear selection after successful addition
    selectedTagsToAdd.value = [];
    
    const toast = await toastController.create({
      message: `Successfully added ${addedCount} tag${addedCount > 1 ? 's' : ''} to problem`,
      duration: 2000,
      color: 'success'
    });
    await toast.present();
    
  } catch (error) {
    console.error('Failed to add tags:', error);
    const toast = await toastController.create({
      message: 'Failed to add some tags to problem',
      duration: 3000,
      color: 'danger'
    });
    await toast.present();
  } finally {
    isAddingTags.value = false;
  }
};

const addMultipleTagsToProblem = async (tagIds: string[]) => {
  if (!problem.value || tagIds.length === 0) return;

  try {
    console.log('Adding multiple tags to problem:', tagIds, 'to problem:', problem.value.refId);
    
    const currentTagIds = getProblemTagIds(problem.value);
    const newTagIds = [...currentTagIds, ...tagIds];

    console.log('Current tag IDs:', currentTagIds);
    console.log('New tag IDs:', newTagIds);

    // Get the refIds for all tags
    const tagRefIds = allTags.value
      .filter(tag => newTagIds.includes(tag.id))
      .map(tag => tag.refId)
      .filter(refId => refId !== undefined);

    console.log('Tag refIds for update:', tagRefIds);

    const changes: Change[] = [
      {
        collection: "problems",
        operation: "update",
        data: {
          refId: problem.value.refId,
          refs: [
            {
              collection: "problemTags",
              refIds: tagRefIds,
            },
          ],
        },
      },
    ];

    console.log('About to publish batch changes:', JSON.stringify(changes, null, 2));

    // Publish changes to API and apply locally using problem's org
    await publishAndApplyChanges(changes, problem.value.org || 'clientApp');

    // Force reload the problem from database to get updated tags
    const db = await getDb();
    if (db) {
      const updatedProblems = await db.selectAll("problems") || [];
      const updatedProblem = updatedProblems.find((p: ProblemsRecord) => p.id === problem.value!.id);
      if (updatedProblem) {
        problem.value = updatedProblem;
        console.log('Updated problem data:', updatedProblem);
      }
    }
    
    // Refresh all tags and problem tags display
    await loadAllTags();
    await loadProblemTags();

  } catch (error) {
    console.error("Error adding multiple tags to problem:", error);
    throw error; // Re-throw to let caller handle it
  }
};

const addTagToProblem = async (tagId: string) => {
  if (!problem.value) return;

  try {
    console.log('Adding tag to problem:', tagId, 'to problem:', problem.value.refId);
    console.log('Problem object:', problem.value);
    
    const currentTagIds = getProblemTagIds(problem.value);
    const newTagIds = [...currentTagIds, tagId];

    console.log('Current tag IDs:', currentTagIds);
    console.log('New tag IDs:', newTagIds);

    // Get the refIds for the tags
    const tagRefIds = allTags.value
      .filter(tag => newTagIds.includes(tag.id))
      .map(tag => tag.refId)
      .filter(refId => refId !== undefined);

    console.log('Tag refIds for update:', tagRefIds);

    const changes: Change[] = [
      {
        collection: "problems",
        operation: "update",
        data: {
          refId: problem.value.refId,
          refs: [
            {
              collection: "problemTags",
              refIds: tagRefIds,
            },
          ],
        },
      },
    ];

    console.log('About to publish changes:', JSON.stringify(changes, null, 2));

    // Let's also check if we can find this problem in the database first
    const db = await getDb();
    if (db) {
      const problems = await db.selectAll("problems") || [];
      const foundProblem = problems.find((p: ProblemsRecord) => p.refId === problem.value!.refId);
      console.log('Found problem in database:', foundProblem);
      
      if (!foundProblem) {
        console.error('Problem not found with refId:', problem.value.refId);
        console.log('Available problems:', problems.map((p: any) => ({ id: p.id, refId: p.refId, name: p.name })));
      }
    }

    // Publish changes to API and apply locally using problem's org
    await publishAndApplyChanges(changes, problem.value.org || 'clientApp');

    // Force reload the problem from database to get updated tags
    if (db) {
      const updatedProblems = await db.selectAll("problems") || [];
      const updatedProblem = updatedProblems.find((p: ProblemsRecord) => p.id === problem.value!.id);
      if (updatedProblem) {
        problem.value = updatedProblem;
        console.log('Updated problem data:', updatedProblem);
      }
    }
    
    // Refresh all tags and problem tags display
    await loadAllTags();
    await loadProblemTags();

    const addedTag = allTags.value.find(tag => tag.id === tagId);
    const toast = await toastController.create({
      message: `Tag "${addedTag?.name}" added to problem`,
      duration: 2000,
      color: 'success'
    });
    await toast.present();

  } catch (error) {
    console.error("Error adding tag to problem:", error);
    const toast = await toastController.create({
      message: 'Failed to add tag to problem',
      duration: 3000,
      color: 'danger'
    });
    await toast.present();
  }
};

const removeTagFromProblem = async (tagId: string) => {
  if (!problem.value) return;

  const tagToRemove = allTags.value.find(tag => tag.id === tagId);
  const alert = await alertController.create({
    header: 'Remove Tag',
    message: `Are you sure you want to remove the tag "${tagToRemove?.name}" from this problem?`,
    buttons: [
      {
        text: 'Cancel',
        role: 'cancel',
      },
      {
        text: 'Remove',
        role: 'destructive',
        handler: async () => {
          await performRemoveTag(tagId);
        },
      },
    ],
  });

  await alert.present();
};

const performRemoveTag = async (tagId: string) => {
  if (!problem.value) return;

  try {
    const currentTagIds = getProblemTagIds(problem.value);
    const newTagIds = currentTagIds.filter(id => id !== tagId);

    const changes: Change[] = [
      {
        collection: "problems",
        operation: "update",
        data: {
          refId: problem.value.refId,
          refs: [
            {
              collection: "problemTags",
              refIds: allTags.value
                .filter(tag => newTagIds.includes(tag.id))
                .map(tag => tag.refId)
                .filter(refId => refId !== undefined),
            },
          ],
        },
      },
    ];

    // Publish changes to API 
    await publishAndApplyChanges(changes, problem.value.org || '');

    // Update local problem data
    problem.value.problemTags = newTagIds;
    
    // Refresh problem tags display
    await loadProblemTags();

    const removedTag = allTags.value.find(tag => tag.id === tagId);
    const toast = await toastController.create({
      message: `Tag "${removedTag?.name}" removed from problem`,
      duration: 2000,
      color: 'success'
    });
    await toast.present();

  } catch (error) {
    console.error("Error removing tag from problem:", error);
    const toast = await toastController.create({
      message: 'Failed to remove tag from problem',
      duration: 3000,
      color: 'danger'
    });
    await toast.present();
  }
};

// Event handlers
const goBack = () => {
  router.push('/browse/problems');
};


// Helper functions for data loading
const loadAllTags = async () => {
  const db = await getDb();
  if (db) {
    allTags.value = (await db.selectAll("problemTags")) || [];
  }
};

const loadProblemTags = async () => {
  if (!problem.value) return;
  
  const problemTagIds = getProblemTagIds(problem.value);
  problemTags.value = allTags.value.filter((tag: ProblemTagsRecord) => 
    problemTagIds.includes(tag.id)
  );
};

// Lifecycle
onMounted(async () => {
  const problemId = route.params.id as string;
  if (!problemId) {
    loading.value = false;
    return;
  }

  try {
    const db = await getDb();
    if (!db) {
      loading.value = false;
      return;
    }

    // Get the specific problem
    const problems = await db.selectAll("problems") || [];
    const foundProblem = problems.find((p: ProblemsRecord) => p.id === problemId);
    
    if (foundProblem) {
      problem.value = foundProblem;

      // Load all tags
      await loadAllTags();
      
      // Load problem-specific tags
      await loadProblemTags();

      // Get associated menus
      const allMenus = await db.selectAll("menus") || [];
      const menuIds = getMenuIds(foundProblem);
      associatedMenus.value = allMenus.filter((menu: MenusRecord) =>
        menuIds.includes(menu.id)
      );
    }
  } catch (error) {
    console.error("Error loading problem details:", error);
  } finally {
    loading.value = false;
  }
});
</script>

<style scoped>
.problem-info {
  margin-bottom: 20px;
}

.description {
  font-size: 1.1em;
  line-height: 1.5;
  margin-bottom: 16px;
  color: var(--ion-color-dark);
}

.problem-id,
.org-info {
  margin-bottom: 8px;
  color: var(--ion-color-medium);
}

.creation-info p {
  margin-bottom: 4px;
  font-size: 0.9em;
  color: var(--ion-color-medium);
}

.problem-metadata h3 {
  margin-top: 0;
  margin-bottom: 12px;
  color: var(--ion-color-primary);
  font-size: 1.1em;
}

.tags-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.tags-header h3 {
  margin: 0;
}

.tags-section,
.menus-section {
  margin-bottom: 24px;
}

.tags-container {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag {
  display: inline-block;
  background-color: var(--ion-color-primary);
  color: var(--ion-color-primary-contrast);
  padding: 4px 12px;
  border-radius: 16px;
  font-size: 0.85em;
  position: relative;
  transition: all 0.2s ease;
}

.tag.removable {
  cursor: pointer;
  padding-right: 28px;
}

.tag.removable:hover {
  background-color: var(--ion-color-danger);
  transform: scale(1.05);
}

.tag .remove-icon {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.9em;
}

.no-tags,
.no-menus {
  color: var(--ion-color-medium);
  font-style: italic;
  margin: 0;
}

.additional-info {
  text-align: left;
}

.additional-info p {
  margin-bottom: 20px;
  color: var(--ion-color-medium);
  line-height: 1.5;
}

.navigation-buttons {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.error-message {
  color: var(--ion-color-danger);
  font-weight: 500;
  text-align: center;
  margin-bottom: 20px;
}

.tag-management {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--ion-color-light);
}

.add-existing-tag,
.create-new-tag {
  margin-bottom: 16px;
}

.add-existing-tag h4,
.create-new-tag h4 {
  margin: 0 0 8px 0;
  font-size: 0.95em;
  color: var(--ion-color-dark);
  font-weight: 500;
}

.new-tag-form {
  display: flex;
  gap: 8px;
  align-items: center;
}

.new-tag-form ion-input {
  flex: 1;
}

.add-tags-button {
  margin-top: 12px;
  width: 100%;
}

.tag-search {
  margin-bottom: 16px;
}

.tag-selection-container {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid var(--ion-color-light);
  border-radius: 8px;
  margin-bottom: 16px;
}

.available-tags-list {
  padding: 8px;
}

.tag-selection-item {
  display: flex;
  align-items: center;
  padding: 8px;
  border-bottom: 1px solid var(--ion-color-light-shade);
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.tag-selection-item:last-child {
  border-bottom: none;
}

.tag-selection-item:hover {
  background-color: var(--ion-color-light);
}

.tag-name {
  margin-left: 12px;
  font-size: 0.9em;
  color: var(--ion-color-dark);
}

.no-tags-found {
  padding: 16px;
  text-align: center;
  color: var(--ion-color-medium);
  font-style: italic;
}

.selected-tags-preview {
  margin-bottom: 16px;
}

.selected-tags-preview h5 {
  margin: 0 0 8px 0;
  font-size: 0.9em;
  color: var(--ion-color-dark);
  font-weight: 500;
}

.selected-tags-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.selected-tag-chip {
  display: inline-flex;
  align-items: center;
  background-color: var(--ion-color-primary);
  color: var(--ion-color-primary-contrast);
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 0.8em;
  cursor: pointer;
  transition: all 0.2s ease;
}

.selected-tag-chip:hover {
  background-color: var(--ion-color-danger);
  transform: scale(1.05);
}

.remove-selected-icon {
  margin-left: 4px;
  font-size: 0.9em;
}

.description-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.description-header h3 {
  margin: 0;
}

.description-display {
  margin-bottom: 16px;
}

.description-edit {
  margin-bottom: 16px;
}

.description-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  justify-content: flex-start;
}

@media (max-width: 768px) {
  .navigation-buttons {
    flex-direction: column;
  }
  
  .navigation-buttons ion-button {
    width: 100%;
  }
}
</style>