Welcome to Chapter 3! In the previous chapter, System Status & Diagnostics, we built a dashboard to check if our system is healthy.
Now that we know the engine is running, we need to check the fuel tank. In the world of APIs, "fuel" usually means Usage Credits or Rate Limits.
Think about your mobile phone plan. You have a limit of 10GB of data per month.
The Problem: Users need to know:
The Solution: The Usage & Quota Monitoring component. It acts like a fuel gauge, fetching real-time data and drawing visual progress bars in the terminal.
To build this, we move away from the "Suspense" model we used in Chapter 2 and use a standard Fetch-then-Render cycle. This gives us more control over errors and retries.
We don't know the usage data when the app starts. We have to send a request to the server: "Hello, how much quota is left?". This takes time (milliseconds to seconds).
Since fetching takes time, our component must handle three distinct scenarios:
Text like "50000 / 100000 tokens" takes time to read. A bar that is half-filled is understood instantly. We need to calculate the width of this bar based on the terminal size.
Integrating this into our Settings Container is simple. Unlike the Status component, the Usage component handles its own data fetching internally, so you just render it.
import { Usage } from './Usage';
// Inside your Tabs definition
<Tab key="usage" title="Usage">
<Usage />
</Tab>
What happens here? When the user clicks the "Usage" tab, the component mounts, immediately triggers a network request, and updates the screen when data arrives.
Let's visualize the lifecycle of this component. It is more interactive than the Status page because it allows the user to Retry if something goes wrong.
Now, let's look at the code blocks to see how this logic is constructed.
We use standard React useState hooks to track the three states we discussed earlier.
export function Usage() {
// 1. Hold the data (null initially)
const [utilization, setUtilization] = useState(null);
// 2. Hold the error message (if any)
const [error, setError] = useState(null);
// 3. Track if we are currently working
const [isLoading, setIsLoading] = useState(true);
We create a specific function to get the data. We wrap it in try/catch to handle network failures safely.
const loadUtilization = useCallback(async () => {
setIsLoading(true); // Start loading
setError(null); // Clear previous errors
try {
const data = await fetchUtilization();
setUtilization(data); // Success! Save data.
} catch (err) {
setError('Failed to load usage data'); // Failure! Save error.
} finally {
setIsLoading(false); // Done (success or fail)
}
}, []);
error state so we can show a message.
We want this to happen automatically when the tab opens. We use useEffect.
useEffect(() => {
void loadUtilization();
}, [loadUtilization]);
If the fetch failed, we want to let the user try again without restarting the app. We listen for a specific key press. This utilizes the system we will discuss in Keybinding & Interaction System.
useKeybinding('settings:retry', () => {
void loadUtilization();
}, {
isActive: !!error && !isLoading
});
loadUtilization again when the retry key is pressed."
If we have data, we render the LimitBar. This visualizes the math.
// Inside the return statement
return (
<Box flexDirection="column" gap={1}>
{limits.map(item => (
<LimitBar
key={item.title}
title={item.title}
limit={item.limit}
maxWidth={80}
/>
))}
</Box>
);
The LimitBar component is a great example of Terminal UI Composition. It takes a number (like 0.45 for 45%) and turns it into a string of block characters (like โโโโโโโโโ).
We will explore exactly how Box, Text, and creating custom visual components like ProgressBar work in the next chapter.
In this chapter, we built the Usage & Quota Monitoring screen.
Now that our data logic is solid, let's learn how to actually draw these beautiful bars, boxes, and layouts using the graphics engine.
Next Chapter: Terminal UI Composition (Ink)
Generated by Code IQ