TypeScript Integration
TypeScript Integration
UltraViolet Pro includes comprehensive TypeScript support with a well-structured admin.ts file that handles all core dashboard functionality.
Overview
The TypeScript integration provides:
- Type Safety - Compile-time error checking and IntelliSense support
- Modern JavaScript Features - ES6+ features with backward compatibility
- Modular Architecture - Well-organized code with clear separation of concerns
- Development Experience - Enhanced IDE support and debugging capabilities
- Maintainability - Self-documenting code with interfaces and type definitions
Project Structure
TypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"outDir": "./public/build",
"rootDir": "./resources/js"
},
"include": [
"resources/js/**/*"
],
"exclude": [
"node_modules",
"public/build"
]
}
File Organization
resources/js/
├── admin.ts # Main TypeScript file
├── admin-background-animations.js # Background animation module
├── code-highlighting.js # Code highlighting utilities
├── toast-helpers.js # Toast notification helpers
└── types/ # Type definitions
├── dashboard.ts # Dashboard-specific types
├── charts.ts # Chart-related types
└── maps.ts # Map-related types
Core Architecture
Main AdminDashboard Class
The admin.ts file is structured around a central AdminDashboard class that manages all dashboard functionality:
class AdminDashboard {
private config: DashboardConfig
private simpleBarInstances: SimpleBar[] = []
public themeVerificationTimeout: NodeJS.Timeout | null = null
public systemThemeListener: ((e: MediaQueryListEvent) => void) | null = null
constructor(config: DashboardConfig) {
this.config = config
this.init()
}
}
Key Features
1. Theme Management
interface DashboardConfig {
defaultTheme: 'dark' | 'light'
themeTransitionDuration: number
}
class AdminDashboard {
private initTheme(): void {
// Initialize theme with system preference detection
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const savedTheme = localStorage.getItem('theme') as 'dark' | 'light' | null
const theme = savedTheme || (systemPrefersDark ? 'dark' : this.config.defaultTheme)
this.applyTheme(theme)
}
private applyTheme(theme: 'dark' | 'light'): void {
document.documentElement.setAttribute('data-bs-theme', theme)
localStorage.setItem('theme', theme)
}
}
2. RTL/LTR Support
interface DashboardConfig {
defaultDirection: 'ltr' | 'rtl'
}
class AdminDashboard {
private initDirection(): void {
const savedDirection = localStorage.getItem('direction') as 'ltr' | 'rtl' | null
const direction = savedDirection || this.config.defaultDirection
document.documentElement.setAttribute('dir', direction)
localStorage.setItem('direction', direction)
}
}
3. Component Initialization
class AdminDashboard {
private initComponents(): void {
this.initScrollbars()
this.initHeadroom()
this.initBackgroundAnimations()
this.initCharts()
this.initMaps()
this.initMasonry()
}
private initScrollbars(): void {
const scrollbarElements = document.querySelectorAll('[data-simplebar]')
scrollbarElements.forEach(element => {
const simpleBar = new SimpleBar(element as HTMLElement)
this.simpleBarInstances.push(simpleBar)
})
}
}
Type Definitions
Core Interfaces
interface DashboardConfig {
apiBaseUrl: string
defaultTheme: 'dark' | 'light'
themeTransitionDuration: number
defaultDirection: 'ltr' | 'rtl'
defaultBackgroundAnimation: string
}
interface MapConfig {
container: string
style: string
center: [number, number]
zoom: number
}
interface ChartConfig {
type: 'line' | 'bar' | 'pie' | 'area' | 'donut'
data: any[]
options: ApexCharts.ApexOptions
}
interface ThemeSettings {
theme: 'dark' | 'light'
direction: 'ltr' | 'rtl'
backgroundAnimation: string
sidebarCollapsed: boolean
}
Chart Types
interface ChartData {
labels: string[]
datasets: {
label: string
data: number[]
backgroundColor?: string | string[]
borderColor?: string | string[]
borderWidth?: number
}[]
}
interface ChartOptions {
responsive: boolean
maintainAspectRatio: boolean
plugins?: {
legend?: {
position: 'top' | 'bottom' | 'left' | 'right'
}
tooltip?: {
enabled: boolean
}
}
scales?: {
x?: {
display: boolean
title?: {
display: boolean
text: string
}
}
y?: {
display: boolean
title?: {
display: boolean
text: string
}
}
}
}
Map Types
interface MapMarker {
id: string
position: [number, number]
title: string
description?: string
icon?: string
color?: string
}
interface MapLayer {
id: string
name: string
type: 'fill' | 'line' | 'symbol' | 'circle'
source: string
paint: Record<string, any>
layout?: Record<string, any>
}
interface MapSource {
type: 'geojson' | 'vector' | 'raster'
data: any
cluster?: boolean
clusterMaxZoom?: number
clusterRadius?: number
}
Module System
Background Animations Module
// admin-background-animations.js
export default class AdminBackgroundAnimations {
constructor() {
this.init()
}
init() {
this.detectSystemPreference()
this.setupEventListeners()
}
detectSystemPreference() {
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (prefersReducedMotion) {
this.disableAnimations()
}
}
setupEventListeners() {
// Animation control event listeners
}
}
Code Highlighting Module
// code-highlighting.js
export function initCodeHighlighting() {
const codeBlocks = document.querySelectorAll('pre code')
codeBlocks.forEach(block => {
// Initialize Prism.js highlighting
Prism.highlightElement(block)
})
}
export function highlightCode(code: string, language: string): string {
return Prism.highlight(code, Prism.languages[language], language)
}
Toast Helpers Module
// toast-helpers.js
export interface ToastOptions {
title?: string
message: string
type?: 'success' | 'error' | 'warning' | 'info'
duration?: number
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
}
export function showToast(options: ToastOptions): void {
const toast = createToastElement(options)
document.body.appendChild(toast)
// Auto-remove after duration
setTimeout(() => {
toast.remove()
}, options.duration || 5000)
}
function createToastElement(options: ToastOptions): HTMLElement {
const toast = document.createElement('div')
toast.className = `toast toast-${options.type || 'info'}`
toast.innerHTML = `
<div class="toast-header">
<strong>${options.title || 'Notification'}</strong>
</div>
<div class="toast-body">
${options.message}
</div>
`
return toast
}
Chart Integration
ApexCharts Integration
class AdminDashboard {
private initCharts(): void {
const chartElements = document.querySelectorAll('[data-chart]')
chartElements.forEach(element => {
const chartType = element.getAttribute('data-chart-type')
const chartData = this.getChartData(element)
if (chartType && chartData) {
this.createChart(element as HTMLElement, chartType, chartData)
}
})
}
private createChart(element: HTMLElement, type: string, data: any): void {
const options: ApexCharts.ApexOptions = {
chart: {
type: type as any,
height: 350
},
series: data.series,
xaxis: {
categories: data.categories
},
theme: {
mode: this.getCurrentTheme()
}
}
const chart = new ApexCharts(element, options)
chart.render()
}
private getCurrentTheme(): 'light' | 'dark' {
return document.documentElement.getAttribute('data-bs-theme') as 'light' | 'dark'
}
}
Chart Data Management
interface ChartDataManager {
getChartData(element: HTMLElement): ChartData | null
updateChart(chartId: string, newData: ChartData): void
destroyChart(chartId: string): void
}
class ChartDataManager implements ChartDataManager {
private charts: Map<string, ApexCharts> = new Map()
getChartData(element: HTMLElement): ChartData | null {
const dataScript = element.querySelector('script[type="application/json"]')
if (dataScript) {
try {
return JSON.parse(dataScript.textContent || '{}')
} catch (error) {
console.error('Error parsing chart data:', error)
return null
}
}
return null
}
updateChart(chartId: string, newData: ChartData): void {
const chart = this.charts.get(chartId)
if (chart) {
chart.updateSeries(newData.series)
}
}
destroyChart(chartId: string): void {
const chart = this.charts.get(chartId)
if (chart) {
chart.destroy()
this.charts.delete(chartId)
}
}
}
Map Integration
Livewire Integration
Livewire Event Handling
class AdminDashboard {
private initLivewire(): void {
// Listen for Livewire events
document.addEventListener('livewire:load', () => {
this.setupLivewireListeners()
})
// Handle Livewire updates
document.addEventListener('livewire:update', () => {
this.refreshComponents()
})
}
private setupLivewireListeners(): void {
// Chart updates
Livewire.on('chart-updated', (data: any) => {
this.updateChart(data.chartId, data.newData)
})
// Map updates
Livewire.on('map-updated', (data: any) => {
this.updateMap(data.mapId, data.newData)
})
// Theme changes
Livewire.on('theme-changed', (theme: 'dark' | 'light') => {
this.applyTheme(theme)
})
}
private refreshComponents(): void {
// Reinitialize components that might have changed
this.initCharts()
this.initMaps()
this.initMasonry()
}
}
Utility Functions
DOM Utilities
class DOMUtils {
static querySelector<T extends HTMLElement>(selector: string): T | null {
return document.querySelector<T>(selector)
}
static querySelectorAll<T extends HTMLElement>(selector: string): NodeListOf<T> {
return document.querySelectorAll<T>(selector)
}
static addClass(element: HTMLElement, className: string): void {
element.classList.add(className)
}
static removeClass(element: HTMLElement, className: string): void {
element.classList.remove(className)
}
static toggleClass(element: HTMLElement, className: string): void {
element.classList.toggle(className)
}
static hasClass(element: HTMLElement, className: string): boolean {
return element.classList.contains(className)
}
}
Event Utilities
class EventUtils {
static on(element: HTMLElement, event: string, handler: EventListener): void {
element.addEventListener(event, handler)
}
static off(element: HTMLElement, event: string, handler: EventListener): void {
element.removeEventListener(event, handler)
}
static once(element: HTMLElement, event: string, handler: EventListener): void {
element.addEventListener(event, handler, { once: true })
}
static emit(element: HTMLElement, event: string, detail?: any): void {
const customEvent = new CustomEvent(event, { detail })
element.dispatchEvent(customEvent)
}
}
Storage Utilities
class StorageUtils {
static setItem(key: string, value: any): void {
try {
localStorage.setItem(key, JSON.stringify(value))
} catch (error) {
console.error('Error saving to localStorage:', error)
}
}
static getItem<T>(key: string, defaultValue?: T): T | null {
try {
const item = localStorage.getItem(key)
return item ? JSON.parse(item) : defaultValue || null
} catch (error) {
console.error('Error reading from localStorage:', error)
return defaultValue || null
}
}
static removeItem(key: string): void {
localStorage.removeItem(key)
}
static clear(): void {
localStorage.clear()
}
}
Development Workflow
Building TypeScript
# Install dependencies
npm install
# Build TypeScript
npm run build
# Watch for changes
npm run dev
# Type checking
npm run type-check
Package.json Scripts
{
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"type-check": "tsc --noEmit",
"lint": "eslint resources/js/**/*.ts",
"lint:fix": "eslint resources/js/**/*.ts --fix"
}
}
Vite Integration
// vite.config.js
import { defineConfig } from 'vite'
import { resolve } from 'path'
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'resources/js/admin.ts'),
name: 'AdminDashboard',
fileName: 'admin'
},
rollupOptions: {
external: ['bootstrap', 'simplebar', 'headroom.js'],
output: {
globals: {
'bootstrap': 'Bootstrap',
'simplebar': 'SimpleBar',
'headroom.js': 'Headroom'
}
}
}
}
})
Best Practices
1. Type Safety
// Always define interfaces for data structures
interface User {
id: number
name: string
email: string
role: 'admin' | 'user' | 'moderator'
}
// Use type guards for runtime type checking
function isUser(obj: any): obj is User {
return obj && typeof obj.id === 'number' && typeof obj.name === 'string'
}
2. Error Handling
class AdminDashboard {
private handleError(error: Error, context: string): void {
console.error(`Error in ${context}:`, error)
// Show user-friendly error message
this.showToast({
type: 'error',
title: 'Error',
message: 'An unexpected error occurred. Please try again.'
})
}
}
3. Memory Management
class AdminDashboard {
private cleanup(): void {
// Clear timeouts
if (this.themeVerificationTimeout) {
clearTimeout(this.themeVerificationTimeout)
}
// Remove event listeners
if (this.systemThemeListener) {
window.matchMedia('(prefers-color-scheme: dark)')
.removeEventListener('change', this.systemThemeListener)
}
// Destroy charts
this.charts.forEach(chart => chart.destroy())
// Destroy maps
if (this.map) {
this.map.remove()
}
}
}
4. Performance Optimization
class AdminDashboard {
private debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout
return (...args: Parameters<T>) => {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
private debouncedResize = this.debounce(() => {
this.handleResize()
}, 250)
}
Troubleshooting
Common Issues
- Type Errors: Ensure all dependencies have type definitions
- Module Resolution: Check tsconfig.json moduleResolution setting
- Build Errors: Verify all imports are correct and files exist
- Runtime Errors: Use proper type guards and error handling
Debugging
// Enable debug mode
const DEBUG = process.env.NODE_ENV === 'development'
if (DEBUG) {
console.log('AdminDashboard initialized with config:', this.config)
}
Production Deployment
Build Optimization
// Use conditional imports for better tree shaking
const loadMapbox = async () => {
if (this.config.mapboxToken) {
const { default: mapboxgl } = await import('mapbox-gl')
return mapboxgl
}
return null
}
Environment Configuration
interface EnvironmentConfig {
development: DashboardConfig
production: DashboardConfig
testing: DashboardConfig
}
const configs: EnvironmentConfig = {
development: {
apiBaseUrl: 'http://localhost:8000/api',
defaultTheme: 'dark',
themeTransitionDuration: 300,
defaultDirection: 'ltr',
defaultBackgroundAnimation: 'cosmic-wave'
},
production: {
apiBaseUrl: '/api',
defaultTheme: 'dark',
themeTransitionDuration: 200,
defaultDirection: 'ltr',
defaultBackgroundAnimation: 'cosmic-wave'
},
testing: {
apiBaseUrl: 'http://localhost:8000/api',
defaultTheme: 'light',
themeTransitionDuration: 0,
defaultDirection: 'ltr',
defaultBackgroundAnimation: 'none'
}
}
The TypeScript integration in UltraViolet Pro provides a robust foundation for building maintainable, type-safe dashboard applications with excellent developer experience and production-ready code.