Hazem Salama
Hazem Salama
From the receipt voucher to the printed report — one bilingual system for the whole stock operation.
EMTCloud runs the stock operation of a trading business end to end: items and categories, multiple warehouses, receipt and issue vouchers, transfers, and reporting — every movement accounted for, every screen editable in place. The interface is Arabic-RTL-first and mirrors completely into English. Built solo on ASP.NET Core 9 + EF Core with a Next.js 16 / React 19 dashboard: 110 API endpoints, 17 production screens, ~131k lines of code.
110
API endpoints
17
Production screens
2
Languages, one UI
131k
Lines of code

The problem
Small and mid-size trading companies in the region mostly run their inventory on scattered Excel files: no permissions, no movement history, and no way to print an official, consistent document. The big off-the-shelf systems are expensive and complex — and their Arabic interfaces are usually a cosmetic translation laid over an English design.
EMTCloud was built to be the right middle: a complete system for stock, vouchers, and reports; light to operate; Arabic from the first line of code; and fast enough at data entry that a clerk never opens a single window all day.
The approach
Every data screen is a live grid: click a cell and it becomes an input, Enter saves, Esc cancels. New records are typed into a bottom entry row; delete arms the row itself into a red confirm bar. Zero popup windows across the whole system.
A role matrix of read / create / update / delete for every module. Menus, buttons, and the backend all read the same matrix — what a user cannot do, they never see.
The UI is built on logical CSS properties only, so it mirrors completely between Arabic and English: 1,234 translation keys per language, with IDs, numbers, and dates direction-isolated so the Arabic text never breaks them.
A drag-and-drop designer with header, content, and footer sections and live data fields. The server renders PDF, Excel, and Word from the very same template.
14
API controllers
20
Domain services
2
Languages, one UI
0
Popup windows in data screens
The signature interaction
The hard part was not drawing a pretty table — it was making a single editing pattern carry everything from a two-field lookup to a twenty-line voucher. A shared descriptor layer defines the grammar once; 17 screens inherit the same behavior. The four states below are the same categories screen, captured live.

One click turns any cell into a focused field. Enter saves, Esc cancels.

Enumerated values open a small menu under the cell itself and save immediately.

Adding is typing into the last row of the table; Enter creates it and re-focuses the row.

The row becomes its own confirmation — and shows the server’s error inline if the delete is refused.
What doesn't fit in a cell opens under the row: the item’s image and barcode table, a voucher’s line items as a sub-grid, a user’s password reset, a role’s full permission matrix. The record expands in place — the screen never navigates away and never opens a window.
Inside the product
Real screens from the running system with live data, in the order a user meets them: sign in, master data, stock, vouchers, reports, and the people who run it.
Workflow 01
A dark, branded front door; after login a module launcher shows only what the role allows.


Workflow 02
The item master with an expanding editor, live balances with reorder limits, and a per-item movement log.


Workflow 03
Every stock movement is a document: suppliers on receipt, customers on issue, lines edited as a sub-grid.


Workflow 04
Parameterised reports that export PDF or Excel — printed on templates the client designs inside the product.


Workflow 05
Team members and roles are grids too — and the permission matrix itself opens under the role’s row.


Arabic ⇄ English
Every capture on this page shows the Arabic-RTL interface, because that is how the product is used — but the English mirror is the same interface, flipped by the language switcher on the login screen. Not a second theme, not a partial translation: the same 17 screens, the same grid grammar, reversed correctly.
The whole layout is written in direction-agnostic CSS — ps- / pe- / ms- / me- / start- / end- — so flipping the locale flips the interface, with a guard layer in globals.css for stray Latin glyphs.
IDs, numbers, dates, and emails sit in dir="ltr" isolates inside the Arabic flow — no truncated digits, no flipped brackets, on any screen.
next-intl with ar.json and en.json: 1,234 leaf keys each, counted — the English mirror is complete, not partial.
Items, categories, units, and warehouses each carry an Arabic and an English name as first-class columns, so documents print correctly in either language.
Architecture
A Next.js 16 frontend over an ASP.NET Core 9 API in a clean architecture of four projects — business logic separated from data access and from the web layer, with company isolation enforced by EF Core global query filters in the data layer itself.
Frontend · Next.js 16
Web · InventorySystem.API
Application + Core
Infrastructure & data
Saving one cell, end to end: the grid sends a PUT with the full entity; the request passes JWT auth, is routed to the controller, delegated to the domain service, and checked against the role’s Update privilege on that module before the database is touched. Every response is the same envelope — { Success, Data, Message, Errors } — so the UI has one display-and-error logic on all screens, and updates the row locally on success instead of reloading. Spreadsheet rhythm, not web-form rhythm.
Hard parts solved
Problem · An editable grid is not one pattern but twenty: cell editing, in-cell menus, a bottom entry row, armed deletes, and records too rich for a row.
Solution · A shared descriptor layer defines the whole grammar once — the focus-ring cell, the in-cell status menu, the entry row, the armed confirm bar, and an expanding row for what a cell cannot hold (permission matrices, passwords, voucher lines as a sub-table). Seventeen screens share one behavior the user learns once.
Problem · Classic bilingual systems hand-write left/right on everything, and the layout shatters the moment the direction flips.
Solution · Logical CSS properties everywhere, a glyph-protection layer in globals.css, and dir="ltr" isolation for IDs, numbers, dates, and email fields — so no screen shows a clipped number or a flipped bracket in either language.
Problem · Frontend visibility rules and backend authorization drift apart the moment they are written twice.
Solution · Every role carries a Module_Action privilege list. The backend checks it at every endpoint; the frontend reads the same list to decide nav items, buttons, and editable cells. Editing the matrix writes to that one source and applies system-wide instantly, with system roles protected server-side.
Problem · Fixed print templates mean a code change every time the client wants their paperwork to look different.
Solution · A three-panel designer (toolbox, A4 canvas with header/content/footer, properties) stores each template as JSON against a report type. The server-side generator renders PDF, Excel, and Word from that same JSON — live data fields, system variables like date and page number included.
Protection, server-side
Hiding a button is a courtesy; the check that matters runs at the endpoint. Five layers, all applied in the API.
Identity
Authorization
Requests
Errors
Data
The delivery ledger
Every number below was measured directly against the source at delivery — the last row is the check itself.
110
API endpoints
[HttpGet/Post/Put/Delete] attributes
14
Controllers
Controllers/V1 files
20
Domain services
Infrastructure/Services files
109,653
Backend C# lines
wc -l outside build folders
21,806
Frontend TS/TSX lines
wc -l on TypeScript/TSX
20
App Router pages
page.tsx files
1,234
Translation keys per language
leaf keys in ar.json & en.json
0
Type errors at delivery
tsc --noEmit on the full project
Let's build the next one