Skip to content

kiSchlag/Nik-Frontend

Repository files navigation

React Vite Tailwind CSS DaisyUI React Router

Nik — Technical Support Agent Frontend

Frontend for an AI-powered technical support agent that helps field technicians get instant answers from product documentation.


Overview

A solar energy installer on a rooftop with an inverter error code needs an answer now — not after a phone queue, an email chain, and a callback the next day. The backend provides the AI agent that searches product documentation and generates precise, cited answers. This frontend gives the technician a way to use it: a single chat interface where they type a question or photograph an error code and receive a streamed response with document citations.

The chat displays the agent's work in real time. When the agent searches documentation, the interface shows a tool status indicator. As the answer generates, tokens stream into the conversation word by word. Source citations appear below each response with document name and page number. Sessions persist across visits — the technician can return to a previous conversation, search session history, or start a new thread. Images can be attached via drag-and-drop for the agent to analyze alongside the question. The entire interface is in German, the working language of the installers.

The application also serves as the product landing page — hero section, product showcase, installation gallery, testimonials, and contact information — with a protected chat area behind a full authentication system: registration with 8-digit email verification, password recovery, and automatic idle logout after 30 minutes.


Tech Stack

Layer Technology
Build tool Vite 7.2 (ESM, HMR, code splitting)
UI framework React 18.3
Routing React Router 6.30 (lazy loading, protected routes)
Styling Tailwind CSS 4.1 (CSS-first config) + DaisyUI 5.5 (custom nikola theme)
Markdown marked + DOMPurify (XSS-safe HTML rendering)
Real-time streaming Custom SSE client built on Fetch API ReadableStream
Security JWT authentication + react-idle-timer (30-min auto-logout)
Utilities clsx (conditional class composition)

Key Capabilities

Capability Description
Chat Experience
SSE streaming Token-by-token response display with live tool status indicators during document search
Markdown rendering XSS-sanitized markdown with Tailwind Typography prose styling
Source citations Each response displays linked document name and page number citations below the answer
Image upload Drag-and-drop or click-to-attach photos for AI analysis with inline preview before sending
Streaming link masking Incomplete markdown links are replaced with shimmer placeholders during streaming, then resolved on completion
Session Management
Persistent sessions Sidebar with session history grouped by date (Heute, Gestern, Letzte 7 Tage, Älter)
Session operations Create, rename, pin, delete, and search sessions with optimistic UI updates and rollback on failure
URL-based routing Each session has a deep-linkable /chat/:sessionId route
Authentication
JWT auth flow Register, verify email (8-digit code), login, forgot password, reset password — five dedicated views
Protected routes ProtectedRoute component redirects unauthenticated users to login with return-path preservation
Auto-logout 30-minute idle timeout via react-idle-timer with toast notification on session expiry
Global 401 handling Any expired-token API response triggers automatic cleanup and redirect to login
Performance
Code splitting Every page lazy-loaded via React.lazy + Suspense with spinner fallbacks
Rate limit handling 429 responses trigger a countdown banner showing seconds until the next message is allowed
Intersection animations Landing page sections animate in on scroll via IntersectionObserver
Landing Page
Multi-section layout Hero, products, gallery, about, testimonials, and contact sections with responsive design
Mobile-first Full mobile, tablet, and desktop support with collapsible sidebar and hamburger navigation

Architecture

Component Layer System

flowchart TD
    A[05-pages] --> B[04-layouts]
    A --> C[03-features]
    A --> D[02-shared]
    A --> E[01-ui]

    B --> C
    B --> D
    B --> E

    C --> D
    C --> E

    D --> E

    A1["HomePage, ChatPage\nLoginPage, RegisterPage\nVerifyEmailPage, ForgotPasswordPage\nResetPasswordPage"] -.-> A
    B1["MainLayout\nChatLayout"] -.-> B
    C1["auth, chat, hero\nproducts, gallery, about\ntestimonials, contact"] -.-> C
    D1["hooks, utils, context\nconstants, components\nNavbar, Footer, ProtectedRoute"] -.-> D
    E1["Button, Input, Card\nModal, Badge, Spinner\nMarkdownContent, IconButton"] -.-> E

    style A fill:#dbeafe,stroke:#93c5fd,color:#1e3a5f
    style B fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style C fill:#fef3c7,stroke:#fcd34d,color:#713f12
    style D fill:#fef3c7,stroke:#fcd34d,color:#713f12
    style E fill:#d1fae5,stroke:#6ee7b7,color:#064e3b
    style A1 fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style B1 fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style C1 fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style D1 fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style E1 fill:#f8fafc,stroke:#cbd5e1,color:#334155
Loading

Chat Data Flow

flowchart LR
    U[User Input] --> CI[ChatInput]
    CI --> UC[useChat Hook]
    UC --> SSE[SSE Client]
    SSE --> API[Backend API]

    SSE --- E1[metadata → session ID]
    SSE --- E2[tool_start → status indicator]
    SSE --- E3[token → streaming content]
    SSE --- E4[sources → citation list]
    SSE --- E5[done → final message]

    UC --> CM[ChatMessage]
    CM --> MD[MarkdownContent]
    MD --> R[Rendered Response]

    style U fill:#dbeafe,stroke:#93c5fd,color:#1e3a5f
    style CI fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style UC fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style SSE fill:#fef3c7,stroke:#fcd34d,color:#713f12
    style API fill:#fef3c7,stroke:#fcd34d,color:#713f12
    style CM fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style MD fill:#f8fafc,stroke:#cbd5e1,color:#334155
    style R fill:#d1fae5,stroke:#6ee7b7,color:#064e3b
Loading

Imports flow strictly downward. A layer may import from any layer below it but never from a layer above. Every layer exports through barrel files (index.js) using named exports only.


Streaming Protocol

The frontend consumes the backend's POST /chat/stream endpoint via a custom SSE client built on the Fetch API's ReadableStream.

Event Handler What the UI does
metadata onMetadata Stores session ID; creates sidebar entry for new sessions
tool_start onToolStart Displays tool status label (e.g., "Dokumente durchsuchen...")
token onToken Appends content to streaming message; masks incomplete markdown links with shimmer placeholders
sources onSources Stores citation array for display below the completed message
done onDone Finalizes message in history; resolves masked links; refreshes session list
error onError Shows toast notification; handles 429 with countdown timer; preserves partial content on connection loss

Routes

Route Page Auth Layout
/ HomePage MainLayout
/chat ChatPage Required ChatLayout
/chat/:sessionId ChatPage Required ChatLayout
/login LoginPage Standalone
/register RegisterPage Standalone
/verify-email VerifyEmailPage Standalone
/forgot-password ForgotPasswordPage Standalone
/reset-password ResetPasswordPage Standalone

Project Structure

📁 src/
├── 01-ui/                              # Stateless UI primitives
│   ├── Button.jsx                      # Primary, secondary, accent, outline, ghost variants
│   ├── Input.jsx                       # Form input with error state and forwardRef
│   ├── Card.jsx                        # Content container with Card.Body and Card.Title
│   ├── Modal.jsx                       # Dialog overlay
│   ├── Badge.jsx                       # Status indicators
│   ├── Spinner.jsx                     # Loading spinner
│   ├── IconButton.jsx                  # Icon-only button
│   ├── SectionHeading.jsx              # Landing page section titles
│   ├── MarkdownContent.jsx             # XSS-safe markdown renderer (marked + DOMPurify)
│   └── index.js                        # Barrel export
│
├── 02-shared/                          # Cross-feature infrastructure
│   ├── components/                     # Navbar, Footer, ProtectedRoute, LazyImage,
│   │                                   # ErrorBoundary, ToastContainer, ScrollToTop
│   ├── hooks/                          # useClickOutside, useDebounce, useFetch,
│   │                                   # useIntersection, useLocalStorage,
│   │                                   # useMediaQuery, useScrollPosition
│   ├── utils/                          # api-client (apiFetch, apiPost, apiPut, apiPatch, apiDelete),
│   │                                   # sse-client (connectSSE), token-store, retry (withRetry)
│   ├── context/                        # AuthProvider + useAuth, ToastProvider + useToast
│   └── constants/                      # routes, endpoints, brand, landing-data
│
├── 03-features/                        # Domain slices
│   ├── auth/                           # auth.service (register, verify, login, forgot/reset password),
│   │                                   # useIdleLogout (30-min timeout)
│   ├── chat/                           # chat.service, useChat, useChatStream, useAuthImage,
│   │   │                               # maskIncompleteLinks, groupSessionsByDate
│   │   └── components/                 # ChatInput, ChatMessage, ChatSources,
│   │                                   # ChatTypingIndicator, ChatImagePreview,
│   │                                   # AuthImage, MessageActionBar
│   ├── hero/components/                # HeroSection
│   ├── products/components/            # ProductsSection
│   ├── gallery/components/             # GallerySection
│   ├── about/components/               # AboutSection
│   ├── testimonials/components/        # TestimonialsSection
│   └── contact/components/             # ContactSection
│
├── 04-layouts/                         # Page shells
│   ├── MainLayout.jsx                  # Landing: Navbar + content + Footer
│   └── ChatLayout.jsx                  # Chat: full-viewport with idle logout
│
├── 05-pages/                           # Thin orchestration pages
│   ├── home-page/HomePage.jsx          # Composes all landing sections
│   ├── chat-page/                      # ChatSidebar + ChatMainArea + ChatWelcome
│   │   └── components/                 # Page-specific chat subcomponents
│   ├── login-page/                     # Email + password login
│   ├── register-page/                  # Registration form
│   ├── verify-email-page/              # 8-digit email verification
│   ├── forgot-password-page/           # Password recovery request
│   └── reset-password-page/            # Password reset with code
│
├── App.jsx                             # ToastProvider → AuthProvider → RouterProvider
├── routes.jsx                          # React Router config with lazy loading
├── index.css                           # Tailwind imports, DaisyUI plugin, custom "nikola" theme, animations
└── main.jsx                            # ReactDOM entry point

