From ba4a7638c09ae7c2efb626da45bc881139bc3487 Mon Sep 17 00:00:00 2001 From: Caleb Fultz Date: Tue, 20 Aug 2024 21:23:06 -0400 Subject: [PATCH] Initial --- app.js | 149 ++++++++++++++++++++++++++++++++++++++++++++++ index.html | 47 +++++++++++++++ manifest.json | 22 +++++++ service-worker.js | 22 +++++++ styles.css | 115 +++++++++++++++++++++++++++++++++++ 5 files changed, 355 insertions(+) create mode 100644 app.js create mode 100644 index.html create mode 100644 manifest.json create mode 100644 service-worker.js create mode 100644 styles.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..6a5e819 --- /dev/null +++ b/app.js @@ -0,0 +1,149 @@ +let cardList = []; + +document.addEventListener('DOMContentLoaded', () => { + const toggle = document.getElementById('dark-mode-toggle'); + + toggle.addEventListener('change', () => { + if (toggle.checked) { + document.documentElement.classList.add('dark-mode'); + localStorage.setItem('theme', 'dark'); + } else { + document.documentElement.classList.remove('dark-mode'); + localStorage.setItem('theme', 'light'); + } + }); + + // Load the saved theme preference + const savedTheme = localStorage.getItem('theme'); + if (savedTheme === 'dark') { + document.documentElement.classList.add('dark-mode'); + toggle.checked = true; + } +}); + +async function fetchCardSuggestions(query) { + const response = await fetch(`https://api.scryfall.com/cards/autocomplete?q=${encodeURIComponent(query)}`); + const data = await response.json(); + return data.data; +} + +document.getElementById('card-name').addEventListener('input', async function() { + const query = this.value; + const suggestions = await fetchCardSuggestions(query); + const suggestionsDiv = document.getElementById('suggestions'); + suggestionsDiv.innerHTML = ''; + suggestions.forEach(suggestion => { + const div = document.createElement('div'); + div.textContent = suggestion; + div.onclick = () => { + document.getElementById('card-name').value = suggestion; + suggestionsDiv.innerHTML = ''; + }; + suggestionsDiv.appendChild(div); + }); +}); + +async function fetchCardDetails(cardName, setCode) { + const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`); + const data = await response.json(); + return { + mana_cost: data.mana_cost || 'N/A', + power: data.power || 'N/A', + toughness: data.toughness || 'N/A', + type: data.type_line.split('—')[0].trim(), // Remove anything after em dash + rarity: data.rarity || 'N/A' + }; +} + +async function addCard() { + const cardName = document.getElementById('card-name').value; + const setCode = document.getElementById('set-code').value; + const cardId = document.getElementById('card-id').value; + const quantity = document.getElementById('quantity').value; + const foil = document.getElementById('foil').value; + + const cardDetails = await fetchCardDetails(cardName, setCode); + + const cardData = { + name: cardName, + set: setCode, + card_id: cardId, + quantity: quantity, + foil: foil, + mana_type: cardDetails.mana_cost, + power: cardDetails.power, + toughness: cardDetails.toughness, + type: cardDetails.type, + rarity: cardDetails.rarity + }; + + cardList.push(cardData); + + // Update the textarea with the new card information + let csvContent = generateCSVContent(); + document.getElementById('csv-output').value = csvContent; + + // Show the card image in the dissolving popup + showCardPopup(cardName, setCode); +} + +function generateCSVContent() { + let csvContent = "Card Name,Set,Card ID,Quantity,Foil,Mana Type,Power,Toughness,Type,Rarity\n"; + cardList.forEach(card => { + const escapedValues = [ + card.name, + card.set, + card.card_id, + card.quantity, + card.foil, + card.mana_type, + card.power, + card.toughness, + card.type, + card.rarity + ].map(value => `"${value.replace(/"/g, '""')}"`); // Escape any existing quotes by doubling them + csvContent += escapedValues.join(",") + "\n"; + }); + return csvContent; +} + +async function showCardPopup(cardName, setCode) { + const cardPopup = document.getElementById('card-popup'); + const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`); + const data = await response.json(); + const imageUrl = data.image_uris ? data.image_uris.small : null; + + if (imageUrl) { + cardPopup.innerHTML = `${cardName}`; + cardPopup.style.display = 'block'; + cardPopup.style.opacity = 1; + + // Dissolve the popup after a few seconds + setTimeout(() => { + cardPopup.style.opacity = 0; + setTimeout(() => { + cardPopup.style.display = 'none'; + }, 500); + }, 3000); + } +} + +function downloadCSV() { + const csvContent = generateCSVContent(); + + // Create a blob with the CSV content + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + + // Create a temporary link element + const link = document.createElement("a"); + link.setAttribute("href", url); + link.setAttribute("download", "mtg_cards.csv"); + document.body.appendChild(link); + + // Trigger the download + link.click(); + + // Clean up and remove the link + document.body.removeChild(link); +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..1342cee --- /dev/null +++ b/index.html @@ -0,0 +1,47 @@ + + + + + + MTG Card CSV Generator + + + + + +
+

MTG Card CSV Generator

+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..fcb32c1 --- /dev/null +++ b/manifest.json @@ -0,0 +1,22 @@ +{ + "name": "MTG Card CSV Generator", + "short_name": "MTG CSV", + "start_url": ".", + "display": "standalone", + "background_color": "#cc5200", + "theme_color": "#007acc", + "description": "A PWA for generating CSV files with Magic: The Gathering card details", + "icons": [ + { + "src": "icon.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icon.png", + "sizes": "512x512", + "type": "image/png" + } + ] + } + \ No newline at end of file diff --git a/service-worker.js b/service-worker.js new file mode 100644 index 0000000..13d9075 --- /dev/null +++ b/service-worker.js @@ -0,0 +1,22 @@ +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open('mtg-csv-generator-v1').then((cache) => { + return cache.addAll([ + '/', + '/index.html', + '/styles.css', + '/app.js', + '/manifest.json', + '/icon.png' + ]); + }) + ); +}); + +self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((response) => { + return response || fetch(event.request); + }) + ); +}); diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..513c32b --- /dev/null +++ b/styles.css @@ -0,0 +1,115 @@ +:root { + --background-color: #f0f0f0; + --text-color: #333; + --card-background: #ffffff; + --button-background: #007acc; + --button-hover: #005fa3; +} + +@media (prefers-color-scheme: dark) { + :root { + --background-color: #1e1e1e; + --text-color: #ffffff; + --card-background: #2e2e2e; + --button-background: #1f6fb2; + --button-hover: #145b8c; + } +} + +body { + font-family: Arial, sans-serif; + background-color: var(--background-color); + color: var(--text-color); + margin: 0; + padding: 20px; +} + +.container { + max-width: 600px; + margin: 0 auto; + background-color: var(--card-background); + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; +} + +h1 { + text-align: center; + margin-bottom: 20px; + font-family: 'Sorts Mill Goudy', serif; +} + +form div { + margin-bottom: 15px; +} + +label { + display: block; + margin-bottom: 5px; +} + +input, select, textarea { + width: 100%; + padding: 8px; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 4px; + background-color: var(--card-background); + color: var(--text-color); +} + +button { + display: block; + width: 100%; + padding: 10px; + background-color: var(--button-background); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + margin-top: 10px; +} + +button:hover { + background-color: var(--button-hover); +} + +#csv-output { + margin-top: 20px; + height: 200px; + background-color: var(--card-background); + color: var(--text-color); +} + +.card-popup { + position: fixed; + bottom: 20px; + right: 20px; + max-width: 20%; + z-index: 1000; + background-color: rgba(0, 0, 0, 0.7); + padding: 10px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + opacity: 0; + transition: opacity 0.5s ease-in-out; + display: none; +} + +.card-popup img { + width: 100%; + height: auto; + border-radius: 4px; +} + +@media (max-width: 600px) { + .card-popup { + max-width: 40%; + } +} + +@media (max-width: 400px) { + .card-popup { + max-width: 60%; + } +}