Simulation Filters

A composite filter component with project, template, calendar, and simulation selectors

Live Example

Basic Filters (3 components)

Project, Template, and Calendar selectors

Full Filters (4 components)

Project, Template, Calendar, and Simulation selectors

Multiselect Templates

Project and multi-template selectors

Multiselect Templates and Simulations

All components with multi-select for templates and simulations

Prerequisites

This component requires sgerp-frontend-lib to be installed in your project. See the Installation guide for setup instructions.

Dependencies

This component will automatically install:

  • react-day-picker - Calendar component
  • date-fns - Date utilities
  • date-fns-tz - Timezone support
  • lucide-react - For icons
  • shadcn/ui components: button, input, popover

Usage

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"

export function MyComponent() {
  return (
    <SimulationFilters
      onProjectChange={(id, project) => console.log("Project:", project)}
      onDateChange={(date) => console.log("Date:", date)}
      showTemplateSelector={true}
      showCalendar={true}
    />
  )
}

Props

PropTypeDescription
onProjectChange(projectId: number | null, project?: Project) => voidOptional. Callback when project selection changes
onTemplateChange(templateId: number | null, template?: Simulation) => voidOptional. Callback when template selection changes (single-select mode)
onTemplatesChange(templateIds: number[], templates?: Simulation[]) => voidOptional. Callback when template selection changes (multi-select mode)
onDateChange(date: Date | undefined) => voidOptional. Callback when calendar date changes
onSimulationChange(simulationId: number | null, simulation?: Simulation) => voidOptional. Callback when simulation selection changes (single-select mode)
onSimulationsChange(simulationIds: number[], simulations?: Simulation[]) => voidOptional. Callback when simulation selection changes (multi-select mode)
showTemplateSelectorbooleanOptional. Show template selector. Default: true
showCalendarbooleanOptional. Show calendar dropdown. Default: true
showSimulationSelectorbooleanOptional. Show simulation selector. Default: false
multiSelectTemplatesbooleanOptional. Use multiselect for templates instead of single-select. Default: false
multiSelectSimulationsbooleanOptional. Use multiselect for simulations instead of single-select. Default: false
classNamestringOptional. Additional CSS classes
autoFetchSimulationsbooleanOptional. Automatically fetch simulations when filters change. Default: true

Features

  • Dependent Filtering: Automatically handles filter dependencies (template depends on project, etc.)
  • Single or Multi-select: Choose between single-select or multi-select mode for templates and simulations
  • Auto-selection: Automatically selects items when only one option is available:
    • If user has access to only one project, it's auto-selected
    • If only one template exists for the project, it's auto-selected (in both single and multi-select modes)
    • If simulation selector is shown, the first simulation is auto-selected (single-select) or only item is auto-selected (multi-select)
  • Timezone Support: Date ranges respect project timezone for accurate filtering
  • Auto-fetch: Automatically fetches simulations when project/template/month changes
  • Flexible Layout: Show/hide individual filter components as needed
  • Type-safe Callbacks: Receive both IDs and full objects in callbacks
  • Responsive: Works on all screen sizes

Examples

Basic Three-Component Filter

Project, template, and calendar selectors:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useState } from "react"

export function BasicFilter() {
  const [selectedDate, setSelectedDate] = useState<Date>()

  return (
    <SimulationFilters
      onDateChange={setSelectedDate}
      showTemplateSelector={true}
      showCalendar={true}
      showSimulationSelector={false}
    />
  )
}

Four-Component Filter with Simulation Selector

Add individual simulation selection:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useState } from "react"
import type { Simulation } from "sgerp-frontend-lib"

export function FullFilter() {
  const [selectedSimulation, setSelectedSimulation] = useState<Simulation | null>(null)

  return (
    <SimulationFilters
      onSimulationChange={(id, simulation) => {
        setSelectedSimulation(simulation || null)
      }}
      showTemplateSelector={true}
      showCalendar={true}
      showSimulationSelector={true}
    />
  )
}

Project and Calendar Only

Minimal filter without template selector:

