Back to BlogIndustry Guides

A Store Admin Dashboard Your Team Can Actually Run

Rupak Amin

Founder & Lead Engineer, RAITHub

10 min read

A store admin your team can actually run needs four things a demo rarely shows: order search that stays fast at scale, bulk actions with guardrails against the one-click disaster, saved views so staff do not rebuild filters daily, and screens scoped to each role. Build these on the core screens, and test the dangerous actions, or the admin becomes the bottleneck.

If you would rather have it built for you, see how RAITHub would build this below.

This post is about operability: what makes an admin usable at volume. For the core areas an admin must cover and the safe Next.js patterns behind them, read building a Next.js ecommerce admin; for whether to build at all, read admin panel: build versus buy.

Why does an admin that works in a demo fail in daily use?

Because a demo has ten orders and one user, and a real store has thousands of orders and a team with different jobs. The screen that paginates fine over ten rows times out over fifty thousand; the refund button that is safe when one careful founder uses it is a hazard when six staff do; the filter someone sets up every morning should have been saved weeks ago. None of this shows up until the store is busy, which is exactly when you cannot afford the admin to be slow or risky.

FeatureWhy the team needs itWhat it prevents
Fast order search and filtersAnswering a customer takes seconds, not a page reload per guessStaff waiting on a slow list; customers on hold
Bulk actions with guardrailsMarking fifty orders shipped at once, safelyA mis-click that refunds or cancels the wrong batch
Saved views"Unfulfilled, this warehouse, paid" is one click, not rebuilt dailyWasted time and inconsistent working lists
Role-scoped screensEach role sees its own job, nothing moreA picker seeing the price book or the refund button
Audit logWho changed this order, and whenBlame guessing and undetected mistakes

How do you keep order search fast at scale?

Use keyset pagination, not offset, and index the columns people actually filter and sort by. Offset pagination (LIMIT 50 OFFSET 100000) makes the database walk and discard every skipped row, so page 2,000 is slow; keyset pagination remembers the last row seen and continues from it, so every page costs the same.

-- Keyset page: "orders after this one", using an index on (created_at, id).
-- The cursor is the created_at + id of the last row on the previous page.
SELECT id, number, status, total, created_at
FROM orders
WHERE status = $1
  AND (created_at, id) < ($2, $3)        -- the cursor
ORDER BY created_at DESC, id DESC
LIMIT 50;

CREATE INDEX orders_status_created ON orders (status, created_at DESC, id DESC);

Back the free-text search (customer name, email, order number) with the right index too, and let staff search by the things they are given on the phone: an email, an order number, the last four digits of a card reference. A fast admin is a performance concern like any other front end, and the same budgets apply as in a normal store.

How do bulk actions avoid a one-click disaster?

Three guardrails, in order: scope, confirm, and record. Scope the action to the exact IDs selected, not "everything matching the current filter", because the filter can change under the staff member's feet. Confirm with the count and the action in words ("Refund 37 orders?"), so a stray click is caught. Record each item changed in the audit log, and make the whole batch safe to retry, so a network blip halfway does not half-apply it.

// A bulk action is a permission-checked server action over explicit IDs.
// It is idempotent per item, so a retry does not double-apply, and every
// change is recorded. Dangerous actions (refund, cancel) require a stronger role.
export async function bulkMarkShipped(ids: number[], actor: StaffUser) {
  requirePermission(actor, 'orders.fulfil')          // checked on the server
  for (const id of ids) {
    const order = await db.orders.get(id)
    if (order.status === 'shipped') continue          // idempotent: skip already-shipped
    await db.orders.transition(id, 'shipped')
    await db.auditLog.record({ actor: actor.id, action: 'bulk_ship', orderId: id })
  }
}

Keep the most dangerous bulk actions, mass refund or cancel, behind a stronger permission and an extra confirmation, because their cost is money, not time.

How do you scope screens to each role?

Check a named permission on the server for every screen and every action, and build the menu from the same permissions, so a role only sees what it can do. Do not hide the refund button in the UI while leaving the endpoint open; the Next.js docs are explicit that a server action is a public endpoint and must check permission itself. A picker gets pick lists and fulfilment; a support agent gets orders and refunds within a limit; an owner gets everything. The isolation discipline is the same one that keeps tenants apart, in how one tenant ends up seeing another's data.

How do you test an admin for operability?

Test the dangerous and the slow. Unit-test the permission checks so a weaker role is refused; Playwright-test that a bulk action confirms and records; and add a performance check that order search over a large seeded dataset stays within budget.

import { test, expect } from '@playwright/test'

test('a picker cannot reach the refund action', async ({ page }) => {
  await signInAs(page, 'picker')
  const res = await page.request.post('/api/orders/123/refund', { data: { amount: 10 } })
  expect(res.status()).toBe(403)                 // refused on the server, not just hidden
})