How the Chat Works

1. Connect

When the user types a message, useChat coordinates the flow: upload any attached images first, then call useChatStream.sendStreaming(). The custom SSE client opens a POST connection to /chat/stream using the Fetch API with ReadableStream. The Authorization: Bearer <token> header is attached from the in-memory token store, which stays synchronized with the React AuthContext. For new conversations, the backend returns a session ID in the first metadata event; for existing sessions, the stored ID is sent in the request body.

2. Stream

The SSE client parses the response byte stream line by line, dispatching each event to its registered callback. A tool_start event replaces the typing indicator with a tool status label. As token events arrive, content appends to the streaming message in real time. Incomplete markdown links during streaming are masked with shimmer placeholders using a regex-based detection in maskIncompleteLinks — this prevents broken [text](partial-url fragments from rendering. If the connection drops mid-stream, partial content is preserved in the conversation rather than discarded.

3. Render

When the done event fires, the complete answer is stored in message history and the session list refreshes. MarkdownContent converts the final markdown to HTML via marked, sanitizes it through DOMPurify, and renders it with Tailwind Typography prose classes. Source citations — document name and page number pairs returned in the sources event — are displayed below the message as linked references.


Getting Started

Prerequisites

  • Node.js 18+
  • The backend running at http://localhost:8000 (see backend README)

Setup

# 1. Install dependencies
npm install

# 2. Configure environment
cp .env.example .env
# Edit .env — set VITE_API_BASE_URL if backend is not at localhost:8000

# 3. Start development server
npm run dev
# App: http://localhost:5173

# 4. Build for production
npm run build
npm run preview

Development Approach

Built in focused, mergeable increments — each PR self-contained and deployable:

PR Title What was built
#1–3 Project Setup Vite scaffold, ESLint + Prettier, 5-layer architecture with barrel exports
#4 Shared Layer + UI Primitives 9 UI components, 7 custom hooks, API client, SSE client, route and endpoint constants
#5 Landing Page Hero, products, gallery, about, testimonials, contact sections with responsive layout and scroll animations
#6 Chat Core Chat service, session management, message history, useChat orchestration hook
#7 Chat Streaming SSE streaming with tool status indicators, markdown rendering, typing indicator
#8 Chat Polish + UX Markdown improvements, message action bar, mobile UX enhancements
#9 Production Hardening Error handling, rate limiting (429 + countdown), image upload with drag-and-drop and 20 MB limit
#10–13 Testing + Bug Fixes Image upload race condition, session history loading, malformed request fix
#14–17 Chat UI Overhaul Spacious layout, message actions, source-link masking during streaming, collapsible sidebar
#18 Full-Page Chat Replace floating widget with dedicated /chat route, ChatLayout, idle logout integration
#19 Full Authentication JWT auth system: register, 8-digit email verification, login, forgot/reset password, ProtectedRoute

The codebase follows strict conventions: named exports only, barrel files (index.js) at every layer, @/ path aliases, PascalCase .jsx components as function declarations, and use-kebab-case.js hooks. Tailwind CSS 4 uses CSS-first configuration — no tailwind.config.js — with a custom DaisyUI theme defined in index.css using oklch colors calibrated for WCAG AA contrast.


Configuration

Variable Description
VITE_API_BASE_URL Backend API URL (default: http://localhost:8000)

All configuration follows Vite's environment variable convention (VITE_ prefix). The production build embeds these values at build time via import.meta.env.


Released under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors