feat: support internal SMS API

This commit is contained in:
unknown
2026-07-16 14:46:57 +09:00
parent bd3d933a40
commit ae1f30592d
5 changed files with 194 additions and 45 deletions

View File

@@ -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>
)}
/>
);
};

View File

@@ -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; }