Case Study · Platform Access & Permissions

User Manager

Shipped and now used to set up access for new customers: who can see and do what, across a brand, its franchises and every location beneath them.

Role
UX/UI Designer · feature owner
Platform
Web · back office
Timeframe
2024 to 2025
Team
Product · Engineering · QA
User Manager · company view
User Manager company view: brand, brand owned locations and franchises with users at each level
User Manager list of all users with hierarchy level, user type and status

Almost every action in the platform runs through one question: is this person allowed? User Manager is where it gets answered. My job was to take a permission model that had grown tangled and make the correct access the obvious next step, not an extra one, the form only ever asking for what actually applied. It shipped, and it's now how the implementation team sets up access for new customers.

At a glance
01

What I walked into

When I took this on, a user's level was never actually stored: the system guessed it from their home location, and there was no explicit hierarchy sitting behind it.

PROCESS

Before the pixels

I mapped this before I designed a single screen: every path a user takes through the system, created, assigned locations, moved between levels, deactivated and reactivated, worked out on paper and on a board until the flow made sense on its own.

User Manager user-flow map
Part of the user flow map behind User Manager: worked out from sketches and planning before a single polished screen.
02

The model I built on top

The platform now stored hierarchy explicitly instead of inferring it from a home location, so I built five levels onto it: four paired a full and a restricted version, Location stood alone at the base, tied to a single home location. Moving someone between levels never meant touching it.

Brand Restricted Brand Franchise Restricted Franchise Location

Mapped directly onto how the business is actually structured: Brand, HQ and brand-owned locations, Franchise, Location.

The full vs restricted split was deliberate: full means all locations, now and future; restricted means an explicit list that never quietly grows when a new location opens, because access should never expand without someone choosing it. A small distinction, but it matters a lot for a growing brand.

Choose user level
Level is chosen first: each option states its access in one plain line, so the person setting it up doesn't need the permission model in their head.

I also made management rights granular inside an assignment (franchise by franchise, you decide whether a restricted user can only access it or also manage it), so "restricted" never had to mean all or nothing.

Manage access, allow managing per franchise
The Manage Access step: an "Allow Managing" toggle per assigned franchise turns access into full management, one franchise at a time.
03

I flipped the order of the form

The old create form asked for everything at once. I turned it around: ask for level first, then branch the rest of the flow to match it. A Restricted Brand user assigns whole franchises, full access to everything in them, now and future, then adds individual locations from elsewhere if needed; a Location user skips assignment entirely, since their one location already is their access. The tradeoff I chose was a longer, staged flow in exchange for never showing anyone a field that doesn't apply to them.

Add new user basic details
Step one: the details every user needs, before the flow diverges by level.

For the location picker I gave people three interchangeable views: Business Units, Hierarchy and a flat list, because a brand admin and a franchise manager picture their estate differently, and I didn't want to force either into the other's mental model. The messy cases are where the real decisions were: HQ selectable, pre active locations permission gated, discontinued ones view only behind a toggle only full access users ever see.

Assign locations, hierarchy view
The hierarchy view: zones, states and regions collapse and expand; selecting a branch selects everything beneath it.
04

Making a three way rule legible

Access here is three separate checks that all have to pass at once. The logic wasn't mine to invent, but making it predictable to a human was, so I designed the flow around the rule rather than leaving people to discover it:

Gate 01

Hierarchy

You must sit at an equal or higher level than the person you're acting on: a location user can't edit a franchise user, even on a shared location.

Gate 02

Role

Your role must rank equal or higher. You can only assign roles at or below your own level, so permissions can't leak upward.

Gate 03

Security level

A numeric level shared with HRM. Equal levels can't view each other; higher can edit lower, never the reverse.

Small choices carry the rule: I had roles auto sort by rank and pinned the default role so it can never be removed; even "Remove All" leaves it. And I finally addressed the failure that started this whole section: instead of an empty modal, the person now gets a clear, specific reason why an action isn't theirs to take.

Assigned roles
Assigning roles: sorted by rank, the default pinned and locked, everything scoped to what the editor is allowed to grant.
05

Duplicate emails, without breaking anyone

This was the last thing I designed on the feature, and my favourite problem on it: turning on email uniqueness against a live database already full of duplicates. We couldn't block everyone with a clash, and we couldn't ignore it either, so we went with a third option: tolerate, but quarantine.

Duplicate email banner on the User Manager list
The banner on the list: it only appears for active users with a clash, and links straight to the resolution page.
Email duplicates resolution page
The dedicated resolution page: duplicates grouped by address in an accordion, each record shown with name, username and location, fixed inline and marked resolved.
Email entered belongs to an active user, blocked at creation
Change Email action for a deactivated record
Cannot reactivate user, email already in use
Blocked outright at creation, with the dedicated fix for a deactivated record and the same block at reactivation.
06

The parts that fought back

Moving someone between levels can quietly break their access, so I gave it its own guarded flow (repick franchises, locations and a valid home location) and designed in who is allowed to move whom: only a brand user moves a brand user, a cross franchise move needs access to both, an employee never moves up to brand. Getting those rules to feel like guardrails instead of dead ends was the hard part.

Two more edge cases needed their own rules:

Two archetypes, one screen

Standard users and HRM employees live in the same list but follow different rules: different editable fields, sync constraints, and a different system of record. The design makes the boundary legible instead of surprising.

Temporary access, done right

Loaned access moved out of the permanent permission table and flipped to the destination location, surfaced with a badge, so a short term loan can never silently become permanent access — and if permanent access is later revoked while the loan is still live, the badge reverts rather than vanishing.

Change user level flow
Change level as a five step flow: reselect level, reassign locations, manage access, repick a valid home location, review.
07

Reflection

The design was mine end to end, though the product decisions and research were shared with a product manager, and I worked closely with the implementation team who use this tool to set up access for new customers. The through line was taming complexity: a genuinely combinatorial permission matrix, shaped into a short series of plain choices. If I point to one piece, it's the duplicate emails resolution: a data integrity problem the redesign itself created, solved without breaking a single existing user.

Delivered by a team across product, engineering and QA; I owned the design. Focuses on the design problem, model and decisions; adoption is described qualitatively and the design reached implementation; hard metrics weren't tracked.