Skip to content

Expeed-Software/dashboard-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reusable Dashboard Components

A comprehensive dashboard system with reusable Angular libraries and Spring Boot Java API. Build customizable, drag-and-drop dashboards with minimal effort.

Project Structure

reusable-dashboard-components/
├── angular/                    # Nx monorepo
│   ├── libs/dashboard/
│   │   ├── core/              # @expeed/ngx-dashboard-core - Services, models, config
│   │   └── widgets/           # @expeed/ngx-dashboard-widgets - Widget components
│   └── apps/demo/             # Demo Angular application
├── api/                        # Gradle multi-module
│   ├── dashboard-core/        # Reusable Spring Boot library
│   └── dashboard-demo/        # Demo API with sample data
└── database/
    └── liquibase/             # Shared database migrations

Features

  • Widget Types: KPI, Chart, Table, Activity Feed (extensible)
  • GridStack Integration: Drag-and-drop widget positioning and resizing
  • Template System: Admin-managed dashboard templates
  • Role-based Access: Template assignment by user roles
  • Auto-provisioning: Automatic dashboard creation for new users
  • Dynamic Data Sources: SQL, API, and static data support
  • Edit Mode: Toggle between view and edit modes
  • Responsive Grid: 12-column grid system with configurable cell height

Prerequisites

  • Java 21+ (for Spring Boot API)
  • Node.js 20+ (for Angular)
  • PostgreSQL 15+ (for database)
  • Docker (optional, for local development)

Quick Start

1. Start PostgreSQL

docker-compose up -d

This starts PostgreSQL on localhost:5432 with database dashboard_demo.

2. Start the API

cd api
./gradlew :dashboard-demo:bootRun

API starts on http://localhost:8080

3. Start the Angular App

cd angular
npm install
npm start

App starts on http://localhost:4200

Demo Accounts

Role Email Password
Admin admin@example.com admin123
Manager manager@example.com manager123
User user@example.com user123

Using the Dashboard Library

Installation

# Install the Angular libraries
npm install @expeed/ngx-dashboard-core @expeed/ngx-dashboard-widgets gridstack

Basic Setup

1. Configure the Dashboard Provider

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideDashboard } from '@expeed/ngx-dashboard-core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideDashboard({
      apiUrl: '/api',
      gridColumns: 12,
      gridCellHeight: 80,
      enableEditMode: true,
      enableWidgetResize: true,
      enableWidgetFloat: true,  // Widgets stay in place, don't auto-sink
      theme: 'light'
    })
  ]
};

2. Add the Dashboard Component

// dashboard-page.component.ts
import { Component } from '@angular/core';
import { DynamicDashboardComponent } from '@expeed/ngx-dashboard-widgets';

@Component({
  selector: 'app-dashboard-page',
  standalone: true,
  imports: [DynamicDashboardComponent],
  template: `
    <dash-dynamic-dashboard
      [dashboardId]="dashboardId"
      (dashboardLoaded)="onDashboardLoaded($event)"
      (layoutChanged)="onLayoutChanged($event)"
      (widgetAdded)="onWidgetAdded($event)"
      (widgetRemoved)="onWidgetRemoved($event)">
    </dash-dynamic-dashboard>
  `
})
export class DashboardPageComponent {
  dashboardId = 'your-dashboard-uuid';

  onDashboardLoaded(dashboard: Dashboard) {
    console.log('Dashboard loaded:', dashboard);
  }

  onLayoutChanged(positions: WidgetPosition[]) {
    console.log('Layout changed:', positions);
  }

  onWidgetAdded(widget: DashboardWidget) {
    console.log('Widget added:', widget);
  }

  onWidgetRemoved(widgetId: string) {
    console.log('Widget removed:', widgetId);
  }
}

3. Import GridStack Styles

// styles.scss
@import 'gridstack/dist/gridstack.min.css';

Widget Types

Built-in Widgets

Widget Type Description Data Format
KPI Single metric display with icon and trend { value: number, label: string, icon?: string, trend?: number }
CHART Bar, line, pie charts (Chart.js) { labels: string[], datasets: [...] }
TABLE Paginated data table { columns: [...], rows: [...], total: number }
ACTIVITY_FEED Timeline of recent activities { items: [{ title, description, timestamp, icon }] }

Creating Custom Widgets

1. Create the Widget Component

// gauge-widget.component.ts
import { Component, Input } from '@angular/core';
import { BaseWidgetComponent } from '@expeed/ngx-dashboard-widgets';

@Component({
  selector: 'app-gauge-widget',
  standalone: true,
  template: `
    <div class="gauge-container">
      <svg viewBox="0 0 100 50">
        <!-- Gauge arc -->
        <path d="M10,50 A40,40 0 0,1 90,50"
              fill="none"
              stroke="#e0e0e0"
              stroke-width="8"/>
        <path d="M10,50 A40,40 0 0,1 90,50"
              fill="none"
              stroke="#3b82f6"
              stroke-width="8"
              [attr.stroke-dasharray]="dashArray"/>
      </svg>
      <div class="gauge-value">{{ data?.value }}%</div>
      <div class="gauge-label">{{ data?.label }}</div>
    </div>
  `,
  styles: [`
    .gauge-container { text-align: center; padding: 1rem; }
    .gauge-value { font-size: 2rem; font-weight: bold; }
    .gauge-label { color: #666; }
  `]
})
export class GaugeWidgetComponent extends BaseWidgetComponent {
  get dashArray(): string {
    const percentage = this.data?.value || 0;
    const circumference = 126; // Half circle
    return `${(percentage / 100) * circumference} ${circumference}`;
  }
}

