456 files
This commit is contained in:
@@ -23,6 +23,16 @@ const css = LitElement.prototype.css;
|
||||
const tmClaimList = (v) => (Array.isArray(v) ? v : []);
|
||||
|
||||
const _safeColor = (c, d) => (typeof c === "string" && /^#[0-9a-fA-F]{3,8}$/.test(c) ? c : d);
|
||||
// Keeps number runs like "3 / 1" reading left to right in RTL text (#995);
|
||||
// identity until the design layer has loaded (it is also what stamps dir).
|
||||
const _ltrNums = (s) => (window.__taskmate_design && window.__taskmate_design.ltrNums ? window.__taskmate_design.ltrNums(s) : s);
|
||||
// "a / b" for a template: the same text nodes as a literal `${a} / ${b}` in
|
||||
// LTR (so it renders pixel-for-pixel as before), the isolated string in RTL.
|
||||
const _ltrRatio = (a, b) => {
|
||||
const s = `${a} / ${b}`;
|
||||
const r = _ltrNums(s);
|
||||
return r === s ? html`${a} / ${b}` : r;
|
||||
};
|
||||
|
||||
class TaskMateRewardsCard extends LitElement {
|
||||
static get properties() {
|
||||
@@ -287,7 +297,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
inset-inline-start: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
@@ -300,8 +310,8 @@ class TaskMateRewardsCard extends LitElement {
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { left: -100%; }
|
||||
100% { left: 200%; }
|
||||
0% { inset-inline-start: -100%; }
|
||||
100% { inset-inline-start: 200%; }
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
@@ -310,7 +320,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
text-align: right;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
/* Jackpot reward styles */
|
||||
@@ -325,8 +335,8 @@ class TaskMateRewardsCard extends LitElement {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
@@ -393,7 +403,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
inset-inline-start: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
@@ -407,21 +417,21 @@ class TaskMateRewardsCard extends LitElement {
|
||||
|
||||
/* Shimmer 3 times then wait ~30s before repeating */
|
||||
@keyframes jackpot-segment-shimmer {
|
||||
0% { left: -100%; }
|
||||
2.5% { left: 200%; }
|
||||
5% { left: -100%; }
|
||||
7.5% { left: 200%; }
|
||||
10% { left: -100%; }
|
||||
12.5% { left: 200%; }
|
||||
15%, 100% { left: -100%; opacity: 0; }
|
||||
0% { inset-inline-start: -100%; }
|
||||
2.5% { inset-inline-start: 200%; }
|
||||
5% { inset-inline-start: -100%; }
|
||||
7.5% { inset-inline-start: 200%; }
|
||||
10% { inset-inline-start: -100%; }
|
||||
12.5% { inset-inline-start: 200%; }
|
||||
15%, 100% { inset-inline-start: -100%; opacity: 0; }
|
||||
}
|
||||
|
||||
.jackpot-segment:first-child {
|
||||
border-radius: 9px 0 0 9px;
|
||||
border-start-start-radius: 9px; border-start-end-radius: 0; border-end-end-radius: 0; border-end-start-radius: 9px;
|
||||
}
|
||||
|
||||
.jackpot-segment:last-child {
|
||||
border-radius: 0 9px 9px 0;
|
||||
border-start-start-radius: 0; border-start-end-radius: 9px; border-end-end-radius: 9px; border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
.jackpot-segment:only-child {
|
||||
@@ -546,7 +556,21 @@ class TaskMateRewardsCard extends LitElement {
|
||||
/* Pending approval state */
|
||||
.reward-row.pending-approval {
|
||||
opacity: 0.6;
|
||||
border-left: 3px solid #e67e22;
|
||||
border-inline-start: 3px solid #e67e22;
|
||||
}
|
||||
|
||||
/* Reject reason (#976) */
|
||||
.reject-note {
|
||||
margin-top: 6px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
background: rgba(244, 67, 54, 0.12);
|
||||
color: var(--error-color, #d32f2f);
|
||||
}
|
||||
|
||||
.pending-label {
|
||||
@@ -747,14 +771,14 @@ class TaskMateRewardsCard extends LitElement {
|
||||
font-weight: 700;
|
||||
font-size: 1.15rem;
|
||||
color: var(--text-primary);
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.spendable-banner .spendable-of {
|
||||
font-weight: 400;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.82rem;
|
||||
margin-left: 4px;
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
|
||||
/* Pool Mode controls — render BELOW the progress bar, full width */
|
||||
@@ -801,7 +825,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.deposit-custom input {
|
||||
@@ -941,7 +965,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
font-size: 0.9rem;
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
}
|
||||
.dialog-points-row {
|
||||
display: flex;
|
||||
@@ -995,6 +1019,12 @@ class TaskMateRewardsCard extends LitElement {
|
||||
.rw-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
||||
.rw-name { font-weight: 800; font-size: 15px; }
|
||||
.rw-desc { font-size: 12px; }
|
||||
.rw-reject {
|
||||
margin: 2px 0 6px; padding: 4px 8px; border-radius: 8px;
|
||||
font-size: 12px; font-weight: 700; line-height: 1.3;
|
||||
white-space: normal; overflow-wrap: anywhere;
|
||||
background: color-mix(in srgb, var(--tmd-bad, #e74c3c) 14%, transparent); color: var(--tmd-bad, #e74c3c);
|
||||
}
|
||||
.rw-avail { align-self: flex-start; font-size: 11px; }
|
||||
.rw-avail-warn { background: color-mix(in srgb, var(--tmd-warn) 16%, transparent);
|
||||
color: var(--tmd-warn); }
|
||||
@@ -1010,7 +1040,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
.rw-status { font-size: 12px; }
|
||||
.rw-status-good { color: var(--tmd-good); font-weight: 800; }
|
||||
.rw-pool-btns { gap: 6px; flex-wrap: wrap; align-items: center; }
|
||||
.rw-pool-btns .deposit-custom { margin-left: auto; gap: 6px; }
|
||||
.rw-pool-btns .deposit-custom { margin-inline-start: auto; gap: 6px; }
|
||||
.rw-deposit-input {
|
||||
width: 70px;
|
||||
min-width: 0;
|
||||
@@ -1047,6 +1077,9 @@ class TaskMateRewardsCard extends LitElement {
|
||||
header_color: '#e67e22',
|
||||
...config,
|
||||
};
|
||||
// Dashboards saved before #1010 stored the English default title; treat it
|
||||
// as unset so the translated default is shown instead.
|
||||
if (this.config.title === "Rewards") this.config.title = "";
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
@@ -1060,7 +1093,6 @@ class TaskMateRewardsCard extends LitElement {
|
||||
static getStubConfig() {
|
||||
return {
|
||||
entity: "sensor.taskmate_overview",
|
||||
title: "Rewards",
|
||||
child_id: null,
|
||||
show_child_badges: true,
|
||||
enable_pool_mode: false,
|
||||
@@ -1287,8 +1319,10 @@ class TaskMateRewardsCard extends LitElement {
|
||||
<ha-icon icon="${pointsIcon}"></ha-icon>
|
||||
<span class="spendable-label">${this._t('rewards.spendable_balance')}</span>
|
||||
<span class="spendable-value">
|
||||
${spendable}
|
||||
${detail}
|
||||
${spendable !== gross && this.getAttribute("dir") === "rtl"
|
||||
// "12 / 40" spans two elements, so isolate it with markup (#995).
|
||||
? html`<bdi dir="ltr">${spendable} ${detail}</bdi>`
|
||||
: html`${spendable} ${detail}`}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@@ -1437,7 +1471,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
<ha-icon icon="mdi:clock-outline"></ha-icon>
|
||||
${this._t('rewards.awaiting_approval')}
|
||||
</div>
|
||||
` : ''}
|
||||
` : this._renderRejectNote(reward, childId ? relevantChild : null, 'reject-note')}
|
||||
|
||||
${showChildBadges && !isJackpot
|
||||
? html`
|
||||
@@ -1472,6 +1506,17 @@ class TaskMateRewardsCard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject reasons (#976): why a parent turned down this child's last claim.
|
||||
* Only with a child in context — the reason is personal. Called from the
|
||||
* classic row AND _designRewardRow, so every design shows it.
|
||||
*/
|
||||
_renderRejectNote(reward, child, cls) {
|
||||
const rej = ((child && child.rejections) || []).find(r => r.kind === "reward" && r.id === reward.id);
|
||||
if (!rej || !rej.reason) return '';
|
||||
return html`<div class="${cls}" role="note">↩️ ${this._t('rewards.rejected_note', { reason: rej.reason })}</div>`;
|
||||
}
|
||||
|
||||
/** "Available Fri, Sat · 18:00–21:00" for a time-locked reward (#857). */
|
||||
_timeLockLabel(reward) {
|
||||
const summary = window.__taskmate_time_lock_label
|
||||
@@ -2060,7 +2105,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
? html`<span class="muted rw-status">⏳ ${this._t('rewards.awaiting_approval')}</span>`
|
||||
: d.canAfford && d.showClaim
|
||||
? html`<span class="rw-status rw-status-good">${this._t('reward_progress.ready_to_claim')}</span>`
|
||||
: html`<span class="muted rw-status">${d.currentStars} / ${d.displayCost}</span>`;
|
||||
: html`<span class="muted rw-status">${_ltrRatio(d.currentStars, d.displayCost)}</span>`;
|
||||
|
||||
return html`
|
||||
<div class="rw-card ${d.isJackpot ? 'rw-jackpot' : ''} ${d.isUnavailable || d.isTimeLocked ? 'rw-unavail' : ''}"
|
||||
@@ -2076,6 +2121,7 @@ class TaskMateRewardsCard extends LitElement {
|
||||
</div>
|
||||
${costBadge}
|
||||
</div>
|
||||
${d.hasPendingClaim ? '' : this._renderRejectNote(reward, d.childId ? d.relevantChild : null, 'rw-reject')}
|
||||
${bar}
|
||||
${d.isJackpot ? html`
|
||||
<div class="row rw-pool-foot">
|
||||
@@ -2231,7 +2277,7 @@ class TaskMateRewardsCardEditor extends LitElement {
|
||||
border-radius: 4px;
|
||||
padding: 4px 10px;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
.colour-helper {
|
||||
color: var(--secondary-text-color);
|
||||
|
||||
Reference in New Issue
Block a user