Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dmaman86/shiftly/llms.txt

Use this file to discover all available pages before exploring further.

Shiftly is built around a clean architecture pattern with a hard boundary between business logic and the React UI. The domain layer contains all salary calculation rules as pure, framework-agnostic functions — it has no knowledge of Redux, React hooks, or MUI components. Adapters translate domain output into view models. Hooks orchestrate data flow. Redux holds application state. The UI renders what it receives. This separation means the entire calculation engine can be tested in isolation, recalculated for any historical period, and evolved without touching presentation code.

Three-Level Aggregation Flow

Every salary calculation in Shiftly moves through three levels of aggregation, each computed independently:
Shift → Day → Month
  1. Shift — A single continuous block of work (start time, end time, date). The shift layer segments it into rate-bearing components: regular hours, overtime brackets, Shabbat bonuses, and night premiums.
  2. Day — All shifts on a given date are combined into a day-level pay breakdown. The day’s classification (regular, partial special, full special/Shabbat) determines which rate multipliers apply.
  3. Month — Day-level breakdowns are accumulated into a monthly aggregate using reducers that support incremental add and subtract operations — no full recomputation is triggered when a single shift changes.

Architectural Layers

1. Domain

The domain layer is the heart of Shiftly. It is entirely framework-agnostic — no React, no Redux, no MUI imports. It contains:
  • Builders — Assemble domain structures (ShiftMapBuilder, ShiftSegmentBuilder, DayPayMapBuilder, WorkDaysForMonthBuilder) without embedding business rules in construction logic.
  • Calculators — Pure functions that implement salary rules, organized by concern: regular hours (per shift and per day), extra and special segments with time-based bonuses, per-diem at shift/day/month granularity, and meal allowance eligibility and rate calculation.
  • Reducers — Accumulate and roll back calculated values. Key reducers include the monthly pay map reducer, regular hours accumulator, fixed segment month reducer, meal allowance month reducer, and workday month reducer. These enable incremental recalculation when individual shifts are added, updated, or removed.
  • Resolvers — Context-aware decision functions: the holiday resolver (Hebcal-based), shift segment resolver, timeline-based per-diem rate resolver, timeline-based meal allowance rate resolver, month resolver, and workday info resolver.
  • Factories — Create specific calculator instances: FixedSegmentFactory and RegularFactory.
  • Pipelines — Composition entry points that wire domain components together: buildCoreServices, buildResolvers, buildCalculators, buildShiftLayer, buildDayLayer, buildMonthLayer, and buildPayMapPipeline (the top-level entry point used in domain.instance.ts).
  • Services — Domain-level utilities: DateService for date manipulation and validation, and ShiftService for shift-related business logic.
All percentages in the domain layer are normalized decimal values: 1 = 100%, 1.25 = 125%, 1.5 = 150%, 2 = 200%. This convention applies uniformly across calculators, resolvers, and rate timelines — never expect a percentage integer in domain code.

2. Adapters

Adapters convert domain objects into UI-friendly view models. This one-way mapping ensures the domain never imports from the presentation layer. Key adapter functions include dayToPayBreakdownVM and monthToPayBreakdownVM, which reshape raw domain pay maps into the structured objects that feature components consume directly.

3. Hooks

React hooks form the orchestration layer between the UI, domain, and Redux state. They coordinate data flow without embedding any salary calculation logic. Key hooks include:
  • useDomain — Provides access to the singleton domain instance (builders, calculators, resolvers, services).
  • useWorkDays — Manages the work days list and dispatches Redux actions for shift add/update/remove.
  • useGlobalState — Reads from and writes to globalSlice (configuration and monthly aggregate).
  • usePageTracking — Fires analytics page-view events on route changes.

4. State Management (Redux)

Redux Toolkit manages two slices:
  • globalSlice — Holds application-wide configuration (standard hours, hourly base rate, selected year/month) and the running monthly aggregate. This is the slice that grows as shifts are added throughout the month.
  • workDaysSlice — Holds the list of work-day records rendered by the Daily and Monthly views.
Reducers in Redux mirror the domain reducer pattern: add and subtract operations are deterministic, so removing a shift subtracts exactly what was previously added — no full recompute.

5. UI Components

