Compare commits
2 Commits
18511b6546
...
8e4eef06f5
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e4eef06f5 | |||
| 1609e11c48 |
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import Button from './Button.svelte';
|
||||
|
||||
let {
|
||||
label,
|
||||
successLabel = 'Saved ✓',
|
||||
variant = 'primary',
|
||||
size = 'sm',
|
||||
...rest
|
||||
}: {
|
||||
label: string;
|
||||
successLabel?: string;
|
||||
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'success' | 'purple';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
[key: string]: unknown;
|
||||
} = $props();
|
||||
|
||||
let saved = $state(false);
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
// Parent forms call this (via bind:this) from their use:enhance callback
|
||||
// on result.type === 'success'. Green for 4s, then back to normal.
|
||||
export function flash() {
|
||||
saved = true;
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => (saved = false), 4000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button type="submit" {size} variant={saved ? 'success' : variant} {...rest}>
|
||||
{saved ? successLabel : label}
|
||||
</Button>
|
||||
@@ -248,6 +248,15 @@
|
||||
// ─── Payday countdown (child view) ───
|
||||
let nowMs = $state(Date.now());
|
||||
let eowFired = $state(false);
|
||||
// Nyan-style payday party: rainbow banner + confetti when payday unlocks.
|
||||
let paydayParty = $state(false);
|
||||
let partyTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
function startPaydayParty() {
|
||||
paydayParty = true;
|
||||
fire();
|
||||
if (partyTimer) clearTimeout(partyTimer);
|
||||
partyTimer = setTimeout(() => (paydayParty = false), 15000);
|
||||
}
|
||||
$effect(() => {
|
||||
const id = setInterval(() => (nowMs = Date.now()), 1000);
|
||||
return () => clearInterval(id);
|
||||
@@ -269,6 +278,7 @@
|
||||
if (!pbToken || !famId) return;
|
||||
eowFired = true;
|
||||
memberApi.payday(famId).catch(() => {});
|
||||
startPaydayParty();
|
||||
});
|
||||
|
||||
function paydayWeekStart(): string {
|
||||
@@ -1273,6 +1283,16 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if paydayParty}
|
||||
<div class="payday-party" role="status" onclick={() => (paydayParty = false)}>
|
||||
<span class="party-cat">🐖</span>
|
||||
<div class="party-body">
|
||||
<p class="party-title">PAYDAY!!</p>
|
||||
<p class="party-sub">Your cash is ready — go collect it! 🎉</p>
|
||||
</div>
|
||||
<span class="party-stars">🌈⭐🌈</span>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- HERO -->
|
||||
<header class="hero">
|
||||
<svg
|
||||
@@ -3107,6 +3127,91 @@
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* ── Payday party: nyan-style colour-cycling celebration ── */
|
||||
.payday-party {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem 1.2rem;
|
||||
border-radius: 14px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#ef4444,
|
||||
#f97316,
|
||||
#eab308,
|
||||
#22c55e,
|
||||
#3b82f6,
|
||||
#a855f7,
|
||||
#ec4899,
|
||||
#ef4444
|
||||
);
|
||||
background-size: 300% 100%;
|
||||
animation:
|
||||
nyan-slide 3s linear infinite,
|
||||
party-pop 0.45s ease-out;
|
||||
box-shadow: 0 8px 24px rgba(168, 85, 247, 0.45);
|
||||
}
|
||||
@keyframes nyan-slide {
|
||||
to {
|
||||
background-position: 300% 0;
|
||||
}
|
||||
}
|
||||
@keyframes party-pop {
|
||||
from {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.party-cat {
|
||||
font-size: 2.6rem;
|
||||
animation: party-bounce 0.55s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes party-bounce {
|
||||
from {
|
||||
transform: translateY(-4px) rotate(-10deg);
|
||||
}
|
||||
to {
|
||||
transform: translateY(4px) rotate(10deg);
|
||||
}
|
||||
}
|
||||
.party-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
}
|
||||
.party-title {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.1em;
|
||||
animation: party-wiggle 1s ease-in-out infinite;
|
||||
}
|
||||
@keyframes party-wiggle {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(2deg);
|
||||
}
|
||||
}
|
||||
.party-sub {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.95;
|
||||
}
|
||||
.party-stars {
|
||||
margin-left: auto;
|
||||
font-size: 1.4rem;
|
||||
animation: party-bounce 0.55s ease-in-out infinite alternate-reverse;
|
||||
}
|
||||
/* Mobile: declared last so it wins over the base .goal-mini (.kanban)
|
||||
rules (same specificity, source order decides). */
|
||||
@media (max-width: 639px) {
|
||||
|
||||
Reference in New Issue
Block a user