Bandmakers · design pass two

Walked every
screen. Fixed
what was there.

The first pass was read off the code. This one was done with the app running on a phone, opening one screen at a time — which is the only way most of this could have been found. Then the two things that would have had it rejected.

These are written comparisons, not screenshots. Every change below was made and then checked on the running simulator; the panels describe what was on screen before and after. Colours and spacing on this page come from the app's own theme.ts.
Screens walked
62
Fixes landed
41
Shared components
7
Store blockers cleared
2

The three that mattered most

Everything else on this page is tidying. These three were doing real damage across the whole app, and all three were invisible until it was running.

Every tab opened 62pt too low

Home · Bookings · Progress · Profile · all five staff tabs

Was

Inside the native tab bar, iOS adds the top safe area to a scroll view by itself — and every screen was also padding by it. The same 62pt was counted twice, so Home's greeting started 135pt down instead of 70. A tenth of the first screenful, on every tab, spent on nothing.

Now

Counted once. One shared hook owns the top of every scrolling screen, and the content container is made a pixel taller than the window so the rule iOS applies is the same on a short screen and a long one.

Measure before tuning. The obvious fix — contentInsetAdjustmentBehavior="never" — does nothing here; it is not reaching the native view at all. Both values were tried on the device and neither moved a pixel. That is written down in the code so nobody spends the hour again.

"Academy staff sign in" did nothing

Profile → the staff switch

Was

The row pushed the staff sign-in, the route guard saw a parent standing on an auth screen and replaced it on the next frame. The screen flashed and the app was back on Home. A row that looks like a destination and is a no-op — the entire staff side was unreachable from inside the app.

Now

The one auth screen a signed-in parent is allowed to reach. Staff are a separate account with separate tokens, so signing into one has nothing to do with being signed into the other, and the only way in is from a screen you reach while already signed in as a parent.

"Step 6 of 5" on the screen before payment

Booking → review

Was

Review was numbered as a sixth step in a five-step flow. Underneath it, the button read "Payment — not built yet" — a developer's note sitting on a customer's screen — and when the quote failed the screen was a dead end with no way back to the choices that caused it.

Now

Review is outside the count and shows its own kicker instead. The button says Pay; the foot note carries the truth that card payment is not open yet. A failed quote offers Pick another time.

This is the last screen before a parent decides whether this academy is careful with their child and their card. It was the screen with the arithmetic error on it.

One shape per pattern

The rule you gave me after the two profile headers. Nobody decided to have four kinds of list — it accumulated one screen at a time. Six shared components now cover what was written out by hand between eleven and fifty-nine times each.

A card per record became one card with hairlines

Staff Desk · Timetable · Branches · Teachers · Books · Stock · Codes · Bundles · Accounts · Family · Payments · Progress · Trial · Careers

Was
Complaints & notes
Home tutoring requests
Music tour requests

Identical rounded cards repeated down the page, with a target-sized dead zone between every pair of tappable rows.

Now
Complaints & notesWhat parents have told us3
Home tutoringWaiting for a teacher1
Music tourWaiting for a teacher0

One card, 1px separators, and a hand-drawn glyph on every row so the list can be scanned rather than read.

Columns of solid black buttons

Branches (7) · Concerts (17) · Teachers (3) · Stock (2 per row)

Was
Mivida
Show code
KODE
Show code

A full-width filled button inside every card. On Concerts that was seventeen black slabs; on Branches, seven — for something you do standing at a printer a few times a year.

Now
MividaNew Cairo · 1 room
KODENew Cairo · 1 room

The row itself opens the thing. Where an action has to stay visible it is a quiet chip that sizes to its own words.

At most one primary per screen — and a control repeated once per row is never the one thing the screen is for.

Buttons named after states

Discount codes · Bundles · Staff accounts

Was

Codes and bundles carried a bordered button reading Switch on / Switch off — the opposite of the current state, spelled out. Staff rows were worse: each offered the two states the person was not in, so "Demo Teacher A · Suspended · Left" looked like a description of them.

Now

Codes and bundles use a real switch — the same control the Instruments screen two rows away was already using. Account status is a three-way segmented control that shows the state and offers the change in one object.

Things that were not true

Copy that said something the system did not mean. These are the ones a parent or a front-desk person would have acted on.

"6 instruments cannot be booked" — three of them could

Lesson prices, and the Academy row that leads to it

Was

The headline counted every instrument missing any tier and called them all unbookable. Drums with no Expert price books perfectly well with a Core teacher. Meanwhile the Academy hub counted only the ones with no price at all — so two screens showed different numbers for what looked like the same fact.

Now

3 instruments cannot be booked, and a second line saying three more are missing a price at one rate only. The hub and the screen agree.

Never round a partial problem up into a total one. The staff member who fixes the wrong three loses an afternoon.

A rejected password stayed on screen while you retyped

Both sign-in screens

Was

"Email or password is incorrect" cleared only on the next submit. After a sign-out it was still sitting in red above two empty boxes, telling a parent their blank password was wrong — and it pushed the Sign in button down as it appeared.

Now

Editing either box clears it, because the thing the message describes is no longer what is in the box.

An expired code still offered to switch itself on

Discount codes

Was

A code that ended in January and one that had used its single use both showed a working Switch on. The server refuses them at checkout either way — a control that does nothing is worse than no control.

Now

They read Expired and Used up, and dates print as "ended 1 January 2026" rather than 2026-01-01.

Nothing destroys on the first tap

Four controls removed something real, immediately, with no undo anywhere in the app — and three of them were the loudest thing on their screen.

Retire · Remove · Replace this code · Mark as left

Timetable · Closed days · Branches · Staff accounts

Was

A red bordered button on every row, firing on the first tap. Retiring a slot stops parents booking it. Removing a closure puts a holiday back on sale. Replacing a branch code invalidates the printed sign on the door — and it sat next to the button you press to read that sign.

Now

A quiet red word, and a confirmation that names the exact thing: "Sunday 11:00 stops being bookable. Lessons already booked into it keep their place."

Confirmations only for the genuinely irreversible — use them anywhere else and people learn to tap through them. Suspending a staff account still passes straight through; marking someone as left does not.

Forms that fit on the screen

The staff "add a weekly time" form asked four questions and every one was a fully expanded list: 2 teachers, 7 branches, 7 instruments, 7 days. Twenty-three rows to answer four questions, so the only thing you actually type sat two screens below the fold.

Long lists collapse onto the answer

Timetable · Day minimums · Closed days · Prices · Cash · Place a make-up · Books

Was

Four walls of rows in a row. The weekday was a seven-row list — while a one-row day strip was already sitting in the same component file, used elsewhere.

Now
MividaNew CairoChange

Once a choice is made the list shows the answer and a Change. The whole form is now four rows and a text box. It only ever collapses onto a real answer — with nothing chosen the list stays open, because a collapsed empty row is a question the form is hiding from you.

The instrument field said "optional" and gave you seven instruments with no way to say any — so it could never be finished, so it never collapsed. "Optional" has to be a choice you can make.

Smaller things, same reasons

Twelve more

Across both sides

Was

· "This time is already taken." printed in full on eight consecutive rows
· core / legacy / cash / accessory printed raw from the database
· "Zamalek · Zamalek", "Sahel · Sahel"
· Space Mono caps under every bar on Reports
· "2026-08" as a cohort label
· A lone "1" floated 350pt from its section heading
· A key glyph drawn as a magnifying glass; a book drawn as the pause symbol
· A section headed "Concerts" containing one row called "Concerts"
· "Closures." on a screen reached from a row called "Closed days"
· Three names for one search box
· "Move to Open to everyone"
· "Events  17"

Now

· A Taken tag, and the 45 minutes every slot shares stated once in the foot note instead of eight times
· One shared place turns a column value into something a person reads
· A subtitle only when it adds something
· The display face on section headings and nowhere else
· "August 2026"
· The count rides with the label
· Both redrawn, and a globe added for Language
· Folded into "Schedule & events"
· The screen takes the row's wording
· One
· "Open to everyone" — a button says what happens when you press it
· "Events (17)"

The booking dead-end is gone

This was the worst thing in the app and it was not a design problem — it needed a change on the server.

You could pick an instrument nobody had priced

Booking step 3 · GET /catalog/instruments

Was

Vocals, Bass and Violin were live choices like any other. You picked one, chose a branch, a teacher and a time — four steps — and the wall came at the review screen: "Pricing is not set for this teacher and instrument yet." The step before payment, at the exact moment the app was asking to be trusted with a card.

Now
CelloNot open yet
Guitar
VocalsNot open yet

Shown, disabled, with the reason — and a note saying to ask at reception. Nobody walks four steps into a wall.

The flag comes from the server and mirrors the quote's price lookup exactly, so it can never disagree with what the checkout will do. It is computed over the teachers who actually teach the instrument, not the instrument alone — an instrument priced only at Expert is bookable if an Expert teaches it and not otherwise. That caught a fourth one nobody had counted: Cello is priced, but its only teacher is inactive.

Two App Store rejections, removed

Both would have bounced the submission on the first review, whatever the rest of the app looked like.

There was no way to delete an account

Guideline 5.1.1(v) · Profile

Was

Apple has required in-app account deletion since 2022 for any app that lets you create an account. There was no such screen. The endpoint had existed the whole time — carefully written, destroying every stored photo and video, deleting the personal records, cancelling live lessons, freeing the email — and nothing in the app ever reached it.

Now

A screen that states plainly what goes and what is kept: payment records stay, because Egyptian tax rules require it. One confirmation, not a typed phrase — Apple is equally clear that deletion must not be made difficult.

A route with no way in is not a feature. That is this project's own rule about what "done" means, and it had been broken quietly since the endpoint was written.

The app icon was still Expo's template

assets/icon.png

Was

The stock blue "A" with the construction guides still visible, untouched since the project was scaffolded. Rejection on sight, and the first thing anyone would have seen on their home screen.

Now
B

Ink ground, yellow Anton B — the same pairing as the Home hero card and the check-in tile, set in the app's own display face rather than something picked for the icon alone. Regenerates from a script, so any size is one command.

Still open

Not design problems. These need the academy or a decision from you.