|
@@ -10,7 +10,10 @@ export class ContentPage {
|
|
|
private shops: ShopWithAnalytics[] = [];
|
|
private shops: ShopWithAnalytics[] = [];
|
|
|
private selectedShopId: string | null = null;
|
|
private selectedShopId: string | null = null;
|
|
|
private contentData: ShopResultsResponse | null = null;
|
|
private contentData: ShopResultsResponse | null = null;
|
|
|
- private filters: ContentFilters = { limit: 50 };
|
|
|
|
|
|
|
+ private filters: ContentFilters = { limit: 20 }; // Reduced default limit
|
|
|
|
|
+ private currentPage: number = 1;
|
|
|
|
|
+ private totalPages: number = 1;
|
|
|
|
|
+ private totalItems: number = 0;
|
|
|
private eventsbound: boolean = false;
|
|
private eventsbound: boolean = false;
|
|
|
|
|
|
|
|
constructor(
|
|
constructor(
|
|
@@ -109,6 +112,82 @@ export class ContentPage {
|
|
|
<span class="ml-3 text-gray-600 dark:text-gray-400">Loading content...</span>
|
|
<span class="ml-3 text-gray-600 dark:text-gray-400">Loading content...</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <!-- Pagination -->
|
|
|
|
|
+ <div id="pagination-container" style="display: none;">
|
|
|
|
|
+ <div class="flex items-center justify-between mt-6 pt-6 border-t border-gray-200 dark:border-gray-600">
|
|
|
|
|
+ <div class="flex items-center space-x-2">
|
|
|
|
|
+ <span class="text-sm text-gray-700 dark:text-gray-300" id="pagination-info">
|
|
|
|
|
+ Showing 1-20 of 150 items
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex items-center space-x-2" id="pagination-controls">
|
|
|
|
|
+ <button class="btn btn-secondary btn-sm" id="prev-page" disabled>
|
|
|
|
|
+ <div class="w-4 h-4 mr-1">${getIcon('arrow-left')}</div>
|
|
|
|
|
+ Previous
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <span class="px-3 py-1 text-sm" id="page-numbers">
|
|
|
|
|
+ Page 1 of 8
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <button class="btn btn-secondary btn-sm" id="next-page">
|
|
|
|
|
+ Next
|
|
|
|
|
+ <div class="w-4 h-4 ml-1">${getIcon('arrow-right')}</div>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Full Content Modal -->
|
|
|
|
|
+ <div class="fixed inset-0 z-50 hidden" id="content-modal">
|
|
|
|
|
+ <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
|
|
|
|
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" id="modal-overlay"></div>
|
|
|
|
|
+ <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
|
|
|
|
+ <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6">
|
|
|
|
|
+ <div class="sm:flex sm:items-start">
|
|
|
|
|
+ <div class="w-full">
|
|
|
|
|
+ <div class="flex justify-between items-start mb-4">
|
|
|
|
|
+ <div class="flex-1">
|
|
|
|
|
+ <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white" id="modal-title">
|
|
|
|
|
+ Content Details
|
|
|
|
|
+ </h3>
|
|
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400" id="modal-url">
|
|
|
|
|
+ URL will appear here
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <button type="button" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" id="close-modal">
|
|
|
|
|
+ <span class="sr-only">Close</span>
|
|
|
|
|
+ <div class="w-6 h-6">${getIcon('x')}</div>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex justify-between items-center mb-4">
|
|
|
|
|
+ <div class="flex space-x-2">
|
|
|
|
|
+ <span class="badge" id="modal-type-badge">Content Type</span>
|
|
|
|
|
+ <span class="badge" id="modal-change-badge">Status</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex space-x-2">
|
|
|
|
|
+ <button type="button" class="btn btn-secondary btn-sm" id="copy-content-btn">
|
|
|
|
|
+ <div class="w-4 h-4 mr-1">${getIcon('copy')}</div>
|
|
|
|
|
+ Copy
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button type="button" class="btn btn-secondary btn-sm" id="open-url-btn">
|
|
|
|
|
+ <div class="w-4 h-4 mr-1">${getIcon('external-link')}</div>
|
|
|
|
|
+ Open URL
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 max-h-96 overflow-y-auto">
|
|
|
|
|
+ <pre class="whitespace-pre-wrap text-sm text-gray-800 dark:text-gray-200" id="modal-content">
|
|
|
|
|
+ Content will appear here...
|
|
|
|
|
+ </pre>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="mt-4 text-xs text-gray-500 dark:text-gray-400" id="modal-metadata">
|
|
|
|
|
+ Last updated: --
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
`;
|
|
`;
|
|
@@ -129,6 +208,62 @@ export class ContentPage {
|
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
|
this.applyFilters();
|
|
this.applyFilters();
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // Pagination controls
|
|
|
|
|
+ const prevBtn = this.element.querySelector('#prev-page');
|
|
|
|
|
+ const nextBtn = this.element.querySelector('#next-page');
|
|
|
|
|
+
|
|
|
|
|
+ prevBtn?.addEventListener('click', () => {
|
|
|
|
|
+ if (this.currentPage > 1) {
|
|
|
|
|
+ this.currentPage--;
|
|
|
|
|
+ this.loadContent();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ nextBtn?.addEventListener('click', () => {
|
|
|
|
|
+ if (this.currentPage < this.totalPages) {
|
|
|
|
|
+ this.currentPage++;
|
|
|
|
|
+ this.loadContent();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Modal controls
|
|
|
|
|
+ const modal = this.element.querySelector('#content-modal');
|
|
|
|
|
+ const modalOverlay = this.element.querySelector('#modal-overlay');
|
|
|
|
|
+ const closeModalBtn = this.element.querySelector('#close-modal');
|
|
|
|
|
+ const copyContentBtn = this.element.querySelector('#copy-content-btn');
|
|
|
|
|
+ const openUrlBtn = this.element.querySelector('#open-url-btn');
|
|
|
|
|
+
|
|
|
|
|
+ const closeModal = () => {
|
|
|
|
|
+ modal?.classList.add('hidden');
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ closeModalBtn?.addEventListener('click', closeModal);
|
|
|
|
|
+ modalOverlay?.addEventListener('click', closeModal);
|
|
|
|
|
+
|
|
|
|
|
+ copyContentBtn?.addEventListener('click', async () => {
|
|
|
|
|
+ if (this.currentModalItem) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await navigator.clipboard.writeText(this.currentModalItem.content);
|
|
|
|
|
+ this.toastService.success('Copied!', 'Content copied to clipboard');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ this.toastService.error('Copy Failed', 'Could not copy to clipboard');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ openUrlBtn?.addEventListener('click', () => {
|
|
|
|
|
+ if (this.currentModalItem) {
|
|
|
|
|
+ window.open(this.currentModalItem.url, '_blank');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // ESC key to close modal
|
|
|
|
|
+ document.addEventListener('keydown', (e) => {
|
|
|
|
|
+ if (e.key === 'Escape') {
|
|
|
|
|
+ closeModal();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async loadShops(): Promise<void> {
|
|
private async loadShops(): Promise<void> {
|
|
@@ -232,7 +367,7 @@ export class ContentPage {
|
|
|
const formData = new FormData(form);
|
|
const formData = new FormData(form);
|
|
|
|
|
|
|
|
this.filters = {
|
|
this.filters = {
|
|
|
- limit: 50, // Keep reasonable limit
|
|
|
|
|
|
|
+ limit: 20, // Reduced limit for pagination
|
|
|
content_type: (formData.get('content-type') as 'shipping' | 'contacts' | 'terms' | 'faq') || undefined,
|
|
content_type: (formData.get('content-type') as 'shipping' | 'contacts' | 'terms' | 'faq') || undefined,
|
|
|
date_from: (formData.get('date-from') as string) || undefined,
|
|
date_from: (formData.get('date-from') as string) || undefined,
|
|
|
date_to: (formData.get('date-to') as string) || undefined,
|
|
date_to: (formData.get('date-to') as string) || undefined,
|
|
@@ -245,6 +380,8 @@ export class ContentPage {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // Reset to first page when filters change
|
|
|
|
|
+ this.currentPage = 1;
|
|
|
this.loadContent();
|
|
this.loadContent();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -262,8 +399,14 @@ export class ContentPage {
|
|
|
</div>
|
|
</div>
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
|
|
|
+ // Add pagination offset to filters
|
|
|
|
|
+ const paginatedFilters = {
|
|
|
|
|
+ ...this.filters,
|
|
|
|
|
+ offset: (this.currentPage - 1) * (this.filters.limit || 20)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- const response = await this.apiService.getShopResults(this.selectedShopId, this.filters);
|
|
|
|
|
|
|
+ const response = await this.apiService.getShopResults(this.selectedShopId, paginatedFilters);
|
|
|
|
|
|
|
|
if (response.success && response.data) {
|
|
if (response.success && response.data) {
|
|
|
this.contentData = response.data;
|
|
this.contentData = response.data;
|
|
@@ -290,30 +433,42 @@ export class ContentPage {
|
|
|
|
|
|
|
|
const container = this.element?.querySelector('#content-results');
|
|
const container = this.element?.querySelector('#content-results');
|
|
|
const summary = this.element?.querySelector('#results-summary');
|
|
const summary = this.element?.querySelector('#results-summary');
|
|
|
|
|
+ const paginationContainer = this.element?.querySelector('#pagination-container');
|
|
|
|
|
|
|
|
if (!container || !summary) return;
|
|
if (!container || !summary) return;
|
|
|
|
|
|
|
|
- const { results, filters } = this.contentData;
|
|
|
|
|
|
|
+ const { results, total_count = 0 } = this.contentData;
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate pagination
|
|
|
|
|
+ this.totalItems = total_count;
|
|
|
|
|
+ this.totalPages = Math.ceil(this.totalItems / (this.filters.limit || 20));
|
|
|
|
|
|
|
|
- // Count total items
|
|
|
|
|
- const totalItems = Object.values(results).reduce((sum, items) => sum + items.length, 0);
|
|
|
|
|
|
|
+ // Count current page items
|
|
|
|
|
+ const currentPageItems = Object.values(results).reduce((sum, items) => sum + items.length, 0);
|
|
|
|
|
|
|
|
// Update summary
|
|
// Update summary
|
|
|
const selectedShop = this.shops.find(s => s.id === this.selectedShopId);
|
|
const selectedShop = this.shops.find(s => s.id === this.selectedShopId);
|
|
|
const shopName = selectedShop ? getDomainFromUrl(selectedShop.url) : 'Unknown Shop';
|
|
const shopName = selectedShop ? getDomainFromUrl(selectedShop.url) : 'Unknown Shop';
|
|
|
|
|
|
|
|
- summary.textContent = `Showing ${totalItems} content items from ${shopName}`;
|
|
|
|
|
|
|
+ const startItem = (this.currentPage - 1) * (this.filters.limit || 20) + 1;
|
|
|
|
|
+ const endItem = Math.min(startItem + currentPageItems - 1, this.totalItems);
|
|
|
|
|
|
|
|
- if (totalItems === 0) {
|
|
|
|
|
|
|
+ summary.textContent = `Showing ${startItem}-${endItem} of ${this.totalItems} content items from ${shopName}`;
|
|
|
|
|
+
|
|
|
|
|
+ // Update pagination
|
|
|
|
|
+ this.updatePagination();
|
|
|
|
|
+
|
|
|
|
|
+ if (currentPageItems === 0) {
|
|
|
container.innerHTML = `
|
|
container.innerHTML = `
|
|
|
<div class="text-center py-12">
|
|
<div class="text-center py-12">
|
|
|
<div class="w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-lg flex items-center justify-center mx-auto mb-4">
|
|
<div class="w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-lg flex items-center justify-center mx-auto mb-4">
|
|
|
<div class="w-8 h-8 text-gray-400">${getIcon('search')}</div>
|
|
<div class="w-8 h-8 text-gray-400">${getIcon('search')}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">No content found</h3>
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">No content found</h3>
|
|
|
- <p class="text-gray-500 dark:text-gray-400">Try adjusting your filters</p>
|
|
|
|
|
|
|
+ <p class="text-gray-500 dark:text-gray-400">Try adjusting your filters or go to previous page</p>
|
|
|
</div>
|
|
</div>
|
|
|
`;
|
|
`;
|
|
|
|
|
+ if (paginationContainer) (paginationContainer as HTMLElement).style.display = 'none';
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -339,12 +494,51 @@ export class ContentPage {
|
|
|
}).filter(Boolean).join('')}
|
|
}).filter(Boolean).join('')}
|
|
|
</div>
|
|
</div>
|
|
|
`;
|
|
`;
|
|
|
|
|
+
|
|
|
|
|
+ // Show pagination if we have multiple pages
|
|
|
|
|
+ if (paginationContainer) {
|
|
|
|
|
+ (paginationContainer as HTMLElement).style.display = this.totalPages > 1 ? 'block' : 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Bind view full content events
|
|
|
|
|
+ this.bindContentEvents();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private updatePagination(): void {
|
|
|
|
|
+ const paginationInfo = this.element?.querySelector('#pagination-info');
|
|
|
|
|
+ const pageNumbers = this.element?.querySelector('#page-numbers');
|
|
|
|
|
+ const prevBtn = this.element?.querySelector('#prev-page') as HTMLButtonElement;
|
|
|
|
|
+ const nextBtn = this.element?.querySelector('#next-page') as HTMLButtonElement;
|
|
|
|
|
+
|
|
|
|
|
+ if (paginationInfo && pageNumbers && prevBtn && nextBtn) {
|
|
|
|
|
+ const startItem = (this.currentPage - 1) * (this.filters.limit || 20) + 1;
|
|
|
|
|
+ const endItem = Math.min(this.currentPage * (this.filters.limit || 20), this.totalItems);
|
|
|
|
|
+
|
|
|
|
|
+ paginationInfo.textContent = `Showing ${startItem}-${endItem} of ${this.totalItems} items`;
|
|
|
|
|
+ pageNumbers.textContent = `Page ${this.currentPage} of ${this.totalPages}`;
|
|
|
|
|
+
|
|
|
|
|
+ prevBtn.disabled = this.currentPage <= 1;
|
|
|
|
|
+ nextBtn.disabled = this.currentPage >= this.totalPages;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private bindContentEvents(): void {
|
|
|
|
|
+ // Bind "View Full" button events
|
|
|
|
|
+ this.element?.querySelectorAll('.view-full-btn').forEach(btn => {
|
|
|
|
|
+ btn.addEventListener('click', (e) => {
|
|
|
|
|
+ const contentId = (e.target as HTMLElement).closest('.view-full-btn')?.getAttribute('data-content-id');
|
|
|
|
|
+ if (contentId) {
|
|
|
|
|
+ this.showFullContent(contentId);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private renderContentItem(item: ShopContent): string {
|
|
private renderContentItem(item: ShopContent): string {
|
|
|
const domain = getDomainFromUrl(item.url);
|
|
const domain = getDomainFromUrl(item.url);
|
|
|
const hasChanged = item.changed;
|
|
const hasChanged = item.changed;
|
|
|
- const preview = item.content.substring(0, 200) + (item.content.length > 200 ? '...' : '');
|
|
|
|
|
|
|
+ const preview = item.content.substring(0, 300) + (item.content.length > 300 ? '...' : '');
|
|
|
|
|
+ const isLongContent = item.content.length > 300;
|
|
|
|
|
|
|
|
return `
|
|
return `
|
|
|
<div class="p-4 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
|
<div class="p-4 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
|
@@ -369,17 +563,28 @@ export class ContentPage {
|
|
|
<a href="${item.url}" target="_blank" class="text-sm text-primary-600 dark:text-primary-400 hover:underline mb-2 block">
|
|
<a href="${item.url}" target="_blank" class="text-sm text-primary-600 dark:text-primary-400 hover:underline mb-2 block">
|
|
|
${item.url}
|
|
${item.url}
|
|
|
</a>
|
|
</a>
|
|
|
- <p class="text-sm text-gray-500 dark:text-gray-400 mb-3">
|
|
|
|
|
- ${preview.replace(/[#*]/g, '')}
|
|
|
|
|
- </p>
|
|
|
|
|
|
|
+ <div class="text-sm text-gray-500 dark:text-gray-400 mb-3">
|
|
|
|
|
+ <div class="whitespace-pre-wrap">${preview.replace(/[#*]/g, '')}</div>
|
|
|
|
|
+ ${isLongContent ? `
|
|
|
|
|
+ <button class="view-full-btn text-primary-600 dark:text-primary-400 hover:underline text-xs mt-1" data-content-id="${item.id}">
|
|
|
|
|
+ View full content
|
|
|
|
|
+ </button>
|
|
|
|
|
+ ` : ''}
|
|
|
|
|
+ </div>
|
|
|
<div class="text-xs text-gray-400 dark:text-gray-500">
|
|
<div class="text-xs text-gray-400 dark:text-gray-500">
|
|
|
- Last updated: ${formatRelativeTime(item.updated_at)}
|
|
|
|
|
|
|
+ Last updated: ${formatRelativeTime(item.updated_at)} • ${item.content.length} characters
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="ml-4 flex-shrink-0">
|
|
|
|
|
|
|
+ <div class="ml-4 flex-shrink-0 space-y-2">
|
|
|
|
|
+ ${isLongContent ? `
|
|
|
|
|
+ <button class="view-full-btn btn btn-secondary btn-sm w-full" data-content-id="${item.id}">
|
|
|
|
|
+ <div class="w-4 h-4 mr-1">${getIcon('eye')}</div>
|
|
|
|
|
+ View Full
|
|
|
|
|
+ </button>
|
|
|
|
|
+ ` : ''}
|
|
|
<button
|
|
<button
|
|
|
- class="btn btn-secondary btn-sm"
|
|
|
|
|
- onclick="navigator.clipboard.writeText(\`${item.content.replace(/`/g, '\\`')}\`).then(() => {
|
|
|
|
|
|
|
+ class="btn btn-secondary btn-sm w-full"
|
|
|
|
|
+ onclick="navigator.clipboard.writeText(\`${item.content.replace(/`/g, '\\`').replace(/\$/g, '\\$')}\`).then(() => {
|
|
|
document.dispatchEvent(new CustomEvent('show-toast', {
|
|
document.dispatchEvent(new CustomEvent('show-toast', {
|
|
|
detail: { type: 'success', title: 'Copied!', message: 'Content copied to clipboard' }
|
|
detail: { type: 'success', title: 'Copied!', message: 'Content copied to clipboard' }
|
|
|
}));
|
|
}));
|
|
@@ -394,6 +599,59 @@ export class ContentPage {
|
|
|
`;
|
|
`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private showFullContent(contentId: string): void {
|
|
|
|
|
+ // Find the content item by ID
|
|
|
|
|
+ if (!this.contentData) return;
|
|
|
|
|
+
|
|
|
|
|
+ let foundItem: ShopContent | null = null;
|
|
|
|
|
+ for (const [_, items] of Object.entries(this.contentData.results)) {
|
|
|
|
|
+ foundItem = items.find(item => item.id === contentId) || null;
|
|
|
|
|
+ if (foundItem) break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!foundItem) return;
|
|
|
|
|
+
|
|
|
|
|
+ // Populate modal
|
|
|
|
|
+ const modal = this.element?.querySelector('#content-modal');
|
|
|
|
|
+ const modalTitle = this.element?.querySelector('#modal-title');
|
|
|
|
|
+ const modalUrl = this.element?.querySelector('#modal-url');
|
|
|
|
|
+ const modalContent = this.element?.querySelector('#modal-content');
|
|
|
|
|
+ const modalMetadata = this.element?.querySelector('#modal-metadata');
|
|
|
|
|
+ const modalTypeBadge = this.element?.querySelector('#modal-type-badge');
|
|
|
|
|
+ const modalChangeBadge = this.element?.querySelector('#modal-change-badge');
|
|
|
|
|
+
|
|
|
|
|
+ if (modalTitle) modalTitle.textContent = `${getContentTypeLabel(foundItem.content_type)} Content`;
|
|
|
|
|
+ if (modalUrl) modalUrl.textContent = foundItem.url;
|
|
|
|
|
+ if (modalContent) modalContent.textContent = foundItem.content;
|
|
|
|
|
+ if (modalMetadata) {
|
|
|
|
|
+ modalMetadata.textContent = `Last updated: ${formatRelativeTime(foundItem.updated_at)} • ${foundItem.content.length} characters • Hash: ${foundItem.content_hash.substring(0, 8)}`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Update badges
|
|
|
|
|
+ if (modalTypeBadge) {
|
|
|
|
|
+ modalTypeBadge.textContent = getContentTypeLabel(foundItem.content_type);
|
|
|
|
|
+ modalTypeBadge.className = 'badge badge-info';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (modalChangeBadge) {
|
|
|
|
|
+ if (foundItem.changed) {
|
|
|
|
|
+ modalChangeBadge.textContent = 'Changed';
|
|
|
|
|
+ modalChangeBadge.className = 'badge badge-warning';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ modalChangeBadge.textContent = 'Unchanged';
|
|
|
|
|
+ modalChangeBadge.className = 'badge badge-success';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Store current item for modal actions
|
|
|
|
|
+ this.currentModalItem = foundItem;
|
|
|
|
|
+
|
|
|
|
|
+ // Show modal
|
|
|
|
|
+ modal?.classList.remove('hidden');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private currentModalItem: ShopContent | null = null;
|
|
|
|
|
+
|
|
|
destroy(): void {
|
|
destroy(): void {
|
|
|
this.eventsbound = false;
|
|
this.eventsbound = false;
|
|
|
}
|
|
}
|