|
<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: linear-gradient(45deg, #f0f0f0 25%, transparent 25%, transparent 75%, #f0f0f0 75%, #f0f0f0), |
|
linear-gradient(45deg, #f0f0f0 25%, transparent 25%, transparent 75%, #f0f0f0 75%, #f0f0f0); |
|
background-size: 20px 20px; |
|
background-position: 0 0, 10px 10px; |
|
} |
|
|
|
.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> |