Appearance
Account holds, round 1: what a suspended or blocked account looks like outside the Admin Panel
Purpose. The Admin Panel MVP lets QR setu staff put an account on hold: suspend or block it, or sign it out everywhere. The owner chose these actions on 2026-09-28. Each one has faces outside the panel, and none of them is designed:
- what a visitor sees when they open or scan the card of a held business;
- what the account holder sees when they open the app or sign in.
Shipping the action without these states would show a broken page to the public and a generic sign-in error to the account holder.
- Status: drafted 2026-09-28, to be sent by the owner in the same round as Admin Panel MVP, round 1.
- Trackers: QRS-1415 (holds and enforcement) and QRS-1404 (programme).
- Decision draft: ADR-0036 account holds and enforcement.
What was measured
| Surface | What exists today (evidence, 2026-09-28) | Verdict |
|---|---|---|
| Public Setu Card and the other pages under a card address | No withheld state appears in the approved designs or the web renderer. The five matches for "unavailable" in apps/web/src/tiers/public (in OrderPanel.tsx, the feature README.md and two tests) all concern online payment availability or a beacon fallback, and none is a held-account state. | Design needed |
| Signing in to the merchant or consumer app | No state for an account on hold. No banned / suspended handling in apps/mobile/src/tiers/user/features/auth or packages/data/src/auth. | Design needed |
| The design's own intent | prototype/platform/users-core.js ACTIONS: suspend says "The owner is told the reason you type here". Block says "Blocks sign-in as well as the card". | Superseded by the owner, 2026-09-28: suspend and block are both bans in this release (QRS-1415). A banned person cannot sign in, so the app can show no reason and cannot tell the two apart. The account holder therefore sees one generic state. |
The prompt
Paste Block 1 and Block 2 together, followed by the PDPR Design Prompt, as the screen coverage mandate directs.
Block 1: the product you are extending
text
YOU ARE EXTENDING AN EXISTING APPLICATION, NOT BUILDING A NEW ONE.
This round adds account-hold states to two surfaces that already exist in this project: the
public pages under a card address (prototype/setu-card/) and the sign-in and entry flow of the
QR setu app (prototype/onboarding/). A person must not be able to tell where the existing product
ends and these states begin.
STUDY THESE BEFORE PROPOSING ANYTHING. They are the product you are extending.
prototype/setu-card/SetuCard.dc.html the public card a visitor opens or scans
prototype/setu-card/BiodataPage.dc.html the public biodata page on a card address
prototype/setu-card/InvitationPage.dc.html an occasion page on a card address
prototype/setu-card/BirthdayCardPage.dc.html another occasion page on a card address
prototype/setu-card/open-in-app.js how a public page hands over to the app
prototype/setu-card/ds-base.js the shared base every public page imports
prototype/setu-card/support.js the runtime every public page loads
prototype/onboarding/Onboarding.dc.html sign-in and entry: phone, WhatsApp code, context
prototype/onboarding/PinGate.dc.html the app's own gate on reopening
prototype/onboarding/BrandSplash.dc.html the first frame on open
prototype/onboarding/ds-base.js the shared base every entry screen imports
prototype/onboarding/support.js the runtime every entry screen loads
prototype/onboarding/icons.js the icon set in use
prototype/desktop-console/Auth.dc.html the desktop sign-in for the same account
prototype/platform/users-core.js the hold vocabulary and what each action means
BEFORE ANY SCREEN, PRODUCE TWO LISTS.
REUSED every existing component, module, pattern, token and layout rule you will use
unchanged, naming the file.
NEW anything you must introduce, each with one sentence saying why the existing
pattern genuinely cannot carry it. A new pattern with no justification is a defect.
EVERY NEW STATE MUST HAVE A NAMED RELATIONSHIP to an existing screen, navigation path, user
state or workflow. Add states to the screens above; do not create a new folder.
Only introduce a new pattern when the use case genuinely requires it, and say why.Block 2: this round
text
ACCOUNT HOLDS, ROUND 1. THE DECISIONS BELOW ARE SETTLED. DESIGN TO THEM.
WHAT A HOLD IS
QR setu staff can place a hold on an account from the Admin Panel, always with a written
reason, which stays on QR setu's own record.
- A hold is placed on a PERSON (their sign-in is refused and every session ends), on a
BUSINESS (its card and every public page under its address are withdrawn and its changes
refused, while its team can still sign in), or on both, which is the default for a solo
merchant.
- SUSPENDED (reversible, while something is resolved) and BLOCKED (a policy violation) do the
same things. In this release the account holder is NOT told the reason, and the app cannot
tell a suspension from a block, so to the account holder the two look exactly the same.
- Lifting a hold returns everything exactly as it was: the same card address, the same printed
QR codes, the same content, the card published again only if it was published before.
- Staff can also SIGN SOMEONE OUT EVERYWHERE. That is not a hold: the person simply signs in
again. It needs no special screen.
- There is no WhatsApp or email notice in this release. The account holder learns about a hold
the next time they open the app or try to sign in.
1. THE PUBLIC VIEW OF A HELD ACCOUNT. A visitor opens or scans the card address of a business
that is suspended or blocked. Printed QR codes keep pointing here, so this page must read as
intentional, never as broken.
- One neutral state, shared by every public page under that address: the card, the biodata
page and the occasion pages.
- Never say suspended, blocked or why. Show no personal details and no order, booking,
contact or chat actions.
- Give the visitor a way on, into QR setu, following open-in-app.js and the existing footer
patterns.
- Light and dark, phone and desktop widths.
2. THE ACCOUNT HOLDER, ON HOLD. They open the app, or sign in with their WhatsApp code, while a
hold is active. Design ONE state for both kinds of hold:
- Say plainly, without blame, that this account cannot be used right now, and how to reach
QR setu. Leave a clearly marked slot for the support contact; the owner fills the channel.
- Show no reason, no "suspended" or "blocked", and no date: the app does not have them.
- It appears at sign-in, right after the code is entered. An app that was already signed in
cannot know about the hold: when it is reopened it only learns that its session ended, so it
returns to sign-in as usual, and the hold state appears when they try to sign in. Put it
where the entry flow already lives, and say where.
- The same state on the desktop sign-in (prototype/desktop-console/Auth.dc.html).
- No loop back into sign-in, and no dead end: there is always a way to close or to contact
QR setu.
3. A BUSINESS ON HOLD, SEEN BY A TEAM MEMBER. A person who works in a held business opens the
app. The business side shows that the business is on hold, without the reason, which belongs
to its owner. Their own personal side, if they have one, is unaffected.
COPY RULES: plain words; no em dash or en dash; no reassurance about privacy volunteered at the
point of use; never "sorry for the inconvenience".
DELIVERABLES: the states added to the screens named above, the REUSED and NEW lists first, and
the SCREENS.md rows updated. For items 1 to 3, a line saying where each state now lives and how
to preview it.After it comes back
This follows the same loop as the admin round. The owner says it was passed; then pull fresh, diff it, and give a verdict per item (resolved · still open · new gap). An open item starts round 2. Implementation (increment 2, Users, in the assessment) waits for approval.
Cross-references
- Admin Panel MVP, round 1, the companion prompt.
- ADR-0036 account holds and enforcement (Proposed).
- Screen coverage mandate, for the process and the PDPR block.