Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - badhon252/Second-sight: Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI · GitHub
Skip to content

Repository files navigation

🚀 Next.js Full-Stack Template

A production-ready, opinionated Next.js 16 template with a clean feature-driven architecture, authentication, typed API layer, and developer tooling pre-configured out of the box.


📋 Table of Contents


🧰 Tech Stack

CategoryLibrary / ToolVersion
FrameworkNext.js16.1.4
LanguageTypeScript^5
StylingTailwind CSS^4
UI ComponentsShadcn UI + Radix UILatest
IconsLucide React^0.553.0
AuthenticationNextAuth.js^4.24
Data FetchingAxios + TanStack Query^1.13 / ^5.90
FormsReact Hook Form + Zod^7.66 / ^3.25
State ManagementZustand^5.0
Rich Text EditorTiptap^3.17
AnimationsFramer Motion^12
NotificationsSonner^2.0
TestingJest + Testing Library^30
LintingESLint + Prettier^9 / ^3
Commit HooksHusky + Lint-staged + Commitlint^8 / ^16 / ^20

📁 Project Structure

src/
├── app/ # Next.js App Router
│ ├── api/
│ │ └── auth/
│ │ └── [...nextauth]/ # NextAuth route handler
│ │ └── route.ts
│ ├── layout.tsx # Root layout with global providers
│ ├── page.tsx # Home page
│ ├── globals.css # Global styles
│ └── not-found.tsx # Custom 404 page
│
├── Providers/
│ ├── MainProviders.tsx # TanStack Query client provider
│ └── Provider.tsx # NextAuth SessionProvider
│
├── components/
│ ├── ui/ # Shadcn UI primitives (Button, Input, Dialog, etc.)
│ └── shared/ # Cross-feature shared components
│
├── features/
│ ├── auth/
│ │ └── api/
│ │ └── refresh-token.api.ts # Token refresh used by NextAuth
│ └── sample-feature/ # Reference architecture — copy this for new features
│ ├── api/ # API call functions (uses src/lib/api.ts)
│ ├── components/ # Feature-specific UI components
│ ├── hooks/ # TanStack Query custom hooks
│ └── types.ts # Feature TypeScript types
│
├── hooks/ # Global reusable hooks
│ └── readme.md
│
├── lib/
│ ├── api.ts # Axios instance with auth interceptors
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── indexed-db-storage.ts # IndexedDB helpers
│ └── readme.md
│
├── store/
│ ├── ui.store.ts # Global UI state (Zustand)
│ └── readme.md
│
├── types/
│ ├── next-auth.d.ts # Extended NextAuth TypeScript types
│ └── readme.md
│
├── tests/ # Jest unit test files
│
└── proxy.ts # Next.js middleware for RBAC routing

⚡ Getting Started

Prerequisites

  • Node.js 18+
  • npm or pnpm

1. Clone the repository

git clone <your-repo-url>cd<project-folder>

2. Install dependencies

npm install

3. Set up environment variables

cp example.env.local .env.local

Edit .env.local with your actual values (see Environment Variables).

4. Run the development server

npm run dev

Open http://localhost:3000 in your browser.


🔑 Environment Variables

Copy example.env.local to .env.local and fill in the following:

