feat: support internal SMS API
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import DatePicker, { registerLocale } from 'react-datepicker';
|
||||
import { ko } from 'date-fns/locale';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
@@ -23,38 +23,40 @@ function fromLocalString(value: string): Date | null {
|
||||
}
|
||||
|
||||
export const DateTimePicker: React.FC<Props> = ({ value, onChange, placeholder }) => {
|
||||
const [draft, setDraft] = useState<Date | null>(fromLocalString(value));
|
||||
|
||||
useEffect(() => {
|
||||
setDraft(fromLocalString(value));
|
||||
}, [value]);
|
||||
|
||||
const apply = () => {
|
||||
if (draft) {
|
||||
onChange(toLocalString(draft));
|
||||
}
|
||||
};
|
||||
const pickerRef = useRef<DatePicker>(null);
|
||||
|
||||
return (
|
||||
<div className="dt-apply-row">
|
||||
<DatePicker
|
||||
selected={draft}
|
||||
onChange={(d: Date | null) => setDraft(d)}
|
||||
showTimeSelect
|
||||
timeIntervals={5}
|
||||
timeCaption="시간"
|
||||
timeFormat="a h:mm"
|
||||
dateFormat="yyyy.MM.dd (eee) a h:mm"
|
||||
dateFormatCalendar="yyyy.MM"
|
||||
locale="ko"
|
||||
shouldCloseOnSelect={false}
|
||||
placeholderText={placeholder ?? '날짜와 시간을 선택하세요'}
|
||||
className="dt-input"
|
||||
popperClassName="acs-datepicker"
|
||||
/>
|
||||
<button type="button" className="btn-ghost dt-apply-button" onClick={apply} disabled={!draft}>
|
||||
적용
|
||||
</button>
|
||||
</div>
|
||||
<DatePicker
|
||||
ref={pickerRef}
|
||||
selected={fromLocalString(value)}
|
||||
onChange={(d: Date | null) => d && onChange(toLocalString(d))}
|
||||
showTimeSelect
|
||||
timeIntervals={5}
|
||||
timeCaption="시간"
|
||||
timeFormat="a h:mm"
|
||||
dateFormat="yyyy.MM.dd (eee) a h:mm"
|
||||
dateFormatCalendar="yyyy.MM"
|
||||
locale="ko"
|
||||
shouldCloseOnSelect={false}
|
||||
placeholderText={placeholder ?? '날짜와 시간을 선택하세요'}
|
||||
className="dt-input"
|
||||
popperClassName="acs-datepicker"
|
||||
renderCustomHeader={({ date, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
|
||||
<div className="dt-picker-header">
|
||||
<button type="button" className="dt-picker-nav" onClick={decreaseMonth} disabled={prevMonthButtonDisabled} aria-label="이전 달">
|
||||
‹
|
||||
</button>
|
||||
<span className="dt-picker-title">{date.getFullYear()}.{pad(date.getMonth() + 1)}</span>
|
||||
<div className="dt-picker-header-actions">
|
||||
<button type="button" className="dt-picker-nav" onClick={increaseMonth} disabled={nextMonthButtonDisabled} aria-label="다음 달">
|
||||
›
|
||||
</button>
|
||||
<button type="button" className="dt-picker-close" onClick={() => pickerRef.current?.setOpen(false)} aria-label="닫기">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -128,11 +128,15 @@ a { color: inherit; text-decoration: none; }
|
||||
/* react-datepicker: make the input fill the field like the native ones */
|
||||
.field .react-datepicker-wrapper { width: 100%; }
|
||||
.field .react-datepicker__input-container input { width: 100%; box-sizing: border-box; }
|
||||
/* clearer popup border + shadow, and a stable field-side apply button */
|
||||
/* clearer popup border + shadow */
|
||||
.acs-datepicker .react-datepicker { border: 1px solid var(--border); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); }
|
||||
.dt-apply-row { display: flex; align-items: center; gap: 8px; }
|
||||
.dt-apply-row .react-datepicker-wrapper { flex: 1; min-width: 0; }
|
||||
.dt-apply-button { flex: 0 0 auto; min-height: 40px; padding-inline: 14px; }
|
||||
.dt-picker-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 8px 4px; }
|
||||
.dt-picker-title { font-weight: 700; color: var(--text); }
|
||||
.dt-picker-header-actions { display: flex; align-items: center; gap: 4px; }
|
||||
.dt-picker-nav,
|
||||
.dt-picker-close { width: 28px; height: 28px; border: 1px solid var(--border); border-radius: 6px; background: #fff; color: var(--text); font-size: 18px; line-height: 1; cursor: pointer; }
|
||||
.dt-picker-nav:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
.dt-picker-close { color: #b42318; font-size: 20px; }
|
||||
|
||||
/* In-app modal dialog (replaces window.prompt/confirm) */
|
||||
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.4); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 16px; }
|
||||
|
||||
Reference in New Issue
Block a user