Calculators are Shiftly’s innermost computation layer. Every salary figure that appears in a payslip breakdown — overtime bracket hours, night bonuses, Shabbat premiums, per-diem points, and meal allowance amounts — originates in a calculator. Each calculator receives a typed input, applies a single, well-scoped rule, and returns a typed output. There is no shared state, no I/O, and no dependency on Redux or React — calculators are plain TypeScript classes that can be instantiated and tested in complete isolation from the rest of the application.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.
The Calculator Interface
Reducer<State> so they can both produce and accumulate their own output type, saving the cost of a separate reducer class for simple structures.
All calculator instances are created once by
buildCalculators() and shared across the entire domain pipeline. They carry no mutable state, so sharing a single instance is safe.Calculators Overview
RegularByShiftCalculator
Applies per-shift bracket logic: 100% → 125% → 150% from lowest to highest, using per-shift hour position.
RegularByDayCalculator
Applies per-day bracket logic using total accumulated day hours: 100% up to standard, then 125% for the next 2h, then 150%.
ExtraCalculator
Sums labeled segment hours for evening (20%) and night (50%) bonus windows from a shift’s
LabeledSegmentRange[].SpecialCalculator
Sums labeled segment hours for Shabbat/holiday windows: 150% daytime and 200% night-time.
FixedSegmentFactory
Creates
Segment values at 100% for sick days, vacation days, and extra-Shabbat hour bookkeeping.LargeMealAllowanceCalculator
Awards 1 large meal allowance point for shifts ≥ 10 hours that span both morning and night, or pure night duty.
SmallMealAllowanceCalculator
Awards 1 small meal allowance point for shifts that include a qualifying night portion but do not qualify for large.
DefaultPerDiemDayCalculator
Determines per-diem tier (A/B/C) from total field-duty hours in a day and calculates the monetary amount.
DefaultPerDiemMonthCalculator
Accumulates and subtracts
PerDiemInfo across days to build the monthly per-diem total.Core Breakdown Types
All calculators produce or consume one of these three breakdown structures:Regular Calculators
Both regular calculators extendBaseRegularCalculator, which also implements Reducer<RegularBreakdown> so the same class handles accumulation without a separate reducer. The input type is shared:
RegularByShiftCalculator
Used in the shift-level builder. Applies the bracket overflow logic starting from the shift’s own hour count:
- If
meta.typeDay === SpecialFulland this is not a cross-day continuation, all hours go to 150% (Shabbat shifts have no regular bracket). - Otherwise, subtracts overflow into each bracket from the top down: anything beyond
standardHours + 2→ 150%; the next 2h → 125%; the remainder → 100%.
RegularByDayCalculator
Used in the day-level builder. Applies bracket logic to the total accumulated hours across all shifts in the day:
- Starts by capping at
standardHoursfor 100%; the excess goes to 125% up to 2 hours; anything further goes to 150%. - This produces the correct bracket split when multiple short shifts combine over a day, which the per-shift version would over-count.
RegularConfig
RegularFactory
RegularFactory is the static entry point for constructing the three regular-related instances used in the pipeline:
ExtraCalculator
ExtraCalculator computes the evening and night bonus hours from the labeled segment list produced by ShiftSegmentBuilder. It implements both Calculator and Reducer so it can calculate a shift’s extra hours and accumulate them across shifts within a day.
Logic
labeledSegments by key:
| Key | Window | Rate |
|---|---|---|
"hours20" | 14:00 – 22:00 | 20% additive bonus |
"hours50" | 22:00 – 06:00 | 50% additive bonus |
(point.end - point.start) / 60.
SpecialCalculator
SpecialCalculator computes Shabbat and holiday pay from the same labeled segment list, using segment keys "shabbat150" and "shabbat200".
Logic
| Key | Window on a full special day | Rate |
|---|---|---|
"shabbat150" | 06:00 – 22:00 | 150% |
"shabbat200" | 00:00 – 06:00 and 22:00 – 30:00 | 200% |
SpecialPartialStart day (Friday, eve-of-holiday), the 150% window begins at 17:00 or 18:00 depending on DST.
FixedSegmentFactory
FixedSegmentFactory creates Segment values with a fixed 100% rate. Three separate instances are created — one each for sick, vacation, and extra-Shabbat tracking — so they remain independently identifiable in the WorkDayMap.
sick— filled withstandardHourson aWorkDayStatus.sickday.vacation— filled withstandardHourson aWorkDayStatus.vacationday.extraShabbat— filled with total Shabbat hours on a normal worked day where a Shabbat/holiday shift was worked, to enable separate payslip line tracking.
Meal Allowance Calculators
Meal allowance eligibility is determined by the structure of the working day: whether it has a meaningful morning portion, a meaningful night portion (≥ 4 night hours), and whether it is a field-duty day.Input Type
LargeMealAllowanceCalculator
{ points: 1, amount: rate }) when:
totalHours >= 10, and- The day has both morning and night portions, or is a pure night shift, or is a non-field-duty day shift.
{ points: 0, amount: 0 } for field-duty day shifts under 10 hours, as per-diem replaces meal allowance in that context.
SmallMealAllowanceCalculator
day.hasNight === true and the large calculator returned 0 points.
Output Type
Per-Diem Calculators
Per-diem tracks days worked in the field (whenisFieldDutyShift: true is set on a shift). The applicable monetary rate is resolved per-year-month from TimelinePerDiemRateResolver.
DefaultPerDiemDayCalculator
| Total field-duty hours | Tier | Points |
|---|---|---|
| < 4h | — | 0 |
| ≥ 4h | A | 1 |
| ≥ 8h | B | 2 |
| ≥ 12h | C | 3 |
DefaultPerDiemMonthCalculator
PerDiemInfo across days for the monthly total. The monthly aggregate does not carry a tier — only points and amount are summed. Subtraction clamps each field at 0 to prevent negative totals.
Pipeline Entry Point
All calculator instances are assembled once by thebuildCalculators() pipeline function and returned as a typed Calculators object:
buildCalculators() is called and how these instances are distributed across the shift, day, and month layers.