loads of mobile ui layout upgrades
This commit is contained in:
@@ -144,10 +144,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Same "Bonus name – prize" split as the ledger, so prize rewards don't
|
||||
// duplicate the full label in both columns of a claim row.
|
||||
function rewardLabelParts(r: any): { name: string; prize: string } {
|
||||
const label = r.label || '';
|
||||
const idx = label.indexOf(' – ');
|
||||
if (idx === -1) return { name: label, prize: label };
|
||||
return { name: label.slice(0, idx), prize: label.slice(idx + 3) };
|
||||
}
|
||||
|
||||
function rewardLabel(r: any): string {
|
||||
if (r.rewardType === 'cash') return `£${Number(r.value).toFixed(2)}`;
|
||||
if (r.rewardType === 'points') return `${r.value} pts`;
|
||||
return r.label;
|
||||
return rewardLabelParts(r).prize;
|
||||
}
|
||||
|
||||
function manualConfigs() {
|
||||
@@ -605,8 +614,14 @@
|
||||
const existingReward = rewards.find(
|
||||
(r) => r.bonusConfigId === cfg.id && r.memberId === memberId
|
||||
);
|
||||
// An earned reward only keeps lighting up the card while it's fresh
|
||||
// (earned in the viewed week) or still outstanding (not yet claimed).
|
||||
// Once claimed and the week rolls over, "Reward earned" disappears.
|
||||
const rDate = (existingReward?.date || '').slice(0, 10);
|
||||
const rewardInWeek = !!rDate && rDate >= weekStart && rDate <= weekEnd;
|
||||
const rewardOutstanding = !!existingReward && existingReward.status !== 'claimed';
|
||||
const achieved = existingReward
|
||||
? true
|
||||
? rewardInWeek || rewardOutstanding
|
||||
: isPercent
|
||||
? totalPotential > 0 && ratio * 100 >= configured
|
||||
: target > 0 && current >= target;
|
||||
@@ -729,6 +744,12 @@
|
||||
function headDay(): number {
|
||||
return headDate().getDate();
|
||||
}
|
||||
function headDayOrdinal(): string {
|
||||
const n = headDay();
|
||||
if (n % 100 >= 11 && n % 100 <= 13) return `${n}th`;
|
||||
const s = ['th', 'st', 'nd', 'rd'];
|
||||
return `${n}${s[n % 10] || s[0]}`;
|
||||
}
|
||||
function headWeekday(): string {
|
||||
return headDate().toLocaleDateString(undefined, { weekday: 'long' });
|
||||
}
|
||||
@@ -914,7 +935,6 @@
|
||||
</div>
|
||||
<div class="gm-bottom">
|
||||
<span class="gm-reward">{g.rewardText}</span>
|
||||
<span class="gm-date">{formatDDMMYY(g.date)}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -1047,7 +1067,7 @@
|
||||
{/if}
|
||||
</Card>
|
||||
|
||||
<Card cols={1} title="Claims" accent="#f59e0b">
|
||||
<Card cols={1} title="Claims" accent="#f97316" class="claims-card">
|
||||
{#if claimableRewards().length === 0}
|
||||
<div class="claims-empty">
|
||||
{@html checkCircleIcon}<span>No outstanding claims</span>
|
||||
@@ -1106,7 +1126,7 @@
|
||||
<p class="section-label">Requested</p>
|
||||
{#each requested as r}
|
||||
<form
|
||||
class="flex justify-between items-center"
|
||||
class="flex flex-wrap justify-between items-center"
|
||||
method="POST"
|
||||
action="?/claim"
|
||||
use:enhance={() => {
|
||||
@@ -1118,7 +1138,7 @@
|
||||
<span class="claim-icon"
|
||||
>{@html r.rewardType === 'cash' ? coinsIcon : giftIcon}</span
|
||||
>
|
||||
<span class="claim-label">{r.label}</span>
|
||||
<span class="claim-label">{rewardLabelParts(r).name}</span>
|
||||
<span class="claim-value">{rewardLabel(r)}</span>
|
||||
</div>
|
||||
<Button type="submit" size="sm" variant="primary">Issue</Button>
|
||||
@@ -1133,7 +1153,7 @@
|
||||
<span class="claim-icon"
|
||||
>{@html r.rewardType === 'cash' ? coinsIcon : giftIcon}</span
|
||||
>
|
||||
<span class="claim-label">{r.label}</span>
|
||||
<span class="claim-label">{rewardLabelParts(r).name}</span>
|
||||
<span class="claim-value">
|
||||
{rewardLabel(r)}
|
||||
{#if paydayLocked(r)}
|
||||
@@ -1265,8 +1285,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="hero-date">
|
||||
<span class="hero-day">{headDay()}</span>
|
||||
<span class="hero-weekday">{headWeekday()}</span>
|
||||
<span class="hero-day">{headDayOrdinal()}</span>
|
||||
<span class="hero-month">{headMonth()}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2224,11 +2244,15 @@
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.hero-days {
|
||||
font-size: 0.85rem;
|
||||
font-size: 1rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
/* Own full-width line, centered under the hero content */
|
||||
flex: 1 1 100%;
|
||||
text-align: center;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.hero-days b {
|
||||
font-size: 1.4rem;
|
||||
font-size: 1.5rem;
|
||||
margin-right: 0.15rem;
|
||||
}
|
||||
.hero-nav {
|
||||
@@ -2483,20 +2507,25 @@
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@media (max-width: 639px) {
|
||||
.goal-mini-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.goal-mini {
|
||||
background: linear-gradient(135deg, #d1d3fa, #ccfbf1);
|
||||
border: 1px solid #a7f3d0;
|
||||
border-radius: 12px;
|
||||
padding: 0.55rem 0.7rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
min-width: 0;
|
||||
/* Own width as a query container, so tight cards drop the member name */
|
||||
container-type: inline-size;
|
||||
}
|
||||
/* Card narrower than 250px: hide the member name so the row doesn't squish */
|
||||
@container (max-width: 249px) {
|
||||
.gm-member {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.gm-top {
|
||||
display: flex;
|
||||
@@ -2526,18 +2555,14 @@
|
||||
.gm-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.gm-reward {
|
||||
font-weight: 800;
|
||||
color: #047857;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.gm-date {
|
||||
color: #9ca3af;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
/* Goal celebration notice (child view) */
|
||||
.celebrate-card.goal {
|
||||
@@ -2954,4 +2979,60 @@
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
/* Mobile: declared last so it wins over the base .goal-mini (.kanban)
|
||||
rules (same specificity, source order decides). */
|
||||
@media (max-width: 639px) {
|
||||
.goal-mini-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.kanban {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
/* Admin overview cards flow full width on phones — same convention as
|
||||
the chores kanban (no horizontal scroll). */
|
||||
.kanban-scroll {
|
||||
overflow-x: visible;
|
||||
}
|
||||
.kanban-inner {
|
||||
min-width: 0;
|
||||
}
|
||||
/* Keep the orange wash visible: restore body padding for this card
|
||||
only (the shared Card removes it on mobile). */
|
||||
:global(.card.claims-card .card-body) {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
/* Claim rows: icon/label/value stay on one line (label truncates
|
||||
with ellipsis) and the Issue button becomes a full-width action
|
||||
below — no squishing or overflow under 450px. */
|
||||
.member-claims form.flex {
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.5rem;
|
||||
}
|
||||
.member-claims form.flex > div {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
/* :global — the button element is rendered by the shared Button
|
||||
component, so a scoped selector never matches it. */
|
||||
.member-claims form.flex :global(button) {
|
||||
flex: 1 1 100%;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
.member-claims .claim-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.member-claims .claim-item {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
/* Claims card — orange wash so pending requests are impossible to miss.
|
||||
:global + !important deliberately override the shared Card's white/
|
||||
mobile-transparent backgrounds. */
|
||||
:global(.card.claims-card) {
|
||||
background: linear-gradient(135deg, #ffedd5, #fdba74 70%, #fb923c) !important;
|
||||
border-color: #f97316;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user