VariableRequiredDescription
NEXT_PUBLIC_API_URLBase URL of your backend REST API (e.g. http://localhost:5001/api/v1)
NEXTAUTH_SECRETA random secret string used to encrypt JWT sessions. Generate with: openssl rand -hex 32
NEXTAUTH_URLThe canonical URL of your deployed app (e.g. http://localhost:3000 for local)
NEXT_PUBLIC_SOCKET_URLOptional WebSocket server URL

Security: Never commit your .env.local file. It is already listed in .gitignore.


🔐 Authentication

Authentication is handled via NextAuth.js v4 using its Credentials Provider strategy.

How It Works

  1. The user submits their email and password.
  2. NextAuth calls your backend POST /auth/login endpoint.
  3. On success, the user object and accessToken are stored in a JWT session.
  4. The JWT is automatically refreshed when it expires (1 hour default), via POST /auth/refresh-access-token.
  5. If the refresh fails, the session is destroyed and the user is signed out.

Session Shape

The session is extended to include custom fields. The types are declared in src/types/next-auth.d.ts:

session.user={id: string;
name: string;
email: string;
image: string;// Maps to profileImage from backend
role: string;// e.g. "ADMIN" | "USER"};session.accessToken=string;session.refreshToken=string;

Accessing the Session

Client-side (in a Client Component):

"use client";import{useSession}from"next-auth/react";exportdefaultfunctionMyComponent(){const{data: session}=useSession();// session.accessToken, session.user.role, etc.}

Server-side (in a Server Component or API Route):

import{getServerSession}from"next-auth";import{authOptions}from"@/app/api/auth/[...nextauth]/route";constsession=awaitgetServerSession(authOptions);

🌐 API Layer

All HTTP requests go through the centralized Axios instance at src/lib/api.ts.

Features

  • Auto-auth injection: Every request automatically includes the Bearer token from the active NextAuth session.
  • Auto-retry on 401: If a request fails with a 401, it tries to use the refreshed token from the session and retries once.
  • Auto-signout: If the token refresh has failed (RefreshAccessTokenError), the user is immediately signed out and redirected to /login.

Usage

import{api}from"@/lib/api";// Example GET requestconstresponse=awaitapi.get("/users");// Example POST requestconstresponse=awaitapi.post("/users",{name: "John"});

Note: You never need to manually set Authorization headers. The interceptor handles it globally.


🧩 Feature Pattern

All business logic lives inside src/features/. Each feature is a self-contained module with a consistent internal structure. Use src/features/sample-feature as your starting point.

Structure

src/features/your-feature/
├── api/
│ └── your-feature.api.ts # Raw API call functions
├── components/
│ └── YourComponent.tsx # Feature UI components
├── hooks/
│ └── useYourFeature.ts # TanStack Query hooks
└── types.ts # TypeScript types for this feature

Step-by-Step: Adding a New Feature

1. Define your types (types.ts):

exportinterfaceUser{id: string;name: string;email: string;}

2. Write your API function (api/users.api.ts):

import{api}from"@/lib/api";import{User}from"../types";exportasyncfunctiongetUsers(): Promise<User[]>{constres=awaitapi.get("/users");returnres.data;}

3. Create a TanStack Query hook (hooks/useUsers.ts):

import{useQuery}from"@tanstack/react-query";import{getUsers}from"../api/users.api";exportfunctionuseUsers(){returnuseQuery({queryKey: ["users"],queryFn: getUsers,});}

4. Consume in a component (components/UserList.tsx):

"use client";import{useUsers}from"../hooks/useUsers";exportdefaultfunctionUserList(){const{ data, isLoading, error }=useUsers();if(isLoading)return<p>Loading...</p>;if(error)return<p>Error loading users.</p>;return(<ul>{data?.map((user)=>(<likey={user.id}>{user.name}</li>))}</ul>);}

5. Add a route in src/app/(your-group)/your-route/page.tsx and import the component.


🗂️ State Management

Global UI state is managed with Zustand in src/store/.

Adding a New Store

// src/store/example.store.tsimport{create}from"zustand";interfaceExampleState{count: number;increment: ()=>void;}exportconstuseExampleStore=create<ExampleState>((set)=>({count: 0,increment: ()=>set((state)=>({count: state.count+1})),}));

Convention: Keep stores small and focused. One store per concern (e.g. ui.store.ts, sidebar.store.ts).


🛡️ Routing & Middleware

The src/proxy.ts file is a Next.js Middleware that runs at the edge before any page renders. It handles Role-Based Access Control (RBAC).

Current Rules

ConditionAction
Unauthenticated user visits /dashboardRedirect to /login?callbackUrl=...
Non-admin user visits /dashboardRedirect to /
All other requestsnext() — passes through

Customizing Routes

Edit the matcher in src/proxy.ts to control which paths trigger the middleware:

exportconstconfig={matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico|sitemap.xml|robots.txt).*)",],};

Adding Route Groups

Create a folder with parentheses in src/app/ to group routes without affecting the URL:

src/app/
├── (dashboard)/
│ └── dashboard/
│ └── page.tsx → URL: /dashboard
├── (auth)/
│ └── login/
│ └── page.tsx → URL: /login

🎨 UI Components

All primitive UI components come from Shadcn UI and live in src/components/ui/.

Available Components

ComponentDescription
ButtonMulti-variant button (default, outline, ghost, etc.)
InputStyled form input
LabelAccessible form label
DialogModal dialog
SelectDropdown select
CheckboxAccessible checkbox
AvatarUser avatar with fallback
Dropdown MenuContext/dropdown menus
AccordionCollapsible accordion panels
And more...Run npx shadcn add <component> to add more

Adding a New Shadcn Component

npx shadcn add <component-name># e.g.
npx shadcn add sheet
npx shadcn add calendar

🛠️ Developer Tooling

ESLint

Configured in eslint.config.mjs. Extends eslint-config-next.

npm run lint

Prettier

Config in .prettierrc. Auto-formats on commit via lint-staged.

# Format all files manually
npx prettier --write .

Husky + Lint-staged

Pre-commit hooks auto-run linting on staged files before every commit.

Config in .lintstagedrc.json and .lintstagedrc.

Jest

Unit and integration tests live in src/tests/.

npm run test# Run all tests once
npm run test:watch # Run tests in watch mode

TypeScript Type Checking

npm run type-check # Runs tsc --noEmit

📜 Scripts Reference

ScriptCommandDescription
Developmentnpm run devStart the dev server with Webpack
Buildnpm run buildCreate an optimized production build
Startnpm run startStart the production server
Lintnpm run lintRun ESLint
Testnpm run testRun Jest tests
Test Watchnpm run test:watchRun tests in watch mode
Type Checknpm run type-checkTypeScript type validation
Commitnpm run commitInteractive commit with Commitizen

📝 Commit Convention

This project enforces Conventional Commits via Commitlint. Every commit message must follow the format:

<type>: <subject>

Allowed Types

TypeWhen to Use
featA new feature
fixA bug fix
docsDocumentation changes only
styleCode style / formatting (no logic change)
refactorCode refactoring (no feature or bug fix)
testAdding or updating tests
buildBuild system or dependency changes
choreMaintenance tasks
ciCI/CD configuration changes
perfPerformance improvements
revertReverting a previous commit
securitySecurity-related changes

Examples

feat: add user profile page
fix: resolve token expiry loop on 401
docs: update environment variable guide
refactor: extract api calls into feature module

Using Commitizen (Interactive)

npm run commit

This launches an interactive CLI to guide you through writing a valid commit message.


📄 License

This project is a starter template. You are free to use, modify, and distribute it.

About

Enabling Businesses and Organizations to Create High-Quality, Professional Scenario Plans with AI

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages