cmorales.me

Coming soon

Something is taking shape here. Check back shortly.

Projects

Systems I've built

Internal software for a construction business — most of it lives in a private monorepo, so it's described rather than linked. Open any entry for the full story.

Internal Tools Platform

2025–
Power Platform · Architecture

A monorepo of React/TypeScript apps on Microsoft Power Platform — shared component library, automated CI, and Dataverse integration for a commercial construction company.

React 19TypeScriptVitePower PlatformDataverseVitestGitHub Actions

Designed and built a monorepo architecture for a suite of line-of-business apps at a mid-size commercial construction company. Each app is a standalone React SPA deployed to Power Platform as a Code App, sharing a common utility package for SDK bootstrapping, theming, offline resilience, and Dataverse type coercion.

Architecture

  • Monorepo with workspace dependencies — shared package resolved at build time via Vite aliases, no separate build step
  • Automated quality gate — GitHub Actions workflow runs typecheck, tests, and build on every push; auto-merges to main on green
  • Dataverse integration layer — type coercion helpers for the all-strings SDK, GUID normalization, OData filter builders, and a two-tier availability pattern with mock fallback for local development
  • Git-managed security roles — JSON specs checked into the repo, diffed and applied to Dataverse via a reconciler script

What made it interesting

Power Platform Code Apps are a relatively new capability — building production React apps on the platform means working within its SDK constraints while still writing modern TypeScript. The challenge was creating an architecture that feels like a normal React project but deploys to and integrates with the Power Platform ecosystem.

In pictures

Field Operations App

2025–
Power Platform · Field

A tablet-first app for field supervisors — crew management, daily logging, photo uploads, and Gantt scheduling, all with offline support.

ReactTypeScriptCSS ModulesDataversePower AutomateSharePoint

Built a three-pane tablet-optimized app for field supervisors to manage their day without needing cell signal. The app handles crew assignments, daily log entries, schedule visualization, and photo documentation — all resilient to the spotty connectivity typical on construction sites.

Key patterns

  • Three-pane layout — left navigation, center list, right detail panel. Responsive down to phone screens but designed for iPad-sized tablets
  • Offline-first architecture — in-memory queue captures writes when offline, flushes on reconnect with retry logic. Network status detection with timeout wrappers around SDK calls that hang indefinitely when connectivity drops
  • Photo upload pipeline — photos queue as Dataverse records, picked up by a Power Automate flow that moves them to SharePoint document libraries organized by date and job
  • Custom Gantt chart — SVG-based schedule visualization showing crew assignments across a timeline, with drag interaction for rescheduling
  • CSS Modules theming — full light/dark mode support via CSS custom properties, no runtime theme switching overhead

Offline challenge

The Power Apps SDK doesn’t have built-in offline support. When the device loses signal, SDK calls hang forever — no timeout, no error. The solution was a timeout wrapper (withTimeout) around every SDK call plus an in-memory queue that captures failed writes and replays them when the network comes back.

In pictures

Project Management App

2025–
Power Platform · Office

Unified PM tool for tracking contracts, budgets, change orders, T&M tickets, and subcontractor relationships across commercial construction projects.

ReactTypeScriptPower PlatformDataverseDocuSignPower BI

A unified project management app that replaced a patchwork of spreadsheets, emails, and manual tracking for a commercial drywall and finishes contractor. Covers the full lifecycle of a construction project — from initial contract through daily field operations to final billing.

Modules

  • Job dashboard — central view of all active projects with status, financial summary, and team contacts
  • Budget tracking — three-layer architecture (on-site budgets, internal change orders, labor budgets) that rolls up to a unified financial view
  • Change order workflow — COR creation, revision tracking, approval status, conversion to executed change orders
  • T&M tickets — time and materials documentation with labor and material line items
  • Subcontractor management — contract tracking, insurance compliance, job assignments

Three-layer budget architecture

The budget system uses three interconnected layers rather than a single flat budget table. On-site field budgets capture what’s happening in the field. Internal change orders track scope changes that affect cost. Labor budgets break down the work by category. Each layer syncs upward so the PM sees a single rolled-up number, but the source data maintains its granularity for auditing and reporting.

Subcontract issuance

A wizard walks coordinators through scoping a subcontract, then generates the actual Word contract document client-side in TypeScript (via the docx library) — no server round-trip, no template drift. A five-stage tracker follows the document through DocuSign signing, and compliance gating blocks issuance until the subcontractor has a current MSA, W-9, and certificate of insurance on file.

One platform constraint shaped this feature: Code Apps run under a strict Content Security Policy that blocks direct calls to external services, so documents route to SharePoint and DocuSign through Dataverse file columns and Power Automate flows — indirection imposed by the platform that turned a simple feature into a real design problem.

In pictures

Integration Backend

2025–
Azure · Integrations

An Azure Functions layer (~25 functions) keeping QuickBooks, DocuSign, timekeeping, and procurement in sync with the operational platform — built for idempotency and drift detection.

TypeScriptAzure FunctionsBicepKey VaultService BusQuickBooks APIDocuSign API

The connective tissue of the platform: a TypeScript Azure Functions app (~25 functions) that moves data between the operational system and the tools the business already runs on — QuickBooks Online for accounting, DocuSign for signatures, QuickBooks Time for payroll hours, and Kojo for material procurement.

