| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- const profits: Promise<Profit[]> = (async function () {
- const response = await fetch('roi.json');
- return await response.json();
- })();
- const lowVolume = document.querySelector('#low-volume') as HTMLInputElement;
- async function render() {
- const formatDecimal = new Intl.NumberFormat(undefined,
- {maximumFractionDigits: 2, maximumSignificantDigits: 6, roundingPriority: 'lessPrecision'}).format;
- const formatWhole = new Intl.NumberFormat(undefined, {maximumFractionDigits: 0}).format;
- const tbody = document.querySelector('tbody')!;
- tbody.innerHTML = '';
- for (const p of await profits) {
- const volumeRatio = p.output_per_day / p.average_traded_7d;
- if (!lowVolume.checked && volumeRatio > 0.05) {
- continue;
- }
- const tr = document.createElement('tr');
- const profit_per_area = p.profit_per_day / p.area;
- const break_even = p.profit_per_day > 0 ? p.capex / p.profit_per_day : Infinity;
- tr.innerHTML = `
- <td>${p.output}</td>
- <td>${p.expertise}</td>
- <td style="color: ${color(profit_per_area, 0, 300)}">${formatDecimal(profit_per_area)}</td>
- <td><span style="color: ${color(break_even, 30, 2)}">${formatDecimal(break_even)}</span>d</td>
- <td style="color: ${color(p.capex, 300_000, 50_000)}">${formatWhole(p.capex)}</td>
- <td style="color: ${color(p.cost_per_day, 40_000, 1_000)}">${formatWhole(p.cost_per_day)}</td>
- <td style="color: ${color(p.logistics_per_area, 2, 0.2)}">${formatDecimal(p.logistics_per_area)}</td>
- <td>
- ${formatDecimal(p.output_per_day)}<br>
- <span style="color: ${color(volumeRatio, 0.05, 0.002)}">${formatWhole(p.average_traded_7d)}</span>
- </td>
- `;
- const output = tr.querySelector('td')!;
- output.dataset.tooltip = p.recipe;
- tbody.appendChild(tr);
- }
- }
- function color(n: number, low: number, high: number): string {
- // scale n from low..high to 0..1 clamped
- const scale = Math.min(Math.max((n - low) / (high - low), 0), 1);
- return `color-mix(in oklch, #0c8 ${scale * 100}%, #f70)`;
- }
- const main = document.querySelector('main')!;
- const popover = document.querySelector('#popover') as HTMLElement;
- main.addEventListener('mouseover', (event) => {
- const target = event.target as HTMLElement;
- if (target.dataset.tooltip) {
- popover.textContent = target.dataset.tooltip;
- const rect = target.getBoundingClientRect();
- popover.style.left = `${rect.left}px`;
- popover.style.top = `${rect.bottom}px`;
- popover.showPopover();
- }
- });
- main.addEventListener('mouseout', (event) => {
- const target = event.target as HTMLElement;
- if (target.dataset.tooltip)
- popover.hidePopover();
- });
- lowVolume.addEventListener('change', render);
- render();
- interface Profit {
- output: string
- recipe: string
- expertise: string
- profit_per_day: number
- area: number
- capex: number
- cost_per_day: number
- logistics_per_area: number
- output_per_day: number
- average_traded_7d: number
- }
|