Shiftly
May 1, 2026 Β· View on GitHub
π Hebrew version available: README_HE.md
Shiftly is a work-hours tracking and salary calculation application built with React + TypeScript. It is designed to accurately calculate monthly salary based on daily shifts, special days, per-diem rules, and real-world labor regulations.
The project focuses not only on correctness, but on clear domain modeling, architectural stability, and long-term maintainability.
β οΈ This system provides indicative calculations only.
The results should not be used for official payroll purposes
and do not replace calculations performed by an authorized payroll department.
Motivation
Shiftly was born from a recurring problem observed in a real government office environment.
Workers track their time in shifts - a continuous block of hours with a clear start and end. But Israeli payslips don't work that way. They operate in weighted segments: overtime brackets that reset daily, Shabbat and holiday bonuses that kick in at specific hours, night premiums that split mid-way through.
The result is a predictable source of confusion: employees receive a payslip they can't verify, and have no practical way to cross-check whether the numbers are correct.
Shiftly bridges that gap. It takes shift input the way workers actually think - and calculates compensation based on Israeli labor law: overtime brackets, Shabbat and holiday bonuses, and night premiums - making the calculation transparent and verifiable for the person receiving the paycheck.
Core Design Principles
The core principle behind Shiftly is that calculation logic remains stable over time.
Salary rules do not change per implementation, but per period context. Dates, daylight saving time, hourly rates, per-diem rules, and allowances are treated as inputs rather than hardcoded logic.
This makes it possible to recalculate past months accurately using the same calculation pipeline, simply by changing the contextual parameters - without modifying domain code.
Features
- Shift-based salary calculation
- Support for:
- Regular workdays
- Partial special days (e.g. Fridays, holiday eves)
- Full special days (Shabbat, holidays)
- Holiday detection via Hebcal API
- Automatic resolution of Jewish holidays
- Differentiation between full and partial special days
- Sick days & vacation days
- Cross-day shifts
- Per-diem calculation with historical rate timeline
- Meal allowance calculation (small / large)
- Monthly aggregated breakdown
- Incremental recalculation (add / update / remove shifts)
- Fully reactive UI
Architecture Overview
Shiftly follows a Clean Architectureβinspired design, with a strong emphasis on keeping business rules isolated from UI, state management, and external services.
The goal is to ensure that domain logic remains predictable, testable, and unaffected by framework or UI changes.
High-Level Flow
Shift -> Day -> Month
Each level is calculated independently and aggregated incrementally.
Architectural Layers
Domain
The domain layer contains pure business logic and is framework-agnostic.
-
Builders Construct domain structures without embedding business rules.
-
Calculators Pure functions implementing salary rules.
-
Reducers Handle accumulation and rollback of calculated values, enabling incremental recalculation.
-
Resolvers Decision logic based on time, date, and contextual rules.
-
Factories & Composition Centralized wiring of domain components.
Adapters
Convert domain objects into UI-friendly view models. This ensures the domain never depends on presentation concerns.
Hooks
Thin orchestration layer between UI, domain, and state. Hooks coordinate data flow without embedding business logic.
State Management (Redux)
- Global and monthly state aggregation
- Deterministic add / subtract logic
- No full recomputation on every change
Key slices:
workDaysSliceglobalSlice
UI Components
Pure presentation logic. UI reacts to data - it does not implement salary rules.
Domain Concepts
Builders
Responsible for assembling domain structures:
ShiftMapBuilderShiftSegmentBuilderDayPayMapBuilderWorkDaysForMonthBuilder
Calculators
Pure calculation logic organized by concern:
- Regular hours: by shift / by day
- Extra & special segments: time-based bonuses
- Per-diem: shift / day / month levels
- Meal allowance: eligibility & rate calculation
Reducers
Accumulate and subtract breakdowns:
- Monthly pay map reducer
- Regular hours accumulator
- Fixed segment month reducer
- Meal allowance month reducer
- Workday month reducer
Resolvers
Context-aware decision logic:
- Holiday resolver (Hebcal-based)
- Shift segment resolver
- Timeline-based per-diem rate resolver
- Timeline-based meal allowance rate resolver
- Month resolver
- Workday info resolver
Services
Domain-level utilities:
DateService: Date manipulation and validationShiftService: Shift-related business logic
Pipelines
Composition pipelines for wiring domain components:
buildCoreServices: Date & shift servicesbuildResolvers: All resolver instancesbuildCalculators: Calculator instancesbuildShiftLayer: Shift-level logicbuildDayLayer: Day-level aggregationbuildMonthLayer: Month-level aggregation
Factories
Create specific calculator instances:
FixedSegmentFactoryRegularFactory
Tech Stack
Core
- React 19.2.3
- TypeScript 5.7.2
- Vite 6.2.0
State & Routing
- Redux Toolkit 2.11.0
- React Router 7.11.0
UI & Styling
- Material UI (MUI) 7.0.2
- Notistack 3.0.2 (notifications)
Data & Services
- Axios 1.9.0 (HTTP client)
- date-fns 4.1.0 (date manipulation)
- Hebcal API (holiday detection)
Testing
- Vitest 4.0.16
- Testing Library (React, Jest-DOM, User Event)
Testing
Shiftly uses Vitest for unit and integration testing, with a focus on domain logic validation.
Running Tests
# Run tests in watch mode
npm run test
# Run tests with UI
npm run test:ui
# Run tests with coverage
npm run test:coverage
# Run tests in CI mode (single run)
npm run test:ci
Test Coverage
Tests cover:
- Calculation logic (builders, calculators, reducers)
- Time-based resolution (holidays, rates, segments)
- Edge cases (cross-day shifts, partial days, sick/vacation)
- End-to-end calculation scenarios
The domain layer is fully testable and framework-independent, making it easy to validate business rules in isolation.
Getting Started
Clone the repository and install dependencies:
git clone https://github.com/dmaman86/shiftly.git
cd shiftly
npm install
npm run dev
Visit http://localhost:5173/shiftly in your browser.
Project Structure
src/
βββ app/ # Application shell
β βββ domain/ # Domain instance & wiring
β βββ providers/ # Context providers
β βββ routes/ # Route configuration
βββ domain/ # Business logic (framework-agnostic)
β βββ builder/ # Domain structure builders
β βββ calculator/ # Salary calculation logic
β β βββ regular/
β β βββ special/
β β βββ perdiem/
β β βββ mealallowance/
β βββ reducer/ # State accumulation & rollback
β βββ resolve/ # Context-aware decisions
β βββ factory/ # Component factories
β βββ pipelines/ # Composition pipelines
β βββ services/ # Domain services (date, shift)
β βββ types/ # Type definitions
βββ adapters/ # Domain -> UI view models
βββ features/ # Feature-specific UI modules
β βββ calculation-rules/
β βββ config/
β βββ info-dialog/
β βββ salary-summary/
β βββ work-table/
β βββ workday-timeline/
βββ hooks/ # React hooks (orchestration layer)
βββ hoc/ # Higher-order components
βββ layout/ # Layout components & error boundaries
βββ pages/ # Page components (Daily, Monthly, Rules)
βββ redux/ # State management
β βββ states/ # Redux slices
βββ services/ # External services
β βββ analytics/ # Salary feedback service
β βββ hebcal/ # Holiday API integration
βββ constants/ # Application constants
βββ utils/ # Helper utilities
Why This Architecture?
This architecture was chosen to handle:
- Complex salary rules
- Time-based edge cases (cross-day shifts, partial days)
- Multiple aggregation levels (shift β day β month)
- Incremental recalculation without full recompute
- Historical accuracy without modifying core logic
It allows the system to scale without turning into tightly coupled conditional logic inside UI components or reducers.
Implementation Notes
- All percentages are normalized (e.g.
1= 100%,1.5= 150%,2= 200%) - Domain logic is framework-agnostic and fully testable
- UI reacts to data, not business rules
- Historical calculations use time-based context, not code changes
UI Behavior Overview
Application Views
Shiftly provides two main calculation views:
Daily View
- Focused on day-by-day shift input
- Allows adding, editing, and validating shifts
- Displays per-day breakdown
- Monthly totals are updated incrementally
Monthly View
- Focused on aggregated monthly salary analysis
- Requires selecting year and month
- Ensures accurate per-diem and meal allowance rates based on period
- Displays a compact monthly salary summary
Both views share the same domain calculation pipeline. Only the presentation and configuration context changes.
Configuration Panel
The ConfigPanel adapts its behavior based on the active view:
-
In Daily mode:
- Allows defining standard hours and hourly rate
- Monthly values are derived incrementally
-
In Monthly mode:
- Year and month selection becomes mandatory
- Ensures correct historical rates for per-diem and meal allowance
- Enforces hourly rate definition for salary calculation
This separation keeps configuration logic explicit and context-aware.
Workday Overview
- If
baseRateis not set: only displays worked hours per day. - If
baseRateis set: shows per-day salary and monthly total. - Sick/Vacation days: disables work segments.
- Shabbat/holiday: only allows work, not absence.
- Cross-day shifts: user must confirm with a checkbox.
Day Configuration Examples
Shabbat or Holiday - Work Hours Allowed
Cannot mark as Sick/Vacation, but work segments are allowed.

Sick Day or Vacation Day - No Work Segments
Marked as Sick/Vacation; no work segments allowed.

Cross-Day Shift - "ΧΧΧ¦Χ ΧΧΧ" Checkbox
End time is next day; system asks to confirm crossing day.
| Cross-Day Warning | Shift Save |
|---|---|
![]() | ![]() |
Breakdown Summaries
Daily Breakdown - Expanded View
Detailed breakdown showing all calculation components for a single day.
| Breakdown Summary 1 | Breakdown Summary 2 |
|---|---|
![]() | ![]() |
Daily Breakdown - Compact View
Condensed view for quick daily salary overview.

Monthly Summary
Aggregated monthly salary calculation with all components.

License
This project is licensed under the MIT License.