The core of it is the QuickBooks Online sync. Accounting data has to reconcile exactly, so the sync is bidirectional and defensive: webhook-driven updates, idempotent writers so retries can’t double-post, a drift detector that compares systems and flags divergence, and an audit trail for every mutation. QBO stays the financial source of truth; the platform never overwrites it blindly.

Around that sits document work — T&M ticket PDFs assembled with pdf-lib, lien waiver overlays, change order package assembly — plus HMAC-signed intake links that let subcontractors submit onboarding paperwork through a public form without an account.

The whole thing is infrastructure-as-code with Bicep: Key Vault for secrets, Service Bus for queued work, OpenTelemetry into Application Insights for tracing. Contract tests replay recorded HTTP against the third-party APIs, and a schema-drift test fails the build if Dataverse changes underneath the code.

In pictures

Financial Data Pipeline

2026–
Automation · Finance

Webhook-driven sync between QuickBooks Online and Dataverse — real-time financial mirrors powering dashboards and automated billing.

Power AutomateDataverseQBO WebhooksCloudEventsPower BI

Designed a webhook-driven data pipeline that mirrors QuickBooks Online entities into Dataverse in near-real-time. The pipeline powers financial dashboards in Power BI and enables the PM app to show live cost and billing data alongside operational project data.

Architecture

A single dispatcher flow receives QBO webhook events and routes them to entity-specific child flows — one each for customers, vendors, invoices, bills, purchase orders, estimates, and journal entries. Each child flow handles creates, updates, and deletes for its entity type, with line items and linked transactions managed as child records.

CloudEvents migration

Migrated the webhook dispatcher from a legacy format to CloudEvents — a standardized envelope for event data. Built a shim layer so the new format could be processed alongside the old one during a parallel-run period. The migration was driven by a change in the accounting platform’s webhook API, not an elective refactoring choice.

Bridging financial and operational data

The trickiest part wasn’t the sync itself but mapping financial entities to operational ones. The accounting system’s “customers” don’t map 1:1 to construction projects — older records use a three-level hierarchy (GC → sub-customer → project) while newer ones are flat. The resolver had to handle both shapes.

In pictures

AI Developer Tooling

2026–
Developer Tooling · AI

Custom MCP servers that give AI coding assistants direct access to Dataverse, SharePoint, Teams, and Power Automate — turning enterprise systems into AI-accessible tools.

TypeScriptMCP ProtocolMSALMicrosoft Graph APIDataverse Web API

Built a set of custom Model Context Protocol (MCP) servers that connect AI coding assistants to the Microsoft enterprise stack. Instead of manually looking up schema definitions, checking SharePoint for documents, or navigating the Power Automate designer, the AI assistant can query these systems directly through structured tool calls.

Servers

  • Dataverse MCP — query tables, describe schema, create/update/delete records, run search. The AI can inspect live table structure before writing service code, eliminating schema guesswork
  • SharePoint MCP — list libraries and folders, search files, get file metadata. Useful for understanding document organization and finding reference files
  • Teams MCP — find teams by project ID, list channels and members. Enables the AI to understand team structure when building notification flows
  • Power Automate MCP — list, read, create, update, toggle, and delete cloud flows. The AI can inspect existing automation and create new flows through the API rather than the visual designer

Authentication pattern

Each server uses MSAL (Microsoft Authentication Library) with device code flow for initial setup, then caches tokens locally. The servers are configured via the repo’s .mcp.json so they activate automatically when a developer opens the project — no per-session setup after the initial auth.

Impact on development

The biggest win is schema accuracy. Before the Dataverse MCP server, writing service code meant guessing at column names and types, then fixing errors after deployment. Now the AI reads the live schema before writing a single line, and the code is correct on the first push.

In pictures

Git-Managed Security Roles

2026–
Infrastructure · Security

A declarative system for managing Dataverse security roles from version-controlled JSON specs — diff, review, and apply RBAC changes like code.

TypeScriptDataverse Web APIMSALNode.js

Built a reconciler that treats Dataverse security role definitions as code. Role specs live as JSON files in the repo — one per role — with a script that diffs the spec against live Dataverse and applies changes.

How it works

Each role’s JSON file declares the table-level privileges (Create, Read, Write, Append, AppendTo, Delete) and their depth (Business Unit, Organization, or User). A schema-names mapping file handles the translation between logical table names and Dataverse’s internal privilege naming.

The reconciler script:

  1. Reads the JSON spec
  2. Fetches the current role definition from Dataverse
  3. Diffs them
  4. Prints what would change (dry run by default)
  5. Applies with --apply flag

Why this approach

The Dataverse admin UI for security roles is a matrix of checkboxes — easy to misconfigure, impossible to review, no audit trail beyond “someone changed something.” Moving the definitions into git means role changes go through pull requests, get reviewed, and have a clear history.

Gotchas discovered

  • Organization-owned tables reject Business Unit depth — the API returns an unhelpful error. Had to detect table ownership type and enforce the right depth
  • Privilege naming is inconsistent — most custom tables use PascalCase (aspire_Job) but some use all-lowercase. The live privilege table is the only source of truth
  • Delete is intentionally excluded from most roles — company policy is soft deletes (deactivate records) to preserve audit trails
In pictures