feat: 调整默认排课节奏并限制时间选择
第一节 08:00-09:00,之后从 09:30 起每小时一节至 21:30;排课时间仅可选整点或半点。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -40,9 +40,10 @@
|
|||||||
<view class="slot-body">
|
<view class="slot-body">
|
||||||
<view class="time-section">
|
<view class="time-section">
|
||||||
<picker
|
<picker
|
||||||
mode="time"
|
mode="multiSelector"
|
||||||
:value="slot.startTime"
|
:range="timePickerRange"
|
||||||
@change="(e: any) => updateSlotTime(slot, 'startTime', e.detail.value)"
|
:value="timeToPickerIndex(slot.startTime)"
|
||||||
|
@change="(e: any) => updateSlotTime(slot, 'startTime', pickerIndexToTime(e.detail.value))"
|
||||||
>
|
>
|
||||||
<view class="time-display">
|
<view class="time-display">
|
||||||
<text class="time-text">{{ slot.startTime }}</text>
|
<text class="time-text">{{ slot.startTime }}</text>
|
||||||
@@ -50,9 +51,10 @@
|
|||||||
</picker>
|
</picker>
|
||||||
<text class="time-separator">–</text>
|
<text class="time-separator">–</text>
|
||||||
<picker
|
<picker
|
||||||
mode="time"
|
mode="multiSelector"
|
||||||
:value="slot.endTime"
|
:range="timePickerRange"
|
||||||
@change="(e: any) => updateSlotTime(slot, 'endTime', e.detail.value)"
|
:value="timeToPickerIndex(slot.endTime)"
|
||||||
|
@change="(e: any) => updateSlotTime(slot, 'endTime', pickerIndexToTime(e.detail.value))"
|
||||||
>
|
>
|
||||||
<view class="time-display">
|
<view class="time-display">
|
||||||
<text class="time-text">{{ slot.endTime }}</text>
|
<text class="time-text">{{ slot.endTime }}</text>
|
||||||
@@ -112,8 +114,9 @@
|
|||||||
<view class="modal-field">
|
<view class="modal-field">
|
||||||
<text class="modal-label">开始时间</text>
|
<text class="modal-label">开始时间</text>
|
||||||
<picker
|
<picker
|
||||||
mode="time"
|
mode="multiSelector"
|
||||||
:value="addForm.startTime"
|
:range="timePickerRange"
|
||||||
|
:value="timeToPickerIndex(addForm.startTime)"
|
||||||
@change="onAddStartTimeChange"
|
@change="onAddStartTimeChange"
|
||||||
>
|
>
|
||||||
<view class="picker-display">
|
<view class="picker-display">
|
||||||
@@ -162,6 +165,14 @@ import { getSystemLayout } from '../../utils/system'
|
|||||||
import { useAdminStore } from '../../stores/admin'
|
import { useAdminStore } from '../../stores/admin'
|
||||||
import { formatDate } from '../../utils/format'
|
import { formatDate } from '../../utils/format'
|
||||||
import DateSelector from '../../components/DateSelector.vue'
|
import DateSelector from '../../components/DateSelector.vue'
|
||||||
|
import {
|
||||||
|
SCHEDULE_TIME_PICKER_RANGE,
|
||||||
|
timeToPickerIndex,
|
||||||
|
pickerIndexToTime,
|
||||||
|
addOneHourCapped,
|
||||||
|
} from '../../utils/schedule-time'
|
||||||
|
|
||||||
|
const timePickerRange = SCHEDULE_TIME_PICKER_RANGE
|
||||||
|
|
||||||
interface EditableSlot {
|
interface EditableSlot {
|
||||||
readonly key: string
|
readonly key: string
|
||||||
@@ -186,8 +197,8 @@ const showAddModal = ref(false)
|
|||||||
const editableSlots = ref<EditableSlot[]>([])
|
const editableSlots = ref<EditableSlot[]>([])
|
||||||
|
|
||||||
const addForm = ref({
|
const addForm = ref({
|
||||||
startTime: '09:00',
|
startTime: '09:30',
|
||||||
endTime: '10:00',
|
endTime: '10:30',
|
||||||
capacityStr: '1',
|
capacityStr: '1',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -270,23 +281,14 @@ function removeSlot(slot: EditableSlot) {
|
|||||||
|
|
||||||
// ── Add slot ──────────────────────────────────────────────
|
// ── Add slot ──────────────────────────────────────────────
|
||||||
|
|
||||||
/** 将 "HH:mm" 加一小时,最大 23:59 */
|
|
||||||
function addOneHour(time: string): string {
|
|
||||||
const [h, m] = time.split(':').map(Number)
|
|
||||||
const newH = Math.min(h + 1, 23)
|
|
||||||
// 如果原本就是 23:xx,结束时间设为 23:59
|
|
||||||
if (h >= 23) return '23:59'
|
|
||||||
return String(newH).padStart(2, '0') + ':' + String(m).padStart(2, '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAddStartTimeChange(e: any) {
|
function onAddStartTimeChange(e: any) {
|
||||||
const start = e.detail.value as string
|
const start = pickerIndexToTime(e.detail.value as number[])
|
||||||
addForm.value.startTime = start
|
addForm.value.startTime = start
|
||||||
addForm.value.endTime = addOneHour(start)
|
addForm.value.endTime = addOneHourCapped(start)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openAddModal() {
|
function openAddModal() {
|
||||||
addForm.value = { startTime: '09:00', endTime: '10:00', capacityStr: '1' }
|
addForm.value = { startTime: '09:30', endTime: '10:30', capacityStr: '1' }
|
||||||
showAddModal.value = true
|
showAddModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +307,10 @@ function submitAdd() {
|
|||||||
uni.showToast({ title: '请选择时间', icon: 'none' })
|
uni.showToast({ title: '请选择时间', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (addForm.value.startTime >= addForm.value.endTime) {
|
||||||
|
uni.showToast({ title: '结束时间须晚于开始时间', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
if (isNaN(capacity) || capacity < 1) {
|
if (isNaN(capacity) || capacity < 1) {
|
||||||
uni.showToast({ title: '请填写有效容量', icon: 'none' })
|
uni.showToast({ title: '请填写有效容量', icon: 'none' })
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -28,7 +28,12 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="form-row">
|
<view class="form-row">
|
||||||
<text class="form-label">开始时间</text>
|
<text class="form-label">开始时间</text>
|
||||||
<picker mode="time" :value="addForm.startTime" @change="(e: any) => addForm.startTime = e.detail.value">
|
<picker
|
||||||
|
mode="multiSelector"
|
||||||
|
:range="timePickerRange"
|
||||||
|
:value="timeToPickerIndex(addForm.startTime)"
|
||||||
|
@change="(e: any) => addForm.startTime = pickerIndexToTime(e.detail.value)"
|
||||||
|
>
|
||||||
<view class="picker-display">
|
<view class="picker-display">
|
||||||
<text class="picker-text">{{ addForm.startTime || '请选择' }}</text>
|
<text class="picker-text">{{ addForm.startTime || '请选择' }}</text>
|
||||||
<text class="picker-arrow">›</text>
|
<text class="picker-arrow">›</text>
|
||||||
@@ -37,7 +42,12 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="form-row">
|
<view class="form-row">
|
||||||
<text class="form-label">结束时间</text>
|
<text class="form-label">结束时间</text>
|
||||||
<picker mode="time" :value="addForm.endTime" @change="(e: any) => addForm.endTime = e.detail.value">
|
<picker
|
||||||
|
mode="multiSelector"
|
||||||
|
:range="timePickerRange"
|
||||||
|
:value="timeToPickerIndex(addForm.endTime)"
|
||||||
|
@change="(e: any) => addForm.endTime = pickerIndexToTime(e.detail.value)"
|
||||||
|
>
|
||||||
<view class="picker-display">
|
<view class="picker-display">
|
||||||
<text class="picker-text">{{ addForm.endTime || '请选择' }}</text>
|
<text class="picker-text">{{ addForm.endTime || '请选择' }}</text>
|
||||||
<text class="picker-arrow">›</text>
|
<text class="picker-arrow">›</text>
|
||||||
@@ -128,7 +138,7 @@
|
|||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="gen-hint">将按默认时间表(每天 8:00-22:00,每小时一节)自动生成所选日期范围内的时段</text>
|
<text class="gen-hint">将按默认时间表(8:00-9:00,之后从 9:30 起每小时一节至 21:30)自动生成所选日期范围内的时段</text>
|
||||||
<view class="action-wrap">
|
<view class="action-wrap">
|
||||||
<view class="action-btn" :class="{ 'action-btn--loading': submitting }" @tap="submitGenerate">
|
<view class="action-btn" :class="{ 'action-btn--loading': submitting }" @tap="submitGenerate">
|
||||||
<text class="action-btn-text">{{ submitting ? '生成中...' : '批量生成' }}</text>
|
<text class="action-btn-text">{{ submitting ? '生成中...' : '批量生成' }}</text>
|
||||||
@@ -145,6 +155,13 @@ import { getSystemLayout } from '../../utils/system'
|
|||||||
import { useAdminStore } from '../../stores/admin'
|
import { useAdminStore } from '../../stores/admin'
|
||||||
import { formatDate } from '../../utils/format'
|
import { formatDate } from '../../utils/format'
|
||||||
import type { TimeSlot } from '@mp-pilates/shared'
|
import type { TimeSlot } from '@mp-pilates/shared'
|
||||||
|
import {
|
||||||
|
SCHEDULE_TIME_PICKER_RANGE,
|
||||||
|
timeToPickerIndex,
|
||||||
|
pickerIndexToTime,
|
||||||
|
} from '../../utils/schedule-time'
|
||||||
|
|
||||||
|
const timePickerRange = SCHEDULE_TIME_PICKER_RANGE
|
||||||
|
|
||||||
const adminStore = useAdminStore()
|
const adminStore = useAdminStore()
|
||||||
|
|
||||||
@@ -157,8 +174,8 @@ const slotsLoading = ref(false)
|
|||||||
// ── Add slot form ────────────────────────────────────────────────
|
// ── Add slot form ────────────────────────────────────────────────
|
||||||
const addForm = ref({
|
const addForm = ref({
|
||||||
date: formatDate(new Date()),
|
date: formatDate(new Date()),
|
||||||
startTime: '09:00',
|
startTime: '09:30',
|
||||||
endTime: '10:00',
|
endTime: '10:30',
|
||||||
capacityStr: '10',
|
capacityStr: '10',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -168,6 +185,10 @@ async function submitAddSlot() {
|
|||||||
uni.showToast({ title: '请填写完整信息', icon: 'none' })
|
uni.showToast({ title: '请填写完整信息', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (addForm.value.startTime >= addForm.value.endTime) {
|
||||||
|
uni.showToast({ title: '结束时间须晚于开始时间', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
const capacity = parseInt(addForm.value.capacityStr, 10)
|
const capacity = parseInt(addForm.value.capacityStr, 10)
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
|
|||||||
67
packages/app/src/utils/schedule-time.ts
Normal file
67
packages/app/src/utils/schedule-time.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
DEFAULT_SCHEDULE_START_TIME,
|
||||||
|
DEFAULT_SCHEDULE_END_TIME,
|
||||||
|
SCHEDULE_MINUTE_STEP,
|
||||||
|
} from '@mp-pilates/shared'
|
||||||
|
|
||||||
|
function buildHourOptions(): string[] {
|
||||||
|
const startHour = parseInt(DEFAULT_SCHEDULE_START_TIME.slice(0, 2), 10)
|
||||||
|
const endHour = parseInt(DEFAULT_SCHEDULE_END_TIME.slice(0, 2), 10)
|
||||||
|
const list: string[] = []
|
||||||
|
for (let h = startHour; h <= endHour; h++) {
|
||||||
|
list.push(String(h).padStart(2, '0'))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMinuteOptions(): string[] {
|
||||||
|
const list: string[] = []
|
||||||
|
for (let m = 0; m < 60; m += SCHEDULE_MINUTE_STEP) {
|
||||||
|
list.push(String(m).padStart(2, '0'))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SCHEDULE_HOUR_OPTIONS = buildHourOptions()
|
||||||
|
export const SCHEDULE_MINUTE_OPTIONS = buildMinuteOptions()
|
||||||
|
export const SCHEDULE_TIME_PICKER_RANGE = [SCHEDULE_HOUR_OPTIONS, SCHEDULE_MINUTE_OPTIONS]
|
||||||
|
|
||||||
|
export function timeToMinutes(time: string): number {
|
||||||
|
const [h, m] = time.split(':').map(Number)
|
||||||
|
return h * 60 + (m || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function minutesToTime(total: number): string {
|
||||||
|
const h = Math.floor(total / 60)
|
||||||
|
const m = total % 60
|
||||||
|
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function timeToPickerIndex(time: string): number[] {
|
||||||
|
let total = timeToMinutes(time || DEFAULT_SCHEDULE_START_TIME)
|
||||||
|
const minTotal = timeToMinutes(DEFAULT_SCHEDULE_START_TIME)
|
||||||
|
const maxTotal = timeToMinutes(DEFAULT_SCHEDULE_END_TIME)
|
||||||
|
total = Math.max(minTotal, Math.min(maxTotal, total))
|
||||||
|
total = Math.round(total / SCHEDULE_MINUTE_STEP) * SCHEDULE_MINUTE_STEP
|
||||||
|
total = Math.max(minTotal, Math.min(maxTotal, total))
|
||||||
|
|
||||||
|
const hour = String(Math.floor(total / 60)).padStart(2, '0')
|
||||||
|
const minute = String(total % 60).padStart(2, '0')
|
||||||
|
return [
|
||||||
|
Math.max(0, SCHEDULE_HOUR_OPTIONS.indexOf(hour)),
|
||||||
|
Math.max(0, SCHEDULE_MINUTE_OPTIONS.indexOf(minute)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pickerIndexToTime(value: number[]): string {
|
||||||
|
const hour = SCHEDULE_HOUR_OPTIONS[value[0]] ?? SCHEDULE_HOUR_OPTIONS[0]
|
||||||
|
const minute = SCHEDULE_MINUTE_OPTIONS[value[1]] ?? SCHEDULE_MINUTE_OPTIONS[0]
|
||||||
|
return `${hour}:${minute}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将 "HH:mm" 加一小时,不超过当日最晚结束时间 21:30 */
|
||||||
|
export function addOneHourCapped(time: string): string {
|
||||||
|
const next = timeToMinutes(time) + 60
|
||||||
|
const cap = timeToMinutes(DEFAULT_SCHEDULE_END_TIME)
|
||||||
|
return minutesToTime(Math.min(next, cap))
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ describe('SlotGeneratorService', () => {
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
describe('generateSlots', () => {
|
describe('generateSlots', () => {
|
||||||
it('creates slots for every day using the default schedule (14 slots per day)', async () => {
|
it('creates slots for every day using the default schedule', async () => {
|
||||||
const defaultSlots = getDefaultTimeSlots()
|
const defaultSlots = getDefaultTimeSlots()
|
||||||
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: defaultSlots.length * 7 })
|
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: defaultSlots.length * 7 })
|
||||||
|
|
||||||
@@ -64,24 +64,25 @@ describe('SlotGeneratorService', () => {
|
|||||||
skipDuplicates: boolean
|
skipDuplicates: boolean
|
||||||
}
|
}
|
||||||
expect(skipDuplicates).toBe(true)
|
expect(skipDuplicates).toBe(true)
|
||||||
// 7 days × 14 slots per day = 98
|
|
||||||
expect(data).toHaveLength(defaultSlots.length * 7)
|
expect(data).toHaveLength(defaultSlots.length * 7)
|
||||||
expect(count).toBe(defaultSlots.length * 7)
|
expect(count).toBe(defaultSlots.length * 7)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('creates 14 slots per day (08:00-22:00 hourly)', async () => {
|
it('creates 13 slots per day (08:00-09:00, then 09:30-21:30 hourly)', async () => {
|
||||||
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: 14 })
|
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: 13 })
|
||||||
|
|
||||||
await service.generateSlots(1)
|
await service.generateSlots(1)
|
||||||
|
|
||||||
const { data } = mockPrisma.timeSlot.createMany.mock.calls[0][0] as {
|
const { data } = mockPrisma.timeSlot.createMany.mock.calls[0][0] as {
|
||||||
data: Array<{ startTime: string; endTime: string }>
|
data: Array<{ startTime: string; endTime: string }>
|
||||||
}
|
}
|
||||||
expect(data).toHaveLength(14)
|
expect(data).toHaveLength(13)
|
||||||
expect(data[0].startTime).toBe('08:00')
|
expect(data[0].startTime).toBe('08:00')
|
||||||
expect(data[0].endTime).toBe('09:00')
|
expect(data[0].endTime).toBe('09:00')
|
||||||
expect(data[13].startTime).toBe('21:00')
|
expect(data[1].startTime).toBe('09:30')
|
||||||
expect(data[13].endTime).toBe('22:00')
|
expect(data[1].endTime).toBe('10:30')
|
||||||
|
expect(data[12].startTime).toBe('20:30')
|
||||||
|
expect(data[12].endTime).toBe('21:30')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('passes skipDuplicates: true to handle existing date+time combinations', async () => {
|
it('passes skipDuplicates: true to handle existing date+time combinations', async () => {
|
||||||
@@ -96,7 +97,7 @@ describe('SlotGeneratorService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sets source to TEMPLATE for all generated slots', async () => {
|
it('sets source to TEMPLATE for all generated slots', async () => {
|
||||||
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: 14 })
|
mockPrisma.timeSlot.createMany.mockResolvedValueOnce({ count: 13 })
|
||||||
|
|
||||||
await service.generateSlots(1)
|
await service.generateSlots(1)
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class SlotGeneratorService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate time slots for the next `daysAhead` days based on the fixed
|
* Generate time slots for the next `daysAhead` days based on the fixed
|
||||||
* default schedule (Mon-Sun, 08:00-22:00 hourly).
|
* default schedule (Mon-Sun: 08:00-09:00, then 09:30-21:30 hourly).
|
||||||
* Uses `createMany` with `skipDuplicates` so re-runs are safe.
|
* Uses `createMany` with `skipDuplicates` so re-runs are safe.
|
||||||
*
|
*
|
||||||
* @returns Number of newly created slots
|
* @returns Number of newly created slots
|
||||||
|
|||||||
@@ -15,17 +15,42 @@ export const DEFAULT_SLOT_CAPACITY = 1
|
|||||||
/** 自动生成时段的天数范围 */
|
/** 自动生成时段的天数范围 */
|
||||||
export const SLOT_GENERATION_DAYS = 14
|
export const SLOT_GENERATION_DAYS = 14
|
||||||
|
|
||||||
/** 默认排课时间表:每天 08:00-22:00,每小时一节课 */
|
/** 默认排课时间表:第一节 08:00-09:00,之后从 09:30 起每小时一节至 21:30 */
|
||||||
export const DEFAULT_SCHEDULE_START_HOUR = 8
|
export const DEFAULT_SCHEDULE_START_TIME = '08:00'
|
||||||
export const DEFAULT_SCHEDULE_END_HOUR = 22
|
export const DEFAULT_SCHEDULE_END_TIME = '21:30'
|
||||||
|
/** 第一节课后的半点课表起点 */
|
||||||
|
export const DEFAULT_SCHEDULE_OFFSET_START_TIME = '09:30'
|
||||||
|
/** 排课时间选择步长:仅整点与半点 */
|
||||||
|
export const SCHEDULE_MINUTE_STEP = 30
|
||||||
|
|
||||||
|
function parseTimeToMinutes(time: string): number {
|
||||||
|
const [hours, minutes] = time.split(':').map(Number)
|
||||||
|
return hours * 60 + (minutes || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMinutesToTime(totalMinutes: number): string {
|
||||||
|
const hours = Math.floor(totalMinutes / 60)
|
||||||
|
const minutes = totalMinutes % 60
|
||||||
|
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
/** 生成默认时段列表 (startTime, endTime) */
|
/** 生成默认时段列表 (startTime, endTime) */
|
||||||
export function getDefaultTimeSlots(): ReadonlyArray<{ readonly startTime: string; readonly endTime: string }> {
|
export function getDefaultTimeSlots(): ReadonlyArray<{ readonly startTime: string; readonly endTime: string }> {
|
||||||
const slots: Array<{ startTime: string; endTime: string }> = []
|
const duration = 60
|
||||||
for (let h = DEFAULT_SCHEDULE_START_HOUR; h < DEFAULT_SCHEDULE_END_HOUR; h++) {
|
const slots: Array<{ startTime: string; endTime: string }> = [
|
||||||
const startTime = String(h).padStart(2, '0') + ':00'
|
{ startTime: DEFAULT_SCHEDULE_START_TIME, endTime: '09:00' },
|
||||||
const endTime = String(h + 1).padStart(2, '0') + ':00'
|
]
|
||||||
slots.push({ startTime, endTime })
|
const endMinutes = parseTimeToMinutes(DEFAULT_SCHEDULE_END_TIME)
|
||||||
|
|
||||||
|
for (
|
||||||
|
let t = parseTimeToMinutes(DEFAULT_SCHEDULE_OFFSET_START_TIME);
|
||||||
|
t + duration <= endMinutes;
|
||||||
|
t += duration
|
||||||
|
) {
|
||||||
|
slots.push({
|
||||||
|
startTime: formatMinutesToTime(t),
|
||||||
|
endTime: formatMinutesToTime(t + duration),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return slots
|
return slots
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ export {
|
|||||||
DEFAULT_STUDIO_GALLERY_PHOTOS,
|
DEFAULT_STUDIO_GALLERY_PHOTOS,
|
||||||
DEFAULT_SLOT_CAPACITY,
|
DEFAULT_SLOT_CAPACITY,
|
||||||
SLOT_GENERATION_DAYS,
|
SLOT_GENERATION_DAYS,
|
||||||
DEFAULT_SCHEDULE_START_HOUR,
|
DEFAULT_SCHEDULE_START_TIME,
|
||||||
DEFAULT_SCHEDULE_END_HOUR,
|
DEFAULT_SCHEDULE_END_TIME,
|
||||||
|
DEFAULT_SCHEDULE_OFFSET_START_TIME,
|
||||||
|
SCHEDULE_MINUTE_STEP,
|
||||||
getDefaultTimeSlots,
|
getDefaultTimeSlots,
|
||||||
TIME_PERIODS,
|
TIME_PERIODS,
|
||||||
DATE_SELECTOR_DAYS,
|
DATE_SELECTOR_DAYS,
|
||||||
|
|||||||
Reference in New Issue
Block a user