Dual device on weekend feature
This commit is contained in:
@@ -75,6 +75,8 @@ class CompileScheduleRequest(BaseModel):
|
|||||||
start_date: str | None = Field(default=None, description="YYYY-MM-DD")
|
start_date: str | None = Field(default=None, description="YYYY-MM-DD")
|
||||||
rule: str = ""
|
rule: str = ""
|
||||||
daytime_testing_today: bool = False
|
daytime_testing_today: bool = False
|
||||||
|
dual_device_weekend_start_enabled: bool = False
|
||||||
|
dual_device_weekend_start_dates: list[str] = Field(default_factory=list)
|
||||||
top_priority_tests: list[str] = Field(default_factory=list)
|
top_priority_tests: list[str] = Field(default_factory=list)
|
||||||
lowest_priority_tests: list[str] = Field(default_factory=list)
|
lowest_priority_tests: list[str] = Field(default_factory=list)
|
||||||
|
|
||||||
@@ -378,6 +380,8 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
|
|||||||
start_date=request.start_date,
|
start_date=request.start_date,
|
||||||
holiday_dates=holiday_dates,
|
holiday_dates=holiday_dates,
|
||||||
daytime_testing_today=request.daytime_testing_today,
|
daytime_testing_today=request.daytime_testing_today,
|
||||||
|
dual_device_weekend_start_enabled=request.dual_device_weekend_start_enabled,
|
||||||
|
dual_device_window_start_dates=set(item.strip() for item in request.dual_device_weekend_start_dates if item.strip()),
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
+33
-4
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import date
|
from datetime import date, timedelta
|
||||||
from test_window import get_shift_sequence_with_capacity, next_window_start_date
|
from test_window import get_shift_sequence_with_capacity, is_off_day, next_window_start_date
|
||||||
from test_bundle import Test, TestBundle, build_test_bundles, bundle_pair_lookup
|
from test_bundle import Test, TestBundle, build_test_bundles, bundle_pair_lookup
|
||||||
|
|
||||||
DUT = (os.getenv("DUT") or "DUT").strip()
|
DUT = (os.getenv("DUT") or "DUT").strip()
|
||||||
@@ -36,6 +36,8 @@ class Scheduler:
|
|||||||
start_date: str | None = None,
|
start_date: str | None = None,
|
||||||
holiday_dates: set[str] = set(),
|
holiday_dates: set[str] = set(),
|
||||||
daytime_testing_today: bool = False,
|
daytime_testing_today: bool = False,
|
||||||
|
dual_device_weekend_start_enabled: bool = False,
|
||||||
|
dual_device_window_start_dates: set[str] | None = None,
|
||||||
priority_weight: int = 100,
|
priority_weight: int = 100,
|
||||||
):
|
):
|
||||||
self.top_priority_tests = top_priority_tests
|
self.top_priority_tests = top_priority_tests
|
||||||
@@ -45,6 +47,8 @@ class Scheduler:
|
|||||||
self.start_date = date.fromisoformat(start_date) if start_date else date.today()
|
self.start_date = date.fromisoformat(start_date) if start_date else date.today()
|
||||||
self.holiday_dates = holiday_dates
|
self.holiday_dates = holiday_dates
|
||||||
self.daytime_testing_today = daytime_testing_today
|
self.daytime_testing_today = daytime_testing_today
|
||||||
|
self.dual_device_weekend_start_enabled = dual_device_weekend_start_enabled
|
||||||
|
self.dual_device_window_start_dates = dual_device_window_start_dates or set()
|
||||||
self.priority_weight = priority_weight
|
self.priority_weight = priority_weight
|
||||||
self.pending_dut_mirror: list[TestBundle] = []
|
self.pending_dut_mirror: list[TestBundle] = []
|
||||||
self.pending_ref_mirror: list[TestBundle] = []
|
self.pending_ref_mirror: list[TestBundle] = []
|
||||||
@@ -102,6 +106,10 @@ class Scheduler:
|
|||||||
active_unscheduled = dut_unscheduled if window_device == DUT else ref_unscheduled
|
active_unscheduled = dut_unscheduled if window_device == DUT else ref_unscheduled
|
||||||
|
|
||||||
shifts, capacity = get_shift_sequence_with_capacity(cursor_date, self.holiday_dates, self.daytime_testing_today)
|
shifts, capacity = get_shift_sequence_with_capacity(cursor_date, self.holiday_dates, self.daytime_testing_today)
|
||||||
|
cursor_date_key = cursor_date.isoformat()
|
||||||
|
dual_device_window = cursor_date_key in self.dual_device_window_start_dates
|
||||||
|
if not dual_device_window and self.dual_device_weekend_start_enabled:
|
||||||
|
dual_device_window = self._is_weekend_start_day(cursor_date)
|
||||||
|
|
||||||
# Evaluate both devices without mutating queue state, then commit once.
|
# Evaluate both devices without mutating queue state, then commit once.
|
||||||
mirrored_bundles, knapsack_bundles, remaining_time = self._select_bundles(
|
mirrored_bundles, knapsack_bundles, remaining_time = self._select_bundles(
|
||||||
@@ -114,6 +122,7 @@ class Scheduler:
|
|||||||
selected_bundles = mirrored_bundles + knapsack_bundles
|
selected_bundles = mirrored_bundles + knapsack_bundles
|
||||||
if len(selected_bundles) == 0:
|
if len(selected_bundles) == 0:
|
||||||
window_device = REF if window_device == DUT else DUT
|
window_device = REF if window_device == DUT else DUT
|
||||||
|
active_unscheduled = dut_unscheduled if window_device == DUT else ref_unscheduled
|
||||||
mirrored_bundles, knapsack_bundles, remaining_time = self._select_bundles(
|
mirrored_bundles, knapsack_bundles, remaining_time = self._select_bundles(
|
||||||
window_device,
|
window_device,
|
||||||
capacity,
|
capacity,
|
||||||
@@ -125,6 +134,20 @@ class Scheduler:
|
|||||||
if len(selected_bundles) == 0:
|
if len(selected_bundles) == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
primary_device = window_device
|
||||||
|
|
||||||
|
if dual_device_window and remaining_time > 0:
|
||||||
|
secondary_device = REF if primary_device == DUT else DUT
|
||||||
|
secondary_unscheduled = dut_unscheduled if secondary_device == DUT else ref_unscheduled
|
||||||
|
secondary_mirrored, secondary_knapsack, remaining_time = self._select_bundles(
|
||||||
|
secondary_device,
|
||||||
|
remaining_time,
|
||||||
|
secondary_unscheduled,
|
||||||
|
tc,
|
||||||
|
mutate=True,
|
||||||
|
)
|
||||||
|
selected_bundles.extend(secondary_mirrored + secondary_knapsack)
|
||||||
|
|
||||||
# Create a mirror of the other device for the next window
|
# Create a mirror of the other device for the next window
|
||||||
bundle_pairs = bundle_pair_lookup(knapsack_bundles, dut_unscheduled, ref_unscheduled)
|
bundle_pairs = bundle_pair_lookup(knapsack_bundles, dut_unscheduled, ref_unscheduled)
|
||||||
|
|
||||||
@@ -134,9 +157,10 @@ class Scheduler:
|
|||||||
pending_target = self.pending_dut_mirror
|
pending_target = self.pending_dut_mirror
|
||||||
|
|
||||||
existing_pending = {(b.index, b.device) for b in pending_target}
|
existing_pending = {(b.index, b.device) for b in pending_target}
|
||||||
|
selected_bundle_keys = {(b.index, b.device) for b in selected_bundles}
|
||||||
for pair_bundle in bundle_pairs:
|
for pair_bundle in bundle_pairs:
|
||||||
pair_key = (pair_bundle.index, pair_bundle.device)
|
pair_key = (pair_bundle.index, pair_bundle.device)
|
||||||
if pair_key in self.scheduled_bundle_keys or pair_key in existing_pending:
|
if pair_key in self.scheduled_bundle_keys or pair_key in existing_pending or pair_key in selected_bundle_keys:
|
||||||
continue
|
continue
|
||||||
pending_target.append(pair_bundle)
|
pending_target.append(pair_bundle)
|
||||||
existing_pending.add(pair_key)
|
existing_pending.add(pair_key)
|
||||||
@@ -148,7 +172,7 @@ class Scheduler:
|
|||||||
capacity_minutes=capacity,
|
capacity_minutes=capacity,
|
||||||
remaining_minutes=remaining_time,
|
remaining_minutes=remaining_time,
|
||||||
assigned_config=tc,
|
assigned_config=tc,
|
||||||
assigned_device=window_device,
|
assigned_device=primary_device,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._place_bundles_in_window(window, selected_bundles)
|
self._place_bundles_in_window(window, selected_bundles)
|
||||||
@@ -170,6 +194,11 @@ class Scheduler:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _is_weekend_start_day(self, current_date: date) -> bool:
|
||||||
|
if is_off_day(current_date, self.holiday_dates):
|
||||||
|
return False
|
||||||
|
return is_off_day(current_date + timedelta(days=1), self.holiday_dates)
|
||||||
|
|
||||||
def _select_bundles(
|
def _select_bundles(
|
||||||
self,
|
self,
|
||||||
device: str,
|
device: str,
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
|
from scheduler import DUT, REF, Scheduler
|
||||||
|
from test_bundle import Test
|
||||||
|
|
||||||
|
|
||||||
|
def _test_config() -> dict[str, dict[str, str | None]]:
|
||||||
|
return {
|
||||||
|
"Station 1": {
|
||||||
|
"test_point": "T1D",
|
||||||
|
"sta": "STA5",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _make_test(test_id: str, device: str, minutes: int) -> Test:
|
||||||
|
return Test(
|
||||||
|
test_id=test_id,
|
||||||
|
device=device,
|
||||||
|
test_type="P2P",
|
||||||
|
rotation=None,
|
||||||
|
rx_tx=None,
|
||||||
|
has_coe_pair=False,
|
||||||
|
coe_pairing=[],
|
||||||
|
config=_test_config(),
|
||||||
|
throttled=False,
|
||||||
|
estimated_minutes=minutes,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SchedulerDualDeviceWeekendStartTests(unittest.TestCase):
|
||||||
|
def _first_window_devices(self, entries, start_date: str) -> set[str]:
|
||||||
|
return {
|
||||||
|
entry.device
|
||||||
|
for entry in entries
|
||||||
|
if entry.scheduled_date == start_date and entry.shift_index == 3
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_weekend_start_window_stays_single_device_when_disabled(self) -> None:
|
||||||
|
start_date = "2026-07-10" # Friday
|
||||||
|
tests = [
|
||||||
|
_make_test("P2PRXAX001", DUT, 900),
|
||||||
|
_make_test("P2PRXAX001", REF, 300),
|
||||||
|
]
|
||||||
|
|
||||||
|
scheduler = Scheduler(
|
||||||
|
tests=tests,
|
||||||
|
top_priority_tests=set(),
|
||||||
|
start_date=start_date,
|
||||||
|
holiday_dates=set(),
|
||||||
|
dual_device_weekend_start_enabled=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
err = scheduler.compile_schedule()
|
||||||
|
self.assertIsNone(err)
|
||||||
|
entries = scheduler.get_schedule()
|
||||||
|
|
||||||
|
self.assertEqual(self._first_window_devices(entries, start_date), {DUT})
|
||||||
|
|
||||||
|
def test_weekend_start_window_runs_both_devices_when_enabled(self) -> None:
|
||||||
|
start_date = "2026-07-10" # Friday
|
||||||
|
tests = [
|
||||||
|
_make_test("P2PRXAX001", DUT, 900),
|
||||||
|
_make_test("P2PRXAX001", REF, 300),
|
||||||
|
]
|
||||||
|
|
||||||
|
scheduler = Scheduler(
|
||||||
|
tests=tests,
|
||||||
|
top_priority_tests=set(),
|
||||||
|
start_date=start_date,
|
||||||
|
holiday_dates=set(),
|
||||||
|
dual_device_weekend_start_enabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
err = scheduler.compile_schedule()
|
||||||
|
self.assertIsNone(err)
|
||||||
|
entries = scheduler.get_schedule()
|
||||||
|
|
||||||
|
self.assertEqual(self._first_window_devices(entries, start_date), {DUT, REF})
|
||||||
|
|
||||||
|
def test_holiday_adjusted_weekend_start_runs_both_devices(self) -> None:
|
||||||
|
start_date = "2026-07-09" # Thursday with Friday as holiday => weekend-start window
|
||||||
|
holidays = {"2026-07-10"}
|
||||||
|
tests = [
|
||||||
|
_make_test("P2PRXAX001", DUT, 700),
|
||||||
|
_make_test("P2PRXAX001", REF, 400),
|
||||||
|
]
|
||||||
|
|
||||||
|
scheduler = Scheduler(
|
||||||
|
tests=tests,
|
||||||
|
top_priority_tests=set(),
|
||||||
|
start_date=start_date,
|
||||||
|
holiday_dates=holidays,
|
||||||
|
dual_device_weekend_start_enabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
err = scheduler.compile_schedule()
|
||||||
|
self.assertIsNone(err)
|
||||||
|
entries = scheduler.get_schedule()
|
||||||
|
|
||||||
|
self.assertEqual(self._first_window_devices(entries, start_date), {DUT, REF})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+45
-10
@@ -37,6 +37,33 @@ function addDays(date, n) {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseHolidayDates(raw = '') {
|
||||||
|
return new Set(
|
||||||
|
String(raw)
|
||||||
|
.split(',')
|
||||||
|
.map((token) => token.trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOffDay(date, holidaySet) {
|
||||||
|
const weekday = date.getDay()
|
||||||
|
if (weekday === 0 || weekday === 6) return true
|
||||||
|
return holidaySet.has(toKey(date))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWeekendStartDatesForWeek(weekStart, holidaySet) {
|
||||||
|
const dates = []
|
||||||
|
for (let offset = 0; offset < 7; offset += 1) {
|
||||||
|
const current = addDays(weekStart, offset)
|
||||||
|
const nextDay = addDays(current, 1)
|
||||||
|
if (!isOffDay(current, holidaySet) && isOffDay(nextDay, holidaySet)) {
|
||||||
|
dates.push(toKey(current))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dates
|
||||||
|
}
|
||||||
|
|
||||||
function getTonightWindowKeys() {
|
function getTonightWindowKeys() {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const hour = now.getHours()
|
const hour = now.getHours()
|
||||||
@@ -120,7 +147,7 @@ function sanitizeSettings(saved = {}) {
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
const [settingsOpen, setSettingsOpen] = useState(false)
|
const [settingsOpen, setSettingsOpen] = useState(false)
|
||||||
const [settings, setSettings] = useState(DEFAULT_SETTINGS)
|
const [settings, setSettings] = useState(DEFAULT_SETTINGS)
|
||||||
const [daytimeEnabled, setDaytimeEnabled] = useState(false)
|
const [dualDeviceWeekendWeekSelections, setDualDeviceWeekendWeekSelections] = useState({})
|
||||||
const [topPriority, setTopPriority] = useState('')
|
const [topPriority, setTopPriority] = useState('')
|
||||||
const [lowestPriority, setLowestPriority] = useState('')
|
const [lowestPriority, setLowestPriority] = useState('')
|
||||||
const [startDateOverride, setStartDateOverride] = useState('')
|
const [startDateOverride, setStartDateOverride] = useState('')
|
||||||
@@ -148,6 +175,13 @@ export default function App() {
|
|||||||
() => scheduleWindows.find((windowDetails) => windowDetails.window_id === selectedWindowId) ?? null,
|
() => scheduleWindows.find((windowDetails) => windowDetails.window_id === selectedWindowId) ?? null,
|
||||||
[scheduleWindows, selectedWindowId],
|
[scheduleWindows, selectedWindowId],
|
||||||
)
|
)
|
||||||
|
const holidaySet = useMemo(() => parseHolidayDates(settings.holidays), [settings.holidays])
|
||||||
|
const activeWeekKey = useMemo(() => toKey(weekStart), [weekStart])
|
||||||
|
const dualDeviceWeekendWeekEnabled = Boolean(dualDeviceWeekendWeekSelections[activeWeekKey])
|
||||||
|
const dualDeviceWindowStartDates = useMemo(() => {
|
||||||
|
if (!dualDeviceWeekendWeekEnabled) return []
|
||||||
|
return getWeekendStartDatesForWeek(weekStart, holidaySet)
|
||||||
|
}, [dualDeviceWeekendWeekEnabled, weekStart, holidaySet])
|
||||||
|
|
||||||
// Fetch schedule for the given weekStart (Monday)
|
// Fetch schedule for the given weekStart (Monday)
|
||||||
const fetchSchedule = useCallback(async (start) => {
|
const fetchSchedule = useCallback(async (start) => {
|
||||||
@@ -245,7 +279,7 @@ export default function App() {
|
|||||||
const effectiveStartDate = startDateOverride.trim()
|
const effectiveStartDate = startDateOverride.trim()
|
||||||
const result = await api.compileSchedule({
|
const result = await api.compileSchedule({
|
||||||
start_date: effectiveStartDate || null,
|
start_date: effectiveStartDate || null,
|
||||||
daytime_testing_today: daytimeEnabled,
|
dual_device_weekend_start_dates: dualDeviceWindowStartDates,
|
||||||
top_priority_tests: topPriority.split(',').map(s => s.trim()).filter(Boolean),
|
top_priority_tests: topPriority.split(',').map(s => s.trim()).filter(Boolean),
|
||||||
lowest_priority_tests: lowestPriority.split(',').map(s => s.trim()).filter(Boolean),
|
lowest_priority_tests: lowestPriority.split(',').map(s => s.trim()).filter(Boolean),
|
||||||
rule: settings.testExclusion ?? '',
|
rule: settings.testExclusion ?? '',
|
||||||
@@ -273,17 +307,13 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRerunDecision(rerunDuringDay) {
|
function handleRerunDecision(_rerunDuringDay) {
|
||||||
const rerunIds = failedTests.map(t => t.test_id).join(', ')
|
|
||||||
setTopPriority(prev => {
|
setTopPriority(prev => {
|
||||||
const existing = prev.split(',').map(s => s.trim()).filter(Boolean)
|
const existing = prev.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
const incoming = failedTests.map(t => t.test_id)
|
const incoming = failedTests.map(t => t.test_id)
|
||||||
const merged = [...new Set([...existing, ...incoming])]
|
const merged = [...new Set([...existing, ...incoming])]
|
||||||
return merged.join(', ')
|
return merged.join(', ')
|
||||||
})
|
})
|
||||||
if (rerunDuringDay) {
|
|
||||||
setDaytimeEnabled(true)
|
|
||||||
}
|
|
||||||
setFailedTests([])
|
setFailedTests([])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,9 +353,16 @@ export default function App() {
|
|||||||
<div className="flex-1 min-w-0 flex flex-col">
|
<div className="flex-1 min-w-0 flex flex-col">
|
||||||
<Calendar
|
<Calendar
|
||||||
scheduleData={scheduleData}
|
scheduleData={scheduleData}
|
||||||
daytimeDateKey={daytimeEnabled ? toKey(new Date()) : null}
|
daytimeDateKey={null}
|
||||||
weekStart={weekStart}
|
weekStart={weekStart}
|
||||||
onWeekChange={setWeekStart}
|
onWeekChange={setWeekStart}
|
||||||
|
dualDeviceWeekendWeekEnabled={dualDeviceWeekendWeekEnabled}
|
||||||
|
onDualDeviceWeekendWeekEnabledChange={(enabled) => {
|
||||||
|
setDualDeviceWeekendWeekSelections((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[activeWeekKey]: enabled,
|
||||||
|
}))
|
||||||
|
}}
|
||||||
windowLookup={windowLookup}
|
windowLookup={windowLookup}
|
||||||
onWindowSelect={handleWindowSelect}
|
onWindowSelect={handleWindowSelect}
|
||||||
/>
|
/>
|
||||||
@@ -335,8 +372,6 @@ export default function App() {
|
|||||||
completionDate={completionDate}
|
completionDate={completionDate}
|
||||||
startDateOverride={startDateOverride}
|
startDateOverride={startDateOverride}
|
||||||
onStartDateOverrideChange={setStartDateOverride}
|
onStartDateOverrideChange={setStartDateOverride}
|
||||||
daytimeEnabled={daytimeEnabled}
|
|
||||||
onDaytimeEnabledChange={setDaytimeEnabled}
|
|
||||||
topPriority={topPriority}
|
topPriority={topPriority}
|
||||||
onTopPriorityChange={setTopPriority}
|
onTopPriorityChange={setTopPriority}
|
||||||
lowestPriority={lowestPriority}
|
lowestPriority={lowestPriority}
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export default function Calendar({
|
|||||||
daytimeDateKey = null,
|
daytimeDateKey = null,
|
||||||
weekStart,
|
weekStart,
|
||||||
onWeekChange,
|
onWeekChange,
|
||||||
|
dualDeviceWeekendWeekEnabled = false,
|
||||||
|
onDualDeviceWeekendWeekEnabledChange,
|
||||||
windowLookup = new Map(),
|
windowLookup = new Map(),
|
||||||
onWindowSelect,
|
onWindowSelect,
|
||||||
}) {
|
}) {
|
||||||
@@ -111,6 +113,25 @@ export default function Calendar({
|
|||||||
>
|
>
|
||||||
Today
|
Today
|
||||||
</button>
|
</button>
|
||||||
|
<div className="ml-auto flex items-center gap-2 text-xs text-gray-300 select-none">
|
||||||
|
<span>Run both devices on weekend this week</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="switch"
|
||||||
|
aria-checked={dualDeviceWeekendWeekEnabled}
|
||||||
|
onClick={() => onDualDeviceWeekendWeekEnabledChange?.(!dualDeviceWeekendWeekEnabled)}
|
||||||
|
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500/70 ${
|
||||||
|
dualDeviceWeekendWeekEnabled ? 'bg-blue-600' : 'bg-gray-600'
|
||||||
|
}`}
|
||||||
|
title="Toggle dual-device weekend-start scheduling for this week"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform ${
|
||||||
|
dualDeviceWeekendWeekEnabled ? 'translate-x-4' : 'translate-x-0'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 7-day grid */}
|
{/* 7-day grid */}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ export default function RightPanel({
|
|||||||
completionDate,
|
completionDate,
|
||||||
startDateOverride,
|
startDateOverride,
|
||||||
onStartDateOverrideChange,
|
onStartDateOverrideChange,
|
||||||
daytimeEnabled,
|
|
||||||
onDaytimeEnabledChange,
|
|
||||||
topPriority,
|
topPriority,
|
||||||
onTopPriorityChange,
|
onTopPriorityChange,
|
||||||
lowestPriority,
|
lowestPriority,
|
||||||
@@ -40,29 +38,6 @@ export default function RightPanel({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Daytime testing toggle */}
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-wider text-gray-500">
|
|
||||||
Day Time Testing (Today)
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
role="switch"
|
|
||||||
aria-checked={daytimeEnabled}
|
|
||||||
onClick={() => onDaytimeEnabledChange(!daytimeEnabled)}
|
|
||||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${
|
|
||||||
daytimeEnabled ? 'bg-blue-600' : 'bg-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform ${
|
|
||||||
daytimeEnabled ? 'translate-x-4' : 'translate-x-0'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Top priority */}
|
{/* Top priority */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-[11px] font-semibold uppercase tracking-wider text-gray-500 mb-1.5">
|
<label className="block text-[11px] font-semibold uppercase tracking-wider text-gray-500 mb-1.5">
|
||||||
|
|||||||
Reference in New Issue
Block a user