Skip to content

Commit

Permalink
Add history trend for detailed page
Browse files Browse the repository at this point in the history
  • Loading branch information
daya0576 committed Nov 24, 2024
1 parent 6b525cf commit 106dea3
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 42 deletions.
3 changes: 2 additions & 1 deletion beaverhabits/configs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import calendar
from enum import Enum
import logging
from enum import Enum

import dotenv
from pydantic_settings import BaseSettings
Expand Down Expand Up @@ -38,6 +38,7 @@ class Settings(BaseSettings):
FIRST_DAY_OF_WEEK: int = calendar.MONDAY
ENABLE_IOS_STANDALONE: bool = False
ENABLE_DESKTOP_ALGIN_CENTER: bool = True
INDEX_SHOW_HABIT_COUNT: bool = False

def is_dev(self):
return self.ENV == "dev"
Expand Down
10 changes: 6 additions & 4 deletions beaverhabits/frontend/add_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ def add_ui(habit_list: HabitList):
habits = HabitListBuilder(habit_list).status(HabitStatus.ACTIVE).build()

for item in habits:
with components.grid(columns=9):
with components.grid(columns=8):
name = HabitNameInput(item)
name.classes("col-span-7 break-all")
name.classes("col-span-6 break-all")

star = HabitStarCheckbox(item, add_ui.refresh)
star.props("flat fab-mini color=grey")
star.classes("col-span-1")

delete = HabitDeleteButton(item, habit_list, add_ui.refresh)
delete.props("flat fab-mini color=grey")
delete.classes("col-span-1")


def add_page_ui(habit_list: HabitList):
with layout():
with ui.column().classes("items-center w-full"):
add_ui(habit_list)

with ui.grid(columns=8, rows=1).classes("w-full gap-0 items-center"):
add = HabitAddButton(habit_list, add_ui.refresh)
add.classes("col-span-6")
51 changes: 50 additions & 1 deletion beaverhabits/frontend/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from dataclasses import dataclass
from typing import Callable, Optional

from dateutil.relativedelta import relativedelta
from nicegui import events, ui
from nicegui.elements.button import Button

Expand Down Expand Up @@ -35,7 +36,7 @@ def compat_menu(name: str, callback: Callable):


def menu_icon_button(icon_name: str, click: Optional[Callable] = None) -> Button:
button_props = "flat=true unelevated=true padding=xs backgroup=none"
button_props = "flat=true unelevated=true padding=xs backgroup=none size=13px"
return ui.button(icon=icon_name, color=None, on_click=click).props(button_props)


Expand Down Expand Up @@ -363,3 +364,51 @@ def habit_heat_map(

def grid(columns: int, rows: int | None = 1) -> ui.grid:
return ui.grid(columns=columns, rows=rows).classes("gap-0 items-center")


def habit_history(today: datetime.date, ticked_days: list[datetime.date]):
# get lastest 6 months, e.g. Feb
months, data = [], []
for i in range(12, 0, -1):
offset_date = today - relativedelta(months=i)
months.append(offset_date.strftime("%b"))

count = sum(
1
for x in ticked_days
if x.month == offset_date.month and x.year == offset_date.year
)
data.append(count)

echart = ui.echart(
{
"xAxis": {
"data": months,
},
"yAxis": {
"type": "value",
"splitLine": {
"show": True,
"lineStyle": {
"color": "#303030",
},
},
},
"series": [
{
"type": "bar",
"data": data,
"itemStyle": {"color": icons.current_color},
"animation": False,
}
],
"grid": {
"top": 15,
"bottom": 25,
"left": 30,
"right": 8,
"show": False,
},
}
)
echart.classes("h-40")
32 changes: 20 additions & 12 deletions beaverhabits/frontend/habit_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,51 @@
CalendarHeatmap,
HabitDateInput,
habit_heat_map,
habit_history,
link,
)
from beaverhabits.frontend.css import CALENDAR_CSS, CHECK_BOX_CSS
from beaverhabits.frontend.layout import layout
from beaverhabits.frontend.layout import layout, redirect
from beaverhabits.storage.meta import get_habit_heatmap_path
from beaverhabits.storage.storage import Habit

WEEKS_TO_DISPLAY = 15


@contextmanager
def card():
def card(link: str | None = None):
with ui.card().classes("p-3 gap-0 no-shadow items-center") as card:
card.classes("w-full")
card.style("max-width: 350px")
if link:
card.classes("cursor-pointer")
card.on("click", lambda: redirect(link))
yield


