Work · Government - agriculture, environment and water (GCC)

National Strategy Delivery Dashboard.

RoleSole builder - data modelling, bilingual UI, delivery
Period2026 - Q2
Statusv1 delivered to stakeholders; awaiting client feedback cycle
Scale~3,300 LOC · 5 screens · 9 commits (v1→v3) · sole author · synthetic data throughout
React 19ViteTypeScriptTailwind v4shadcn/Base UIRechartsreact-routerPython/openpyxl codegenAzure Static Web Apps

National Strategy Delivery Dashboard

Execution monitoring for a national agriculture strategy - bilingual Arabic/English, tracking strategic objectives against 2030 and 2034 targets alongside ~38 delivery initiatives with budget, timeline, and RAG status.


1. The problem

A national ministry needed to monitor execution of its multi-year agriculture strategy. Progress data lived in a working spreadsheet across three sheets in mixed Arabic and English, and leadership had no way to see, at a glance, whether the strategy was actually being delivered.

Two structural problems in the source data had to be solved before any UI work:

The objective-to-initiative mapping was wide-encoded. The mapping sheet expressed relationships as up to five repeated objective-ID columns per initiative row - with the same header text duplicated across those columns. This is a spreadsheet-native way of representing a many-to-many relationship, and it has to be normalised in code to (initiative → objective[]) before anything downstream can reason about it. Read naively, you get one arbitrary objective per initiative and quietly lose the rest of the strategy map.

Bilingual throughout. Objectives, indicators, segments, and entity labels all carry Arabic and English variants. This is not a translation layer bolted on at the end - it is a property of the data model, and the UI has to render both without either language becoming second-class.


2. Data model

Strategic objectives (~22 indicator rows): objective ID, objective and indicator in both languages, unit, baseline, target 2030, target 2034, current level, last update. Two target horizons rather than one, so progress is measured against the correct milestone rather than a single blurred endpoint.

Initiatives (~38 rows, 17 columns): bilingual objective/segment/entity labels, timeline start and end, budget to 2030, dashboard status, overall progress percentage, RAG status, owner, next milestone, and dependency/escalation notes.

Mapping: normalised in code from the wide five-column encoding into a proper many-to-many relation.

I extracted an authoritative schema document from the live workbook before building, so the type layer was derived from verified structure rather than assumption - the same discipline applied on the preceding portfolio-monitoring build.


3. Build approach - deliberate reuse and deliberate rebuild

This project ran immediately after a portfolio-monitoring dashboard in a different domain, and I made an explicit decision about what to carry over:

Layer Decision Reason
Frontend stack (React 19, Vite, Tailwind v4, component library) Reuse fully Proven, current, no reason to diverge
Cloud static hosting + enterprise single-tenant auth Reuse fully Identical deployment shape
Header / card / KPI tile primitives Reuse fully Rebrand palette only
Sparkline, delta pill, score-ring components Reuse partially Only where they genuinely fit
Domain types Rebuild Strategic objectives + initiatives ≠ assets + returns
Calculations Rebuild Target-progress percentage ≠ portfolio return
Visualisations Rebuild Progress bars + RAG status + objective mapping ≠ heatmaps + return timelines
Bilingual Arabic/English New Prior build was English-only

The judgement here is the transferable part: infrastructure and design system transfer; domain semantics do not. Reusing calculations across genuinely different domains is how you end up with a dashboard that looks right and means nothing.

Palette and visual language were rebuilt for the ministry’s domain rather than carried over from a financial-portfolio product.


4. Two things stated precisely

The data is synthetic, and generated rather than hand-written. A Python script parses a synthetic workbook and emits typed TypeScript literals marked auto-generated and not to be hand-edited. That is the right call - figures are plausible placeholders, the client’s real workbook never enters the repo, and the data layer regenerates from source rather than drifting by hand. But nothing on screen is a real ministry number.

The “AI Assistant” screen is a scripted demo, not an LLM. Answers are canned Arabic strings supplied verbatim by the requesting stakeholder, with streaming and a “thinking” indicator simulated in the UI. The code says so explicitly, and I’d rather record it here than let the label imply otherwise.

Defensible as a v1 in a 1–2 day build for a stakeholder conversation about what to build - the scripted answers were the client’s own words, so the screen accurately previews intended behaviour. But it is a clickable prototype of a chatbot, not a chatbot, and describing it as the latter would be a misrepresentation. The genuine engineering on this project is the bilingual RTL data model and the many-to-many normalisation, not the assistant.

5. What I’d highlight

  • Normalising a wide-encoded many-to-many relationship - the kind of quiet data-modelling trap that invalidates a whole strategy map if missed.
  • Bilingual RTL as a data-model property, not a late-stage translation pass.
  • Dual target horizons (2030 / 2034) modelled explicitly so progress measures against the right milestone.
  • Schema extracted and verified before building, so types derive from reality.
  • Explicit reuse-versus-rebuild reasoning, delivered as a documented decision table rather than an implicit assumption.
  • Fast turnaround - scoped and delivered as a 1–2 day v1 build, which is what made the client conversation possible at all.

6. Skills demonstrated

government strategy monitoring · bilingual (Arabic/English) interface development · many-to-many data normalisation · schema extraction and verification · RAG status and progress visualisation · React 19 / TypeScript / Tailwind v4 · design-system adaptation · Azure Static Web Apps · rapid delivery under stakeholder timelines

The first step

Twenty minutes. You describe where AI is stuck.

You leave the call knowing whether I can help, roughly what it would take, and what it would cost to find out for sure. If I am not the right person, I will say so on the call.

The 20 minutes, in order

  1. You talk first. Where AI is stuck, what has been tried, what it costs today.
  2. I answer plainly. Whether I can help, and what I would look at first.
  3. You leave with a next step. An audit scope, a pointer elsewhere, or a clean no.

Before you book

  1. Your stack is not too messy to start. Messy is the normal starting condition.
  2. Training that does not survive the week is the normal outcome. These sessions build on your backlog and ship something real.
  3. You do not need budget approved to take the call. You need it approved to start step two.