mirror of
https://github.com/System-End/stickers.git
synced 2026-04-19 16:28:17 +00:00
no comment
This commit is contained in:
parent
2c63a8c505
commit
a56ee61017
16 changed files with 1354 additions and 400 deletions
BIN
src/lib/assets/images/resize.jpg
Normal file
BIN
src/lib/assets/images/resize.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
|
|
@ -1,34 +1,50 @@
|
|||
<script>
|
||||
import hackClubLogo from '$lib/assets/images/hackClubLogo.svg';
|
||||
|
||||
export let active = '';
|
||||
let { active = '' } = $props();
|
||||
let menuOpen = $state(false);
|
||||
|
||||
function toggleMenu() {
|
||||
menuOpen = !menuOpen;
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<nav class="navbar">
|
||||
<a class="navbar-brand" href="/dash">
|
||||
<img src={hackClubLogo} alt="Hack Club Logo" style="height: 40px;">
|
||||
<img src={hackClubLogo} alt="Hack Club Logo" />
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
||||
<button class="navbar-toggler" onclick={toggleMenu} aria-label="Toggle navigation">
|
||||
<span class="toggler-icon"></span>
|
||||
<span class="toggler-icon"></span>
|
||||
<span class="toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
|
||||
<div class="navbar-collapse" class:open={menuOpen}>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item" class:active={active === 'dash'}>
|
||||
<a class="nav-link" href="/dash">| Home |</a>
|
||||
<a class="nav-link" href="/dash">|Home ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'trade'}>
|
||||
<a class="nav-link" href="/trade">Trade |</a>
|
||||
<a class="nav-link" href="/trade">|Trade ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'earn'}>
|
||||
<a class="nav-link" href="/earn">Earn |</a>
|
||||
<a class="nav-link" href="/earn">|Get ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'leaderboard'}>
|
||||
<a class="nav-link" href="/leaderboard">Leaderboard |</a>
|
||||
<a class="nav-link" href="/leaderboard">|Leaderboard ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'designs'}>
|
||||
<a class="nav-link" href="/designs">Designs |</a>
|
||||
<li class="nav-item" class:active={active === 'stickers'}>
|
||||
<a class="nav-link" href="/stickers">|Stickers ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'my-designs'}>
|
||||
<a class="nav-link" href="/my-designs">|My Designs ⠀</a>
|
||||
</li>
|
||||
<li class="nav-item" class:active={active === 'vote'}>
|
||||
<a class="nav-link" href="/vote">|Vote ⠀</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="help-text">help us archive stickers! DM @EuanRipper or @Nora on slack</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
|
@ -36,14 +52,120 @@
|
|||
.navbar {
|
||||
font-family: 'Departure Mono', monospace;
|
||||
padding: 1rem 2rem;
|
||||
background: #f8f9fa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.navbar :global(.nav-link) {
|
||||
font-size: 1.25rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
}
|
||||
|
||||
.navbar :global(.navbar-brand img) {
|
||||
.navbar-brand img {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
background: none;
|
||||
border: 2px solid #333;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.toggler-icon {
|
||||
width: 24px;
|
||||
height: 3px;
|
||||
background: #333;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: block;
|
||||
font-size: 1.25rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.nav-item.active .nav-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin-left: auto;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.navbar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.toggler-icon {
|
||||
width: 28px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
display: none;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
.navbar-collapse.open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding: 1rem 1rem;
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin-left: 0;
|
||||
margin-top: 1rem;
|
||||
white-space: normal;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@ body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, p, a, li, span, button, input, label {
|
||||
h1, h2, h3, h4, h5, h6, p, a, li, span, button, input, label, mark, th, td, select, option {
|
||||
font-family: 'Departure Mono', monospace;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@
|
|||
}
|
||||
|
||||
function handleLogin() {
|
||||
|
||||
window.location.href = '/dash';
|
||||
window.location.href = 'http://localhost:9292/auth/login';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,6 @@
|
|||
import Peelable from '$lib/components/Peelable.svelte';
|
||||
import stickersBg from '$lib/assets/images/stickers.jpg';
|
||||
|
||||
let showClaimModal = $state(false);
|
||||
let requiredFingers = $state(Math.floor(Math.random() * 5) + 1);
|
||||
let uploadedFile = $state(null);
|
||||
let previewUrl = $state(null);
|
||||
let isDragging = $state(false);
|
||||
|
||||
function goToTrade() {
|
||||
window.location.href = '/trade';
|
||||
}
|
||||
|
|
@ -19,125 +13,8 @@
|
|||
function goToVote() {
|
||||
window.location.href = '/vote';
|
||||
}
|
||||
|
||||
function openClaimModal() {
|
||||
requiredFingers = Math.floor(Math.random() * 5) + 1;
|
||||
showClaimModal = true;
|
||||
}
|
||||
|
||||
function closeClaimModal() {
|
||||
showClaimModal = false;
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function handleFileSelect(event) {
|
||||
const input = event.target;
|
||||
if (input.files && input.files[0]) {
|
||||
setFile(input.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop(event) {
|
||||
event.preventDefault();
|
||||
isDragging = false;
|
||||
if (event.dataTransfer?.files && event.dataTransfer.files[0]) {
|
||||
setFile(event.dataTransfer.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragOver(event) {
|
||||
event.preventDefault();
|
||||
isDragging = true;
|
||||
}
|
||||
|
||||
function handleDragLeave() {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
function setFile(file) {
|
||||
if (file.type.startsWith('image/')) {
|
||||
uploadedFile = file;
|
||||
previewUrl = URL.createObjectURL(file);
|
||||
}
|
||||
}
|
||||
|
||||
function clearFile() {
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
uploadedFile = null;
|
||||
previewUrl = null;
|
||||
}
|
||||
|
||||
function submitClaim() {
|
||||
if (!uploadedFile) return;
|
||||
alert('Claim submitted for verification!');
|
||||
closeClaimModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showClaimModal}
|
||||
<div class="modal-overlay">
|
||||
<div class="modal-content">
|
||||
<button class="close-btn" onclick={closeClaimModal}>✕</button>
|
||||
|
||||
<h1><mark>Claim Your Stickers</mark></h1>
|
||||
|
||||
<div class="instructions">
|
||||
<h2>Verification Required</h2>
|
||||
<p>To verify you have the stickers in your possession, please upload a photo showing:</p>
|
||||
<ul>
|
||||
<li>Your sticker(s) clearly visible</li>
|
||||
<li>Your hand holding up <strong>{requiredFingers}</strong> finger{requiredFingers > 1 ? 's' : ''}</li>
|
||||
</ul>
|
||||
<div class="finger-display">
|
||||
<span class="finger-count">{requiredFingers}</span>
|
||||
<span class="finger-label">finger{requiredFingers > 1 ? 's' : ''} required</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="upload-area"
|
||||
class:dragging={isDragging}
|
||||
class:has-preview={previewUrl}
|
||||
ondrop={handleDrop}
|
||||
ondragover={handleDragOver}
|
||||
ondragleave={handleDragLeave}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
{#if previewUrl}
|
||||
<div class="preview-container">
|
||||
<img src={previewUrl} alt="Upload preview" class="preview-image" />
|
||||
<button class="clear-file-btn" onclick={clearFile}>✕</button>
|
||||
</div>
|
||||
{:else}
|
||||
<label class="upload-label">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onchange={handleFileSelect}
|
||||
class="file-input"
|
||||
/>
|
||||
<div class="upload-content">
|
||||
|
||||
<span class="upload-text">Drop your photo here or click to upload</span>
|
||||
</div>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="submit-btn"
|
||||
onclick={submitClaim}
|
||||
disabled={!uploadedFile}
|
||||
>
|
||||
Submit for Verification
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="layout">
|
||||
<div class="left-column">
|
||||
<h1><mark>What's new</mark></h1>
|
||||
|
|
@ -150,10 +27,12 @@
|
|||
corner="bottom-right"
|
||||
peelOnHover={true}
|
||||
hoverPeelAmount={0.3}
|
||||
peelOnClick={true}
|
||||
onPeelComplete={goToTrade}
|
||||
borderRadius="0.5rem"
|
||||
>
|
||||
{#snippet topContent()}
|
||||
<div class="sticker-btn">Open</div>
|
||||
<div class="sticker-btn">Trade</div>
|
||||
{/snippet}
|
||||
{#snippet backContent()}
|
||||
<div class="sticker-back"></div>
|
||||
|
|
@ -171,6 +50,8 @@
|
|||
corner="bottom-right"
|
||||
peelOnHover={true}
|
||||
hoverPeelAmount={0.3}
|
||||
peelOnClick={true}
|
||||
onPeelComplete={goToEarn}
|
||||
borderRadius="0.5rem"
|
||||
>
|
||||
{#snippet topContent()}
|
||||
|
|
@ -192,6 +73,8 @@
|
|||
corner="bottom-right"
|
||||
peelOnHover={true}
|
||||
hoverPeelAmount={0.3}
|
||||
peelOnClick={true}
|
||||
onPeelComplete={goToVote}
|
||||
borderRadius="0.5rem"
|
||||
>
|
||||
{#snippet topContent()}
|
||||
|
|
@ -214,221 +97,19 @@
|
|||
<div class="right-header">
|
||||
<h1><mark>Your collection</mark></h1>
|
||||
<div class="button-group">
|
||||
<button class="btn-left" onclick={openClaimModal}>Claim stickers</button>
|
||||
<button class="btn-left">Claim stickers</button>
|
||||
<button class="btn-right">Place stickers</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="canvas" style="background-image: url({stickersBg});">
|
||||
<div class="empty-overlay">
|
||||
<b><u>No stickers placed!</u></b>
|
||||
<b><u>No stickers placed!</u></b><br>X stickers owned.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: rgba(250, 248, 245, 0.98);
|
||||
padding: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
max-width: 550px;
|
||||
width: 90%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.modal-content h1 {
|
||||
font-size: 2rem;
|
||||
margin: 0 0 1.5rem 0;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.instructions h2 {
|
||||
font-size: 1.25rem;
|
||||
margin: 0 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.instructions p {
|
||||
font-size: 1rem;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.instructions ul {
|
||||
font-size: 1rem;
|
||||
margin: 0 0 1rem 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.instructions li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.finger-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
background: #d9c9b6;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.finger-count {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.finger-label {
|
||||
font-size: 1rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 3px dashed #999;
|
||||
border-radius: 0.5rem;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
margin-bottom: 1rem;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.upload-area.dragging {
|
||||
border-color: #333;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.upload-area.has-preview {
|
||||
border-style: solid;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.upload-label {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 250px;
|
||||
object-fit: contain;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.clear-file-btn {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #333;
|
||||
background: #fff;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.clear-file-btn:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 0.875rem;
|
||||
background: #444;
|
||||
color: white;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.submit-btn:hover:not(:disabled) {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
background: #999;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
<script>
|
||||
let selectedRarity = $state('all');
|
||||
let searchQuery = $state('');
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<h1><mark>Designs</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
<div class="card info-card">
|
||||
<p>Browse and vote on sticker designs here.</p>
|
||||
<p>Check out all the Hack Club stickers, let us know if we missed any!</p>
|
||||
</div>
|
||||
|
||||
<div class="card filter-card">
|
||||
<label for="rarity">Filter by rarity:</label>
|
||||
<label for="rarity">Sort:</label>
|
||||
<select id="rarity" bind:value={selectedRarity}>
|
||||
<option value="all">All</option>
|
||||
<option value="current">Current</option>
|
||||
<option value="rarity">Rarity</option>
|
||||
<option value="current">Available</option>
|
||||
<option value="old">Old</option>
|
||||
<option value="special">Special</option>
|
||||
<option value="in-person">In-Person</option>
|
||||
|
|
@ -32,6 +35,35 @@
|
|||
<button class="card upload-btn">+ Upload a design</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Design</th>
|
||||
<th>Name</th>
|
||||
<th>In Stock</th>
|
||||
<th>Price</th>
|
||||
<th>Program</th>
|
||||
<th>Created</th>
|
||||
<th>Artist</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each mockDesigns as design}
|
||||
<tr>
|
||||
<td class="design-icon">{design.design}</td>
|
||||
<td>{design.name}</td>
|
||||
<td class:out-of-stock={design.inStock === 0}>{design.inStock}</td>
|
||||
<td>{design.price} hrs</td>
|
||||
<td>{design.program}</td>
|
||||
<td>{design.created}</td>
|
||||
<td>{design.artist}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
|
|
@ -41,7 +73,7 @@
|
|||
.content-row {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +92,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.filter-card label {
|
||||
|
|
@ -94,10 +127,11 @@
|
|||
}
|
||||
|
||||
.upload-btn {
|
||||
font-family: inherit;
|
||||
font-size: 1.25rem;
|
||||
font-family: 'Departure Mono', monospace;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.upload-btn:hover {
|
||||
|
|
@ -107,10 +141,52 @@
|
|||
p {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 2rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgba(217, 201, 182, 0.5);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(250, 248, 245, 0.5);
|
||||
}
|
||||
|
||||
.design-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.out-of-stock {
|
||||
color: #cc0000;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
</svelte:head>
|
||||
|
||||
<Background />
|
||||
<Navbar active="leaderboard" />
|
||||
<Navbar active="earn" />
|
||||
|
||||
<div class="page-content">
|
||||
{@render children()}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
<h1><mark>Hack For Stickers</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
<div class="card info-card">
|
||||
<p>You can uh get free stickers or ship 10 hours for a monthly crate of cool ones valid for 6 months! Link ur hackatime or smth...</p>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-card p {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
<script>
|
||||
let sortBy = $state('total');
|
||||
|
||||
const mockUsers = [
|
||||
{ rank: 1, username: 'MSW', totalStickers: 42, uniques: 33, rares: 4 },
|
||||
{ rank: 2, username: 'ZRL', totalStickers: 30, uniques: 20, rares: 4 },
|
||||
{ rank: 3, username: 'LFD', totalStickers: 28, uniques: 18, rares: 3 },
|
||||
{ rank: 4, username: 'Nora', totalStickers: 19, uniques: 15, rares: 3 },
|
||||
{ rank: 5, username: 'Euan', totalStickers: 17, uniques: 16, rares: 3 },
|
||||
{ rank: 6, username: 'KittyCat', totalStickers: 15, uniques: 10, rares: 3 },
|
||||
{ rank: 7, username: 'AVD', totalStickers: 14, uniques: 14, rares: 2 },
|
||||
{ rank: 8, username: 'Neon', totalStickers: 12, uniques: 6, rares: 0 },
|
||||
{ rank: 9, username: 'Kartikey', totalStickers: 11, uniques: 10, rares: 0 },
|
||||
{ rank: 10, username: 'Jmeow', totalStickers: 9, uniques: 9, rares: 1 },
|
||||
{ rank: 1192, username: 'You', totalStickers: 2, uniques: 2, rares: 0 }
|
||||
];
|
||||
|
||||
const youUser = mockUsers.find(u => u.username === 'You');
|
||||
const sortedUsers = $derived(
|
||||
[...mockUsers].filter(u => u.username !== 'You').sort((a, b) => {
|
||||
if (sortBy === 'total') return b.totalStickers - a.totalStickers;
|
||||
if (sortBy === 'uniques') return b.uniques - a.uniques;
|
||||
if (sortBy === 'rares') return b.rares - a.rares;
|
||||
return 0;
|
||||
}).map((user, i) => ({ ...user, rank: i + 1 }))
|
||||
);
|
||||
</script>
|
||||
|
||||
<h1><mark>Leaderboard</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
<div class="card info-card">
|
||||
<p>Top Sticker collectors! This really is a bragging right.</p>
|
||||
</div>
|
||||
|
||||
<div class="card filter-card">
|
||||
<label for="sort">Sort by:</label>
|
||||
<select id="sort" bind:value={sortBy}>
|
||||
<option value="total">Total Stickers</option>
|
||||
<option value="uniques">Unique Stickers</option>
|
||||
<option value="rares">Rare Stickers</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Username</th>
|
||||
<th>Total Stickers</th>
|
||||
<th>Uniques</th>
|
||||
<th>Rares</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each sortedUsers as user}
|
||||
<tr class:top-three={user.rank <= 3}>
|
||||
<td class="rank rank-{user.rank}">{user.rank}</td>
|
||||
<td class="username">{user.username}</td>
|
||||
<td>{user.totalStickers}</td>
|
||||
<td>{user.uniques}</td>
|
||||
<td>{user.rares}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
{#if youUser}
|
||||
<div class="table-container you-container">
|
||||
<table>
|
||||
<thead class="hidden-header">
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Username</th>
|
||||
<th>Total Stickers</th>
|
||||
<th>Uniques</th>
|
||||
<th>Rares</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="you-row">
|
||||
<td class="rank">{youUser.rank}</td>
|
||||
<td class="username">{youUser.username}</td>
|
||||
<td>{youUser.totalStickers}</td>
|
||||
<td>{youUser.uniques}</td>
|
||||
<td>{youUser.rares}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
.content-row {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: nowrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.filter-card label {
|
||||
font-size: 1.25rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-card select {
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(250, 248, 245, 0.95);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 2rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgba(217, 201, 182, 0.5);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(250, 248, 245, 0.5);
|
||||
}
|
||||
|
||||
.top-three {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rank {
|
||||
font-family: 'Departure Mono', monospace;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.rank-1 {
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
.rank-2 {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
.rank-3 {
|
||||
color: #cd7f32;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.you-container {
|
||||
background: #d4edda;
|
||||
}
|
||||
|
||||
.hidden-header {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
.you-row:hover {
|
||||
background: #c3e6cb;
|
||||
}
|
||||
|
||||
.you-row td {
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
24
src/routes/my-designs/+layout.svelte
Normal file
24
src/routes/my-designs/+layout.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import Background from '$lib/components/Background.svelte';
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import '$lib/styles/global.css';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
</svelte:head>
|
||||
|
||||
<Background />
|
||||
<Navbar active="my-designs" />
|
||||
|
||||
<div class="page-content">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.page-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
299
src/routes/my-designs/+page.svelte
Normal file
299
src/routes/my-designs/+page.svelte
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
<script>
|
||||
import resizeIcon from '$lib/assets/images/resize.jpg';
|
||||
|
||||
let showModal = $state(false);
|
||||
let selectedDesign = $state(null);
|
||||
|
||||
const myDesigns = [
|
||||
{ designUrl: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/80c59fb29dd5a3fbffc1b2297b512ac8cbb719cb_IMG_5304.png ', name: 'FlavorTownRed', status: 'approved', votes: 42, width: 100, height: 100, submittedAt: '2024-03-15' },
|
||||
{ designUrl: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/80c59fb29dd5a3fbffc1b2297b512ac8cbb719cb_IMG_5304.png ', name: 'FlavorTownRed', status: 'pending', votes: 42, width: 100, height: 100, submittedAt: '2024-03-15' }
|
||||
];
|
||||
|
||||
function openModal(design) {
|
||||
selectedDesign = design;
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal = false;
|
||||
selectedDesign = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1><mark>My Designs</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
<div class="card info-card">
|
||||
<p>Submit a sticker design, after voting it could be shipped out in the monthly sticker box</p>
|
||||
</div>
|
||||
|
||||
<button class="card upload-btn">+ Upload a design</button>
|
||||
</div>
|
||||
|
||||
{#if myDesigns.length > 0}
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Design</th>
|
||||
<th>Name</th>
|
||||
<th>Submitted</th>
|
||||
<th>Status</th>
|
||||
<th>Votes</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each myDesigns as design}
|
||||
<tr>
|
||||
<td class="design-cell">
|
||||
<img src={design.designUrl} alt={design.name} class="design-thumb" />
|
||||
<button class="resize-btn" onclick={() => openModal(design)}>
|
||||
<img src={resizeIcon} alt="Resize" />
|
||||
</button>
|
||||
</td>
|
||||
<td>{design.name}</td>
|
||||
<td>{design.submittedAt}</td>
|
||||
<td>
|
||||
<span class="status status-{design.status}">{design.status}</span>
|
||||
</td>
|
||||
<td>{design.votes}</td>
|
||||
<td>
|
||||
<button class="action-btn">Edit</button>
|
||||
<button class="action-btn delete">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="empty-state">
|
||||
<p>You haven't uploaded any designs yet.</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showModal && selectedDesign}
|
||||
<div class="modal-overlay" onclick={closeModal}>
|
||||
<div class="modal-content" onclick={(e) => e.stopPropagation()}>
|
||||
<button class="modal-close" onclick={closeModal}>×</button>
|
||||
<img src={selectedDesign.designUrl} alt={selectedDesign.name} class="modal-image" />
|
||||
<div class="modal-footer">
|
||||
{selectedDesign.name} — {selectedDesign.width}mm × {selectedDesign.height}mm
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
.content-row {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: nowrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
font-family: 'Departure Mono', monospace;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.upload-btn:hover {
|
||||
background: rgba(250, 248, 245, 1);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 2rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgba(217, 201, 182, 0.5);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(250, 248, 245, 0.5);
|
||||
}
|
||||
|
||||
.design-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.design-thumb {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.resize-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.resize-btn img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.resize-btn:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.status-approved {
|
||||
background: #c8f7c5;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: #fff3cd;
|
||||
}
|
||||
|
||||
.status-rejected {
|
||||
background: #f8d7da;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4rem 0.75rem;
|
||||
border: 1px solid #333;
|
||||
border-radius: 0.25rem;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.action-btn.delete {
|
||||
background: #f8d7da;
|
||||
border-color: #cc0000;
|
||||
}
|
||||
|
||||
.action-btn.delete:hover {
|
||||
background: #f5c6cb;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
margin-top: 2rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 3rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: #fff;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
padding: 2rem;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
max-width: 70vw;
|
||||
max-height: 60vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
margin-top: 1.5rem;
|
||||
font-family: 'Departure Mono', monospace;
|
||||
font-size: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
24
src/routes/stickers/+layout.svelte
Normal file
24
src/routes/stickers/+layout.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import Background from '$lib/components/Background.svelte';
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import '$lib/styles/global.css';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
</svelte:head>
|
||||
|
||||
<Background />
|
||||
<Navbar active="stickers" />
|
||||
|
||||
<div class="page-content">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.page-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
let selectedFilter = $state('all');
|
||||
let searchQuery = $state('');
|
||||
let expandedId = $state(null);
|
||||
let selectedSticker = $state(null);
|
||||
let stickers = $state([]);
|
||||
let loading = $state(true);
|
||||
let error = $state(null);
|
||||
|
|
@ -31,11 +31,23 @@
|
|||
})
|
||||
);
|
||||
|
||||
function toggleExpand(id) {
|
||||
expandedId = expandedId === id ? null : id;
|
||||
function openModal(sticker) {
|
||||
selectedSticker = sticker;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
selectedSticker = null;
|
||||
}
|
||||
|
||||
function handleKeydown(e) {
|
||||
if (e.key === 'Escape' && selectedSticker) {
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
<h1><mark>Stickers</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
|
|
@ -72,8 +84,7 @@
|
|||
{#each filteredDesigns as design}
|
||||
<div
|
||||
class="panel"
|
||||
class:expanded={expandedId === design.id}
|
||||
onclick={() => toggleExpand(design.id)}
|
||||
onclick={() => openModal(design)}
|
||||
>
|
||||
<div class="panel-image">
|
||||
<img src={design.image} alt={design.name} />
|
||||
|
|
@ -84,20 +95,6 @@
|
|||
<span class="panel-artist">by {design.artist}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if expandedId === design.id}
|
||||
<div class="panel-details">
|
||||
{#if design.event}
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Event:</span>
|
||||
<span>{design.event}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">ID:</span>
|
||||
<span>{design.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -107,6 +104,30 @@
|
|||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if selectedSticker}
|
||||
<div class="modal-overlay" onclick={closeModal}>
|
||||
<div class="modal-content" onclick={(e) => e.stopPropagation()}>
|
||||
<button class="modal-close" onclick={closeModal}>×</button>
|
||||
<div class="modal-image">
|
||||
<img src={selectedSticker.image} alt={selectedSticker.name} />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<h2>{selectedSticker.name}</h2>
|
||||
<div class="modal-info">
|
||||
{#each Object.entries(selectedSticker) as [key, value]}
|
||||
{#if key !== 'image'}
|
||||
<div class="info-row">
|
||||
<span class="info-key">{key}:</span>
|
||||
<span class="info-value">{Array.isArray(value) ? value.join(', ') : value}</span>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
|
|
@ -214,10 +235,6 @@
|
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.panel.expanded {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.panel-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
|
|
@ -252,27 +269,6 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
.panel-details {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid #333;
|
||||
background: rgba(250, 248, 245, 0.5);
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.detail-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
|
|
@ -282,4 +278,99 @@
|
|||
color: #666;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border: 3px solid #333;
|
||||
border-radius: 0.75rem;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
background: #333;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
background: #f5f5f5;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.modal-image img {
|
||||
max-width: 100%;
|
||||
max-height: 60vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 1.5rem;
|
||||
border-top: 2px solid #333;
|
||||
}
|
||||
|
||||
.modal-footer h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.modal-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: rgba(250, 248, 245, 0.5);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.info-key {
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,309 @@
|
|||
<script>
|
||||
const mockTrades = [
|
||||
{
|
||||
id: 1,
|
||||
from: 'LFD',
|
||||
get: { name: 'Crow thing', img: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/d2edc2953c00399264ec0c9d93167794a1cb1867_image.png ', quantity: 2 },
|
||||
give: { name: 'FlavorTownRed', img: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/80c59fb29dd5a3fbffc1b2297b512ac8cbb719cb_IMG_5304.png', quantity: 1 },
|
||||
message: "Yo midnight just wrapped and i have one billion left over stickers, would trade most things."
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
from: 'LFD',
|
||||
get: { name: 'Crow thing', img: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/d2edc2953c00399264ec0c9d93167794a1cb1867_image.png ', quantity: 2 },
|
||||
give: { name: 'FlavorTownRed', img: 'https://hc-cdn.hel1.your-objectstorage.com/s/v3/80c59fb29dd5a3fbffc1b2297b512ac8cbb719cb_IMG_5304.png', quantity: 1 },
|
||||
message: "Yo midnight just wrapped and i have one billion left over stickers, would trade most things."
|
||||
}
|
||||
];
|
||||
|
||||
let showModal = $state(false);
|
||||
|
||||
function openMakeDeal() {
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1><mark>Trade</mark></h1>
|
||||
|
||||
<button class="make-deal-btn" onclick={openMakeDeal}>+ Make a Deal</button>
|
||||
|
||||
<h2><div style="font-weight: bold; background-color: #d9c9b6; padding: 0 1rem; font-family: 'Departure Mono', monospace; height: 2rem; width: max-content;">Incoming Trades</div></h2>
|
||||
|
||||
<div class="trades-list">
|
||||
{#each mockTrades as trade}
|
||||
<div class="trade-row">
|
||||
<div class="trade-box">
|
||||
<div class="trade-side give-side">
|
||||
<img src={trade.give.img} alt={trade.give.name} class="sticker-img" />
|
||||
<div class="quantity">×{trade.give.quantity}</div>
|
||||
</div>
|
||||
<div class="trade-side get-side">
|
||||
<img src={trade.get.img} alt={trade.get.name} class="sticker-img" />
|
||||
<div class="quantity">×{trade.get.quantity}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trade-message">
|
||||
<div class="message-header">
|
||||
<span class="from-user">{trade.from}</span>
|
||||
</div>
|
||||
<p class="message-text">{trade.message}</p>
|
||||
<button class="discuss-btn">Discuss</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if showModal}
|
||||
<div class="modal-overlay" onclick={closeModal}>
|
||||
<div class="modal" onclick={(e) => e.stopPropagation()}>
|
||||
<h2>Propose a Trade</h2>
|
||||
<div class="modal-content">
|
||||
<div class="trade-side give-side">
|
||||
<label>You Give</label>
|
||||
<select>
|
||||
<option>Select sticker...</option>
|
||||
<option>Orpheus</option>
|
||||
<option>Hack Club Flag</option>
|
||||
<option>HackOS </option>
|
||||
</select>
|
||||
<input type="number" placeholder="Qty" value="1" min="1" />
|
||||
</div>
|
||||
<div class="trade-arrow">⇄</div>
|
||||
<div class="trade-side get-side">
|
||||
<label>You Get</label>
|
||||
<select>
|
||||
<option>Select sticker...</option>
|
||||
<option>🦕 Orpheus</option>
|
||||
<option>🚩 Hack Club Flag</option>
|
||||
<option>💻 Retro Computer</option>
|
||||
</select>
|
||||
<input type="number" placeholder="Qty" value="1" min="1" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="cancel-btn" onclick={closeModal}>Cancel</button>
|
||||
<button class="submit-btn" onclick={closeModal}>Post Trade</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin: 2rem 0 1rem 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
.make-deal-btn {
|
||||
background: #444;
|
||||
color: white;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.make-deal-btn:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.trades-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.trade-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.trade-box {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.trade-side {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.give-side {
|
||||
background: #ffebee;
|
||||
}
|
||||
|
||||
.get-side {
|
||||
background: #e8f5e9;
|
||||
}
|
||||
|
||||
.sticker-img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
font-family: 'Departure Mono', monospace;
|
||||
}
|
||||
|
||||
.trade-message {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.from-user {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.discuss-btn {
|
||||
align-self: flex-start;
|
||||
padding: 0.5rem 1rem;
|
||||
background: #444;
|
||||
color: white;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.discuss-btn:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: white;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
padding: 2rem;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-content .trade-side {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-content .trade-side label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-content .trade-side select,
|
||||
.modal-content .trade-side input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.25rem;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.modal-content .trade-side input {
|
||||
margin-top: 0.5rem;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #eee;
|
||||
color: #333;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
border: 2px solid #333;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
</style>
|
||||
24
src/routes/vote/+layout.svelte
Normal file
24
src/routes/vote/+layout.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import Background from '$lib/components/Background.svelte';
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import '$lib/styles/global.css';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
</svelte:head>
|
||||
|
||||
<Background />
|
||||
<Navbar active="vote" />
|
||||
|
||||
<div class="page-content">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.page-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
38
src/routes/vote/+page.svelte
Normal file
38
src/routes/vote/+page.svelte
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
<h1><mark>Vote</mark></h1>
|
||||
|
||||
<div class="content-row">
|
||||
<div class="card info-card">
|
||||
<p>Voting will be open in [time]! Right now please take a look at the current submissions. Remember the highest voted design will be produced and shipped.</p>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin: 0 0 2rem 0;
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #333;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-card p {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: #d9c9b6;
|
||||
padding: 0 0.2rem;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
Loading…
Add table
Reference in a new issue