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.

The Configuration panel (ConfigPanel) appears at the top of both the Daily and Monthly pages. It is divided into two cards side by side: Date (year and month selectors) and Work Parameters (standard hours and hourly rate). Every field feeds directly into Redux state and causes the rest of the page to react in real time.
Changing the year or month clears all shift data entered for that period. The globalBreakdown is reset to an empty MonthPayMap and dailyPayMaps is emptied. Make sure you have noted any values you need before switching periods.

Configuration Fields

FieldRedux ActionDefaultValidation
YearsetYearCurrent calendar yearInteger ≥ SYSTEM_START_YEAR (2015) and ≤ current year
MonthsetMonthCurrent calendar monthDropdown restricted to months available for the selected year
Standard HourssetStandardHours6.67 hours/dayAny non-negative number
Base Rate (Hourly Rate)setBaseRate0 (unset)Any non-negative number; 0 hides salary columns

Field Behaviour in Detail

Year

The year field is a free-text input backed by a 500 ms debounce. If the typed value is below SYSTEM_START_YEAR (2015) an inline error message appears: “Minimum supported year is 2015”. If the value is valid and different from the stored year, updateYear dispatches setYear, which also resets the month to the resolver’s default month for that year and clears all monthly data.
// ConfigPanel.tsx — year validation and dispatch
useEffect(() => {
  const parsedYear = Number(debouncedYear);
  if (
    !isNaN(parsedYear) &&
    parsedYear >= SYSTEM_START_YEAR &&
    parsedYear <= monthResolver.getCurrentYear() &&
    parsedYear !== year
  ) {
    updateYear(parsedYear);
    updateMonth(monthResolver.resolveDefaultMonth(parsedYear));
  }
}, [debouncedYear, year, monthResolver, updateYear, updateMonth]);

Month

The month field is rendered as a <Select> dropdown. The list of available months is generated by monthResolver.getAvailableMonthOptions(year) — for the current year only months up to the present calendar month are offered, while past years show all twelve months. Selecting a different month dispatches setMonth and resets monthly data.

Standard Hours

Standard Hours defines the daily regular-hours threshold. The first standardHours hours of any workday are billed at 100%; the next two hours at 125%; everything above that at 150%. The default of 6.67 hours corresponds to the statutory base for security-sector employees in Israeli government offices. Changes are debounced at 500 ms and dispatched via setStandardHours. Unlike year/month changes, updating standard hours does not reset existing shift data — it changes how future (or re-saved) shifts are calculated.

Base Rate (Hourly Rate)

The hourly base rate is used as the monetary multiplier for every pay category. A rate of 0 means no monetary conversion takes place:
  • In Daily mode: salary columns are hidden if baseRate === 0, but hours columns are always visible. A helper text reads “Enter hourly rate to display daily or monthly salary”.
  • In Monthly mode: the base rate is treated as required. If baseRate === 0 the field shows an error state and helper text reads “Monthly salary calculation requires setting hourly rate”.
// ConfigPanel.tsx — mode-aware helper text
const helperTextBaseRate = (): string => {
  if (baseRate === 0) {
    if (mode === "daily") return t("config.base_rate_helper_daily");
    else return t("config.base_rate_helper_monthly");
  }
  return "";
};

Mode: "daily" vs "monthly"

ConfigPanel accepts a mode prop that changes one visual element:
  • mode="daily" — no additional banner; the base rate is optional and the table still shows hours if omitted.
  • mode="monthly" — a blue info banner is shown below the month selector confirming the period in use: “Calculation based on <Month> <Year>”. This banner is a reminder that per-diem and meal-allowance rates are resolved to this exact period.
{mode === "monthly" && (
  <Box /* ... info banner styles ... */ >
    <InfoIcon fontSize="small" color="info" />
    <Typography variant="caption" color="info.dark">
      {t("config.calculation_based_on", { monthName: monthNames[month - 1], year })}
    </Typography>
  </Box>
)}

Debounced Input Behaviour

Year, Standard Hours, and Base Rate are all free-text inputs managed by useDebounce with a 500 ms delay. The component keeps its own local inputsValues state so the UI reflects keystrokes instantly. Only after the debounce settles is the parsed numeric value validated and dispatched to Redux. This prevents unnecessary Redux updates and downstream recalculations on every keystroke.
const debouncedYear          = useDebounce({ value: inputsValues.yearInput,          delay: 500 });
const debouncedStandardHours = useDebounce({ value: inputsValues.standardHoursInput, delay: 500 });
const debouncedBaseRate      = useDebounce({ value: inputsValues.baseRateInput,       delay: 500 });
If the global Redux state is updated from outside (e.g. year reset cascades a month change), the local inputs are synchronised back via a separate effect that watches year, standardHours, and baseRate.

Daily View

See how the configured year, month, standard hours, and base rate are consumed when entering and saving shifts.

Monthly View

See how the base rate and selected period drive the aggregated salary summary.

Build docs developers (and LLMs) love