Technical Blog Article — App 76: Log Viewer Dashboard with React, TypeScript, Fluent UI, and Enterprise Architecture

Introduction

Modern enterprise systems generate thousands or even millions of log entries every day. Every user login, API request, database operation, file upload, workflow execution, approval action, or system error can generate logs.

Organizations depend on log systems to:

  • Monitor application health
  • Troubleshoot production issues
  • Track user activities
  • Analyze performance
  • Detect security incidents
  • Support auditing requirements
  • Maintain compliance

Examples include:

  • Microsoft 365 audit logs
  • SharePoint activity logs
  • Azure Monitor
  • Azure Application Insights
  • Microsoft Sentinel
  • Enterprise ERP systems
  • CRM platforms
  • Internal business applications

For this reason, one of the most common enterprise dashboards is a Log Viewer Dashboard.

App 76 introduces the concept of building a professional log monitoring interface using:

  • React
  • TypeScript
  • Vite
  • Fluent UI
  • Service Layer Architecture
  • Type-safe Models
  • useEffect
  • Derived Data

This application belongs to Block 4 — Effects and Architecture, where the focus is learning how React synchronizes with external systems and how enterprise applications organize code into layers.


Why Log Dashboards Matter

In small applications, developers often debug using:

console.log(...)

However, enterprise systems require centralized monitoring.

Imagine:

10 users
100 users
1,000 users
10,000 users
100,000 users

At scale, manually reading console output becomes impossible.

Organizations instead use dashboards that aggregate information such as:

MetricExample
Total Logs12,450
Errors125
Warnings842
Information Events11,483
Critical Alerts7
Active Users1,284

The purpose of App 76 is to simulate such a dashboard.


Learning Objectives

This app introduces:

ConceptDescription
useEffectLoad data from services
Service LayerSeparate UI from data access
ModelsStrong TypeScript contracts
Derived DataSummary calculations
Component CompositionDashboard structure
Fluent UIEnterprise visual components
Type ImportsModern TypeScript architecture
TS1484Type-only imports

Creating the Project

Create the Application

mkdir bloco04
cd bloco04
npm create vite@latest app76-log-viewer-dashboard -- --template react-ts
cd app76-log-viewer-dashboard
npm install
npm install @fluentui/react-components

Create the Folder Structure

mkdir src\components
mkdir src\models
mkdir src\services
mkdir src\data
mkdir src\styles

Create files:

New-Item src\models\LogEntry.ts -ItemType File
New-Item src\services\logService.ts -ItemType File
New-Item src\components\LogSummary.tsx -ItemType File
New-Item src\components\LogGrid.tsx -ItemType File
New-Item artigo.md -ItemType File

Final Project Structure

app76-log-viewer-dashboard/
src/
├── components/
│ ├── LogSummary.tsx
│ └── LogGrid.tsx
├── models/
│ └── LogEntry.ts
├── services/
│ └── logService.ts
├── data/
├── styles/
├── App.tsx
├── main.tsx
└── index.css

This structure follows the architecture established throughout ReactLab.


Understanding the LogEntry Model

LogEntry.ts

export interface LogEntry {
id: number;
timestamp: string;
level: string;
source: string;
message: string;
}

This interface represents a single log record.

Each log entry contains:

PropertyPurpose
idUnique identifier
timestampEvent date/time
levelSeverity
sourceSystem module
messageDescription

This model acts as a contract.

Every component that consumes logs knows exactly what properties exist.

This is one of the major advantages of TypeScript.


Why Models Matter

Without a model:

const logs = [];

React components become fragile.

Developers must guess:

What properties exist?
What types are expected?
What fields are mandatory?

With TypeScript:

LogEntry

becomes the source of truth.


Understanding the Service Layer

logService.ts

The service layer simulates an API.

import type { LogEntry } from "../models/LogEntry";
export async function getLogs(): Promise<LogEntry[]> {
return [
{
id: 1,
timestamp: "2026-06-02 09:30",
level: "Information",
source: "Authentication",
message: "User logged in successfully",
},
{
id: 2,
timestamp: "2026-06-02 09:35",
level: "Warning",
source: "Storage",
message: "Disk utilization above 80%",
},
{
id: 3,
timestamp: "2026-06-02 09:40",
level: "Error",
source: "API Gateway",
message: "Service unavailable",
},
];
}

Notice:

import type { LogEntry }

This becomes extremely important later.


Why Use a Service Layer

Many beginners place everything inside App.tsx.

Enterprise applications do not.

Instead:

UI Layer
Service Layer
API Layer
Database

This separation improves:

  • Maintainability
  • Testing
  • Scalability
  • Reusability

Understanding useEffect

The application loads logs using:

useEffect(() => {
loadLogs();
}, []);

This is the correct use of useEffect.

Why?

Because React must synchronize with an external source:

Service Layer

According to React Learn:

Effects synchronize components with external systems.

This is exactly our scenario.


React Rendering Flow

The rendering cycle looks like:

App renders
logs = []
useEffect executes
loadLogs()
Service returns data
setLogs(...)
State changes
React re-renders
Dashboard updates

This is a perfect example of proper Effect usage.


Understanding the TS1484 Error

During development you encountered:

TS1484:
'LogEntry' is a type and must be imported using a type-only import

This error appeared because Vite + modern TypeScript enable:

"verbatimModuleSyntax": true

When this option is enabled:

import { LogEntry }

is treated as a runtime import.

However:

LogEntry

is not a runtime object.

It only exists during compilation.


Incorrect Import

import { LogEntry } from "../models/LogEntry";

TypeScript interprets this as:

Import a runtime value

But interfaces do not exist at runtime.

Result:

TS1484

Correct Import

import type { LogEntry } from "../models/LogEntry";

The keyword:

type

tells TypeScript:

This import only exists for typing.
Remove it from the generated JavaScript.

Problem solved.


Why Modern TypeScript Requires This

Benefits include:

  • Smaller bundles
  • Faster compilation
  • Cleaner output
  • Better tree-shaking
  • Explicit architecture

This is now considered best practice.


Understanding LogSummary

The dashboard summary component calculates:

Total Logs
Errors
Warnings
Information

These values are derived.

We do not store them separately.

Example:

const totalLogs = logs.length;
const errors =
logs.filter(x => x.level === "Error").length;

This follows React guidance:

Avoid redundant state.

Derived values should be calculated.

Not stored.


Understanding LogGrid

The grid displays:

Timestamp
Level
Source
Message

The component receives:

logs

through props.

This demonstrates React composition:

App
├── LogSummary
└── LogGrid

Both components consume the same data.


Component Responsibility

A React component should have one responsibility.

LogSummary

Responsible for:

Aggregation
Metrics
Statistics

LogGrid

Responsible for:

Display
Tabular visualization

App

Responsible for:

State ownership
Loading data
Composition

This separation improves maintainability.


Why Fluent UI Is Used

Fluent UI provides:

  • Accessibility
  • Consistent spacing
  • Enterprise styling
  • Keyboard support
  • Microsoft design language

Instead of manually building:

cards
tables
panels

we use:

Card
Text
Title
Table

This accelerates development.


Enterprise Architecture Introduced

App 76 introduces a common architecture:

Models
Services
State
Components
UI

Future apps will extend this architecture to:

REST APIs
Authentication
Context API
Caching
Pagination
Reporting
Analytics

Why This App Belongs to Block 4

Block 4 focuses on:

Effects
Architecture
Services
External Data

App 76 is one of the first examples where:

UI is no longer static.

Data comes from outside the component.

That makes useEffect necessary.


Technical Summary

ConceptExplanation
useEffectSynchronizes with services
Service LayerIsolates data access
LogEntryTypeScript model
Derived DataSummary calculations
Component CompositionDashboard architecture
Fluent UIEnterprise visual layer
TS1484Type-only import requirement
import typeModern TypeScript best practice
State OwnershipApp controls data
PropsComponents receive data

Concept Table

ConceptFileWhy It Matters
LogEntry ModelLogEntry.tsDefines contracts
Service LayerlogService.tsSimulates APIs
Effect LoadingApp.tsxLoads external data
Dashboard MetricsLogSummary.tsxDerived state
Grid DisplayLogGrid.tsxData visualization
Type ImportsAll filesFixes TS1484
Fluent UIComponentsEnterprise UI
React StateApp.tsxControls rendering

Official Documentation

React

TypeScript

Fluent UI

Vite


Current Project Progress

BlockAppNameStatus
Block 461REST API ConsumerCompleted
Block 462API DashboardCompleted
Block 463Async SearchCompleted
Block 464GitHub ExplorerCompleted
Block 465Weather AppCompleted
Block 466Pagination SystemCompleted
Block 467Infinite ScrollCompleted
Block 468Data CacheCompleted
Block 469Custom Fetch HookCompleted
Block 470Context APICompleted
Block 471Favorites SystemCompleted
Block 472API DataGridCompleted
Block 473Analytics DashboardCompleted
Block 474Crypto MonitorCompleted
Block 475Repository ExplorerCompleted
Block 476Log Viewer DashboardCurrent
Block 477Reporting SystemNext

Final Architectural Insight

The most valuable lesson from App 76 is not the dashboard itself.

It is understanding how React applications evolve from:

Static Components

to:

Services
→ Effects
→ State
→ Derived Data
→ Enterprise Dashboards

And equally important, this app introduces one of the most common TypeScript enterprise issues:

TS1484

By fixing it with:

import type { LogEntry }

you adopted the modern TypeScript pattern used in professional React applications built with Vite, strict TypeScript settings, and enterprise-scale architectures.

Edvaldo Guimrães Filho Avatar

Published by