웹 화면 — 날짜 규칙 · 달력 시트

2026-09-23 15:52 (KST). 대상은 웹 modam_doc/app_schedule 이다. 동기화는 이미 반영되어 있다. 이 문서는 동기화를 고치지 않는다. 앱(auto_schedule_app)에 들어간 날짜 의미·목록·달력 시트를 웹 화면에 맞춘다.

0. 이 문서에서 빼는 것

아래는 이미 됐다고 보고 손대지 않는다.

원문 규칙: 할일일정기록_날짜규칙.html. 동기화 배포 순서·병합 필드는 서버동기화_날짜규칙.html 에 남아 있으나 이번 작업의 범위가 아니다.

1. 지금 웹 (2026-09-23 확인)

조회와 화면이 아직 기한 칸이다. 앱과 같은 항목이 다른 날에 보이거나, 별표 미래 할일이 오늘 목록에 들어간다.

곳지금앱(목표)
list_events_only · cal_events_in_range due_date 로 칸을 찾음 일정 칸 = DATE(created_at)
cal_todos_in_range · list_todos 비반복 할일도 due_date 비반복 칸 = 생성일(없으면 기한 폴백)
_today_where 기한≤오늘 OR 나의하루 OR 별표 미래 별표 조건 없음. 생성일=오늘을 넣음
enrich_todo 오늘 상태 별표 + 기한≥오늘 이면 오늘 별표만으로는 오늘이 아님
todo_add_event 고른 날을 due_date 에 넣음 고른 날 = 생성일. 기한은 비움
달력 칸 줄 공휴일 → 일정 → 기록 → 할일 공휴일 → 기록 → 일정 → 할일
선택일 패널 목록 일정 블록 → 기록 블록 → 할일 기록 → 일정 → 할일
사이드 「중요」 항상 보임. 이름 「중요」 이름 「별표」. 개수 있을 때만
일정 타일 「기한 {{ date }}」 기한 줄 없음. 날은 생성일
달력 아래 .sch-todo 고정 높이. 앱 시트 0.6/0.9 없음 1050 + 1213 높이 규칙

기록 칸은 지금처럼 due_date 다. 바꾸지 않는다.

2. 맞출 날짜 의미

필드할일일정기록
created_at 달력·목록에 놓는 날 일정 그 날. 기한을 대신함 만든 시각. 칸에 안 씀
due_date 마감. 비울 수 있음. 칸이 아님 없음. 저장 때 비움 기록한 날. 칸 위치

오늘 목록 (할일만)

  1. 기한 ≤ 오늘
  2. is_my_day = 1
  3. 비반복이고 DATE(created_at) = 오늘
_today_where 의 is_star = 1 AND due_date >= today 를 지운다. enrich_todo 의 오늘 판정에서도 같은 별표 분기를 지운다.

3. 조회 SQL

파일: app_schedule/pkg_sql_statement/sql_statement.py. 병합 함수는 그대로 둔다.

함수바꿀 점
list_events_only due_date = %s → DATE(created_at) = %s
cal_events_in_range · events_in_range_full 구간을 생성일 날짜로. 반환 키를 뷰가 칸 묶기에 쓰게 맞춘다
list_todos · cal_todos_in_range 비반복: COALESCE(DATE(created_at), due_date). 반복은 기한 전개 유지
_today_where 별표 조건 삭제. 생성일=오늘 추가
list_repeating 할일: 기한 있음. 일정: 생성일 있음. 일정을 기한 필수로 두면 기한 지운 뒤 반복 일정이 빠진다
기록 조회 그대로 due_date

뷰 views_schedule.py:

기존 일정 기한 (한 번)

앱 dropEventDueDates() 와 같다. 순수 일정만. updated_at 은 건드리지 않는다. 동기화 병합이 아니다.

UPDATE tb_todo
   SET created_at = CONCAT(due_date, ' 00:00:00')
 WHERE on_calendar = 1 AND is_todo = 0 AND is_record = 0 AND is_deleted = 0
   AND due_date IS NOT NULL AND due_date != ''
   AND (created_at IS NULL OR DATE(created_at) > due_date);

UPDATE tb_todo
   SET due_date = NULL
 WHERE on_calendar = 1 AND is_todo = 0 AND is_record = 0 AND is_deleted = 0
   AND due_date IS NOT NULL;

init_schema() 끝, 또는 배포 시 1회.

4. 웹에서 저장할 때

동기화 API가 아니라 todo_add / todo_add_event / todo_edit 폼이다.

5. 화면

템플릿: app_schedule/templates/app_schedule/

5.1 폼 _todo_fields.html

5.2 타일

5.3 달력 칸 · 선택일 목록 순서

schedule.html 칸(cal_only 가 비어 있을 때)과 .sch-todo-scroll 블록 순서:

  1. 날짜 숫자
  2. 공휴일 이름
  3. 기록
  4. 일정
  5. 할일
  6. 구글 점

지금은 칸이 일정→기록, 패널도 일정→기록이다. 둘 다 기록→일정으로 바꾼다.

5.4 사이드 · 제목

6. 달력 아래 패널 (1050 + 1213)

앱 바텀시트와 같은 높이 규칙이다. 원문: 20260923_1050_바텀시트_안드로이드웹이식.html 4절. 날짜+건수 토글은 1050의 「최대일 때만 접기」가 아니라 20260923_1213_날짜건수_올라온시트접기.html 이다.

지금 웹 .sch-todo 는 달력 아래 문서 흐름이다. 앱과 맞추려면 달력 영역 안에서 아래를 덮는 패널로 바꾼다. 데스크톱에서 레이아웃을 유지하려면, 좁은 화면(또는 폰 웹)에만 시트를 적용해도 된다. 높이 식은 화면 너비와 무관하다.

조작결과
날짜칸 클릭 목록 맨 위. 높이 = 내용 + 여유 24px + 헤더 48px. 0.6 미만 금지, 0.9 초과 금지. 이전 높이·스크롤을 재사용하지 않음.
접힌 패널에서 날짜+건수(제목 줄) 최대 높이 0.9
이미 올라온 패널에서 날짜+건수 바로 접는다. 0.6 → 0.9 로 한 단 더 올리지 않는다. (1213)
휠 패널 높이는 그대로. 목록만 스크롤
핸들 드래그 높이만. 접힘(약 84px) ~ 90%
.cal-sheet {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; overflow: hidden;
  background: #f7f9fc;
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -2px 8px rgba(0, 0, 0, .15);
  height: 84px;
  max-height: 90%;
}
.cal-sheet .head { flex: 0 0 auto; text-align: center; font-weight: 700; }
.cal-sheet .list { flex: 1 1 auto; overflow-y: auto; min-height: 0; }

var SHEET_RAISED = 0.6, SHEET_MAX = 0.9;
var SHEET_HEADER_H = 48, SHEET_SLACK = 24;

function sheetSizeOnDayTap(contentHeight, bodyHeight) {
  if (!(bodyHeight > 0) || !(contentHeight > 0)) return SHEET_RAISED;
  var size = (contentHeight + SHEET_SLACK + SHEET_HEADER_H) / bodyHeight;
  if (size <= SHEET_RAISED) return SHEET_RAISED;
  if (size >= SHEET_MAX) return SHEET_MAX;
  return size;
}

function openDaySheet(sheet, list, body) {
  list.scrollTop = 0;
  var size = sheetSizeOnDayTap(list.scrollHeight, body.clientHeight);
  var px = Math.round(size * body.clientHeight);
  var minPx = Math.round(SHEET_RAISED * body.clientHeight);
  var maxPx = Math.round(SHEET_MAX * body.clientHeight);
  if (px < minPx) px = minPx;
  if (px > maxPx) px = maxPx;
  sheet.style.height = px + 'px';
}

/* 1213: 올라가 있으면 접고, 접혀 있으면 0.9 */
function onDateCountClick(sheet, body) {
  var collapsed = 84;
  var raised = sheet.offsetHeight > collapsed + 24;
  var maxPx = Math.round(SHEET_MAX * body.clientHeight);
  sheet.style.height = (raised ? collapsed : maxPx) + 'px';
}

날짜 목록을 그린 다음 프레임에 openDaySheet 를 부른다. 1050 원문의 atMax 토글은 쓰지 않는다.

20260923_1313_바텀시트_아래화살표 는 시트가 0.9 이고 목록을 더 내릴 수 있을 때만 목록 다음 줄에 아래 화살표를 둔다. 글자 위에 겹치지 않는다. 시트를 넣은 뒤에만 붙인다.

7. 손볼 파일

파일할 일
pkg_sql_statement/sql_statement.py 조회 SQL, 오늘 WHERE, 일정 기한 마이그레이션. 병합·스냅샷은 금지
pkg_views/views_schedule.py 칸 묶기, 이달 일정 날짜, 정렬, 저장 시 일정 기한 비우기·생성일
pkg_mdl_common/mdl_recur.py 일정 반복 앵커 = 생성일
pkg_mdl_common/mdl_todo_display.py 오늘/지연 판정에서 별표 제거. 일정은 생성일로 완료/예정
templates/.../_todo_fields.html 일정 기한 hidden 제거, 할일 생성일, 오늘 이동
templates/.../schedule.html 칸·패널 순서, 일정 타일, 별표 칩, 제목 옆 오늘, 시트 마크업
templates/.../_todo_item.html 일정 기한 라벨 제거, 별은 제목 뒤
static/app_schedule/schedule.css (+ 달력 JS) 시트 높이·스크롤·날짜+건수 토글

8. 확인

  1. 생성 9/25 · 기한 9/27 할일이 웹 달력 9/25에만 있다. 9/27에는 없다.
  2. 내일 칸에 일정 저장 → 내일 칸. 기한 필드 없음. 오늘 칸에 안 나온다.
  3. 기한을 생성일보다 이르게 저장할 수 없다.
  4. 「오늘」을 켜면 원래 칸에서 사라지고 오늘 칸으로 이동한다.
  5. 기한 없고 생성일만 지난 할일은 지연이 아니다. 오늘 목록에도 없다.
  6. 별표만 있는 미래 할일은 오늘 목록에 없다.
  7. 어제 일정은 완료, 내일 일정은 예정. 내림차순이어도 묶음은 오늘 → 예정 → 완료.
  8. 칸과 선택일 목록이 기록 → 일정 → 할일 이다.
  9. 사이드가 「별표」이고, 별이 없으면 그 링크가 없다.
  10. (시트) 적은 날 0.6, 중간은 최대 미만, 많은 날은 0.9 + 목록 스크롤. 올라온 제목을 누르면 접힌다.