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 helperslib/actions/review/reviewActions.ts- review CRUD and querieslib/actions/user/getAllUsersSimple.ts- user list helperslib/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 inparamsonpage.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-rule6) 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.