<SimulationFilters
  onProjectChange={(id, project) => console.log("Project:", project)}
  onDateChange={(date) => console.log("Date:", date)}
  showTemplateSelector={false}
  showCalendar={true}
  showSimulationSelector={false}
/>

With All Callbacks

Handle all selection changes:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useState } from "react"
import type { Project, Simulation } from "sgerp-frontend-lib"

export function CompleteExample() {
  const [project, setProject] = useState<Project | null>(null)
  const [template, setTemplate] = useState<Simulation | null>(null)
  const [date, setDate] = useState<Date>()
  const [simulation, setSimulation] = useState<Simulation | null>(null)

  return (
    <div>
      <SimulationFilters
        onProjectChange={(id, proj) => setProject(proj || null)}
        onTemplateChange={(id, tmpl) => setTemplate(tmpl || null)}
        onDateChange={setDate}
        onSimulationChange={(id, sim) => setSimulation(sim || null)}
        showTemplateSelector={true}
        showCalendar={true}
        showSimulationSelector={true}
      />

      {/* Display selected values */}
      {project && <div>Project: {project.name} (Timezone: {project.timezone})</div>}
      {template && <div>Template: {template.name}</div>}
      {date && <div>Date: {date.toLocaleDateString()}</div>}
      {simulation && <div>Simulation: {simulation.name}</div>}
    </div>
  )
}

Custom Styling

Apply custom classes to the container:

<SimulationFilters
  className="p-4 bg-muted rounded-lg"
  showTemplateSelector={true}
  showCalendar={true}
  showSimulationSelector={false}
/>

Multi-select Templates

Use multiselect dropdown for template selection:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useState } from "react"
import type { Simulation } from "sgerp-frontend-lib"

export function MultiSelectTemplates() {
  const [templateIds, setTemplateIds] = useState<number[]>([])
  const [templates, setTemplates] = useState<Simulation[]>([])

  return (
    <SimulationFilters
      onTemplatesChange={(ids, tmpls) => {
        setTemplateIds(ids)
        setTemplates(tmpls || [])
      }}
      showTemplateSelector={true}
      showCalendar={false}
      showSimulationSelector={false}
      multiSelectTemplates={true}
    />
  )
}

Multi-select Templates and Simulations

Use multiselect for both templates and simulations:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useState } from "react"
import type { Simulation } from "sgerp-frontend-lib"

export function FullMultiSelect() {
  const [templateIds, setTemplateIds] = useState<number[]>([])
  const [templates, setTemplates] = useState<Simulation[]>([])
  const [simulationIds, setSimulationIds] = useState<number[]>([])
  const [simulations, setSimulations] = useState<Simulation[]>([])

  return (
    <SimulationFilters
      onTemplatesChange={(ids, tmpls) => {
        setTemplateIds(ids)
        setTemplates(tmpls || [])
      }}
      onSimulationsChange={(ids, sims) => {
        setSimulationIds(ids)
        setSimulations(sims || [])
      }}
      showTemplateSelector={true}
      showCalendar={true}
      showSimulationSelector={true}
      multiSelectTemplates={true}
      multiSelectSimulations={true}
    />
  )
}

Manual Fetch Control

Disable auto-fetching and handle it manually:

import { SimulationFilters } from "@/components/ui/sgerp-simulation-filters"
import { useSGERP } from "sgerp-frontend-lib"
import { useState } from "react"
import type { Project } from "sgerp-frontend-lib"

export function ManualFetch() {
  const api = useSGERP()
  const [projectId, setProjectId] = useState<number | null>(null)

  const handleProjectChange = async (id: number | null, project?: Project) => {
    setProjectId(id)

    // Manually fetch simulations with custom logic
    if (id && api) {
      await api.collections.simulation.fetch({
        project_id: id,
        limit: 100,
      })
    }
  }

  return (
    <SimulationFilters
      onProjectChange={handleProjectChange}
      autoFetchSimulations={false}
      showTemplateSelector={true}
      showCalendar={true}
    />
  )
}

Filter Logic

