Skip to main content

Technology Stack

Core Technologies

TechnologyVersionPurpose
React19.2UI Framework
TypeScript5.8Type Safety
Vite6.4Build Tool & Dev Server
Tailwind CSS4.2Utility-first CSS
React Router7.6Client-side Routing

UI Components

shadcn/ui

UI component library berbasis Radix UI primitives dengan styling Tailwind CSS:

// shadcn/ui setup
// Style: New York
// Base Color: Slate
// Icons: Lucide

46 Components tersedia di src/components/ui/:

Forms

  • Button, Checkbox, Input, Label
  • Radio Group, Select, Slider
  • Switch, Textarea, Toggle, Toggle Group

Layout

  • Accordion, Aspect Ratio
  • Resizable Panel, Separator
  • Scroll Area

Data Display

  • Avatar, Badge, Card, Table
  • Calendar, Day Picker

Feedback

  • Alert, Alert Dialog, Dialog
  • Drawer, Popover, Tooltip
  • Sonner (Toast notifications)
  • Breadcrumb, Command
  • Context Menu, Dropdown Menu
  • Menubar, Navigation Menu
  • Pagination, Tabs

Overlay

  • Collapsible, Hover Card

State Management

TanStack Query (React Query)

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 1,
},
},
});

Use cases:

  • API data fetching & caching
  • Background refetching
  • Optimistic updates
  • Infinite queries

React Context

// AppContext for global state
const Ctx = createContext<AppState | null>(null);

// Available state:
- tenantId: string
- roleId: RoleId
- isAuthed: boolean
- userName: string
- userRoles: string[]
- tenantIdClaim: string
- tenantName: string

localStorage Persistence

// State persisted to localStorage
const KEY = "tssmartlog-state-v1";

// Auto-restore on mount
// Auto-save on change

Forms & Validation

React Hook Form

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
query: z.string().min(1),
timeRange: z.enum(['1h', '24h', '7d', '30d']),
});

const form = useForm({
resolver: zodResolver(schema),
});

Charts & Visualization

Recharts

import {
LineChart, Line, BarChart, Bar,
PieChart, Pie, AreaChart, Area,
XAxis, YAxis, CartesianGrid, Tooltip, Legend
} from 'recharts';

Use cases:

  • Log volume histograms
  • Error rate trends
  • Throughput charts
  • Latency distributions

HTTP Client

Custom Fetch Wrapper

// src/api/http.ts
const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "";

export async function fetchJson<T>(input: string, init?: RequestInit): Promise<T> {
const token = getAccessToken();
const url = input.startsWith("http") ? input : `${BASE_URL}${input}`;

const response = await fetch(url, {
headers: {
"content-type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(init?.headers ?? {}),
},
...init,
});

if (response.status === 401) {
triggerSessionExpired();
throw new Error("Session expired");
}

return (await response.json()) as T;
}

Internationalization

i18next

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
resources: {
id: { translation: idTranslations },
en: { translation: enTranslations },
},
lng: 'id', // Default language
fallbackLng: 'id',
});

Supported languages:

  • id - Bahasa Indonesia (default)
  • en - English

Development Tools

ESLint

// eslint.config.js
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';

export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
reactHooks.configs['flat/recommended'],
{
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
);

Prettier

// .prettierrc
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false
}

TypeScript

// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

Vite

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
plugins: [react(), tailwindcss(), tsconfigPaths()],
server: {
port: 3000,
host: '0.0.0.0',
proxy: {
'/api': { target: 'http://10.8.0.48:8085/', changeOrigin: true },
'/healthz': { target: 'http://10.8.0.48:8085/', changeOrigin: true },
'/auth': { target: 'http://10.8.0.48:8085/', changeOrigin: true },
},
},
});

Build Scripts

{
"scripts": {
"dev": "vite dev",
"build": "vite build",
"build:dev": "vite build --mode development",
"preview": "vite preview",
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"format": "prettier --write ."
}
}

Next Steps