fix(cafe24): 예약 시각을 datetime-local 대신 날짜+시간 별도 입력으로
datetime-local 단일 입력은 한국어 로캘에서 "2026. 08. 14. 오후 07:00" 형태로 렌더링되는데 브라우저·로캘마다 표시 폭이 달라 잘리는 사고가 있었다(실제 발생, 직전 커밋에서 칸 폭을 넓혀 임시로 막았지만 근본 원인은 남아 있었다). type=date + type=time 두 개의 별도 input 으로 바꿨다. 각각 폭이 고정이라 잘릴 여지가 없다. 제출 직전 JS 가 두 값을 "YYYY-MM-DD" + "T" + "HH:MM" 로 합쳐 hidden scheduled_at 에 넣는다 — 이 형식은 예전 datetime-local 이 만들던 값과 동일해서 서버(store.parse_schedule_at)는 손대지 않았다. date/time 두 입력에 required 를 둬서, 브라우저의 기본 폼 검증이 빈 값 제출을 막는다(hidden 필드는 타입 특성상 required 검증 대상이 아니라 시각 입력 쪽에 걸었다). 검증: 유닛테스트 61개 통과(백엔드 무변경이라 영향 없음). 브라우저 실측 — 두 입력 각 170px 로 내부 잘림 없음(scrollWidth==clientWidth), 가로 스크롤 없음, 실제 제출 이벤트를 발생시켜 2026-08-20 19:30 입력이 hidden 필드에 "2026-08-20T19:30" 으로 정확히 합쳐지는 것을 확인. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -87,10 +87,19 @@
|
|||||||
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
||||||
<input type="hidden" name="html" id="cf24-schedule-html" />
|
<input type="hidden" name="html" id="cf24-schedule-html" />
|
||||||
|
|
||||||
|
{# datetime-local 은 로캘·브라우저마다 표시 폭이 달라 잘리는 사고가 있었다(실제
|
||||||
|
발생). 날짜/시간을 별도 input 으로 나누면 각각 폭이 고정이라 잘릴 일이 없다.
|
||||||
|
제출 직전 JS 가 두 값을 합쳐 hidden scheduled_at 에 넣는다 — 서버(store.
|
||||||
|
parse_schedule_at)는 예전과 같은 'YYYY-MM-DDTHH:MM' 형식을 그대로 받으므로
|
||||||
|
백엔드는 변경하지 않았다. #}
|
||||||
|
<input type="hidden" name="scheduled_at" id="cf24-schedule-at" />
|
||||||
<div class="cf24-schedule-grid">
|
<div class="cf24-schedule-grid">
|
||||||
<label class="cf24-schedule-datetime">
|
<label class="cf24-schedule-datetime">
|
||||||
<span>예약 시각</span>
|
<span>예약 시각</span>
|
||||||
<input class="cf24-schedule-input" type="datetime-local" name="scheduled_at" required />
|
<span class="cf24-schedule-datetime-row">
|
||||||
|
<input class="cf24-schedule-input" type="date" id="cf24-schedule-date" required />
|
||||||
|
<input class="cf24-schedule-input" type="time" id="cf24-schedule-time" required />
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>진열</span>
|
<span>진열</span>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -235,6 +235,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 예약 폼 — 편집기 내용을 hidden 에 복사해 함께 보낸다(폼이 서로 형제라서).
|
// 예약 폼 — 편집기 내용을 hidden 에 복사해 함께 보낸다(폼이 서로 형제라서).
|
||||||
|
// 날짜·시간도 두 개의 별도 input 값을 합쳐 서버가 기대하는 형식으로 만든다.
|
||||||
var schedForm = pane.querySelector("#cf24-schedule-form");
|
var schedForm = pane.querySelector("#cf24-schedule-form");
|
||||||
if (schedForm) {
|
if (schedForm) {
|
||||||
schedForm.addEventListener("submit", function (e) {
|
schedForm.addEventListener("submit", function (e) {
|
||||||
@@ -242,6 +243,13 @@
|
|||||||
var holder = document.getElementById("cf24-schedule-html");
|
var holder = document.getElementById("cf24-schedule-html");
|
||||||
var wantHtml = schedForm.querySelector('[name="apply_html"]').checked;
|
var wantHtml = schedForm.querySelector('[name="apply_html"]').checked;
|
||||||
if (holder && box) holder.value = wantHtml ? box.value : "";
|
if (holder && box) holder.value = wantHtml ? box.value : "";
|
||||||
|
|
||||||
|
var dateEl = document.getElementById("cf24-schedule-date");
|
||||||
|
var timeEl = document.getElementById("cf24-schedule-time");
|
||||||
|
var atHolder = document.getElementById("cf24-schedule-at");
|
||||||
|
// date/time 이 필수(required)라 브라우저가 빈 값이면 여기까지 오지 않는다.
|
||||||
|
if (atHolder && dateEl && timeEl) atHolder.value = dateEl.value + "T" + timeEl.value;
|
||||||
|
|
||||||
if (!window.confirm(schedForm.dataset.confirm)) { e.preventDefault(); return; }
|
if (!window.confirm(schedForm.dataset.confirm)) { e.preventDefault(); return; }
|
||||||
// 예약 등록으로 화면을 떠나므로 편집 중 경고를 끈다.
|
// 예약 등록으로 화면을 떠나므로 편집 중 경고를 끈다.
|
||||||
dirty = false;
|
dirty = false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814t" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814u" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
+14
-3
@@ -620,11 +620,22 @@
|
|||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* datetime-local 은 한국어 로캘에서 "2026. 08. 14. 오후 07:00" 형태로 길어져
|
/* 날짜/시간을 별도 input 으로 나눈다 — datetime-local 은 한국어 로캘에서
|
||||||
auto-fit minmax(160px, 1fr) 칸에 잘렸다(실측). 이 칸만 최소폭을 넓힌다. */
|
"2026. 08. 14. 오후 07:00" 형태로 길어져 auto-fit minmax(160px, 1fr) 칸에
|
||||||
|
잘렸다(실측). date/time 은 각각 폭이 고정이라 잘릴 일이 없다. */
|
||||||
.cf24-schedule-datetime {
|
.cf24-schedule-datetime {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
min-width: 240px;
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-schedule-datetime-row {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--sp-8, 8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-schedule-datetime-row .cf24-schedule-input {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cf24-schedule-input {
|
.cf24-schedule-input {
|
||||||
|
|||||||
@@ -174,7 +174,13 @@ app/modules/cafe24/ ← 상품관리 모듈
|
|||||||
단건 적용과 같은 다듬기(URL 인코딩 → 소스 정리)를 거치므로 화면에서 본 값이 저장된다.
|
단건 적용과 같은 다듬기(URL 인코딩 → 소스 정리)를 거치므로 화면에서 본 값이 저장된다.
|
||||||
- 예약 폼은 적용 폼과 **형제**로 둔다(HTML 은 폼 중첩을 허용하지 않는다). 편집기 내용은
|
- 예약 폼은 적용 폼과 **형제**로 둔다(HTML 은 폼 중첩을 허용하지 않는다). 편집기 내용은
|
||||||
JS 가 hidden 에 복사해 함께 보낸다.
|
JS 가 hidden 에 복사해 함께 보낸다.
|
||||||
- **시각은 KST 로 해석**한다(`datetime-local` 은 타임존이 없다). 과거 시각은 거부하되
|
- **입력은 날짜/시간 별도 input** 이다(`type=date` + `type=time`). `datetime-local`
|
||||||
|
단일 입력은 한국어 로캘에서 표시 폭이 브라우저마다 달라 잘리는 사고가 있었다(실제
|
||||||
|
발생 — "2026. 08. 14. 오후 07:00" 형태). 두 input 은 각각 폭이 고정이라 잘릴 일이
|
||||||
|
없다. 제출 직전 JS 가 두 값을 `"YYYY-MM-DD" + "T" + "HH:MM"` 로 합쳐 hidden
|
||||||
|
`scheduled_at` 에 넣는다 — 서버(`store.parse_schedule_at`)는 예전과 같은 형식을
|
||||||
|
받으므로 백엔드는 그대로다.
|
||||||
|
- **시각은 KST 로 해석**한다. 과거 시각은 거부하되
|
||||||
폼을 채우는 동안 시간이 흐른 경우를 위해 1분 여유를 둔다.
|
폼을 채우는 동안 시간이 흐른 경우를 위해 1분 여유를 둔다.
|
||||||
- **실행은 `app/modules/cafe24/worker.py`** — compose 서비스 `dbx-cafe24-worker` 가
|
- **실행은 `app/modules/cafe24/worker.py`** — compose 서비스 `dbx-cafe24-worker` 가
|
||||||
`--loop 60` 으로 돈다. 웹 요청 안에서 기다리는 방식은 프록시 타임아웃·재기동에
|
`--loop 60` 으로 돈다. 웹 요청 안에서 기다리는 방식은 프록시 타임아웃·재기동에
|
||||||
|
|||||||
Reference in New Issue
Block a user