Files
PromptBase/app/public/scripts/fetch-prompts.js
2025-07-29 10:02:24 -05:00

138 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm';
const env = document.getElementById('supabase-env');
const supabaseUrl = env.dataset.url;
const supabaseKey = env.dataset.key;
const supabase = createClient(supabaseUrl, supabaseKey);
const promptContainer = document.getElementById('prompt-container');
const errorBox = document.getElementById('prompt-error');
const formatDate = (dateStr) => {
if (!dateStr) return "";
const date = new Date(dateStr);
return isNaN(date.getTime())
? "Invalid date"
: date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
};
function renderPromptCard(prompt) {
const tagSpans = (prompt.tags ?? []).map(tag =>
`<span class="text-sm bg-gray-200 text-gray-800 px-2 py-1 pt-0 rounded">${tag}</span>`
).join('');
const escapedDescription = (prompt.description ?? '').replace(/\n/g, '<br />');
const notes = prompt.notes ?? '';
return `
<div class="prompt-card border border-gray-400 rounded p-4 bg-gray-700 text-gray-200 shadow-sm flex flex-col gap-2 min-h-[12rem]"
data-type="${prompt.type}" data-tags="${(prompt.tags ?? []).join(',')}">
<div class="flex items-center justify-between">
<h3 class="text-xl font-semibold">${prompt.title}</h3>
<span class="text-sm font-medium px-2 py-1 rounded ${
prompt.type === 'System'
? 'bg-blue-100 text-blue-700'
: 'bg-green-100 text-green-700'
}">${prompt.type}</span>
</div>
<p class="text-md">${notes}</p>
<div class="flex flex-wrap gap-2 mt-2">${tagSpans}</div>
<details name="prompt-details">
<summary class="cursor-pointer font-semibold mt-2 text-lg">View Details</summary>
<div class="text-md border-t mt-2 pt-2">
<div class="flex justify-between mx-2 items-center border-b border-b-gray-400 pb-2 mb-2">
<h3 class="text-xl font-semibold">Prompt</h3>
<a class="bg-green-600 text-white px-2 py-0 rounded text-sm hover:bg-green-700 transition-colors duration-300" href="/edit?slug=${prompt.slug}">Edit</a>
</div>
<p class="my-2 px-2 text-balance">${escapedDescription}</p>
<hr class="my-2" />
<p class="text-sm"><strong>Created:</strong> ${formatDate(prompt.created_at)} &bull; <strong>Updated:</strong> ${formatDate(prompt.updated_at)}</p>
</div>
</details>
</div>
`;
}
function updateFiltersFromTags(prompts) {
const tagSet = new Set(prompts.flatMap(p => p.tags ?? []));
const tagContainer = document.querySelector('#filters fieldset > div');
if (!tagContainer) return;
tagContainer.innerHTML = '';
tagSet.forEach(tag => {
const label = document.createElement('label');
label.className = "relative inline-flex items-center cursor-pointer";
label.innerHTML = `
<input type="checkbox" name="tag" value="${tag}" class="sr-only peer">
<span class="px-3 py-1 pt-0 rounded-full border border-gray-300 peer-checked:bg-blue-600 peer-checked:text-white hover:bg-blue-600 hover:text-white transition-colors duration-300 text-sm">${tag}</span>
`;
tagContainer.appendChild(label);
});
}
function filterCards() {
const form = document.getElementById('filter-form');
const cards = document.querySelectorAll('.prompt-card');
const params = new URLSearchParams(new FormData(form));
const type = params.get('type');
const query = params.get('q')?.toLowerCase() || '';
const tagParams = params.getAll('tag');
let visibleCount = 0;
cards.forEach(card => {
const cardType = card.dataset.type;
const cardTags = card.dataset.tags.split(',');
const cardText = card.innerText.toLowerCase();
const matchesType = !type || type === cardType;
const matchesTags = tagParams.length === 0 || tagParams.some(t => cardTags.includes(t));
const matchesSearch = !query || cardText.includes(query);
const show = matchesType && matchesTags && matchesSearch;
card.style.display = show ? 'block' : 'none';
if (show) visibleCount++;
});
const countLabel = document.getElementById('prompt-count');
if (countLabel) {
countLabel.textContent = visibleCount === 1
? '1 prompt shown'
: `${visibleCount} prompts shown`;
}
}
async function loadPrompts() {
const { data: prompts, error } = await supabase.from('prompts').select('*').order('title');
if (error) {
promptContainer.innerHTML = '';
errorBox.textContent = error.message;
errorBox.classList.remove('hidden');
return;
}
promptContainer.innerHTML = prompts.map(renderPromptCard).join('');
updateFiltersFromTags(prompts);
filterCards(); // initial run
const form = document.getElementById('filter-form');
form?.addEventListener('input', filterCards);
const clearBtn = document.getElementById('clear-filters');
clearBtn?.addEventListener('click', () => {
form.reset();
filterCards();
});
}
document.addEventListener('DOMContentLoaded', loadPrompts);