Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import DateTimePicker from '../ui/DateTimePicker'
|
||||
|
||||
/**
|
||||
* SchedulePublishPicker
|
||||
@@ -82,14 +83,18 @@ export default function SchedulePublishPicker({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
)
|
||||
const [dateStr, setDateStr] = useState(initial.date || '')
|
||||
const [timeStr, setTimeStr] = useState(initial.time || '')
|
||||
const [localDateTime, setLocalDateTime] = useState(initial.date && initial.time ? `${initial.date}T${initial.time}` : '')
|
||||
const [error, setError] = useState('')
|
||||
const minScheduleLocalDateTime = (() => {
|
||||
const next = toLocalDateTimeString(new Date(Date.now() + MIN_FUTURE_MS).toISOString(), timezone)
|
||||
return next.date && next.time ? `${next.date}T${next.time}` : ''
|
||||
})()
|
||||
|
||||
const validate = useCallback(
|
||||
(d, t) => {
|
||||
if (!d || !t) return 'Date and time are required.'
|
||||
const iso = localToUtcIso(d, t, timezone)
|
||||
(value) => {
|
||||
const [datePart = '', timePart = ''] = String(value || '').split('T')
|
||||
if (!datePart || !timePart) return 'Date and time are required.'
|
||||
const iso = localToUtcIso(datePart, timePart.slice(0, 5), timezone)
|
||||
if (!iso) return 'Invalid date or time.'
|
||||
const target = new Date(iso)
|
||||
if (Number.isNaN(target.getTime())) return 'Invalid date or time.'
|
||||
@@ -101,31 +106,38 @@ export default function SchedulePublishPicker({
|
||||
[timezone]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const next = toLocalDateTimeString(scheduledAt, timezone)
|
||||
setLocalDateTime(next.date && next.time ? `${next.date}T${next.time}` : '')
|
||||
}, [scheduledAt, timezone])
|
||||
|
||||
useEffect(() => {
|
||||
if (mode !== 'schedule') {
|
||||
setError('')
|
||||
return
|
||||
}
|
||||
if (!dateStr && !timeStr) {
|
||||
if (!localDateTime) {
|
||||
setError('')
|
||||
onScheduleAt?.(null)
|
||||
return
|
||||
}
|
||||
const err = validate(dateStr, timeStr)
|
||||
const err = validate(localDateTime)
|
||||
setError(err)
|
||||
if (!err) {
|
||||
onScheduleAt?.(localToUtcIso(dateStr, timeStr, timezone))
|
||||
const [datePart = '', timePart = ''] = localDateTime.split('T')
|
||||
onScheduleAt?.(localToUtcIso(datePart, timePart.slice(0, 5), timezone))
|
||||
} else {
|
||||
onScheduleAt?.(null)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dateStr, timeStr, mode])
|
||||
}, [localDateTime, mode, timezone])
|
||||
|
||||
const previewLabel = useMemo(() => {
|
||||
if (mode !== 'schedule' || error) return null
|
||||
const iso = localToUtcIso(dateStr, timeStr, timezone)
|
||||
const [datePart = '', timePart = ''] = localDateTime.split('T')
|
||||
const iso = localToUtcIso(datePart, timePart.slice(0, 5), timezone)
|
||||
return formatPreviewLabel(iso, timezone)
|
||||
}, [mode, error, dateStr, timeStr, timezone])
|
||||
}, [mode, error, localDateTime, timezone])
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
@@ -167,45 +179,18 @@ export default function SchedulePublishPicker({
|
||||
|
||||
{mode === 'schedule' && (
|
||||
<div className="space-y-2 rounded-xl border border-white/10 bg-white/[0.03] p-3">
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<div className="flex-1">
|
||||
<label className="block text-[10px] uppercase tracking-wide text-white/40 mb-1" htmlFor="schedule-date">
|
||||
Date
|
||||
</label>
|
||||
<input
|
||||
id="schedule-date"
|
||||
type="date"
|
||||
disabled={disabled}
|
||||
value={dateStr}
|
||||
onChange={(e) => setDateStr(e.target.value)}
|
||||
min={new Date().toISOString().slice(0, 10)}
|
||||
className="w-full rounded-lg border border-white/15 bg-white/8 px-3 py-1.5 text-sm text-white placeholder-white/30 focus:outline-none focus:ring-1 focus:ring-sky-400/60 disabled:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-28 shrink-0">
|
||||
<label className="block text-[10px] uppercase tracking-wide text-white/40 mb-1" htmlFor="schedule-time">
|
||||
Time
|
||||
</label>
|
||||
<input
|
||||
id="schedule-time"
|
||||
type="time"
|
||||
disabled={disabled}
|
||||
value={timeStr}
|
||||
onChange={(e) => setTimeStr(e.target.value)}
|
||||
className="w-full rounded-lg border border-white/15 bg-white/8 px-3 py-1.5 text-sm text-white placeholder-white/30 focus:outline-none focus:ring-1 focus:ring-sky-400/60 disabled:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-white/35">
|
||||
Timezone: <span className="text-white/55">{timezone}</span>
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<p className="text-xs text-red-400" role="alert">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
<DateTimePicker
|
||||
id="schedule-datetime"
|
||||
label="Release date and time"
|
||||
value={localDateTime}
|
||||
onChange={setLocalDateTime}
|
||||
placeholder="Pick a release slot"
|
||||
disabled={disabled}
|
||||
minDateTime={minScheduleLocalDateTime}
|
||||
clearable
|
||||
hint={`Timezone: ${timezone}`}
|
||||
error={error}
|
||||
/>
|
||||
|
||||
{previewLabel && (
|
||||
<p className="text-xs text-emerald-300/80">
|
||||
|
||||
Reference in New Issue
Block a user