UI components are purely presentational. They render data passed to them via props or selectors, and dispatch actions or call domain hooks in response to user interaction. The features/ directory is organized by product area:
  • work-table — The shift input table rendered on the Daily page.
  • salary-summary — Per-day and monthly salary breakdown panels.
  • workday-timeline — Visual timeline for a selected workday.
  • config — The ConfigPanel that adapts between Daily mode (standard hours and rate) and Monthly mode (year/month selection with enforced rate).
  • calculation-rules — The read-only rules reference page.

Project Directory Structure

src/
├── app/              # Application shell, providers, routes
├── domain/           # Business logic (framework-agnostic)
│   ├── builder/
│   ├── calculator/
│   ├── reducer/
│   ├── resolve/
│   ├── factory/
│   ├── pipelines/
│   ├── services/
│   └── types/
├── adapters/         # Domain → UI view models
├── features/         # Feature-specific UI modules
├── hooks/            # React orchestration hooks
├── hoc/              # Higher-order components
├── layout/           # Layout & error boundaries
├── pages/            # DailyPage, MonthlySummaryPage, CalculationRulesPage
├── redux/            # Redux store & slices
├── services/         # External services (hebcal, analytics)
├── constants/        # App-wide constants & enums
└── utils/            # Helper utilities

Domain Instance Bootstrap

The domain is wired once at application startup in src/app/domain/domain.instance.ts via the buildPayMapPipeline() factory. The resulting singleton is placed into a React context by DomainProvider and accessed anywhere in the tree via the useDomain hook — ensuring that all pages and features share the same initialized builder and resolver instances.
// src/app/domain/domain.instance.ts
import { buildPayMapPipeline } from "@/domain";

const pipelineInstance = buildPayMapPipeline();

export const domain: DomainContextType = {
  payMap: {
    shiftMapBuilder: pipelineInstance.payMap.shiftMapBuilder,
    dayPayMapBuilder: pipelineInstance.payMap.dayPayMapBuilder,
    monthPayMapCalculator: pipelineInstance.payMap.monthPayMapCalculator,
    workDaysMonthBuilder: pipelineInstance.payMap.workDaysForMonthBuilder,
  },
  resolvers: {
    holidayResolver: pipelineInstance.resolvers.holidayResolver,
    perDiemResolver: pipelineInstance.resolvers.perDiemRateResolver,
    dayInfoResolver: pipelineInstance.resolvers.workDayInfoResolver,
    monthResolver: pipelineInstance.resolvers.monthResolver,
    mealAllowanceRateResolver: pipelineInstance.resolvers.mealAllowanceRateResolver,
  },
  services: {
    dateService: pipelineInstance.services.dateService,
    shiftService: pipelineInstance.services.shiftService,
  },
};

Provider Stack

The application shell wraps the component tree with a carefully ordered set of providers defined in AppProviders:
DirectionContext  →  Redux Provider  →  CacheProvider (RTL/LTR Emotion)
  →  ThemeProvider (MUI)  →  AppSnackbarProvider  →  DomainProvider
    →  LocalizationProvider
Direction is derived from the URL language segment (/he/ → RTL, /en/ → LTR) on initialization, and the MUI theme and Emotion CSS cache are both recreated whenever direction changes.

Routing

Routes are lazy-loaded using React’s Suspense and lazy. The URL structure is /:lang/(daily|monthly|calculation-rules). Any unmatched path under a language segment redirects to /:lang/daily. The root path / redirects to /he/daily.
/he/daily                →  DailyPage (Hebrew, RTL)
/he/monthly              →  MonthlySummaryPage (Hebrew, RTL)
/he/calculation-rules    →  CalculationRulesPage (Hebrew, RTL)
/en/daily                →  DailyPage (English, LTR)
/en/monthly              →  MonthlySummaryPage (English, LTR)
/en/calculation-rules    →  CalculationRulesPage (English, LTR)

Domain Architecture Reference

Builders

Domain structure builders: ShiftMapBuilder, ShiftSegmentBuilder, DayPayMapBuilder, and WorkDaysForMonthBuilder. Responsible for assembling inputs before calculation.

Calculators

Pure calculation functions for regular hours, overtime brackets, per-diem, meal allowance, and special-day rate segments. No side effects.

Reducers

Accumulation and rollback logic enabling incremental monthly recalculation. Supports add and subtract operations on pay maps without full recomputes.

Resolvers

Context-aware decision logic: holiday classification via Hebcal, time-based rate timeline resolution, shift segment classification, and workday info resolution.

Build docs developers (and LLMs) love