The component implements these filtering rules:

  1. Template Selector:

    • Disabled until project is selected
    • Filters by: project_id and simulation_mode: 'template'
    • Resets when project changes
    • Single-select mode: Uses SGERPAutocomplete, calls onTemplateChange
    • Multi-select mode: Uses SGERPMultiselect, calls onTemplatesChange
  2. Calendar:

    • Disabled until project is selected
    • Fetches simulations for visible month
    • Uses project timezone for date ranges
    • Shows dots on dates with simulations
    • Works with both single and multiple template IDs
  3. Simulation Selector (when enabled):

    • Disabled until project is selected
    • If calendar is shown, disabled until date is selected
    • Filters by project, template(s) (if set), and date (if set)
    • Resets when filters change
    • Single-select mode: Uses SGERPAutocomplete, calls onSimulationChange
    • Multi-select mode: Uses SGERPMultiselect, calls onSimulationsChange

Auto-selection Behavior

The component automatically selects items to streamline the user experience:

Project Auto-selection

When the component mounts, it fetches all available projects. If exactly one project is found, it's automatically selected:

// Triggers automatically on mount
if (projects.models.length === 1) {
  // Auto-select the single project
  // Calls onProjectChange callback
}

Template Auto-selection

When a project is selected (manually or automatically), templates are fetched. If exactly one template exists for that project, it's automatically selected:

Single-select mode:

// Triggered when projectId changes
if (templates.length === 1) {
  // Auto-select the single template
  // Calls onTemplateChange callback
}

Multi-select mode:

// Triggered when projectId changes
if (templates.length === 1) {
  // Auto-select the single template
  // Calls onTemplatesChange callback with array
}

Simulation Auto-selection

When simulations are fetched (and showSimulationSelector={true}), simulations are automatically selected based on mode:

Single-select mode:

// Triggered when simulations are loaded
if (simulations.models.length > 0) {
  // Auto-select first simulation
  // Calls onSimulationChange callback
}

Multi-select mode:

// Triggered when simulations are loaded
if (simulations.models.length === 1) {
  // Auto-select the single simulation
  // Calls onSimulationsChange callback with array
}

Resetting Auto-selection

Auto-selection flags are reset when users manually change selections, allowing them to choose different options at any time.

Timezone Handling

The component automatically respects project timezones:

// When fetching simulations for a month, dates are formatted in project timezone
const timezone = project.timezone || "UTC"
const startDate = formatInTimeZone(monthStart, timezone, "yyyy-MM-dd")
const endDate = formatInTimeZone(monthEnd, timezone, "yyyy-MM-dd")

// API filters use timezone-aware dates
filters = {
  start_time__gte: startDate + "T00:00:00",
  start_time__lte: endDate + "T23:59:59",
}

This ensures accurate filtering for projects in different timezones.

Performance

The component is optimized for performance:

  • Auto-fetch: Only fetches when filters change (project, template, month)
  • Efficient queries: Uses only_fields to limit data transfer
  • Smart dependencies: Template and simulation selectors only fetch when dependencies are met
  • Memoized calculations: Date filtering uses React.useMemo

API Integration

The component uses the SGERP Collections API:

Single-select mode:

// Auto-fetches simulations based on filters
await api.collections.simulation.fetch({
  limit: 1000,
  start_time__gte: startDate + "T00:00:00",
  start_time__lte: endDate + "T23:59:59",
  project_id: projectId,
  template_id__in: templateId, // if single template selected
})

Multi-select mode:

// Auto-fetches simulations based on filters
await api.collections.simulation.fetch({
  limit: 1000,
  start_time__gte: startDate + "T00:00:00",
  start_time__lte: endDate + "T23:59:59",
  project_id: projectId,
  template_id__in: templateIds.join(','), // if multiple templates selected
})

Make sure you have set up a connection before using this component. See the Try It Out page for connection setup.

Styling

The component uses a flex layout with equal-width columns:

display: flex
gap: 0.75rem (12px)
each child: flex: 1

Customize spacing and layout with the className prop.