feat: add interactive cost calculator to the hosting costs page#2101
feat: add interactive cost calculator to the hosting costs page#2101jkuester wants to merge 81 commits intomedic:mainfrom
Conversation
…lator widget to improve logical flow.
…ed breakdown section. Adjust layout for improved readability.
…proved readability and maintainability.
… update labels for clarity.
…cing and alignment
…te instance load ranges and adjust user count limits for consistency.
…r. Update instance load range for c6g.8xlarge.
…r. Update related calculations and layout.
…lations for improved clarity.
|
wow! a lot of hard work put in here - thanks. I'm a bit backed up but hope to get to this this week! |
|
Thanks! No rush. 👍 Also forgot to include this link to the raw data and calculations that I collected from Watchdog. |
There was a problem hiding this comment.
This is great! Thank so much for tending all my feedback - it just works! I think the optimizations you've made make the tool much easier to read and more accessible for the high level planners that might use it. While I have some suggestions, I don't think they're blocking and will approve now assuming someone will bust out the code review while I'm out next week and we can ship.
I'll be happy to do a deeper dive either if this PR is still open when I get back or on the live code in a separate issue/PR as needed.
The biggie that I didn't have time to dive into the calculations. They feel right, but I didn't do any sanity checks. Before going live we should still have Elijah/Loukman/Philip do this sanity check.
I think we should move the Calculation details section right below the calculator and shift all the other sections as is down so it's followed by Accuracy, prod vs dev etc.. Really, the calculator is what people will be here for and we want to answer questions about how we came to our numbers right away.
It might be nice to have thousandth separators? I suspect that's US centric, so no biggie, but $11,020/yr is so much easier for me to parse than $11020/yr. No biggie if others disagree and we don't add it.
I still think it's weird that when you do something silly like 1.5M people, 1 workflow, 10 users, you have 156k people per user, but with a max of 250. Same for docs per people (370k max of 20k). Maybe remove the small numbers (250 & 20k)? I don't feel strongly, but feel like it might open us up to unneeded scrutiny.
Maybe make this change? Seems simpler, but I don't feel strongly:
I'd also consider removing the base 50GB somewhat like it was before - but - meh - not that big of a deal.
|
Thanks for the feedback @mrjones-plip! I will have a look at it next week. (Enjoy your week away! 🎉 )
Yeah, I have gone back and forth on this. While the current setup allows you to key in some weird values, I am not sure there are any better alternatives. 🤷 If we locked it down so one slider value depended on the other sliders (have to add more users if you want more pop) then the user might need to flip back and forth adjusting sliders multiple times just to dial in their desired numbers... Instead I have tried to set reasonable(ish) min/max on the ranges and then I am thinking the "People per user" and "Docs per user" indicators will provide some feedback to the user about how reasonable their setup is. (Basically, let them key in a silly setup, but then show them it is silly.) Happy to iterate on this more, though, if we can come up with something that makes more sense. |
Good call! If there is one thing that JS is good at, it is making it easy to render content in the user's current locale. I just updated the code to format all the number/currency values according to the user's current locale. So, everyone should see the delimiters as they expect. |
Reading this again, I think I see your actual point here about the small numbers on the range bars. 😅 I agree that they are actually unnecessary and have the potential to be confusing when your actual value goes outside the range. I will remove them! |
Def open to discussing this more. The main reason I felt it was useful was because especially on the smaller instances, the disk cost estimate was not realistic. As I understand it, on a normal EC2 instance, I think you are billed the same for your root host volume as you are for EBS ( |
Co-authored-by: mrjones <8253488+mrjones-plip@users.noreply.github.com>
|
@sugat009 can I get a review of the code implementation? 🙏 This has largely been implemented with Claude, but I have gone through line-by-line to cleanup/review the code and am happy with the quality of the result! The main focus was to stick to vanilla js/html/css without requiring additional libs. |
|
@eljhkrr, when you get the chance, can you help me with two things?
|
| const deploymentAge = Number.parseFloat(els.deploymentAgeValue.value); | ||
| const workflowCount = Number.parseFloat(els.workflowCount.value); | ||
| const populationCount = Number.parseFloat(els.populationCount.value); | ||
| const userCount = Number.parseFloat(els.userCountInput.value); |
There was a problem hiding this comment.
parseFloat vs parseInt inconsistency for userCount
userCount is parsed with Number.parseFloat here (line 55), but with Number.parseInt in updateOutputElements (line 103). Since userCount is always an integer (slider step="10", type="number"), these will usually produce the same result — but if a user types a decimal, they will diverge.
Use Number.parseInt consistently for all integer quantities (workflowCount, populationCount, userCount), or use Math.round(Number.parseFloat(...)) for all of them.
| {{< callout type="warning" >}} | ||
| Be sure to read the [Accuracy section](/hosting/cht/costs/#accuracy) so you understand what the costs on these page mean for your deployment. | ||
| {{< /callout >}} | ||
| {{< cost-calculator >}} |
There was a problem hiding this comment.
Minor style guide fixes
A few small items per the CHT style guide:
- Double spaces after periods on lines 88 and 90 — should be single spaces
e.g.on line 90 → use "such as" (style guide: "For example" not "e.g.")- Extra space in heading
## Production vs Development(line 92) — two spaces after## - Spelling:
targetting→targeting(line 108)
| <input type="range" class="calc-input" data-calc="user-count-input" min="10" max="15000" value="1000" step="10"> | ||
|
|
||
| <div class="calc-divider"> | ||
| <a href="#" data-calc="show-advanced" class="calc-link" onclick="event.preventDefault();">Advanced →</a> |
There was a problem hiding this comment.
Accessibility & Separation of Concerns
These <a href="#"> elements with inline onclick="event.preventDefault();" have two issues:
-
Separation of concerns: The
preventDefaultis split between the HTML template and the JS (attachListenersalready wires click handlers viadata-calcselectors). A future developer reading the JS would not knowpreventDefaultis already handled in the HTML. -
Accessibility: Screen readers announce
<a href="#">as links to the page top. Since these trigger in-page behavior with no navigation target, they should be<button type="button">elements styled to match the current link appearance.
Suggestion:
<button type="button" data-calc="show-advanced" class="calc-link">Advanced →</button>Then add e.preventDefault() in the JS click handler in attachListeners (or remove it entirely since buttons do not navigate).
| ); | ||
|
|
||
| els.popPerUser.textContent = m.popPerUser.toFixed(); | ||
| const popPct = updateRangeMarker(els.popPerUserMarker, m.popPerUser, 1, 250); |
There was a problem hiding this comment.
Magic numbers not in DEFAULTS
The bounds 250 (max people per user) and 20000 (line 121, max docs per user) are hardcoded here but have no corresponding entry in the DEFAULTS object at the top of the file. If these values need tuning (the same way USERS_PER_CPU and MEDIC_DOCS_PER_GB were derived from production data), they are invisible to future maintainers.
Suggestion: add them to DEFAULTS:
MAX_POP_PER_USER: 250,
MAX_DOCS_PER_USER: 20000,| {{ $js := resources.Get "js/cost-calculator.js" }} | ||
| <script src="{{ $js.RelPermalink }}"></script> | ||
|
|
||
| <div class="cht-cost-calculator calc-grid" id="calc-container-{{ $id }}"> |
There was a problem hiding this comment.
Unused CSS class calc-grid
The class calc-grid is applied here but is never defined in cost-calculator.css. The grid layout is defined on .cht-cost-calculator instead. This looks like a leftover from an earlier iteration — either remove it or add the corresponding CSS rule.
Description
This is a little present for @mrjones-plip when he returns from the holidays! 🎄
It is a first draft of an interactive Hosting Cost estimation tool (forum thread).
It turns out it is pretty easy to add interactive HTML to your Hugo site via a custom shortcode. And, the modern CSS functionality provide very nice layout and visualization so I did not have to resort to any dependencies.
I am pretty happy with the features/layout/functionality of the whole thing, but the tuning of it could use more eyes.
One simplification that I have started with (but which could be easily refactored) is that I have only factored in the costs of 5 example EC2 instances and I try to pick one of those based on load (users * workflows). This approach has obvious limitations, but it was simple enough to let me get started with real world numbers. Definitely open to suggestions on the best way to improve this.
The other things that need more tuning are the various constant value that are used in down-stream computations:
DISK_COST_PER_GBis a rounded-up estimate from EBS.CONTACTS_PER_PLACEis roughly the "household size", but it gets used to estimate how many ancestor contacts are in the hierarchy tree (given the user input of the population size). Currently the logic assumes a completely even distribution and density of contacts throughout the tree.WORKFLOW_YEARLY_DOCS_PER_CONTACTis by far the most hand-wavy. Basically I need some way to connect how many workflows are being supported by the instance with an estimate of how many reports will be generated per year. So, in this case12means that I think we will have an average of 1 report created per month per workflow per contact. This may be way off.DOCS_PER_GBis a rough estimate that I made based on Watchdog data from a large production instance.Most of this code was written (or heavily influenced) by Claude, but I have carefully reviewed and edited it for maximum maintainability.
License
The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.