Skip to content
KW
Back to projects

sample 06/prototype

OrçaFácil

A metalwork quote built in minutes, with the platform suggesting and the metalworker deciding.

  • Next
  • Prisma
  • Zod
  • Motion

01

Context

OrçaFácil is an independent prototype inspired by a family metalwork workflow, with a simple goal: build a complete quote in minutes.

The scenario was modeled from a materials catalog and a realistic flow for adding each item to a quote.

02

Starting point

The prototype starts from a 15-category catalog and a seven-step flow, both defined before the first line of code.

15

iron and service categories in the catalog

7

steps in the original flow to add a material

4

steps in the final, condensed flow

03

Timeline

Milestones recorded during development.

  1. Sep 16, 2026

    Catalog and flow defined

    A catalog with 15 categories and the seven-step flow for adding a material.

  2. Sep 16, 2026

    Data model

    A schema ready for multiple companies from the start, with each quote line storing a snapshot of its values.

  3. Sep 17, 2026

    Clickable prototype

    Quote dashboard, editor and the four-step assistant with live calculation, still on simulated data.

04

Decision map

Reconstructed from the commit history, the README and the project documentation.

01

The platform suggests, the metalworker decides

Every calculated value can be overridden. Prices shift with the market, and whoever is in the shop knows the real value.

02

A quote is a snapshot

Each line copies its values at the moment it's created. Changing the catalog later doesn't alter a quote already sent.

03

Seven steps became four

The original flow was condensed into category, material, measurements and review, with the calculation appearing as you type.

04

Ready for many companies

Every table is created with the shop identifier. The prototype simulates one operation and can evolve to many without rebuilding the database.

05

Expired is a calculation, not a job

Validity is calculated from the issue date. There is no scheduled job just to flag expired quotes.

06

Made for the shop's lighting

A fixed light theme and a high-contrast primary button make the interface easier to read in different workshop conditions.

05

Architecture

The same five layers as this page, applied to the project.

  1. 000 m/interface

    • Quote dashboard
    • Four-step assistant with live calculation
    • Accessible dialogs and selects with Radix
  2. 060 m/service

    • Single calculation module: usage, waste, bars and totals
  3. 090 m/repository

    • Prisma schema with the company on every table
    • Quote line as a snapshot of values
  4. 120 m/persistence

    • Postgres, connected in the next phase

06

Code

An excerpt from the private repository, just to show the layer at work.

From measurement to purchased bar

The business rule lives in one place, and the interface only consumes it. Bars are bought whole, so rounding always goes up.

src/lib/calc.tsts
// Regra de ouro: a plataforma SUGERE, o serralheiro DECIDE. Estas funções
// calculam os valores propostos; os componentes deixam o usuário sobrescrever
// qualquer campo depois.

/** Consumo real de material somando a perda. Ex.: 14 m + 10% = 15,40 m. */
export function consumoComPerda(quantidadeConsumo: number, perdaPercent: number): number {
  const q = Math.max(0, quantidadeConsumo);
  const p = Math.max(0, perdaPercent);
  return q * (1 + p / 100);
}

/**
 * Quantas barras inteiras comprar pra cobrir o consumo com perda.
 * Compra-se barra fechada: por isso arredonda pra cima. Ex.: 15,40 m ÷ 6 m = 3.
 */
export function barrasNecessarias(consumoComPerdaM: number, comprimentoBarraM: number): number {
  if (!comprimentoBarraM || comprimentoBarraM <= 0) return 0;
  return Math.ceil(consumoComPerdaM / comprimentoBarraM);
}

/** Faz o caminho completo: consumo → perda → barras → subtotal (sugerido). */
export function calcularMaterial(e: EntradaCalculoMaterial): ResultadoCalculoMaterial {
  const comPerda = consumoComPerda(e.quantidadeConsumo, e.perdaPercent);
  const barras = barrasNecessarias(comPerda, e.comprimentoBarraM);
  return {
    consumoComPerda: comPerda,
    quantidadeCompra: barras,
    subtotal: barras * Math.max(0, e.precoUnitario),
  };
}

07

Outcome

4

steps to add an item

6

models in the data schema

100%

of values editable by the metalworker

Current status

Clickable prototype on simulated data. The next phase connects the database and replaces the simulation with real data.