def habit_page(today: datetime.date, habit: Habit):
ticked_data = {x: True for x in habit.ticked_days}
habit_calendar = CalendarHeatmap.build(today, WEEKS_TO_DISPLAY, calendar.MONDAY)
with ui.column().classes("gap-y-3"):
ticked_data = {x: True for x in habit.ticked_days}
habit_calendar = CalendarHeatmap.build(today, WEEKS_TO_DISPLAY, calendar.MONDAY)

with card():
# ui.label("Calendar").classes("text-base")
HabitDateInput(today, habit, ticked_data)
with card():
HabitDateInput(today, habit, ticked_data)

with card():
link("Last 3 Months", get_habit_heatmap_path(habit)).classes("text-base")
habit_heat_map(habit, habit_calendar, ticked_data=ticked_data)
with card():
link("Last 3 Months", get_habit_heatmap_path(habit)).classes(
"text-base flex justify-center"
)
habit_heat_map(habit, habit_calendar, ticked_data=ticked_data)

with card():
link("Total", get_habit_heatmap_path(habit)).classes("text-base")
with card():
ui.label("History").classes("text-base flex justify-center")
habit_history(today, list(ticked_data.keys()))


def habit_page_ui(today: datetime.date, habit: Habit):
ui.add_css(CHECK_BOX_CSS)
ui.add_css(CALENDAR_CSS)

with layout(title=habit.name):
# with ui.row().classes("gap-y-3"):
habit_page(today, habit)
6 changes: 4 additions & 2 deletions beaverhabits/frontend/icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
# fmt: off
DONE = SVG_TEMPLATE.format(height="24", color="rgb(103,150,207)", data="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z")
CLOSE = SVG_TEMPLATE.format(height="24", color="rgb(97,97,97)", data="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z")

MENU = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z")
ADD = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M440-440H240q-17 0-28.5-11.5T200-480q0-17 11.5-28.5T240-520h200v-200q0-17 11.5-28.5T480-760q17 0 28.5 11.5T520-720v200h200q17 0 28.5 11.5T760-480q0 17-11.5 28.5T720-440H520v200q0 17-11.5 28.5T480-200q-17 0-28.5-11.5T440-240v-200Z")
SWAP = SVG_TEMPLATE.format(height="12", color="rgb(255,255,255)", data="M360.21-432q-15.21 0-25.71-10.35T324-468v-258l-80 80q-11 11-25.67 11-14.66 0-25.33-11-11-10.67-11-25.33Q182-686 193-697l142-142q5.4-5 11.7-7.5 6.3-2.5 13.5-2.5t13.5 2.5Q380-844 385-839l142 142q11 11 11 25t-11 25.48Q516-636 501.5-636T476-647l-80-79v258q0 15.3-10.29 25.65Q375.42-432 360.21-432ZM599.8-111q-7.2 0-13.5-2.5T575-121L433-263q-11-10.91-10.5-25.45.5-14.55 11.5-26.03Q445-325 459.5-325t25.5 11l79 80v-258q0-15.3 10.29-25.65Q584.58-528 599.79-528t25.71 10.35Q636-507.3 636-492v258l80-80q11-11 25.67-11 14.66 0 25.33 11 11 10.67 11 25.33Q778-274 767-263L625-121q-5.4 5-11.7 7.5-6.3 2.5-13.5 2.5Z")

DELETE = SVG_TEMPLATE.format(height="24", color="rgb(158,158,158)", data="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z")
DELETE_F = SVG_TEMPLATE.format(height="24", color="rgb(158,158,158)", data="m376-300 104-104 104 104 56-56-104-104 104-104-56-56-104 104-104-104-56 56 104 104-104 104 56 56Zm-96 180q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520Zm-400 0v520-520Z")
SWAP = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M320-440v-287L217-624l-57-56 200-200 200 200-57 56-103-103v287h-80ZM600-80 400-280l57-56 103 103v-287h80v287l103-103 57 56L600-80Z")
ADD = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z")
EDIT = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M216-216h51l375-375-51-51-375 375v51Zm-72 72v-153l498-498q11-11 23.84-16 12.83-5 27-5 14.16 0 27.16 5t24 16l51 51q11 11 16 24t5 26.54q0 14.45-5.02 27.54T795-642L297-144H144Zm600-549-51-51 51 51Zm-127.95 76.95L591-642l51 51-25.95-25.05Z")
MORE = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="M479.79-192Q450-192 429-213.21t-21-51Q408-294 429.21-315t51-21Q510-336 531-314.79t21 51Q552-234 530.79-213t-51 21Zm0-216Q450-408 429-429.21t-21-51Q408-510 429.21-531t51-21Q510-552 531-530.79t21 51Q552-450 530.79-429t-51 21Zm0-216Q450-624 429-645.21t-21-51Q408-726 429.21-747t51-21Q510-768 531-746.79t21 51Q552-666 530.79-645t-51 21Z")
ARCHIVE = SVG_TEMPLATE.format(height="24", color="rgb(255,255,255)", data="m480-276 144-144-51-51-57 57v-150h-72v150l-57-57-51 51 144 144ZM216-624v408h528v-408H216Zm0 480q-29.7 0-50.85-21.15Q144-186.3 144-216v-474q0-14 5.25-27T165-741l54-54q11-11 23.94-16 12.94-5 27.06-5h420q14.12 0 27.06 5T741-795l54 54q10.5 11 15.75 24t5.25 27v474q0 29.7-21.15 50.85Q773.7-144 744-144H216Zm6-552h516l-48-48H270l-48 48Zm258 276Z")
Expand Down
8 changes: 7 additions & 1 deletion beaverhabits/frontend/index_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ def habit_list_ui(days: List[datetime.date], habit_list: HabitList):
redirect_page = os.path.join(
get_root_path(), "habits", str(habit.id)
)
habit_name = link(habit.name, target=redirect_page)
if settings.INDEX_SHOW_HABIT_COUNT:
habit_name = link(
f"{habit.name} ({len(habit.ticked_days)})",
target=redirect_page,
)
else:
habit_name = link(habit.name, target=redirect_page)
habit_name.classes(left_classes)

d_d = {r.day: r.done for r in habit.records}
Expand Down
10 changes: 4 additions & 6 deletions beaverhabits/frontend/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,16 @@ def layout(title: str | None = None, with_menu: bool = True):

path = context.client.page.path
logger.info(f"Rendering page: {path}")
with ui.row().classes("min-w-full gap-x-2"):
with ui.row().classes("min-w-full gap-x-0"):
menu_header(title, target=root_path)
if with_menu:
ui.space()
if "add" in path:
menu_icon_button(icons.SWAP, click=lambda: redirect("order"))
if "order" in path:
menu_icon_button(icons.ADD, click=lambda: redirect("add"))

# if "/habits/{habit_id}" in path:
if "add" in path:
menu_icon_button("drag_indicator", click=lambda: redirect("order"))
# elif "/habits/{habit_id}" in path:
# menu_icon_button(icons.EDIT, click=lambda: redirect("edit"))

with menu_icon_button(icons.MENU):
menu_component(root_path)

Expand Down
15 changes: 7 additions & 8 deletions beaverhabits/frontend/order_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,23 @@ def add_ui(habit_list: HabitList):

for item in habits:
with components.HabitOrderCard(item):
with components.grid(columns=8):
with components.grid(columns=7):
if item:
name = ui.label(item.name)
name.classes("col-span-7 col-3")
name.classes("col-span-6 col-3")

btn = HabitDeleteButton(item, habit_list, add_ui.refresh)
btn.classes("col-span-1")
# if item.status == HabitStatus.ACTIVE:
# btn.classes("invisible")
if item.status == HabitStatus.ACTIVE:
btn.classes("invisible")

else:
add = HabitAddButton(habit_list, add_ui.refresh)
add.classes("col-span-8")
add.props("borderless")
ui.separator().props("w-full col-span-8").props("size=2px")


def order_page_ui(habit_list: HabitList):
with layout():
with ui.column().classes("items-center sortable gap-3 w-full"):
with ui.column().classes("items-center sortable gap-2 w-full"):
add_ui(habit_list)

ui.add_body_html(
Expand Down
40 changes: 35 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
nicegui = "^2.4.0"
nicegui = {extras = ["highcharts"], version = "^2.7.0"}
pydantic-settings = "^2.2.1"
fastapi-users = {extras = ["sqlalchemy"], version = "^13.0.0"}
uvicorn = {extras = ["standard"], version = "^0.28.0"}
sqlalchemy = {extras = ["asyncio"], version = "^2.0.29"}
asyncpg = "^0.29.0"
aiosqlite = "^0.20.0"
pytz = "^2024.1"

sentry-sdk = {extras = ["fastapi"], version = "^2.15.0"}
python-dateutil = "^2.9.0.post0"

[tool.poetry.group.dev.dependencies]
icecream = "^2.1.3"
autopep8 = "^2.0.4"
Expand Down

0 comments on commit 106dea3

Please sign in to comment.