enlarge journal calendar cells and pair day/title on one line #1
@@ -116,10 +116,15 @@ class JournalAttachmentOut(BaseModel):
|
|||||||
has_thumbnail: bool
|
has_thumbnail: bool
|
||||||
|
|
||||||
|
|
||||||
|
class JournalCalendarEntry(BaseModel):
|
||||||
|
color: str # 그 엔트리가 속한 카테고리 색상 (점 표시용)
|
||||||
|
title: str # 제목(없으면 내용 일부)
|
||||||
|
|
||||||
|
|
||||||
class JournalCalendarDay(BaseModel):
|
class JournalCalendarDay(BaseModel):
|
||||||
entry_date: date
|
entry_date: date
|
||||||
total_count: int
|
total_count: int
|
||||||
category_colors: list[str] # 그 날 엔트리가 있는 카테고리들의 색상(점 표시용, 중복 제거)
|
entries: list[JournalCalendarEntry] # 엔트리별 (색상, 제목) 쌍 — 점과 제목이 한 줄에 붙어 나오도록 1:1로 매칭
|
||||||
|
|
||||||
|
|
||||||
class JournalDayDetailItem(BaseModel):
|
class JournalDayDetailItem(BaseModel):
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from app.models.journal import (
|
|||||||
from app.schemas.journal import (
|
from app.schemas.journal import (
|
||||||
JournalAttachmentOut,
|
JournalAttachmentOut,
|
||||||
JournalCalendarDay,
|
JournalCalendarDay,
|
||||||
|
JournalCalendarEntry,
|
||||||
JournalCategoryCreate,
|
JournalCategoryCreate,
|
||||||
JournalDayDetailItem,
|
JournalDayDetailItem,
|
||||||
JournalEntryCreate,
|
JournalEntryCreate,
|
||||||
@@ -363,25 +364,33 @@ def get_monthly_journal_summary(
|
|||||||
last_day = date(year, month, days_in_month)
|
last_day = date(year, month, days_in_month)
|
||||||
|
|
||||||
stmt = (
|
stmt = (
|
||||||
select(JournalEntry.entry_date, JournalCategory.color)
|
select(JournalEntry.entry_date, JournalCategory.color, JournalEntry.title, JournalEntry.content)
|
||||||
.join(JournalCategory, JournalEntry.category_id == JournalCategory.id)
|
.join(JournalCategory, JournalEntry.category_id == JournalCategory.id)
|
||||||
.where(JournalEntry.user_id == user_id, JournalEntry.entry_date.between(first_day, last_day))
|
.where(JournalEntry.user_id == user_id, JournalEntry.entry_date.between(first_day, last_day))
|
||||||
|
.order_by(JournalEntry.entry_date, JournalEntry.created_at)
|
||||||
)
|
)
|
||||||
if category_id is not None:
|
if category_id is not None:
|
||||||
stmt = stmt.where(JournalEntry.category_id == category_id)
|
stmt = stmt.where(JournalEntry.category_id == category_id)
|
||||||
rows = db.execute(stmt).all()
|
rows = db.execute(stmt).all()
|
||||||
|
|
||||||
counts: dict[date, int] = {}
|
counts: dict[date, int] = {}
|
||||||
colors_by_date: dict[date, list[str]] = {}
|
entries_by_date: dict[date, list[JournalCalendarEntry]] = {}
|
||||||
for entry_date, color in rows:
|
for entry_date, color, title, content in rows:
|
||||||
counts[entry_date] = counts.get(entry_date, 0) + 1
|
counts[entry_date] = counts.get(entry_date, 0) + 1
|
||||||
colors = colors_by_date.setdefault(entry_date, [])
|
entries = entries_by_date.setdefault(entry_date, [])
|
||||||
color = color or "var(--color-accent)"
|
entries.append(
|
||||||
if color not in colors:
|
JournalCalendarEntry(
|
||||||
colors.append(color)
|
color=color or "var(--color-accent)",
|
||||||
|
title=title or (content[:12] + ("…" if len(content) > 12 else "")),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
d: JournalCalendarDay(entry_date=d, total_count=counts[d], category_colors=colors_by_date[d])
|
d: JournalCalendarDay(
|
||||||
|
entry_date=d,
|
||||||
|
total_count=counts[d],
|
||||||
|
entries=entries_by_date[d],
|
||||||
|
)
|
||||||
for d in counts
|
for d in counts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -872,15 +872,48 @@ label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 저널링 */
|
/* 저널링 */
|
||||||
.journal-day-dots {
|
.calendar-cell-journal {
|
||||||
|
aspect-ratio: auto;
|
||||||
|
min-height: 76px;
|
||||||
|
padding: 6px 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cell-journal .calendar-date {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-day-entries {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 2px;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-day-entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal-day-dot {
|
.journal-day-dot {
|
||||||
width: 5px;
|
width: 6px;
|
||||||
height: 5px;
|
height: 6px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-day-title {
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal-prompt-card,
|
.journal-prompt-card,
|
||||||
|
|||||||
@@ -252,16 +252,19 @@
|
|||||||
{% for d in week %}
|
{% for d in week %}
|
||||||
{% set day_summary = summary_map.get(d) %}
|
{% set day_summary = summary_map.get(d) %}
|
||||||
<div
|
<div
|
||||||
class="calendar-cell clickable{{ '' if d.month == month else ' muted' }}"
|
class="calendar-cell calendar-cell-journal clickable{{ '' if d.month == month else ' muted' }}"
|
||||||
hx-get="/journal/day/{{ d.isoformat() }}"
|
hx-get="/journal/day/{{ d.isoformat() }}"
|
||||||
hx-target="#day-detail"
|
hx-target="#day-detail"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
>
|
>
|
||||||
<span class="calendar-date">{{ d.day }}</span>
|
<span class="calendar-date">{{ d.day }}</span>
|
||||||
{% if day_summary %}
|
{% if day_summary %}
|
||||||
<span class="journal-day-dots">
|
<span class="journal-day-entries">
|
||||||
{% for color in day_summary.category_colors %}
|
{% for entry in day_summary.entries %}
|
||||||
<span class="journal-day-dot" style="background: {{ color }};"></span>
|
<span class="journal-day-entry">
|
||||||
|
<span class="journal-day-dot" style="background: {{ entry.color }};"></span>
|
||||||
|
<span class="journal-day-title">{{ entry.title }}</span>
|
||||||
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ def test_get_monthly_journal_summary_counts_entries_per_day(db_session, test_use
|
|||||||
|
|
||||||
summary = journal_service.get_monthly_journal_summary(db_session, test_user.id, today.year, today.month)
|
summary = journal_service.get_monthly_journal_summary(db_session, test_user.id, today.year, today.month)
|
||||||
assert summary[today].total_count == 2
|
assert summary[today].total_count == 2
|
||||||
assert summary[today].category_colors == ["#ff0000"]
|
assert [e.color for e in summary[today].entries] == ["#ff0000", "#ff0000"]
|
||||||
|
|
||||||
|
|
||||||
def test_get_day_entries_returns_entries_for_that_date(db_session, test_user):
|
def test_get_day_entries_returns_entries_for_that_date(db_session, test_user):
|
||||||
|
|||||||
Reference in New Issue
Block a user