Back to works
PlateI

Zirve Asansör — Elevator Maintenance Platform

My father runs an elevator maintenance company in Trabzon.

The six projects

Backend — One REST Contract Over MySQL
I2026

Backend — One REST Contract Over MySQL

I decided early not to use an ORM. I had Oracle SQL training, I could read a query faster than a model layer, and the schema was obviously going to change a lot. It did: 28 numbered migration files, applied in order from an empty database, and an API contract that went through thirteen revisions. It is Node and Express over MySQL, raw SQL through prepared statements, one JWT scheme for three roles. Money is a DECIMAL column and integer kuruş in code, so float rounding never touches a balance. The monthly dues are produced by a cron that does not ask what day it is. It asks whether this month's dues exist yet, and if the server was down on the first, it makes them when it comes back.

Node.jsExpressMySQL
Yönetim Paneli — The Boss's Desktop Panel
II2026

Yönetim Paneli — The Boss's Desktop Panel

A desktop app for one user was a strange choice in 2026 and I made it on purpose. I know Java, I wanted one client in the system that wasn't JavaScript, and a desktop client can't quietly share code with the server, so the REST API had to be complete from day one. This is that app: JavaFX, nine sections from the dashboard to the vehicle list, talking to the backend through Java's HttpClient. Taking a payment shows which months it will close before anything is saved, then prints a receipt that copies the firm's paper makbuz, letterhead and signature included. Contracts and finance reports go the same way, previewed in a WebView and exported to PDF.

Java 21JavaFXMaven
Yönetim PWA — The Boss's Panel on an iPad
III2026

Yönetim PWA — The Boss's Panel on an iPad

The desktop panel lives on one Mac in the office and my father wanted it on the iPad he carries. Rewriting it was out of the question, so this is a copy. Every screen was transcribed from its JavaFX class: same wording, same endpoints, same paging. Where the desktop had a bug, the copy reproduces the bug and a table in the docs records it for me to decide. Five days, 26 commits. It installs from a link as a PWA and needs no store.

ReactTypeScriptVite
Müşteri PWA — Apartment Manager's App
IV2026

Müşteri PWA — Apartment Manager's App

The first version made every building manager log in with a building code and a password we would send over WhatsApp. It was the standard thing to do and it was never going to work. Around 300 managers, most of them older, many of them changing every year, each needing a password typed into a phone by someone who might not want to. So login became the manager's phone number, the one the firm already has, and a password is something you can add later if you want one. The app itself shows what the building owes month by month, the payments taken and the ones cancelled, the last maintenance visit, and a button to report a fault with the emergency number next to it. It installs from a link or a QR code, so it reaches iPhones without an App Store listing.

ReactTypeScriptVite
Teknisyen — Field App That Works Offline
V2026

Teknisyen — Field App That Works Offline

Machine rooms and basements have no signal, and that is where a technician is standing when he wants to record a visit. So the app writes to the phone first. The screen says saved right away, the record sits in a queue, and the queue syncs when a connection comes back. Each record carries an id made on the phone, so resending after a dropped connection updates the same visit instead of creating a second one. Around that sit the building list by district, the open faults nobody has claimed yet, and the service form, which reproduces the paper form the firm has always used and comes out as a PDF the customer can be handed.

React NativeExpoTypeScript
Beşikdüzü Asansör — Marketing Site
VI2026

Beşikdüzü Asansör — Marketing Site

The first plan for the site had the elevator cabin sliding down the page as you scroll. The redesign flipped it. A cabin that moves with the page is just another thing going past. The version that shipped does the opposite: the cabin stays fixed in the shaft on the right and the floors move past it, the way it looks from inside a glass lift. A dot matrix indicator in the header shows the floor you are on, and a panel of buttons on the right rides you between sections. The page makes no API calls, stores nothing, and is prerendered to plain HTML so it paints before any script arrives. It is the one part of the platform that is actually live.

ReactViteSSR

The problem

The company had no software at all. Dues were kept in a notebook, payments were cash handed to my father, and which buildings still owed money for which months lived in his head. When a manager argued about a balance there was nothing to point at.

Apartment managers are mostly older people, not comfortable with technology, and they change every year or two. Anything built for them had to work for someone holding a phone in a stairwell. The technicians spend their days in machine rooms and basements without signal, which ruled out an app that needs a connection to save a visit.

Architecture

One Node and Express backend over MySQL owns everything. It is the only thing that touches the database and the only thing allowed to change the API contract. Each client is its own repository, written against a copy of that contract, and not allowed to edit it.

The desktop panel is JavaFX. I chose it partly because I know Java and partly because a desktop client can't cheat: it forces the API to be complete from the first day. The iPad panel is a web copy of the desktop one, screen for screen. The customer app is a PWA installed from a link or a QR code, because a native iOS app can't be distributed outside the App Store and these users were never going to search a store. The technician app is React Native, since it needs offline storage and, eventually, push.

Under all of it, every record belongs to a building and never to a person; the manager changes, the debt stays. A balance is never stored. It is the sum of valid dues minus the sum of valid payments, recomputed every time it is asked for. Nothing financial is deleted; a wrong payment is cancelled with a reason and stays visible. The one exception I allowed is a repair charge line, which can be removed outright.

My role

A solo project, and the decisions were mine: the architecture, the data model, the API contract and how each screen should behave. I drove the implementation with AI coding agents, documentation first. There is one architecture document and one contract. Every client session was pointed at those files and told it could not change them; when the contract had to change, the backend changed it and the copies were refreshed.

The parts I had not built before, the offline sync queue, push, Docker with Caddy, the JWT details, I listed up front as zones where I was learning rather than directing, and I read those harder than the rest. The rule I kept for myself: a file I couldn't explain didn't get committed.

Scale

Six repositories and 168 commits between June 12 and September 16, 2026. The backend is 28 SQL migrations, 15 tables and 58 endpoints, with fourteen test suites; the ones that touch a database refuse to run against the real one. The contract went through thirteen revisions on the way, each recorded with its date and its reason. In the business it covers about 300 buildings in two cities, four technicians, and one person collecting the money.

What I took from it

Three things I built were wrong and had to be replaced, and they taught me more than the parts that worked.

The first version of payments put maintenance dues and repair charges in one pool and paid off whichever was oldest. It was tested, it was correct, and in practice it confused my father: money handed over for a repair could land on an old maintenance month. It lasted a day. Then payments got a type, and the two debts got separate books.

Customer login started as a building code plus a required password, sent over WhatsApp. Around 300 managers, most of them older and not comfortable with technology, were never going to be onboarded that way. Login became the manager's phone number. The password became something you add if you want it.

The technician home screen was first built around days: a strip of dates, tap one, see that day's work. The technicians don't work by day. They work by district. That screen lasted two days.