test('bulk cancel confirms the count and records each change', async ({ page }) => {
  await signInAs(page, 'support')
  await selectOrders(page, 3)
  await page.getByRole('button', { name: 'Cancel orders' }).click()
  await expect(page.getByText('Cancel 3 orders?')).toBeVisible()  // count in the confirm
  await page.getByRole('button', { name: 'Confirm' }).click()
  await expect(page.getByTestId('audit-entries')).toContainText('bulk_cancel')
})

End to end, search should return within budget over a large dataset, a saved view should reload the same filter, and a dangerous action should be refused for a role that lacks the permission. These are the journeys staff live in, so they belong in CI, the same way the marketplace QA checklist gates the money paths.

Buy, build or hire?

OptionChoose this whenThe catch
Platform admin (Shopify, Medusa, a hosted store)Your operations fit the platform's back officeBespoke bulk actions, saved views and your own roles may be limited
An admin framework (Retool, admin UI libraries)You want screens fast over your own dataYou still own permissions, guardrails and performance at scale; see build versus buy
Custom buildYour team's daily workflow, roles and bulk operations are specific, and volume is highYou own the operability and the tests that keep dangerous actions safe

How long does it take to build yourself, and what is the risk?

For an experienced developer adding these operability features to an existing admin, our estimate is 2 to 4 weeks: keyset search, bulk actions with guardrails, saved views, role scoping and the tests. Built fresh with the core screens, an admin is a 4 to 6 week fixed-scope build. The main risk is a dangerous bulk action without guardrails; a mass refund or cancel over the wrong selection is expensive and hard to undo, so build the confirm, the scoping and the audit log before you ship the button.

Why RAITHub for this

  • Role-scoped admins in production. Sundor Skin, a B2B wholesale platform RAITHub built, runs 12 staff roles from 88 permission codes with row-level security, across 146 PostgreSQL tables and 530+ tests, so each role sees only its own screens. See the Sundor Skin case study.
  • Dangerous actions are tested. Permission checks and bulk actions get unit and Playwright tests gated in CI, so a weaker role is refused on the server.
  • Audit by default. On TheSkinProof, the founder's own venture built and run by RAITHub, staff actions are recorded across 217 endpoints and 750+ tests.

When you don't need us

  • The platform admin fits. If your operations match a hosted back office, use it.
  • Low volume, one operator. A careful founder and a simple list may be enough for now.
  • You only need the patterns. The keyset and bulk-action code above is a fair start.

How RAITHub would build this

  • Fast lists: keyset pagination, the right indexes, and search by what staff are given on the phone.
  • Bulk actions: scoped to explicit IDs, confirmed with a count, idempotent per item, recorded in the audit log.
  • Saved views and roles: per-user saved filters, and every screen and action behind a server-checked permission.
  • Tests: permission and bulk-action tests plus a search-performance check over a large dataset, gated in CI.

Timeline: operability features on an existing admin are typically 6 to 12 weeks as backend-heavy work; a focused admin built with the core screens fits the 4 to 6 week fixed-scope range. See SaaS development, API and backend development and the ecommerce industry page.

You receive: automated tests and CI covering permissions and dangerous actions, handover docs and runbooks, and full IP in your name under NDA.

Next step: book the free 15-minute technical audit with your team's roles and daily tasks, and we will follow up with a written fixed quote.

Frequently asked questions

Why is my order list slow in the admin?

Usually offset pagination over a large table: the database walks and discards every skipped row. Switch to keyset pagination, which continues from the last row seen, and index the columns staff filter and sort by, so every page costs the same.

How do I make bulk actions safe?

Scope the action to the exact IDs selected, confirm with the count and the action in words, record each change in the audit log, and make the batch idempotent so a retry does not double-apply. Keep mass refund and cancel behind a stronger permission.

How do I stop a staff member seeing things they should not?

Check a named permission on the server for every screen and action, and build the menu from the same permissions. Hiding a button in the UI is not enough, because a server action is a public endpoint and must check permission itself.

What are saved views and why do they matter?

Per-user saved filters, such as "unfulfilled, this warehouse, paid", that reload in one click. They save staff rebuilding the same filter every morning and keep everyone working from the same list, which matters more the busier the store gets.

Should I build the admin or use my platform's back office?

Use the platform's admin when your operations fit it. Build custom when your team's workflow, roles and bulk operations are specific and volume is high enough that a slow or risky admin costs real time and money.

How do I test an admin dashboard?

Test the dangerous and the slow: unit-test permission checks so weaker roles are refused, Playwright-test that bulk actions confirm and record, and add a performance check that search over a large seeded dataset stays within budget. Gate them in CI.

store admin dashboardecommerce adminbulk actionssaved viewsrbacadmin operability

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.