Skip to content

Available for freelance work: custom websites and apps

Full-stack developer. I turn ideas into fast, accessible web products, ready for production.

Portrait of Jeremy Orellana wearing glasses and a dark jacket

Jeremy Orellana

Selected work

Live projects. Each one lists the problems I faced and how I solved them.

Vikoma agenda in Spanish, shown on a wide desktop screen with one column per professional and on a phone as a list of appointments.

Vikoma

A platform for appointment-based businesses in Ecuador, such as beauty salons, hair salons, nail studios, spas and barbershops. It replaces notebooks, chats and hand-balanced cash. Live in production: its first pilot barbershop has already signed up and set up its business.

Each business gets its own space, with its own team, services and clients. The owner signs in with Google and finishes setup in about 5 minutes, measured with the pilot barbershop. It includes conflict-free scheduling, inventory that drops automatically with each sale, cash flow and revenue reports, WhatsApp confirmations, a waitlist, reference photos with consent under Ecuador's data law, and separate permissions for the owner and the team. The agenda stays readable when the internet drops. I built it solo: money in whole cents, times in UTC and 2,679 automated tests that run in about 16 seconds, with ≈81% line coverage of business logic and repositories.

Problems

  1. 01One business could reference another's data. The tursodb engine silently ignored foreign keys.

How I solved them

  1. 01I confirmed it with an insert that had to fail and moved production to libSQL. Data goes through a single access point, with tests proving it never mixes. The app refuses to boot if foreign keys are not enforced.
Show 4 more problems and solutionsHide the extra problems and solutions

Problems

  1. 02Two clients must never book the same slot, even at the same moment.
  2. 03Two simultaneous checkouts could sell sold-out stock or repeat an invoice number.
  3. 04The OAuth sign-in check accepted any value. A hex parser read base64url input and returned empty buffers, so everything matched.
  4. 05A WhatsApp failure must not block a booking.

How I solved them

  1. 02Every booking rechecks the slot inside the database transaction, not just on screen.
  2. 03Invoice numbers come from an atomic UPDATE with RETURNING. A test with 25 concurrent requests demands 25 distinct numbers. Stock uses conditional updates and retries lock errors.
  3. 04A negative security test caught it. The comparison is now constant-time and hashes both sides first.
  4. 05The message is queued with the appointment and sent later, with retries. Without client consent, it never goes out.

Built with

  • Next.js 16
  • TypeScript
  • Turso
  • Drizzle
  • WhatsApp Cloud API
  • Cloudflare R2
  • Vitest
Three Tickets App screens: the waiting-room display board, the desk selection view and the ticket kiosk.

Tickets App

Kiosks, desks and display boards kept in sync over native WebSockets.

A turn-management system for service desks, with normal and preferential tickets. It pairs a React 19 client with a Bun and TypeScript server, and the backend runs in a Docker container.

Problems

  1. 01Kiosks, desks and display boards had to show the same queue state at the same time, without each component managing its own socket.

How I solved them

  1. 01The Bun server shares queue state with every client over native WebSockets (Bun.serve) and validates each incoming message with a Zod schema. A custom hook, useSocketTicket, wraps the socket, and a React Context exposes the queue state to every component.
Show 1 more problem and solutionHide the extra problems and solutions

Problems

  1. 02Kiosks and display boards had to recover on their own when the network connection dropped.

How I solved them

  1. 02Clients reconnect automatically with exponential backoff.

Built with

  • React 19
  • Bun
  • WebSockets
  • TypeScript
  • Zod
Three Luxe Estate screens: the property search home, a listing detail with a map, and the admin property table.

Luxe Estate

Property search with filters synced to the map, multilingual content and server-first rendering.

Full-stack, built on Next.js Server Components and Server Actions. Supabase handles data and auth, and Leaflet powers the map search.

Problems

  1. 01The Leaflet map and the server-side filters had to stay in sync.

How I solved them

  1. 01Filters run as server-side queries through Supabase PostgREST, which keeps the map and the result list consistent.
Show 2 more problems and solutionsHide the extra problems and solutions

Problems

  1. 02Favorites and sign-in felt slow when every action waited for a round trip to the server.
  2. 03Property pages had to load fast and be indexable by search engines.

How I solved them

  1. 02Server Actions validate their input strictly and pair with optimistic UI, so favorites and sign-in respond without waiting.
  2. 03Pages render with Server Components first, which sends less JavaScript to the browser and keeps content crawlable.

Built with

  • Next.js 16
  • Supabase
  • Tailwind CSS 4
  • Leaflet
  • i18n
Three Assembly: Endgame screens showing the game board, a win state with confetti and a game-over state.

Assembly: Endgame

An accessible word-guessing game with live feedback for screen readers.

Built in React 19. Each wrong guess removes a programming language from the board, and the whole game is playable with a keyboard and a screen reader.

Problems

  1. 01The game state (remaining languages, guesses, win and loss) changes on every key press and has to stay consistent.

How I solved them

  1. 01The game is split into small components (Status, LanguageChips, Keyboard), and each one renders a single part of the state.
Show 2 more problems and solutionsHide the extra problems and solutions

Problems

  1. 02Screen reader users needed to hear each guess and the final result without seeing the board.
  2. 03The on-screen keyboard had to match the guess history at all times.

How I solved them

  1. 02An aria-live region announces each guess and the final result to screen readers.
  2. 03The keyboard renders from the same game state as the status area, so it always matches the guess history.

Built with

  • React 19
  • CSS Grid
  • ARIA
  • Hooks

About

Base
Ecuador, remote
Focus
Full-stack web apps
Available for
Custom web and apps, freelance

I am Jeremy, a full-stack developer in Ecuador, working remotely. I take web products from the idea to production.

I like owning the whole path: the data model, the business rules and the interface. You work with one person who understands your business, replies quickly and delivers a tested, accessible product ready for production.

  • SaaS products

    Multi-tenant apps with owner and staff roles, data isolated per business, billing and cash reports, covered by automated tests.

  • Real-time systems

    Kiosks, desks and displays that share one state over WebSockets and reconnect after a network drop.

  • Accessible frontend

    React and Next.js interfaces that work with a keyboard and a screen reader, in several languages and with interactive maps.

  • Integrations

    WhatsApp Cloud API, Google sign-in, Supabase and file storage on Cloudflare R2.

Stack

The tools I use across my projects.

Frontend
  • React 19
  • Next.js 16
  • Astro
  • TypeScript
  • Tailwind CSS 4
  • Radix UI
Backend and data
  • Node.js
  • Bun
  • PostgreSQL
  • Supabase
  • Turso (libSQL)
  • Drizzle ORM
  • WebSockets
Tooling and delivery
  • Docker
  • Vitest
  • Playwright
  • Zod
  • Biome
  • Cloudflare R2

How I work

Built to be changed

I keep business rules in plain functions, apart from the framework, so the next person can change them safely.

Performance is a feature

Pages render on the server first and send less JavaScript to the browser, as in Luxe Estate.

Plan for failure

Reconnection, validation and clear error messages go in the first version. Vikoma refuses to boot if the database does not enforce foreign keys.

Automated checks

Vikoma uses ESLint, strict TypeScript and Vitest tests. This site uses Biome, strict TypeScript and Playwright end-to-end tests.

  • Biome
  • TypeScript strict
  • Vitest
  • Playwright

Let's work together

Tell me about the role or project you have in mind.

Email

jejorm8@gmail.com