Client work
Furkan Interior
Furkan Interior is an independent cleaning and renovation company in Ankara. It came to us for a marketing site. What it runs on now is a platform: a public site that quotes a price without a phone call in between, a customer portal that follows every job through to the invoice, and an admin console the office works in daily.
A price without a phone call
The public site has one job above all others: turn a visitor into a booked service without a phone call in the middle. A visitor picks a service, answers a short questionnaire that changes shape as the answers come in, and gets a real price range in seconds.
The part that matters is what the browser never receives. It sends the answers and gets back a minimum and a maximum. Rates, multipliers and volume tiers stay in the database, where the office can change them without a deployment and where nobody can read the pricing model out of a JavaScript file.
Seven services are priced this way, each with its own question set, conditional fields and validation. Renovation work that has to be measured area by area gets a repeatable entry step instead of a single number. Requests are rate limited per visitor, and a submitted request notifies the office and the customer at once.
Every job becomes a record
Once work is booked it gets a reference code and a page of its own. Customers sign in and see the history: what was done, when, what it cost, what was discounted, what is still owed.
Invoices are files in a private bucket, handed out as links that expire. A customer can also ask for one by email. The address is read from the verified session rather than from the browser, and ownership of the record is checked before anything is generated.
The same record carries the job photographs the team uploads, compressed in the browser before they are ever stored, and a two way note thread between customer and office. Corporate accounts get a little more: an invitation code to join, a structured e-invoice profile, a pinned service address, and a loyalty balance that returns a share of every payment as discount on the next job.
The loyalty balance is not a number in the browser
One point is one lira of discount. Points are awarded by the database itself when a job completes or a corporate payment lands. Spending them runs through a single server side operation that checks ownership, locks the rows it touches, and writes the discount, the new balance and the ledger entry together or not at all. The browser never updates a balance.
The state machine is deliberately one way. A job paid with points cannot be flipped back to a cash paid state, because the award would then run twice. That rule sits in the service layer, not in admin discipline.
An admin console, not a spreadsheet
The office side is a full operations console. Two lead inboxes sit side by side, anonymous price requests and requests from signed in members, each with its own unread count and status pipeline. The job list is filterable and paginated, with the discount arithmetic built in. Customers carry their own default rates, loyalty ledger and full history.
On a job the team can mark it paid, upload an invoice or photographs straight from a phone camera, gift loyalty points, write internal notes, and send the customer's map coordinates to the field crew in one tap.
The blog is the discovery engine
Articles are written in Markdown, and the page derives its own structured data from what was written: an article record always, a question and answer record when the headings are questions, a step by step record when they are numbered steps. Reading time, tags and related posts are computed rather than typed in.
Readers comment and react without creating an account, on an identity kept separate from the customer accounts. A draft survives the sign in round trip and posts itself on return. Comments are escaped, pattern checked and rate limited. The machine readable files, the sitemap and the plain text summaries an assistant reads, are regenerated from live content on every build.
How it is put together
| Frontend | React 18, TypeScript, Vite, Tailwind CSS, Radix primitives |
|---|---|
| Routing | React Router, with every route except the landing page split out of the first download |
| Backend | Supabase: PostgreSQL with row level security, auth, storage, edge functions |
| Trust boundary | The database. No separate API server, no object relational layer, the browser treated as untrusted by design |
| Interface | three.js for the hero, Lottie, Leaflet for map pinning |
| Languages | Turkish and English throughout, including the price engine |
| Hosting | Vercel, with edge functions for social previews and the feed |
Decisions worth naming
A few of these only show up under real traffic, which is the reason to write them down.
- The public key is public on purpose. Row level security is the boundary, not key secrecy. Every write policy states its post write condition explicitly, because leaving that out makes PostgreSQL silently reuse the read condition and quietly breaks the rules that lock a completed job.
- Invoices and job photographs live in private storage, scoped to the record they belong to and reachable only through short lived signed links.
- The heading font on the landing page loads blocking. Making it asynchronous cost about a second and a half of largest contentful paint, measured, so it stayed. Everything below the fold loads asynchronously.
- Application code never writes to the console. A logger writes to the console in development and to a table in production, with user, page, stack and context, so one person's error timeline can be reconstructed afterwards. Global error and rejection handlers feed the same place.
- React is pinned to 18 because the 3D layer is. Moving the 3D layer would cascade into a React 19 migration, so the constraint is written down rather than drifted past quietly.
What this page does not show
Most of the platform sits behind a login: the customer portal, the corporate area, loyalty redemption, invoice delivery and the entire admin console. The portal captures above come from a test account. The admin screens are not shown at all, because they carry real customer data.
Furkan Interior is an independent company. ObliqueX designed and built this platform and maintains it. The business itself, its pricing and its customer relationships are the company's own, and no figure about its trade appears on this page.
Work like this
If an operation is currently running on phone calls, spreadsheets and message threads, this is roughly the shape of the thing that replaces it. We build custom web, mobile and backend software for companies, and we run our own products alongside it.
bliqueX