UX/UI Assessment
This assessment walks the running Tarsco Bolted Tank application with an estimator and sales lens rather than scanning code alone. The foundations are strong. The opportunity is not visual cleanup. It is interaction quality and role fit.
Read this application as one shared tool serving two distinct audiences. Estimators are engineers: technically savvy, comfortable with domain jargon, and the owners of the pricing formulas. Sales are roughly half of daily users, less technical, and they live in the post-lock stages, the proposal, scope of work, and delivery. The product was built as an engineering tool, but half the people using it every day are not engineers. That single tension shapes the highest-leverage recommendations below.
The visual layer is in good shape: a clean Optics token system, few hardcoded values, and a polished, on-brand customer Proposal. Those are assets to protect. The work that matters most sits in interaction: how the app starts a task, reports progress and errors, keeps a user safe from a destructive click, and who it quietly leaves out.
The single most important recommendation: treat estimators and sales as two audiences of one shared tool, beginning with a role and stage aware estimate view and a validated sales workflow. In parallel, a cluster of low-effort, high-trust fixes and a broad accessibility gap are worth addressing regardless of appetite.
The review combined breadth and reality. Four parallel code-scan agents swept the codebase, then every candidate finding was walked live in the running application against seeded data, under an estimator and sales user lens.
The live walkthrough is what separates this report from a static scan. Walking the estimate and tank edit forms, setting an estimate to processing and triggering a guarded action, and deleting a comment in the running app corrected two findings that a code scan alone would have reported incorrectly. Those corrections are documented in full later in this report.
Three roles use the application. Understanding who they are, and where they work in the lifecycle of an estimate, is the frame for every finding that follows.
Effectively a senior estimator. Full access, owns configuration and pricing rules.
An engineer. Technically savvy, owns the pricing formulas, and builds the estimate through the active stages.
Less technical. Works the post-lock stages: proposal, scope of work, deliver to customer.
A representative is an external commissioned rep, not an application user. Estimator and sales volume are roughly equal day to day, which is the crux: the tool is engineering-first, but about half of the people in it are not engineers.
An estimate moves through sixteen statuses. The pipeline locks at bid_review: everything before it is active estimating work, and everything from the lock onward is sales-facing and no longer editable in the same way. Estimators own the stages before the lock; sales own the stages after it.
The estimate view is identical for both roles at every status. It interleaves estimator knobs (margin, contingency, commission, field wage) with sales and customer data on one undifferentiated sheet. Domain jargon such as hoppers, portal frames, rings, and BOM, BOL, and RFQ is acceptable for estimators but raises the barrier for sales. The product never adapts to who is looking or what stage the estimate is in. That is the root cause behind several findings, and the reason a role and stage aware view is the lead recommendation.
Seventeen findings, grouped into four themes. Each carries a severity label, the observation, the user impact, and a file and line reference. Confidence is noted on every finding: verified live in the running app, confirmed in code, or, for the one strategic finding, flagged as needing validation.
Observation. The dashboard is home for both roles, yet it offers no New Estimate or New Site action, and its empty state is a dead end.
User impact. A user lands with nowhere obvious to begin. The primary task, building an estimate, has no visible entry point from the first screen.
Observation. The estimate view interleaves estimator knobs (margin, contingency, commission, field wage) with sales and customer data on one undifferentiated sheet, identical for both roles and every status.
User impact. Sales users wade through engineering controls they never touch, and estimators get no emphasis on what matters at their stage. One layout serves neither audience well.
Observation. There is no inline validation, and HTML5 validation is globally disabled (browser_validations=false). An error on a long form costs a full reload and a scroll-hunt for the offending field.
User impact. Correcting a single mistake means resubmitting the whole form and losing your place. The cost compounds on the longest, most detailed forms.
Observation. Tank#name carries only a uniqueness check, no presence validation. Clearing the name and saving produces a nameless tank.
User impact. Blank rows appear in lists, making tanks hard to tell apart and easy to mis-select.
Observation. Assemblies are added in a modal, while tanks and estimates force full-page navigation. There is no single "add a child" interaction model.
User impact. The interaction is unpredictable. Users cannot build a reliable habit for adding related records.
Observation. A static "Processing, please wait" message shows with no spinner. All actions stay enabled, and guarded actions are refused only after a click, via a transient alert.
User impact. Users cannot tell whether work is happening or how long it will take, and they only learn an action is blocked after attempting it.
Observation. The app's primary output renders as unstyled placeholder text with no skeleton or aria-busy state, and calculation failures render "Calculation Error" with no cause and no retry.
User impact. The most important number in the app appears as plain "Loading" text, and when it fails the user has no way to understand why or recover.
Observation. There is no dismiss control and no persistent variant. Success and error differ only by color, and all messages use role="alert".
User impact. An error message can vanish before it is read, and a colorblind user cannot distinguish a failure from a success.
Observation. Comment delete is a plain delete with no turbo_confirm, while every other delete in the app confirms first. It is one click and permanent.
User impact. A single misclick destroys a comment with no prompt and no undo.
Observation. A trailing if @companies.any? suppresses the helper's built-in empty state, so an index with no records shows nothing at all.
User impact. A user with no data sees a blank page and no guidance, and cannot tell whether the app is broken or simply empty.
Observation. Deletes prompt with a generic "Are you sure?" while duplicate and revise actions already name the record.
User impact. The most destructive action gives the least context, exactly where naming the record would prevent a mistake.
Observation. The daily workflow entry, Sites, is visually identical to eleven reference and configuration screens. Site, Estimate, and Tank all light up the single Sites link, and there is no global search or recents.
User impact. Users cannot separate work from configuration at a glance, cannot tell how deep they are, and have no fast path back into a recent estimate.
Observation. The resource_table helper emits a bare table, and only a handful of views opt into a scroll wrapper. Application-level breakpoints appear almost exclusively in the sidebar stylesheet.
User impact. Wide pricing tables clip on smaller screens, and the main content does not adapt away from a desktop width.
Observation. There are zero ARIA attributes across 338 views. The icon helper renders Material Symbols ligature text as content, so screen readers announce "edit_note", and the title attribute defaults to the raw icon name.
User impact. Screen-reader users hear icon codenames instead of actions, and sighted users see tooltips built from internal names.
Observation. No custom Stimulus controller handles keydown, focus, or tabindex. The :focus-visible style exists only on buttons, so tabs, links, selects, and menu triggers show no focus. There is no main landmark and no skip-nav.
User impact. A keyboard-only user cannot see where focus is or operate custom controls, and cannot skip repeated navigation.
Observation. Usable volume below the customer minimum is shown as red text only, with no icon and no label.
User impact. A colorblind user, or anyone scanning quickly, can miss that a value is out of spec, on a number that drives the estimate.
Observation. The hand-rolled index search forms rely on placeholder text with no associated label. The main resource forms use simple_form correctly.
User impact. Screen-reader users get no name for the search field, and the placeholder disappears once typing begins.
Two findings from the initial code scan were wrong, and the live walkthrough caught them. They are documented here in full rather than dropped silently, because knowing what the app does not do is as valuable as knowing what it does.
The work sorts into three effort tiers plus one discovery track. The quick wins are workflow-independent and worth doing regardless of appetite. The role-aware view and the accessibility sweep are the substantial investments. The sales-workflow discovery is a prerequisite, not a build.
Low-effort, high-trust fixes. Each is small and independent, and together they remove the sharpest edges a daily user hits.
if @companies.any? and let the helper render its built-in empty state. A one-line change.data: { turbo_confirm: ... } the rest of the app already uses, so a comment is not destroyed on a single misclick.The lead structural investment. Make the estimate view aware of who is looking and what stage the estimate is in. Group estimator knobs (margin, contingency, commission, field wage) separately from sales and customer data, and emphasize what each role needs at each stage. Surface report generation, a top sales goal, as a first-class action rather than an item buried in a kebab menu. Add a New Estimate and New Site call to action to the dashboard so the primary task has a visible starting point.
A focused pass that closes the broad accessibility gap. The individual changes are small; the value is in doing them together across the app.
Observation. About half of daily users are sales, yet the sales-facing experience has never been checked against how sales actually work.
Recommendation. Run a short discovery session with one or two sales users before building a sales landing: what they check on landing, when they generate versus send the proposal, and what is missing. A best-guess redesign can be sketched, but only as a hypothesis to confirm with sales first.
| Track | Addresses | Effort |
|---|---|---|
| Quick wins | Blank empty state, comment-delete confirm, flash dismiss and persistence, delete copy, blank tank name | Minutes to a few hours each |
| Role-aware estimate view | One-screen-two-jobs, no dashboard CTA, buried reports | Substantial, structural |
| Accessibility sweep | Icon names, keyboard and focus, color-only status, search labels, table scroll | Moderate, best done together |
| Sales-workflow discovery | Unvalidated sales UX | One short session first |
The quick wins can begin immediately and independently. The role-aware view and the accessibility sweep are the investments worth planning around. Before either touches the sales experience, one conversation should come first.
We would welcome the chance to walk through these findings together and shape the sequence around what matters most to your team.