Mark crops as planted (on the way)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
35df8dbd01
commit
07057e6c3d
4 changed files with 46 additions and 18 deletions
|
|
@ -19,6 +19,10 @@
|
|||
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
|
||||
const escapeHtml = (value) => String(value).replace(/[&<>'"]/g, char => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ }[char]));
|
||||
const itemKey = crop => crop.sharedId || crop.id;
|
||||
// Planting is tracked as its own shared flag so "on the way" never counts
|
||||
// toward bundle completion.
|
||||
const plantedId = key => `planted-${key}`;
|
||||
const isPlanted = key => !state.checks[key] && Boolean(state.checks[plantedId(key)]);
|
||||
|
||||
function saveDate() {
|
||||
localStorage.setItem("valley-year", state.year);
|
||||
|
|
@ -126,7 +130,10 @@
|
|||
if (birthday) cards.push(card("birthday", "Birthday", birthday, "A birthday gift gives 8× the usual friendship effect.", dailyId("birthday", birthday), giftGuide(birthday)));
|
||||
if (event) cards.push(card("event", "Event", event[0], event[1], dailyId("event", event[0])));
|
||||
reminders().forEach(([title, copy]) => cards.push(card("reminder", "Reminder", title, copy, dailyId("reminder", title))));
|
||||
cropDeadlineCards().forEach(crop => cards.push(card("deadline", "Plant by today", crop.name, crop.purpose, itemKey(crop))));
|
||||
cropDeadlineCards().forEach(crop => {
|
||||
const planted = isPlanted(itemKey(crop));
|
||||
cards.push(card(planted ? "deadline planted" : "deadline", planted ? "Planted · on the way" : "Plant by today", crop.name, crop.purpose, itemKey(crop)));
|
||||
});
|
||||
if (!cards.length) cards.push(card("quiet", "Open day", "Make your own luck", "No fixed birthdays, festivals, or last planting dates today."));
|
||||
$("#today-cards").innerHTML = cards.join("");
|
||||
const noun = cards.length === 1 ? "thing" : "things";
|
||||
|
|
@ -138,15 +145,21 @@
|
|||
const rows = crops.map(crop => {
|
||||
const key = itemKey(crop);
|
||||
const checked = Boolean(state.checks[key]);
|
||||
const overdue = state.day > crop.last && !checked;
|
||||
const today = state.day === crop.last && !checked;
|
||||
return `<label class="crop-row ${checked ? "complete" : ""} ${overdue ? "overdue" : ""}">
|
||||
const planted = isPlanted(key);
|
||||
const overdue = state.day > crop.last && !checked && !planted;
|
||||
const today = state.day === crop.last && !checked && !planted;
|
||||
const plantButton = checked ? "" : `<button type="button" class="plant-toggle" data-plant-id="${plantedId(key)}" aria-pressed="${planted}" aria-label="${planted ? "Unmark" : "Mark"} ${escapeHtml(crop.name)} as planted" ${state.saving.has(plantedId(key)) ? "disabled" : ""}><span aria-hidden="true">🌱</span>${planted ? "Planted" : "Plant"}</button>`;
|
||||
const date = planted
|
||||
? `On the way<small>planted</small>`
|
||||
: `${overdue ? "Passed" : `${seasonNames[crop.season]} ${crop.last}`}<small>${overdue ? "for this season" : "plant by"}</small>`;
|
||||
return `<div class="crop-row ${checked ? "complete" : ""} ${overdue ? "overdue" : ""} ${planted ? "planted" : ""}">
|
||||
<input type="checkbox" data-check-id="${key}" aria-label="Mark ${escapeHtml(crop.name)} complete" ${checked ? "checked" : ""} ${state.saving.has(key) ? "disabled" : ""}>
|
||||
<span class="checkmark" aria-hidden="true"></span>
|
||||
<span class="crop-name"><strong>${escapeHtml(crop.name)}</strong><small>${escapeHtml(crop.purpose)}</small></span>
|
||||
<span class="plant-slot">${plantButton}</span>
|
||||
<span class="grow-time">${crop.grow} days<small>to grow</small></span>
|
||||
<span class="plant-date ${today ? "today" : ""}">${overdue ? "Passed" : `${seasonNames[crop.season]} ${crop.last}`}<small>${overdue ? "for this season" : "plant by"}</small></span>
|
||||
</label>`;
|
||||
<span class="plant-date ${today ? "today" : ""}">${date}</span>
|
||||
</div>`;
|
||||
});
|
||||
$("#crop-list").innerHTML = rows.length ? rows.join("") : `<div class="empty-state"><span>❄</span><h3>No mandatory field crops</h3><p>Use Winter for the foraging bundle, mines, fishing, and any unfinished animal or artisan goods.</p></div>`;
|
||||
}
|
||||
|
|
@ -194,9 +207,10 @@
|
|||
return seasons.map(season => seasonNames[season]).join(" + ");
|
||||
}
|
||||
|
||||
function statusBadges(status) {
|
||||
function statusBadges(status, entry) {
|
||||
if (status.checked) return "";
|
||||
const badges = [];
|
||||
if (isPlanted(entry.id)) badges.push(`<span class="status-badge planted">🌱 Planted</span>`);
|
||||
if (!status.needed) badges.push(`<span class="status-badge optional">Bundle ready</span>`);
|
||||
else if (status.seasonExclusive) badges.push(`<span class="status-badge exclusive">Only ${seasonNames[state.season]}</span>`);
|
||||
else if (status.inSeason) badges.push(`<span class="status-badge in-season">In season</span>`);
|
||||
|
|
@ -217,8 +231,8 @@
|
|||
<div class="bundle-items">${items.map(entry => {
|
||||
const checked = Boolean(state.checks[entry.id]);
|
||||
const status = entryStatus(entry, group);
|
||||
const classes = [checked ? "complete" : "", status.needed ? "needed" : "optional", status.needed && status.availableNow ? "available-now" : "", status.seasonExclusive ? "season-exclusive" : "", status.weather ? "weather-specific" : ""].filter(Boolean).join(" ");
|
||||
return `<label class="bundle-item ${classes}"><input type="checkbox" data-check-id="${entry.id}" aria-label="Mark ${escapeHtml(entry.label)} complete" ${checked ? "checked" : ""} ${state.saving.has(entry.id) ? "disabled" : ""}><span class="checkmark" aria-hidden="true"></span><span class="item-copy"><strong>${escapeHtml(entry.label)}</strong><small>${escapeHtml(entry.meta)}</small>${statusBadges(status)}</span></label>`;
|
||||
const classes = [checked ? "complete" : "", status.needed ? "needed" : "optional", status.needed && status.availableNow ? "available-now" : "", status.seasonExclusive ? "season-exclusive" : "", status.weather ? "weather-specific" : "", isPlanted(entry.id) ? "planted" : ""].filter(Boolean).join(" ");
|
||||
return `<label class="bundle-item ${classes}"><input type="checkbox" data-check-id="${entry.id}" aria-label="Mark ${escapeHtml(entry.label)} complete" ${checked ? "checked" : ""} ${state.saving.has(entry.id) ? "disabled" : ""}><span class="checkmark" aria-hidden="true"></span><span class="item-copy"><strong>${escapeHtml(entry.label)}</strong><small>${escapeHtml(entry.meta)}</small>${statusBadges(status, entry)}</span></label>`;
|
||||
}).join("")}</div>
|
||||
</article>`;
|
||||
}).filter(Boolean).join("");
|
||||
|
|
@ -351,6 +365,8 @@
|
|||
$$("[data-filter]").forEach(button => button.setAttribute("aria-pressed", String(button.dataset.filter === state.filter)));
|
||||
renderBundles();
|
||||
}
|
||||
const plantButton = event.target.closest("button[data-plant-id]");
|
||||
if (plantButton && !plantButton.disabled) updateCheck(plantButton.dataset.plantId, !state.checks[plantButton.dataset.plantId]);
|
||||
const navLink = event.target.closest(".section-nav a");
|
||||
if (navLink) {
|
||||
$$(".section-nav a").forEach(link => link.classList.toggle("active", link === navLink));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue