Chapter 1: Web Interface (Frontend)#
Welcome to the first chapter of the Tutorial-Codebase-Knowledge project tutorial!
Imagine you're visiting an online store. You see a beautifully designed website, buttons to click, forms to fill out (like your shipping address), and pictures of products. That's the Web Interface or Frontend. It's everything you see and interact with directly in your web browser.
In our project, the Web Interface is the friendly face that helps you turn a complex codebase into an easy-to-follow tutorial. Think of it as the special tool you use to tell the system what you want, and then where you read the awesome result.
What Does Our Web Interface Do?#
Our project's frontend has two main jobs:
- Getting Information: It asks you for the details needed to create a tutorial. This includes things like the location (URL) of the code you want to explain and any special access keys (like API keys or GitHub tokens) required.
- Showing the Result: Once the tutorial is ready, the frontend fetches it and displays it neatly. This includes showing the different chapters and lessons in a navigation sidebar and presenting the content (like explanations, code examples, and even diagrams) in a readable way.
Let's walk through the typical user journey:
- You open the project's website in your browser.
- You see a form asking for details about the GitHub repository you want to analyze.
- You fill in the required information (like the repo URL and your Gemini API key).
- You click a button like "Generate Tutorial".
- The page might show a message indicating the tutorial is being generated (this takes time!).
- Once it's done, the page updates to show the tutorial structure on one side and the content of the first lesson on the other.
- You can click on different lessons in the sidebar to read through the tutorial.
This entire experience β the form, the button, the loading message, the sidebar, and the formatted text β is the Web Interface in action!
How it Works (Simplified)#
The frontend is built using modern web technologies. The core building blocks are:
- React: A popular JavaScript library that makes building interactive user interfaces much easier. It helps us organize the page into smaller, reusable pieces (like the form, the sidebar, the header, etc.).
- Vite: A tool that helps developers build and run React projects quickly. It handles things like making sure our code works in different browsers and preparing it for deployment.
- Standard Web Technologies: HTML (for structure), CSS (for making it look good), and JavaScript (for interactivity and talking to the backend).
Let's peek at some small code snippets to get a feel for it. Don't worry about understanding every line β the goal is just to see how these pieces fit together at a high level.
First, how does the project get started in the browser? It uses main.jsx
:
// frontend/src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css'; // Import our main styles
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App /> {/* This is our main application component */}
</React.StrictMode>
);
This little file is the entry point. It tells React to take control of the part of the HTML page with the root
id (which you can see in index.html
) and render our main App
component there. It also loads the main CSS file (index.css
).
What's in index.css
? Mostly just imports for other styles:
/* frontend/src/index.css */
/* Main CSS imports */
@import "./styles/variables.css";
@import "./styles/global.css";
/* Add any root-level styling here if needed */
This shows how CSS files are often organized β you have a main file that pulls in smaller files (variables.css
for colors, global.css
for general page styles).
User Input: The Form#
The frontend needs to get information from you. This is handled by a component like InputForm.jsx
.
A very simplified look at the form structure:
// frontend/src/components/InputForm/InputForm.jsx (Simplified)
import React, { useState } from 'react';
import './InputForm.css'; // Styles for the form
function InputForm() {
const [repoUrl, setRepoUrl] = useState(''); // Holds the text the user types
const [isLoading, setIsLoading] = useState(false); // Tracks if we're waiting for a response
const handleSubmit = async (event) => {
event.preventDefault(); // Stop the page from refreshing
setIsLoading(true); // Show a loading state
// ... (code to send repoUrl to the backend goes here) ...
setIsLoading(false); // Hide loading when done
};
return (
<div className="form-container">
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="repoUrl">GitHub Repo URL *</label>
<input
type="url"
id="repoUrl"
value={repoUrl}
onChange={(e) => setRepoUrl(e.target.value)} // Update state when typing
required
/>
</div>
{/* ... other input fields ... */}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Loading...' : 'Submit'} {/* Button text changes based on state */}
</button>
</form>
</div>
);
}
export default InputForm;
This shows:
* How useState
is used to keep track of what the user types (repoUrl
) and whether the form is currently submitting (isLoading
).
* How the onChange
event updates the state whenever the input value changes.
* How the onSubmit
event triggers the handleSubmit
function when you click the button.
* How the button's disabled
state and text change based on the isLoading
state, providing feedback to the user.
The InputForm.css
file contains styles specific to making this form look good:
/* frontend/src/components/InputForm/InputForm.css (Snippet) */
@import "../../styles/variables.css";
.form-container {
padding: 40px;
/* ... other styles ... */
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #f5e0dc; /* Using a color from variables.css */
}
.form-group input[type="url"] {
width: 100%;
padding: 12px 16px;
border: 1px solid #45475a;
/* ... other input styles ... */
}
You can see here how variables.css
(which we saw imported in index.css
) is used to keep colors consistent using CSS variables like --f5e0dc
.
Displaying the Output: The Tutorial#
Once the backend generates the tutorial, the frontend needs to show it. This is handled by a component like OutputDisplay.jsx
.
A simplified look at how it might display content:
// frontend/src/components/OutputDisplay/OutputDisplay.jsx (Simplified)
import React, { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown'; // Library to convert markdown to HTML
import './OutputDisplay.css'; // Styles for output display
function OutputDisplay() {
const [tutorialContent, setTutorialContent] = useState('');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// This runs when the component first appears
const fetchContent = async () => {
setIsLoading(true);
// ... (code to fetch tutorial content from backend) ...
setTutorialContent("## Chapter 1\n\nThis is the introduction...");
setIsLoading(false);
};
fetchContent();
}, []); // Empty array means this runs only once
if (isLoading) {
return <div>Loading tutorial...</div>; // Simple loading indicator
}
return (
<div className="content">
{/* ReactMarkdown takes markdown text and renders it as HTML */}
<ReactMarkdown>{tutorialContent}</ReactMarkdown>
</div>
);
}
export default OutputDisplay;
This snippet shows:
* useState
is used again to hold the text content (tutorialContent
) and loading status (isLoading
).
* useEffect
is used to perform an action (fetching content) when the component is first shown.
* ReactMarkdown
is used to take the raw text content (which is in Markdown format) and render it nicely in the browser with headings, paragraphs, code blocks, etc.
The OutputDisplay.css
file styles the tutorial view:
/* frontend/src/components/OutputDisplay/OutputDisplay.css (Snippet) */
@import "../../styles/variables.css";
.output-page {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #1e1e2e; /* Dark background */
color: #cdd6f4; /* Light text */
}
.sidebar {
width: 300px;
background-color: #181825; /* Dark sidebar background */
/* ... more styles ... */
}
.content {
flex: 1;
padding: 30px 40px;
overflow-y: auto;
background-color: #1e1e2e; /* Dark content background */
}
.markdown-content h1 {
font-size: 28px;
color: #f5e0dc; /* Soft pink headers */
/* ... more styles ... */
}
Here you can see how the overall page structure (.output-page
, .sidebar
, .content
) and specific elements within the markdown output (.markdown-content h1
) are styled using CSS, again leveraging variables from variables.css
.
The frontend also includes components like SkeletonLoader
and SidebarSkeletonLoader
(SkeletonLoader.jsx
, SkeletonLoader.css
, SidebarSkeletonLoader.jsx
) which provide visual feedback while content is loading, preventing the page from appearing empty.
Talking to the Backend#
The frontend needs to communicate with the part of the system that does the heavy lifting β the backend server. It doesn't perform the code fetching, analysis, or tutorial generation itself. It relies on the backend for that.
This communication happens using HTTP requests (like when your browser asks a website for a page). Libraries like axios
make this easy in JavaScript.
The backend's address is configured in apiConfig.js
:
// frontend/src/utils/apiConfig.js
// Get the appropriate API base URL based on the current environment
export const getApiBaseUrl = () => {
// import.meta.env.VITE_BASE_URL comes from Vite's environment variables
const baseUrl = import.meta.env.VITE_BASE_URL;
console.log("BASE_URL", baseUrl); // Helps during development
return baseUrl || "http://localhost:5001/api"; // Default to local backend if not set
};
// Export a configured API URL instance
export const API_BASE_URL = getApiBaseUrl();
This code simply defines where the frontend should send its requests. It uses a special Vite feature (import.meta.env.VITE_BASE_URL
) to potentially get this address from the build configuration (useful for deployment), but defaults to http://localhost:5001/api
for local development.
When you click "Generate Tutorial", the InputForm
component uses axios
to send a request to the backend's /start-job
address:
// frontend/src/components/InputForm/InputForm.jsx (Snippet)
import axios from 'axios';
import { API_BASE_URL } from '../../utils/apiConfig'; // Import the backend address
const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
// ... gather data from state (geminiKey, githubToken, repoUrl, etc.) ...
try {
// Send a POST request to the backend
const response = await axios.post(`${API_BASE_URL}/start-job`, {
// data to send in the request body
gemini_key: geminiKey,
github_token: githubToken || null,
repo_url: repoUrl,
include_patterns: includePatterns,
exclude_patterns: excludePatterns,
max_file_size: maxFileSize * 1024,
});
if (response.status === 202 || response.status === 200) {
// Backend successfully accepted the job
console.log("Job started successfully:", response.data);
// Redirect user to the output page
navigate(`/output/${repoUrl.split('/').pop()}`);
}
// ... handle other responses or errors ...
} catch (err) {
console.error("Job submission failed:", err);
// ... show error message to user ...
} finally {
setIsLoading(false);
}
};
Similarly, when the OutputDisplay
page loads or you click a lesson, it uses axios.get
to fetch the tutorial structure and content from the backend.
Here's a sequence diagram illustrating the basic interaction flow for generating and viewing a tutorial:
sequenceDiagram
participant User
participant Browser as Frontend
participant Server as Backend
User->>Browser: Visits Website (Input Form)
Browser->>User: Displays Input Form
User->>Browser: Enters Repo URL, Keys etc.<br>Clicks "Generate"
Browser->>Server: POST /start-job (with repo info, keys)
Server-->>Browser: 202 Accepted
Browser->>User: Shows "Generating..." message<br>Redirects to /output/repoName
User->>Browser: Visits /output/repoName
Browser->>Server: GET /output-structure/repoName
Server-->>Browser: 200 OK (Tutorial Structure)
Browser->>User: Displays Sidebar with Lessons
User->>Browser: Clicks on a Lesson Link (e.g., intro.md)
Browser->>Server: GET /output-content/repoName/intro.md
Server-->>Browser: 200 OK (Markdown Content)
Browser->>User: Displays Formatted Lesson Content
Note over User: Can click other links<br>to load more content
This diagram shows how the frontend (Browser
) acts as an intermediary between the user and the Server
(Backend
), handling user input and displaying the results fetched from the backend.
Routing#
The project uses react-router-dom
to handle navigation between different "pages" or views in the frontend without requiring full page reloads. You can see this configured in App.jsx
:
// frontend/src/App.jsx
import React from 'react';
// Import necessary components for routing
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// Import the components for different pages
import InputForm from './components/InputForm/InputForm';
import OutputDisplay from './components/OutputDisplay/OutputDisplay';
import ExamplesPage from './components/ExamplesPage/ExamplesPage';
import './App.css';
function App() {
return (
<Router> {/* Enables routing */}
<Routes> {/* Defines possible routes */}
<Route path="/" element={<InputForm />} /> {/* Home page shows the input form */}
<Route path="/examples" element={<ExamplesPage />} /> {/* /examples shows the examples page */}
{/* Routes for displaying output, including optional lessonPath */}
<Route path="/output/:repoName" element={<OutputDisplay />} />
<Route path="/output/:repoName/:lessonPath/*" element={<OutputDisplay />} />
{/* Add other routes as needed */}
</Routes>
</Router>
);
}
export default App;
This code tells the application which component to render based on the URL path. For example, visiting the root path (/
) shows the InputForm
, while visiting /output/some-repo-name
shows the OutputDisplay
for that specific repository.
Conclusion#
The Web Interface (Frontend) is your gateway to interacting with the Tutorial-Codebase-Knowledge project. It provides the user-friendly forms to input repository details and a clean, navigable way to read the generated tutorials. While it handles the presentation and user interaction, it relies heavily on communication with the backend to perform the core tasks of fetching, analyzing, and generating the tutorial content.
In the next chapter, we'll dive into how the backend part of the application is structured and deployed using Azure Functions.
Next Chapter: Serverless Deployment (Azure Functions)
Generated by AI Codebase Knowledge Builder. References: 1(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/.gitignore), 2(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/README.md), 3(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/eslint.config.js), 4(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/index.html), 5(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/package-lock.json), 6(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/package.json), 7(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/public/vite.svg), 8(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/App.css), 9(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/App.jsx), 10(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/assets/react.svg), 11(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/ExamplesPage/ExamplesPage.css), 12(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/ExamplesPage/ExamplesPage.jsx), 13(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/InputForm/InputForm.css), 14(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/InputForm/InputForm.jsx), 15(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/OutputDisplay/OutputDisplay.css), 16(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/OutputDisplay/OutputDisplay.jsx), 17(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/AppHeader.css), 18(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/AppHeader.jsx), 19(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/AppLayout.css), 20(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/SidebarSkeletonLoader.jsx), 21(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/SkeletonLoader.css), 22(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/components/common/SkeletonLoader.jsx), 23(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/index.css), 24(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/main.jsx), 25(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/styles/global.css), 26(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/styles/variables.css), 27(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/src/utils/apiConfig.js), 28(https://github.com/hieuminh65/Tutorial-Codebase-Knowledge/blob/be7f595a38221b3dd7b1585dc226e47c815dec6e/frontend/vite.config.js)