Customizing the UI
Relevant source files
The following files were used as context for generating this wiki page:The user interface of the QA Dashboard is built using Next.js and styled with Tailwind CSS. The UI is designed to be responsive, dynamic, and state-aware, with customization handled through a combination of global styles, component-specific logic, and a well-defined component architecture. This document outlines the key aspects of the UI structure and how it can be customized.
The frontend technology stack includes Next.js, React, and Tailwind CSS, which enables a utility-first approach to styling and a component-based structure.
Sources: README.md:16-19, web/package.json:13-19
Global Styling and Theming
Global styles, fonts, and CSS variables are defined at the root of the application, providing a consistent base for all components.
Fonts
The project uses next/font to load the Geist and Geist_Mono font families. These are applied globally to the application body within the root layout, ensuring consistent typography.
Sources: web/src/app/layout.js:1-15
// web/src/app/layout.js:1-15
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { AuthProvider } from '@/context/AuthContext';
import { WebSocketProvider } from '@/context/WebSocketContext';
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
CSS Variables and Theming
The primary mechanism for theming (e.g., light/dark mode) is through CSS variables defined in web/src/app/globals.css. These variables control the application's background and foreground colors. A @media (prefers-color-scheme: dark) query is used to switch these variables for dark mode.
The following table details the core CSS variables:
| Variable | Light Mode Value | Dark Mode Value | Description |
|---|---|---|---|
--background | #ffffff | #0a0a0a | The main background color of the application. |
--foreground | #000000 | #ededed | The primary text color. |
--font-sans | var(--font-geist-sans) | var(--font-geist-sans) | The default sans-serif font. |
--font-mono | var(--font-geist-mono) | var(--font-geist-mono) | The default monospace font. |
Sources: web/src/app/globals.css:3-23
Component Architecture
The UI is built with a modular, component-based architecture. Pages in the app directory compose smaller, reusable components from the src/components directory to build complex interfaces.
The diagram below illustrates the component hierarchy for the main forum page.
This diagram shows that RootLayout wraps all pages, and ForumPage assembles the main view using Header, QuestionForm, and QuestionList.
Sources: web/src/app/layout.js, web/src/app/forum/page.js, web/src/components/QuestionList.js, web/src/components/QuestionCard.js
Key UI Components
| Component | File Path | Description |
|---|---|---|
Header | web/src/components/Header.js | Displays the application title and navigation. Conditionally renders user info/logout or login/register buttons. |
QuestionForm | web/src/components/QuestionForm.js | A form for submitting new questions. Includes validation and a duplicate question warning. |
QuestionList | web/src/components/QuestionList.js | Renders a list of QuestionCard components. |
QuestionCard | web/src/components/QuestionCard.js | Displays a single question, its status, and provides controls for viewing answers, getting AI suggestions, and (for admins) changing status. |
AnswerForm | web/src/components/AnswerForm.js | A form for submitting an answer to a specific question. |
AnswerList | web/src/components/AnswerList.js | Renders a list of answers for a question. |
Sources: web/src/components/Header.js, web/src/components/QuestionForm.js, web/src/components/QuestionList.js, web/src/components/QuestionCard.js, web/src/components/AnswerForm.js, web/src/components/AnswerList.js
Dynamic and Conditional UI
The UI dynamically changes based on application state, such as user authentication, data from the server, and real-time events.
Authentication-Based Rendering
The Header component is a key example of conditional rendering. It checks if a user is authenticated to decide whether to display user information and a "Logout" button or "Login" and "Register" links.
The following flow chart illustrates this logic:
This logic is implemented in the Header.js component.
Sources: web/src/components/Header.js:15-45
State-Driven Styling
Components use state to apply different styles. The QuestionCard component changes its appearance based on the question's status property (Pending, Escalated, Answered).
The status is visually indicated by a colored badge and a left border on the card.
| Status | Badge CSS Classes | Border CSS Class |
|---|---|---|
Pending | bg-yellow-100 text-yellow-800 | border-blue-500 |
Escalated | bg-red-100 text-red-800 | border-red-500 |
Answered | bg-green-100 text-green-800 | border-green-500 |
Sources: web/src/components/QuestionCard.js:14-18, web/src/components/QuestionCard.js:93-97
The implementation uses a statusColors map to dynamically select the correct CSS classes.
// web/src/components/QuestionCard.js:14-18
const statusColors = {
'Pending': 'bg-yellow-100 text-yellow-800',
'Escalated': 'bg-red-100 text-red-800',
'Answered': 'bg-green-100 text-green-800'
};
Real-time UI Updates
The forum page reflects real-time updates via WebSockets. A connection status indicator visually informs the user if they are connected to the live update service.
// web/src/app/forum/page.js:52-57
<div className="flex items-center gap-2">
<div className={`w-3 h-3 rounded-full ${isConnected ? 'bg-green-500' : 'bg-gray-300'}`}></div>
<span className="text-sm text-gray-600">
{isConnected ? 'Connected' : 'Disconnected'}
</span>
</div>
When disconnected and no questions are loaded, a loading spinner is shown, enhancing the user experience by providing clear feedback on the application's state.
Sources: web/src/app/forum/page.js:52-57, web/src/app/forum/page.js:67-72
Conditional Warnings
The QuestionForm component provides real-time feedback to users. When a user types a question that is potentially a duplicate of an existing one, a warning message is displayed. This is handled via a debounced API call. The warning UI is only rendered if the duplicateWarning state variable is not null.