| <script lang="ts"> | |
| import { onMount } from 'svelte'; | |
| import { getAllMonsters } from '$lib/db/monsters'; | |
| import type { Monster } from '$lib/db/schema'; | |
| let monsters: Monster[] = $state([]); | |
| let isLoading = $state(true); | |
| onMount(async () => { | |
| try { | |
| monsters = await getAllMonsters(); | |
| } catch (err) { | |
| console.error('Failed to load monsters:', err); | |
| } finally { | |
| isLoading = false; | |
| } | |
| }); | |
| </script> | |
| <div class="pictuary-page"> | |
| <header class="page-header"> | |
| <h2>Your Pictuary</h2> | |
| <p class="monster-count">{monsters.length} Piclets collected</p> | |
| </header> | |
| {#if isLoading} | |
| <div class="loading-state"> | |
| <div class="spinner"></div> | |
| <p>Loading collection...</p> | |
| </div> | |
| {:else if monsters.length === 0} | |
| <div class="empty-state"> | |
| <img | |
| src="https://huggingface.co/spaces/Fraser/piclets/resolve/main/assets/pictuary_logo.png" | |
| alt="Pictuary" | |
| class="empty-icon" | |
| /> | |
| <h3>No Piclets Yet</h3> | |
| <p>Start scanning photos to build your collection!</p> | |
| </div> | |
| {:else} | |
| <div class="monster-grid"> | |
| {#each monsters as monster} | |
| <div class="monster-card"> | |
| <img | |
| src={monster.imageData || monster.imageUrl} | |
| alt={monster.name} | |
| class="monster-image" | |
| /> | |
| <h4 class="monster-name">{monster.name}</h4> | |
| <p class="monster-date"> | |
| {new Date(monster.createdAt).toLocaleDateString()} | |
| </p> | |
| </div> | |
| {/each} | |
| </div> | |
| {/if} | |
| </div> | |
| <style> | |
| .pictuary-page { | |
| height: 100%; | |
| overflow-y: auto; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| .page-header { | |
| padding: 1.5rem 1rem; | |
| background: white; | |
| border-bottom: 1px solid #eee; | |
| position: sticky; | |
| top: 0; | |
| z-index: 10; | |
| } | |
| .page-header h2 { | |
| margin: 0; | |
| font-size: 1.5rem; | |
| color: #333; | |
| } | |
| .monster-count { | |
| margin: 0.25rem 0 0; | |
| color: #666; | |
| font-size: 0.9rem; | |
| } | |
| .loading-state, | |
| .empty-state { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: calc(100% - 100px); | |
| padding: 2rem; | |
| text-align: center; | |
| } | |
| .spinner { | |
| width: 40px; | |
| height: 40px; | |
| border: 3px solid #f3f3f3; | |
| border-top: 3px solid #007bff; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-bottom: 1rem; | |
| } | |
| .empty-icon { | |
| width: 80px; | |
| opacity: 0.5; | |
| margin-bottom: 1rem; | |
| } | |
| .empty-state h3 { | |
| margin: 0 0 0.5rem; | |
| color: #333; | |
| } | |
| .empty-state p { | |
| margin: 0; | |
| color: #666; | |
| } | |
| .monster-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); | |
| gap: 1rem; | |
| padding: 1rem; | |
| } | |
| .monster-card { | |
| background: white; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| transition: transform 0.2s; | |
| } | |
| .monster-card:active { | |
| transform: scale(0.95); | |
| } | |
| .monster-image { | |
| width: 100%; | |
| aspect-ratio: 1; | |
| object-fit: contain; | |
| background: #f0f0f0; | |
| } | |
| .monster-name { | |
| margin: 0; | |
| padding: 0.75rem; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| color: #333; | |
| } | |
| .monster-date { | |
| margin: 0; | |
| padding: 0 0.75rem 0.75rem; | |
| font-size: 0.75rem; | |
| color: #999; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| </style> |