Skip to content

UI-PORTAL-TEMPLATE-SPEC — Astro + Preact UI Starter Repository Specification

Status: Draft Owner: Platform Engineering Repository: simpaisa/ui-portal-template Framework: Astro 6.x + Preact Last Updated: 2026-04-03


1. Overview

Canonical UI starter repository for Simpaisa merchant and internal portals. Built on Astro 6.x with Preact islands for interactive components. Deployed to Cloudflare Pages. Handles dashboard and management interfaces for the payment gateway serving 270M+ annual transactions across PK, BD, NP, and IQ corridors.

2. Directory Structure

ui-portal-template/
├── src/
│   ├── pages/
│   │   ├── index.astro              # Landing / dashboard page
│   │   ├── login.astro              # Authentication page
│   │   ├── payin/
│   │   │   ├── index.astro          # Pay-in transaction list
│   │   │   └── [id].astro           # Pay-in transaction detail
│   │   ├── payout/
│   │   │   ├── index.astro          # Pay-out list
│   │   │   └── [id].astro           # Pay-out detail
│   │   ├── remittance/
│   │   │   ├── index.astro          # Remittance list
│   │   │   └── [id].astro           # Remittance detail
│   │   ├── cards/
│   │   │   ├── index.astro          # Card transactions list
│   │   │   └── [id].astro           # Card transaction detail
│   │   └── settings/
│   │       ├── index.astro          # Account settings
│   │       ├── api-keys.astro       # API key management
│   │       └── webhooks.astro       # Webhook configuration
│   ├── components/
│   │   ├── layout/
│   │   │   ├── Header.astro         # Navigation header (static)
│   │   │   ├── Sidebar.astro        # Side navigation (static)
│   │   │   └── Footer.astro         # Page footer (static)
│   │   ├── ui/
│   │   │   ├── Button.astro         # Button variants
│   │   │   ├── Card.astro           # Content card
│   │   │   ├── Badge.astro          # Status badge
│   │   │   ├── Table.astro          # Data table (static)
│   │   │   └── Alert.astro          # Alert/notification
│   │   └── common/
│   │       ├── StatusBadge.astro    # Transaction status with colour coding
│   │       ├── CurrencyDisplay.astro # Formatted currency (corridor-aware)
│   │       └── DateDisplay.astro    # Localised date/time display
│   ├── islands/
│   │   ├── TransactionFilter.tsx    # Interactive filter/search (Preact)
│   │   ├── TransactionTable.tsx     # Sortable, paginated table (Preact)
│   │   ├── DashboardCharts.tsx      # Real-time charts (Preact)
│   │   ├── WebhookTester.tsx        # Send test webhooks (Preact)
│   │   ├── ApiKeyManager.tsx        # Create/revoke API keys (Preact)
│   │   ├── BatchUploader.tsx        # CSV batch upload (Preact)
│   │   └── NotificationToast.tsx    # Toast notification system (Preact)
│   ├── layouts/
│   │   ├── BaseLayout.astro         # HTML shell, head, meta, fonts
│   │   ├── DashboardLayout.astro    # Authenticated layout with sidebar
│   │   └── AuthLayout.astro         # Unauthenticated layout (login, register)
│   ├── styles/
│   │   ├── global.css               # CSS reset, custom properties, base styles
│   │   ├── tokens.css               # Design system tokens
│   │   └── utilities.css            # Utility classes
│   ├── lib/
│   │   ├── auth.ts                  # ControlPlane.com auth client
│   │   ├── api.ts                   # Simpaisa API client (fetch wrapper)
│   │   ├── constants.ts             # Environment URLs, corridor config
│   │   └── utils.ts                 # Formatting, validation helpers
│   └── middleware/
│       └── index.ts                 # Astro middleware (auth guard, CSRF)
├── public/
│   ├── favicon.svg
│   └── fonts/
├── tests/
│   ├── unit/                        # Vitest unit tests
│   └── e2e/                         # Playwright E2E tests
├── astro.config.mjs
├── tsconfig.json
├── vitest.config.ts
├── playwright.config.ts
├── package.json
└── wrangler.toml                    # Cloudflare Pages config

3. Astro 6.x Configuration

// astro.config.mjs
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare(),
  integrations: [preact({ compat: true })],
  vite: {
    build: { target: 'esnext' },
  },
});
  • Server-side rendering via Cloudflare Pages Functions
  • Preact integration with React compatibility mode
  • TypeScript strict mode enabled

4. Preact Islands Architecture

  • Static content rendered at build time or server-side by Astro
  • Interactive components in src/islands/ as Preact TSX files
  • Islands hydrate on the client using Astro's client:* directives:
  • client:load — hydrate immediately (critical interactive elements)
  • client:visible — hydrate when scrolled into view (charts, tables below fold)
  • client:idle — hydrate when browser is idle (non-critical)
  • Islands communicate via custom events or shared state (nano stores)
  • Keep islands small and focused; prefer Astro components for static UI

5. ControlPlane.com Auth Middleware

// src/lib/auth.ts
export interface AuthConfig {
  orgId: string;
  clientId: string;
  redirectUri: string;
  audience: string;
}
  • OIDC/OAuth 2.0 integration with ControlPlane.com identity provider
  • Astro middleware intercepts requests, validates JWT access tokens
  • Redirect unauthenticated users to login page
  • Token refresh handled automatically (refresh token rotation)
  • Role-based access: merchant:admin, merchant:operator, merchant:viewer
  • Session stored in encrypted HTTP-only cookie

6. Cloudflare Pages Adapter

  • SSR via Cloudflare Pages Functions (V8 isolates)
  • Environment variables via wrangler.toml and Cloudflare dashboard
  • KV binding for session storage
  • Preview deployments on every pull request branch
  • Production deployment on merge to main

7. Design System Tokens

/* src/styles/tokens.css */
:root {
  /* Colour palette */
  --colour-primary-50: #f0f4ff;
  --colour-primary-500: #3366ff;
  --colour-primary-900: #0a1a4d;
  --colour-success: #22c55e;
  --colour-warning: #f59e0b;
  --colour-error: #ef4444;
  --colour-neutral-50: #f9fafb;
  --colour-neutral-900: #111827;

  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;

  /* Spacing */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  /* Border radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.07);

  /* Breakpoints (reference, used in media queries) */
  /* --bp-sm: 640px; --bp-md: 768px; --bp-lg: 1024px; --bp-xl: 1280px; */
}
  • Dark mode via [data-theme="dark"] selector on <html>
  • Corridor-specific branding tokens (optional override per deployment)
  • No CSS framework dependency — custom properties only

8. TypeScript Strict Mode

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true
  }
}
  • All .ts and .tsx files under strict mode
  • API response types shared with @simpaisa/sdk type definitions
  • No any types permitted (enforced by ESLint rule)

9. Testing

Unit tests (Vitest): - Test Preact islands in isolation with @testing-library/preact - Test utility functions and API client - Test auth middleware logic - Coverage threshold: >80%

E2E tests (Playwright): - Login flow (ControlPlane.com auth) - Transaction list navigation and filtering - Transaction detail view - Webhook configuration flow - Responsive layout verification (mobile, tablet, desktop) - Accessibility audit via @axe-core/playwright

10. Responsive Layout (Mobile-First)

  • Base styles target mobile (320px+)
  • Breakpoints: 640px (sm), 768px (md), 1024px (lg), 1280px (xl)
  • Sidebar collapses to hamburger menu on mobile
  • Tables scroll horizontally on small screens or switch to card layout
  • Touch-friendly tap targets (minimum 44x44px)
  • No horizontal scroll on any viewport width

11. Accessibility (WCAG 2.1 AA)

  • Semantic HTML: proper heading hierarchy, landmarks, labels
  • ARIA attributes on interactive islands
  • Keyboard navigation for all interactive elements
  • Focus indicators visible on all focusable elements
  • Colour contrast ratio minimum 4.5:1 (text), 3:1 (large text/UI)
  • Screen reader testing with VoiceOver and NVDA
  • prefers-reduced-motion respected for animations
  • Skip-to-content link on every page

12. API Client — src/lib/api.ts

export class ApiClient {
  constructor(private baseUrl: string, private token: string) {}

  async get<T>(path: string, params?: Record<string, string>): Promise<T>;
  async post<T>(path: string, body: unknown): Promise<T>;
  async put<T>(path: string, body: unknown): Promise<T>;
  async delete(path: string): Promise<void>;
}
  • Wraps fetch with automatic auth header injection
  • JSON serialisation/deserialisation
  • Error mapping to user-friendly messages
  • Request timeout (default 30s)
  • Automatic token refresh on 401

13. Deployment

# Preview (PR branch)
npx wrangler pages deploy ./dist --project-name=simpaisa-portal --branch=$BRANCH

# Production
npx wrangler pages deploy ./dist --project-name=simpaisa-portal --branch=main
  • Bitbucket Pipelines: lint → typecheck → test → build → deploy
  • Preview URL generated for every PR
  • Production deploy on merge to main
  • Environment variables injected via Cloudflare dashboard (secrets for auth config)

14. Performance Targets

Metric Target
Lighthouse Performance > 90
First Contentful Paint < 1.5s
Largest Contentful Paint < 2.5s
Cumulative Layout Shift < 0.1
Total JS shipped (initial) < 50KB gzipped
  • Astro ships zero JS by default; only island code is sent to client
  • Fonts preloaded and subset to Latin + Arabic character sets
  • Images optimised via Astro <Image> component

15. Security

  • CSP headers enforced via Cloudflare Pages _headers file
  • CSRF protection via double-submit cookie pattern
  • XSS protection: auto-escaped templates (Astro default), DOMPurify for dynamic HTML
  • Sensitive data (API keys) masked in UI, revealed on click
  • Session timeout: 30 minutes idle, 8 hours absolute
  • Audit log for sensitive actions (key creation, webhook changes)