Improve cost estimator controls and print view
This commit is contained in:
parent
b4a6cf6067
commit
545a341efb
2 changed files with 283 additions and 11 deletions
|
|
@ -9,6 +9,10 @@ server {
|
||||||
return 302 "https://events.neeldhara.com/?year=$1#events";
|
return 302 "https://events.neeldhara.com/?year=$1#events";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location = /gh-cost-estimator {
|
||||||
|
return 301 "https://events.neeldhara.com/gh-cost-estimator/";
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,14 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
<section class="calculator-page" aria-labelledby="calculator-title">
|
<section class="calculator-page" aria-labelledby="calculator-title">
|
||||||
<div class="calculator-heading">
|
<div class="calculator-heading">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Utility</p>
|
<p class="eyebrow screen-only">Utility</p>
|
||||||
<h1 id="calculator-title">Basic Calculator</h1>
|
<h1 id="calculator-title">
|
||||||
|
<span class="screen-title">Basic Calculator</span>
|
||||||
|
<span class="print-only">Guest House Cost Estimate</span>
|
||||||
|
</h1>
|
||||||
|
<p class="print-only print-report-note">
|
||||||
|
Rates include 18% GST. Accommodation rates are for one night.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="button button--secondary print-button" type="button" data-print-estimate>
|
<button class="button button--secondary print-button" type="button" data-print-estimate>
|
||||||
Print Cost Estimate
|
Print Cost Estimate
|
||||||
|
|
@ -79,10 +85,38 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
<th scope="col">Single Occupancy</th>
|
<th scope="col">
|
||||||
<th scope="col">Double Occupancy</th>
|
<span class="column-heading">
|
||||||
<th scope="col">Lunch</th>
|
Single Occupancy
|
||||||
<th scope="col">Dinner</th>
|
<button type="button" data-toggle-column="single" aria-label="Toggle single occupancy for all visible rows">
|
||||||
|
Toggle all
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<span class="column-heading">
|
||||||
|
Double Occupancy
|
||||||
|
<button type="button" data-toggle-column="double" aria-label="Toggle double occupancy for all visible rows">
|
||||||
|
Toggle all
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<span class="column-heading">
|
||||||
|
Lunch
|
||||||
|
<button type="button" data-toggle-column="lunch" aria-label="Toggle lunch for all visible rows">
|
||||||
|
Toggle all
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<span class="column-heading">
|
||||||
|
Dinner
|
||||||
|
<button type="button" data-toggle-column="dinner" aria-label="Toggle dinner for all visible rows">
|
||||||
|
Toggle all
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -176,6 +210,7 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
const totalCost = document.querySelector<HTMLElement>("[data-total-cost]");
|
const totalCost = document.querySelector<HTMLElement>("[data-total-cost]");
|
||||||
const totalNote = document.querySelector<HTMLElement>("[data-total-note]");
|
const totalNote = document.querySelector<HTMLElement>("[data-total-note]");
|
||||||
const printButton = document.querySelector<HTMLButtonElement>("[data-print-estimate]");
|
const printButton = document.querySelector<HTMLButtonElement>("[data-print-estimate]");
|
||||||
|
const columnToggleButtons = Array.from(document.querySelectorAll<HTMLButtonElement>("[data-toggle-column]"));
|
||||||
|
|
||||||
function getPeopleCount() {
|
function getPeopleCount() {
|
||||||
return Number(peopleInput?.value ?? 1);
|
return Number(peopleInput?.value ?? 1);
|
||||||
|
|
@ -199,6 +234,36 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
if (totalNote) totalNote.textContent = `For ${peopleCount} ${peopleCount === 1 ? "person" : "people"}.`;
|
if (totalNote) totalNote.textContent = `For ${peopleCount} ${peopleCount === 1 ? "person" : "people"}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVisibleRows() {
|
||||||
|
return rows.filter((row) => !row.hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnInputs(cost: keyof typeof rateMap) {
|
||||||
|
return getVisibleRows()
|
||||||
|
.map((row) => row.querySelector<HTMLInputElement>(`input[data-cost='${cost}']`))
|
||||||
|
.filter((input): input is HTMLInputElement => Boolean(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncColumnToggles() {
|
||||||
|
columnToggleButtons.forEach((button) => {
|
||||||
|
const cost = button.dataset.toggleColumn as keyof typeof rateMap | undefined;
|
||||||
|
if (!cost) return;
|
||||||
|
|
||||||
|
const inputs = getColumnInputs(cost);
|
||||||
|
const allChecked = inputs.length > 0 && inputs.every((input) => input.checked);
|
||||||
|
button.setAttribute("aria-pressed", allChecked ? "true" : "false");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncPrintValues() {
|
||||||
|
rows.forEach((row) => {
|
||||||
|
row.querySelectorAll<HTMLLabelElement>(".check-cell").forEach((label) => {
|
||||||
|
const input = label.querySelector<HTMLInputElement>("input[type='checkbox']");
|
||||||
|
label.dataset.printValue = input?.checked ? "Yes" : "No";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateTotal() {
|
function updateTotal() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
|
|
@ -212,6 +277,8 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (totalCost) totalCost.textContent = currencyFormatter.format(total);
|
if (totalCost) totalCost.textContent = currencyFormatter.format(total);
|
||||||
|
syncColumnToggles();
|
||||||
|
syncPrintValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
peopleInput?.addEventListener("input", () => {
|
peopleInput?.addEventListener("input", () => {
|
||||||
|
|
@ -234,7 +301,32 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
columnToggleButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const cost = button.dataset.toggleColumn as keyof typeof rateMap | undefined;
|
||||||
|
if (!cost) return;
|
||||||
|
|
||||||
|
const inputs = getColumnInputs(cost);
|
||||||
|
const shouldCheck = !inputs.every((input) => input.checked);
|
||||||
|
|
||||||
|
inputs.forEach((input) => {
|
||||||
|
const row = input.closest<HTMLTableRowElement>("[data-estimate-row]");
|
||||||
|
|
||||||
|
if (shouldCheck && (cost === "single" || cost === "double")) {
|
||||||
|
row?.querySelectorAll<HTMLInputElement>("[data-accommodation]").forEach((accommodationInput) => {
|
||||||
|
if (accommodationInput !== input) accommodationInput.checked = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
input.checked = shouldCheck;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateTotal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
printButton?.addEventListener("click", () => {
|
printButton?.addEventListener("click", () => {
|
||||||
|
syncPrintValues();
|
||||||
window.print();
|
window.print();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -261,6 +353,10 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
font-size: clamp(2.2rem, 5vw, 3.4rem);
|
font-size: clamp(2.2rem, 5vw, 3.4rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.print-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.calculator-shell {
|
.calculator-shell {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
@ -371,6 +467,34 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-heading {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.36rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-heading button {
|
||||||
|
width: fit-content;
|
||||||
|
min-height: 32px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--card);
|
||||||
|
color: color-mix(in oklch, var(--foreground) 70%, var(--muted));
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0.35rem 0.58rem;
|
||||||
|
box-shadow: 0 0 0 1px var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-heading button[aria-pressed="true"] {
|
||||||
|
background: color-mix(in oklch, var(--primary) 11%, white);
|
||||||
|
color: var(--foreground);
|
||||||
|
box-shadow: 0 0 0 1px color-mix(in oklch, var(--primary) 24%, var(--border));
|
||||||
|
}
|
||||||
|
|
||||||
.calculator-table td {
|
.calculator-table td {
|
||||||
padding: 0.6rem 0.7rem;
|
padding: 0.6rem 0.7rem;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
@ -481,6 +605,10 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
|
@page {
|
||||||
|
margin: 16mm 14mm;
|
||||||
|
}
|
||||||
|
|
||||||
.site-header,
|
.site-header,
|
||||||
.site-footer,
|
.site-footer,
|
||||||
.print-button,
|
.print-button,
|
||||||
|
|
@ -488,8 +616,20 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.screen-only,
|
||||||
|
.screen-title {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
|
color: #151812;
|
||||||
|
font-size: 11pt;
|
||||||
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calculator-page {
|
.calculator-page {
|
||||||
|
|
@ -497,47 +637,175 @@ const currency = new Intl.NumberFormat("en-IN", {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calculator-heading {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid #20231d;
|
||||||
|
padding-bottom: 0.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-heading h1 {
|
||||||
|
color: #151812;
|
||||||
|
font-size: 24pt;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-report-note {
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
color: #50554c;
|
||||||
|
font-size: 9.5pt;
|
||||||
|
}
|
||||||
|
|
||||||
.calculator-panel {
|
.calculator-panel {
|
||||||
|
border-radius: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 0.5rem 0;
|
padding: 0.35rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calculator-shell {
|
.calculator-shell {
|
||||||
gap: 0.45rem;
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-control,
|
||||||
|
.table-head {
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-control h2,
|
||||||
|
.table-head h2 {
|
||||||
|
color: #151812;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-control p,
|
||||||
|
.table-head p,
|
||||||
|
.total-bar p {
|
||||||
|
color: #50554c;
|
||||||
|
font-size: 9pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-count {
|
||||||
|
min-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #151812;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rate-grid {
|
.rate-grid {
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 0;
|
||||||
|
margin-top: 0.45rem;
|
||||||
|
border: 1px solid #d6d8d1;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-grid div {
|
||||||
|
border-radius: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: none;
|
||||||
|
border-right: 1px solid #d6d8d1;
|
||||||
|
padding: 0.35rem 0.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-grid dt {
|
||||||
|
color: #50554c;
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-grid dd {
|
||||||
|
color: #151812;
|
||||||
|
font-size: 10.5pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calculator-table-wrap {
|
.calculator-table-wrap {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
box-shadow: 0 0 0 1px #ddd;
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calculator-table {
|
.calculator-table {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
border: 1px solid #d6d8d1;
|
||||||
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calculator-table th,
|
.calculator-table th,
|
||||||
.calculator-table td {
|
.calculator-table td {
|
||||||
padding: 0.35rem 0.4rem;
|
border-bottom: 1px solid #d6d8d1;
|
||||||
|
padding: 0.26rem 0.34rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-table th {
|
||||||
|
background: #f4f2ec;
|
||||||
|
color: #30342d;
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-heading {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-heading button {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-cell span {
|
.check-cell span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.check-cell {
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
justify-content: center;
|
||||||
|
color: #151812;
|
||||||
|
font-size: 9pt;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-cell input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-cell::after {
|
||||||
|
content: attr(data-print-value);
|
||||||
|
}
|
||||||
|
|
||||||
.name-input {
|
.name-input {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
color: #151812;
|
||||||
|
font-size: 9pt;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-bar {
|
.total-bar {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #f8f6ef;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #d6d8d1;
|
||||||
|
padding: 0.45rem 0.55rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-bar span {
|
||||||
|
color: #50554c;
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-bar strong {
|
||||||
|
color: #151812;
|
||||||
|
font-size: 20pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
break-inside: avoid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue