Staff Portal

Internal operations area for Viet Vibe Foundation.

Portal Coding Rules

1) Use shared color system

Always use colors from lib/ui/css/globals.css and tailwind.config.ts instead of hardcoded hex values.

// Good examples
<div className="bg-bgColor-brand900 text-textColor-white" />
<p className="text-textColor-secondary600" />
<button className="bg-bgColor-gray500 hover:bg-bgColor-black/80" />

2) Reuse existing components in /components

Reuse design system components (for example components/ui/button.tsx) and follow existing usage patterns in current pages before creating new UI primitives.

import { Button } from '@/components/ui/button'

<Button variant="default">Save</Button>
<Button variant="outline">Cancel</Button>

3) Prefer existing functions in /lib (especially /lib/actions)

Before writing new business logic, check existing actions and reuse them when possible.

  • lib/actions/event/getEvent.ts - event query helpers
  • lib/actions/review/reviewActions.ts - review CRUD and queries
  • lib/actions/user/getAllUsersSimple.ts - user list helpers
  • lib/actions/payment/linkGuestPayments.ts - payment/user linking

4) Database changes must follow Prisma workflow

To add or update database models, edit prisma/schema.prisma and then run migration immediately:

npx prisma migrate dev

5) Next.js route & pathname naming

In the App Router, folder names are URL segments unless the folder is special-cased. Follow these naming rules for pathnames:

  • URL segments (folders): use kebab-case — lowercase words with hyphens (e.g. portal-home, coding-rule,create-event). Avoid camelCase or spaces in segment names.
  • Dynamic segments: bracket folder names — [locale], [eventKeyName]. Param names are camelCase by convention; they appear in params on page.tsx / layout.tsx.
  • Route groups: parentheses — (Home), (auth). The group name is not part of the URL; it only groups layouts.
  • Private folders (underscore): a segment whose name starts with _ — for example _components, _lib — is not part of the URL. Use these to colocate components, hooks, or tests next to a route without adding another pathname segment. (Same idea as route groups, but marked explicitly as implementation-only.)
  • Reserved file names: page.tsx (route UI), layout.tsx (nested layout), loading.tsx, error.tsx, route.ts (Route Handler). File name is fixed; only the folder path becomes the pathname.
  • Catch-all / optional catch-all: [...slug], [[...slug]] — use when one segment must capture multiple path parts.
app/[locale]/portal-home/page.tsx
  -> URL path: /{locale}/portal-home

app/[locale]/portal-home/coding-rule/page.tsx
  -> URL path: /{locale}/portal-home/coding-rule

app/[locale]/(Home)/events/page.tsx
  -> URL path: /{locale}/events   // (Home) is omitted from the pathname

app/[locale]/(Home)/profile/_components/Foo.tsx
  -> _components does not appear in the URL; still /{locale}/profile/...

On portal.vietvibe.org, middleware rewrites short URLs to portal-home internally:
  /{locale}  ->  /{locale}/portal-home
  /{locale}/coding-rule  ->  /{locale}/portal-home/coding-rule

6) No localization required for portal pages

For portal features, localization is not required. Keep content in one language unless explicitly requested.

7) Use Prisma cache + revalidation pattern

Read documentation/CACHE_REVALIDATION_DOCUMENTATION.md and follow existing unstable_cache, tag, and invalidation patterns (for example events tag revalidation) when building new data queries.