Added configurable shift types and holiday settings

This commit is contained in:
2026-03-18 16:51:34 +08:00
parent 4eaf9444f0
commit 3af7a1e348
7 changed files with 1463 additions and 138 deletions
@@ -0,0 +1,31 @@
-- Ensure rotation_config has new defaults without overwriting existing custom values.
-- This migration is idempotent: rerunning it does not change already-set values.
-- Ensure the row exists (older installs may not have created it yet).
INSERT INTO app_settings (key, value)
VALUES ('rotation_config', '{}'::jsonb)
ON CONFLICT (key) DO NOTHING;
-- Add missing keys (only) using a conditional merge.
UPDATE app_settings
SET value = value || (
(CASE
WHEN value ? 'shift_types' THEN '{}'::jsonb
ELSE jsonb_build_object(
'shift_types',
'[{"id":"am","label":"AM Duty","start_hour":7,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"pm","label":"PM Duty","start_hour":15,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"on_call","label":"On Call","start_hour":23,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"on_call_saturday","label":"On Call (Saturday)","start_hour":17,"start_minute":0,"duration_minutes":900,"allowed_roles":["it_staff"]},
{"id":"on_call_sunday","label":"On Call (Sunday)","start_hour":17,"start_minute":0,"duration_minutes":840,"allowed_roles":["it_staff"]},
{"id":"normal","label":"Normal","start_hour":8,"start_minute":0,"duration_minutes":540,"allowed_roles":["admin","dispatcher","programmer","it_staff"]},
{"id":"normal_ramadan_islam","label":"Normal (Ramadan - Islam)","start_hour":8,"start_minute":0,"duration_minutes":480,"allowed_roles":["admin","dispatcher","programmer","it_staff"]},
{"id":"normal_ramadan_other","label":"Normal (Ramadan - Other)","start_hour":8,"start_minute":0,"duration_minutes":540,"allowed_roles":["admin","dispatcher","programmer","it_staff"]}]'::jsonb
)
END)
|| (CASE WHEN value ? 'role_weekly_hours' THEN '{}'::jsonb ELSE jsonb_build_object('role_weekly_hours', '{}'::jsonb) END)
|| (CASE WHEN value ? 'holidays' THEN '{}'::jsonb ELSE jsonb_build_object('holidays', '[]'::jsonb) END)
|| (CASE WHEN value ? 'sync_philippines_holidays' THEN '{}'::jsonb ELSE jsonb_build_object('sync_philippines_holidays', false) END)
|| (CASE WHEN value ? 'holidays_year' THEN '{}'::jsonb ELSE jsonb_build_object('holidays_year', NULL) END)
)
WHERE key = 'rotation_config';
@@ -0,0 +1,59 @@
-- Merge stored shift_types with defaults so new default shift types automatically appear
-- without overwriting existing customizations.
UPDATE app_settings
SET value = jsonb_set(
value,
'{shift_types}',
(
WITH
-- Default shift types (must match RotationConfig._defaultShiftTypes())
defaults AS (
SELECT '[
{"id":"am","label":"AM Duty","start_hour":7,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"pm","label":"PM Duty","start_hour":15,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"on_call","label":"On Call","start_hour":23,"start_minute":0,"duration_minutes":480,"allowed_roles":["it_staff"]},
{"id":"on_call_saturday","label":"On Call (Saturday)","start_hour":17,"start_minute":0,"duration_minutes":900,"allowed_roles":["it_staff"]},
{"id":"on_call_sunday","label":"On Call (Sunday)","start_hour":17,"start_minute":0,"duration_minutes":840,"allowed_roles":["it_staff"]},
{"id":"normal","label":"Normal","start_hour":8,"start_minute":0,"duration_minutes":540,"allowed_roles":["admin","dispatcher","programmer","it_staff"]},
{"id":"normal_ramadan_islam","label":"Normal (Ramadan - Islam)","start_hour":8,"start_minute":0,"duration_minutes":480,"allowed_roles":["admin","dispatcher","programmer","it_staff"]},
{"id":"normal_ramadan_other","label":"Normal (Ramadan - Other)","start_hour":8,"start_minute":0,"duration_minutes":540,"allowed_roles":["admin","dispatcher","programmer","it_staff"]}
]'::jsonb AS val
),
stored AS (
SELECT value->'shift_types' AS val
FROM app_settings
WHERE key = 'rotation_config'
)
SELECT
-- Start with defaults, overriding with stored by id
(
SELECT jsonb_agg(COALESCE(stored_elem_val, default_elem))
FROM defaults, stored,
LATERAL jsonb_array_elements(defaults.val) default_elem
LEFT JOIN LATERAL (
SELECT s AS stored_elem_val
FROM jsonb_array_elements(stored.val) s
WHERE s->>'id' = default_elem->>'id'
LIMIT 1
) stored_elem ON true
)
-- Append any stored entries that aren't in defaults
|| COALESCE(
(
SELECT jsonb_agg(s)
FROM stored,
jsonb_array_elements(stored.val) s
WHERE NOT EXISTS (
SELECT 1
FROM defaults,
LATERAL jsonb_array_elements(defaults.val) d
WHERE d->>'id' = s->>'id'
)
),
'[]'::jsonb
)
)
)
WHERE key = 'rotation_config'
AND (value ? 'shift_types');