βοΈ Next.js 14 Frontend - Modern React dashboard with TypeScript, Tailwind CSS, and real-time Q&A interface.
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Pages β β Components β β Hooks β
β β β β β β
β β’ Dashboard βββββΊβ β’ Q&A Interface βββββΊβ β’ useQA β
β β’ Workspaces β β β’ File Upload β β β’ useWorkspaces β
β β’ Documents β β β’ Team Mgmt β β β’ useDocuments β
β β’ Settings β β β’ Auth Forms β β β’ useAuth β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β
βββββββββββββββββββ
β API Client β
β β
β β’ JWT Tokens β
β β’ HTTP Client β
β β’ Error Handle β
βββββββββββββββββββ
- Node.js 18+
- npm or yarn
- Backend API running on port 8000
# Navigate to frontend
cd frontend
# Install dependencies
npm install
# or
yarn install# Copy environment template
cp .env.example .env.local
# Edit environment variables
nano .env.localRequired Environment Variables:
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:8000
# Development
NODE_ENV=development# Start development server
npm run dev
# or
yarn dev
# Open browser
open http://localhost:3000- Tailwind CSS - Utility-first CSS framework
- shadcn/ui - Modern React component library
- Lucide Icons - Beautiful SVG icons
- Responsive Design - Mobile-first approach
src/components/
βββ ui/ # Base UI components (shadcn/ui)
β βββ button.tsx
β βββ card.tsx
β βββ input.tsx
β βββ ...
βββ auth/ # Authentication components
β βββ LoginForm.tsx
β βββ SignupForm.tsx
β βββ ProtectedRoute.tsx
βββ qa/ # Q&A interface components
β βββ QuestionInput.tsx
β βββ AnswerDisplay.tsx
β βββ SourceCard.tsx
β βββ QueryHistory.tsx
βββ workspaces/ # Workspace management
β βββ WorkspaceCard.tsx
β βββ AddWorkspaceModal.tsx
β βββ BackfillButton.tsx
βββ documents/ # Document management
β βββ FileUpload.tsx
β βββ DocumentList.tsx
β βββ UploadProgress.tsx
βββ team/ # Team management
βββ InviteUserModal.tsx
βββ UserTable.tsx
βββ RoleSelector.tsx
frontend/
βββ app/ # Next.js 14 App Router
β βββ (auth)/ # Auth route group
β β βββ login/
β β βββ signup/
β βββ dashboard/ # Protected dashboard routes
β β βββ page.tsx # Dashboard home
β β βββ qa/ # Q&A interface
β β βββ workspaces/ # Workspace management
β β βββ documents/ # Document upload
β β βββ team/ # Team management
β β βββ settings/ # Organization settings
β βββ layout.tsx # Root layout
β βββ page.tsx # Landing page
βββ src/
β βββ components/ # React components
β βββ hooks/ # Custom React hooks
β βββ lib/ # Utilities and configurations
β β βββ api.ts # API client
β β βββ auth.ts # JWT token management
β β βββ validations.ts # Form validation schemas
β βββ store/ # State management
β βββ useAuthStore.ts # Authentication store
βββ public/ # Static assets
βββ package.json # Dependencies and scripts
βββ tailwind.config.js # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ next.config.js # Next.js configuration
- JWT Token Management - Secure token storage and refresh
- Protected Routes - Route-level authentication guards
- Persistent Sessions - Automatic login state restoration
// Example: Using authentication
import { useAuthStore } from '@/src/store/useAuthStore';
function MyComponent() {
const { user, login, logout, isAuthenticated } = useAuthStore();
if (!isAuthenticated) {
return <LoginForm />;
}
return <Dashboard user={user} />;
}- Real-time Search - Instant AI-powered responses
- Source Attribution - Links to original Slack messages
- Query History - Persistent search history
- Confidence Scoring - AI confidence indicators
// Example: Q&A hook usage
import { useQA } from '@/src/hooks/useQA';
function QAInterface() {
const { askQuestion, isLoading, response } = useQA();
const handleSubmit = (question: string) => {
askQuestion({
question,
workspace_id: 'selected-workspace',
include_documents: true
});
};
return (
<div>
<QuestionInput onSubmit={handleSubmit} isLoading={isLoading} />
{response && <AnswerDisplay response={response} />}
</div>
);
}- Drag & Drop Upload - Intuitive file upload interface
- Multi-format Support - PDF, DOCX, TXT, Markdown
- Upload Progress - Real-time upload status
- Workspace Tagging - Associate documents with workspaces
- User Invitations - Email-based team invitations
- Role Management - Admin, Member, Viewer roles
- User Status - Active/inactive user management
// Authentication
const { user, login, logout, isAuthenticated } = useAuthStore();
// Workspaces
const { data: workspaces, isLoading } = useWorkspaces();
const addWorkspaceMutation = useAddWorkspace();
// Documents
const { data: documents } = useDocuments(workspaceId);
const uploadMutation = useUploadDocuments();
// Q&A
const { askQuestion, response, isLoading } = useQA();
// Team Management
const { data: members } = useTeamMembers();
const inviteMutation = useInviteUser();// Zustand store for authentication
import { create } from 'zustand';
interface AuthState {
user: User | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
export const useAuthStore = create<AuthState>((set) => ({
user: null,
isAuthenticated: false,
login: async (email, password) => {
// Login logic
},
logout: () => {
// Logout logic
}
}));/* Common patterns */
.btn-primary {
@apply bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg;
}
.card {
@apply bg-white rounded-lg shadow-sm border p-6;
}
.input {
@apply border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500;
}// Example: Styled component
function Button({ variant = 'primary', children, ...props }) {
const baseClasses = 'px-4 py-2 rounded-lg font-medium transition-colors';
const variantClasses = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
outline: 'border border-gray-300 hover:bg-gray-50'
};
return (
<button
className={`${baseClasses} ${variantClasses[variant]}`}
{...props}
>
{children}
</button>
);
}// src/lib/api.ts
class ApiClient {
private baseURL: string;
constructor(baseURL: string) {
this.baseURL = baseURL;
}
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseURL}${endpoint}`;
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
...TokenManager.getAuthHeader(),
...options.headers,
},
...options,
};
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
// API methods
async login(email: string, password: string) {
return this.request('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
}
}# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Type checking
npm run type-check
# Linting
npm run lint// Component test
import { render, screen } from '@testing-library/react';
import { LoginForm } from '@/src/components/auth/LoginForm';
test('renders login form', () => {
render(<LoginForm />);
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
});
// Hook test
import { renderHook } from '@testing-library/react';
import { useAuthStore } from '@/src/store/useAuthStore';
test('authentication store', () => {
const { result } = renderHook(() => useAuthStore());
expect(result.current.isAuthenticated).toBe(false);
});# Build for production
npm run build
# Start production server
npm start
# Export static site (if needed)
npm run export- Code Splitting - Automatic route-based code splitting
- Image Optimization - Next.js Image component
- Bundle Analysis - Webpack bundle analyzer
- Caching - React Query for API response caching
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}// tailwind.config.js
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./src/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
}
}
},
},
plugins: [require('@tailwindcss/forms')],
}# Enable React DevTools
npm install --save-dev @next/bundle-analyzer
# Analyze bundle size
ANALYZE=true npm run build
# Debug API calls
# Check Network tab in browser DevTools-
Hydration Mismatch
// Use dynamic imports for client-only components import dynamic from 'next/dynamic'; const ClientOnlyComponent = dynamic( () => import('./ClientOnlyComponent'), { ssr: false } );
-
CORS Issues
// Check API_URL in .env.local NEXT_PUBLIC_API_URL=http://localhost:8000
/* Tailwind CSS breakpoints */
sm: 640px /* Small devices */
md: 768px /* Medium devices */
lg: 1024px /* Large devices */
xl: 1280px /* Extra large devices */
2xl: 1536px /* 2X large devices */// Example: Responsive component
function ResponsiveGrid({ children }) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{children}
</div>
);
}- Follow React/Next.js best practices
- Use TypeScript for all components
- Write tests for new features
- Follow the established component structure
- Use Tailwind CSS for styling