2. Register the Custom Widget

// app.config.ts
import { provideWidgetRegistry } from '@expeed/ngx-dashboard-widgets';
import { GaugeWidgetComponent } from './widgets/gauge-widget.component';

export const appConfig: ApplicationConfig = {
  providers: [
    provideDashboard({ apiUrl: '/api' }),
    provideWidgetRegistry([
      { type: 'GAUGE', component: GaugeWidgetComponent }
    ])
  ]
};

3. Add Widget Definition (API)

INSERT INTO widget_definition (id, name, display_name, widget_type, data_source_type, default_width, default_height)
VALUES ('gauge-widget', 'gauge', 'Gauge Chart', 'GAUGE', 'SQL', 4, 2);

API Integration

Endpoints

Method Endpoint Description
POST /api/auth/login Login and get JWT token
GET /api/dashboards List user dashboards
GET /api/dashboards/{id} Get dashboard with widgets
POST /api/dashboards Create dashboard
PUT /api/dashboards/{id} Update dashboard
DELETE /api/dashboards/{id} Delete dashboard
PUT /api/dashboards/{id}/layout Save widget positions
POST /api/dashboards/{id}/widgets Add widget
PUT /api/dashboards/{id}/widgets/{wid} Update widget
DELETE /api/dashboards/{id}/widgets/{wid} Remove widget
POST /api/dashboards/{id}/widgets/{wid}/data Get widget data
GET /api/widget-definitions List available widget types
GET /api/dashboard-templates List templates
POST /api/dashboard-templates/{id}/provision Create dashboard from template

Using the Dashboard Service

import { DashboardService } from '@expeed/ngx-dashboard-core';

@Component({ ... })
export class MyComponent {
  private dashboardService = inject(DashboardService);

  // Load a dashboard
  loadDashboard(id: string) {
    this.dashboardService.loadDashboard(id).subscribe(dashboard => {
      console.log(dashboard);
    });
  }

  // Toggle edit mode
  toggleEdit() {
    this.dashboardService.toggleEditMode();
  }

  // Check current state
  get isEditing() {
    return this.dashboardService.editMode();
  }

  get currentDashboard() {
    return this.dashboardService.currentDashboard();
  }
}

Java API Library

Adding to Your Project

// build.gradle.kts
dependencies {
    implementation(project(":dashboard-core"))
    // or from Maven
    // implementation("com.expeed:dashboard-core:1.0.0")
}

Configuration

# application.yml
dashboard:
  context:
    tenant-id: ${TENANT_ID:default}

Using DashboardContext

@RestController
public class MyController {

    @Autowired
    private DashboardService dashboardService;

    @Autowired
    private DashboardContext dashboardContext;

    @GetMapping("/my-dashboards")
    public List<Dashboard> getMyDashboards() {
        String userId = dashboardContext.getUserId();
        return dashboardService.getDashboardsForUser(userId);
    }
}

Custom Data Providers

@Component
public class CustomWidgetDataProvider implements WidgetDataProvider {

    @Override
    public boolean supports(String dataSourceType) {
        return "CUSTOM_API".equals(dataSourceType);
    }

    @Override
    public WidgetDataResponse getData(WidgetDefinition definition, Map<String, Object> params) {
        // Fetch data from external API
        var data = externalApiClient.fetchData(definition.getDataSourceConfig());
        return new WidgetDataResponse(data);
    }
}

Configuration Reference

Angular Dashboard Config

Option Type Default Description
apiUrl string /api Base URL for API calls
gridColumns number 12 Number of grid columns
gridCellHeight number 80 Height of each grid cell in pixels
enableEditMode boolean true Allow users to edit dashboards
enableWidgetResize boolean true Allow widget resizing
enableWidgetFloat boolean true Widgets stay in place (don't auto-compact)
defaultRefreshInterval number 60 Auto-refresh interval in seconds
theme string light Theme: light, dark, or auto

Environment Variables (API)

Variable Default Description
DB_HOST localhost PostgreSQL host
DB_PORT 5432 PostgreSQL port
DB_NAME dashboard_demo Database name
DB_USERNAME postgres Database user
DB_PASSWORD postgres Database password
JWT_SECRET (generated) JWT signing secret

Development

Running Tests

# API tests (uses Testcontainers)
cd api
./gradlew test

# Angular tests
cd angular
npm test

Building for Production

# Build API
cd api
./gradlew :dashboard-demo:build

# Build Angular libraries
cd angular
npm run build:libs

# Build Angular demo app
cd angular
npm run build

Project Commands

# Angular
npm start              # Start dev server
npm test               # Run tests
npm run build          # Production build
npm run build:libs     # Build libraries only
npm run lint           # Lint code

# API
./gradlew bootRun                    # Start dev server
./gradlew test                       # Run tests
./gradlew build                      # Production build
./gradlew :dashboard-core:publish    # Publish library

Troubleshooting

Widgets not moving in edit mode

Ensure enableWidgetFloat: true in your dashboard config. This prevents widgets from auto-sinking when others are moved.

GridStack styles not loading

Import the GridStack CSS in your global styles:

@import 'gridstack/dist/gridstack.min.css';

API CORS errors

Configure CORS in your Spring Boot application:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://localhost:4200")
            .allowedMethods("*");
    }
}

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages