Skip to content

Commit

Permalink
Merge branch 'main' of github.com:daya0576/beaverhabits
Browse files Browse the repository at this point in the history
  • Loading branch information
daya0576 committed Nov 16, 2024
2 parents 0e14b77 + 898787c commit d144fc7
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 49 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Options:
## Streaks

Here are my table tennis training sessions in the past year :)
<img width="760" alt="image" src="https://github.com/user-attachments/assets/327f3855-299e-4307-87af-f30b4b4f7c60">
<img alt="streaks" src="https://github.com/user-attachments/assets/a7a35d96-8fe6-4c01-87d8-84aa820a334f">

## Import

Expand Down
2 changes: 1 addition & 1 deletion beaverhabits/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Settings(BaseSettings):
# Customization
FIRST_DAY_OF_WEEK: int = calendar.MONDAY
ENABLE_IOS_STANDALONE: bool = False
ENABLE_DESKTOP_ALGIN_CENTER: bool = False
ENABLE_DESKTOP_ALGIN_CENTER: bool = True

def is_dev(self):
return self.ENV == "dev"
Expand Down
5 changes: 2 additions & 3 deletions beaverhabits/frontend/add_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
HabitStarCheckbox,
)
from beaverhabits.frontend.layout import layout
from beaverhabits.storage.storage import HabitList
from beaverhabits.storage.storage import HabitList, HabitListBuilder, HabitStatus


@ui.refreshable
def add_ui(habit_list: HabitList):
habits = habit_list.habits
habits.sort(key=lambda x: x.star, reverse=True)
habits = HabitListBuilder(habit_list).status(HabitStatus.ACTIVE).build()

for item in habits:
with ui.grid(columns=8, rows=1).classes("w-full gap-0 items-center"):
Expand Down
54 changes: 43 additions & 11 deletions beaverhabits/frontend/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import datetime
from typing import Callable, Optional

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

from beaverhabits.configs import settings
from beaverhabits.frontend import icons
from beaverhabits.logging import logger
from beaverhabits.storage.dict import DAY_MASK, MONTH_MASK
from beaverhabits.storage.storage import Habit, HabitList
from beaverhabits.storage.storage import Habit, HabitList, HabitStatus
from beaverhabits.utils import WEEK_DAYS
from nicegui import events, ui
from nicegui.elements.button import Button

strptime = datetime.datetime.strptime

Expand Down Expand Up @@ -76,21 +75,30 @@ def __init__(self, habit: Habit | None = None) -> None:
self.habit = habit
self.props("flat dense")
self.classes("py-0.5 w-full")

if habit:
self.props("draggable")
self.classes("cursor-grab")

if habit.status == HabitStatus.ARCHIVED:
# self.props("disabled")
self.classes("opacity-50")

if not habit:
self.classes("opacity-50")


class HabitNameInput(ui.input):
def __init__(self, habit: Habit) -> None:
super().__init__(value=habit.name, on_change=self._async_task)
super().__init__(value=habit.name)
self.habit = habit
self.validation = self._validate
self.props("dense hide-bottom-space")
self.on("blur", self._async_task)

async def _async_task(self, e: events.ValueChangeEventArguments):
self.habit.name = e.value
logger.info(f"Habit Name changed to {e.value}")
async def _async_task(self):
self.habit.name = self.value
logger.info(f"Habit Name changed to {self.value}")

def _validate(self, value: str) -> Optional[str]:
if not value:
Expand Down Expand Up @@ -121,9 +129,29 @@ def __init__(self, habit: Habit, habit_list: HabitList, refresh: Callable) -> No
self.habit = habit
self.habit_list = habit_list
self.refresh = refresh
self.props("flat fab-mini color=grey")

async def _async_task(self):
if self.habit.status == HabitStatus.ACTIVE:
self.habit.status = HabitStatus.ARCHIVED
logger.info(f"Archive habit: {self.habit.name}")
elif self.habit.status == HabitStatus.ARCHIVED:
self.habit.status = HabitStatus.SOLF_DELETED
logger.info(f"Soft delete habit: {self.habit.name}")

self.refresh()


class HabitEditButton(ui.button):
def __init__(self, habit: Habit, habit_list: HabitList, refresh: Callable) -> None:
super().__init__(on_click=self._async_task, icon="edit")
self.habit = habit
self.habit_list = habit_list
self.refresh = refresh
self.props("flat fab-mini color=grey")

async def _async_task(self):
await self.habit_list.remove(self.habit)
# await self.habit_list.remove(self.habit)
self.refresh()
logger.info(f"Deleted habit: {self.habit.name}")

Expand All @@ -135,13 +163,13 @@ def __init__(self, habit_list: HabitList, refresh: Callable) -> None:
self.refresh = refresh
self.on("keydown.enter", self._async_task)
self.props("dense")
self.props("flat fab-mini color=grey")

async def _async_task(self):
logger.info(f"Adding new habit: {self.value}")
await self.habit_list.add(self.value)
logger.info(f"Added new habit: {self.value}")
self.refresh()
self.set_value("")
logger.info(f"Added new habit: {self.value}")


TODAY = "today"
Expand Down Expand Up @@ -334,3 +362,7 @@ def habit_heat_map(
week_day_abbr_label = ui.label(habit_calendar.week_days[i])
week_day_abbr_label.classes("indent-1.5 text-gray-300")
week_day_abbr_label.style("width: 22px; line-height: 20px; font-size: 9px;")


def grid(columns: int, rows: int | None = 1) -> ui.grid:
return ui.grid(columns=columns, rows=rows).classes("gap-0 items-center")
16 changes: 10 additions & 6 deletions beaverhabits/frontend/index_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
from beaverhabits.frontend.components import HabitCheckBox, link
from beaverhabits.frontend.layout import layout
from beaverhabits.storage.meta import get_root_path
from beaverhabits.storage.storage import HabitList
from beaverhabits.storage.storage import HabitList, HabitListBuilder, HabitStatus

HABIT_LIST_RECORD_COUNT = settings.INDEX_HABIT_ITEM_COUNT

row_compat_classes = "pl-4 pr-1 py-0"


@ui.refreshable
def habit_list_ui(days: List[datetime.date], habits: HabitList):
if not habits.habits:
ui.label("List is empty.").classes("mx-auto")
def habit_list_ui(days: List[datetime.date], habit_list: HabitList):
active_habits = HabitListBuilder(habit_list).status(HabitStatus.ACTIVE).build()

if not active_habits:
ui.label("List is empty.").classes("mx-auto w-80")
return

with ui.column().classes("gap-1.5"):
Expand All @@ -39,10 +41,12 @@ def habit_list_ui(days: List[datetime.date], habits: HabitList):
label = ui.label(str(date.strftime(fmt))).classes(right_classes)
label.style("color: #9e9e9e; font-size: 85%; font-weight: 500")

for habit in habits.habits:
for habit in active_habits:
with ui.card().classes(row_compat_classes).classes("shadow-none"):
with grid(1):
redirect_page = os.path.join(get_root_path(), "habits", habit.id)
redirect_page = os.path.join(
get_root_path(), "habits", str(habit.id)
)
habit_name = link(habit.name, target=redirect_page)
habit_name.classes(left_classes)

Expand Down
3 changes: 1 addition & 2 deletions beaverhabits/frontend/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,10 @@ def open_tab(x):

with ui.menu():
compat_menu("Add", lambda: redirect("add"))
compat_menu("Sort", lambda: redirect("order"))
ui.separator()

compat_menu("Export", lambda: open_tab("export"))
ui.separator()

if not root_path.startswith("/demo"):
compat_menu("Import", lambda: redirect("import"))
ui.separator()
Expand Down
52 changes: 42 additions & 10 deletions beaverhabits/frontend/order_page.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
from nicegui import ui

from beaverhabits.frontend import components
from beaverhabits.frontend.components import HabitDeleteButton
from beaverhabits.frontend.components import (
HabitAddButton,
HabitDeleteButton,
HabitNameInput,
)
from beaverhabits.frontend.layout import layout
from beaverhabits.logging import logger
from beaverhabits.storage.storage import HabitList

grid_classes = "w-full gap-0 items-center"
from beaverhabits.storage.storage import HabitList, HabitListBuilder, HabitStatus


async def item_drop(e, habit_list: HabitList):
new_index = e.args["new_index"]
logger.info(f"Item drop: {e.args['id']} -> {new_index}")

# Move element
elements = ui.context.client.elements
dragged = elements[int(e.args["id"][1:])]
Expand All @@ -22,27 +27,53 @@ async def item_drop(e, habit_list: HabitList):
for x in dragged.parent_slot.children
if isinstance(x, components.HabitOrderCard) and x.habit
]

# Unarchive dragged habit
if new_index < len(habits) - 1:
if habits[new_index + 1].status == HabitStatus.ACTIVE:
habits[new_index].status = HabitStatus.ACTIVE
# Archive dragged Habit
if new_index > 1:
if habits[new_index - 1].status == HabitStatus.ARCHIVED:
habits[new_index].status = HabitStatus.ARCHIVED

habit_list.order = [str(x.id) for x in habits]
logger.info(f"New order: {habits}")

add_ui.refresh()


@ui.refreshable
def add_ui(habit_list: HabitList):
for item in habit_list.habits:
habits = (
HabitListBuilder(habit_list)
.status(HabitStatus.ACTIVE, HabitStatus.ARCHIVED)
.build()
)

for item in habits:
with components.HabitOrderCard(item):
with ui.grid(columns=7, rows=1).classes("gap-0 items-center"):
with components.grid(columns=12):
name = ui.label(item.name)
name.classes("col-span-6")
name.classes("col-span-4 col-3")

ui.space().classes("col-span-7")

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


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

with components.HabitOrderCard():
with components.grid(columns=12):
add = HabitAddButton(habit_list, add_ui.refresh)
add.classes("col-span-12")
add.props("borderless")

ui.add_body_html(
r"""
Expand All @@ -58,4 +89,5 @@ def order_page_ui(habit_list: HabitList):
</script>
"""
)

ui.on("item_drop", lambda e: item_drop(e, habit_list))
35 changes: 21 additions & 14 deletions beaverhabits/storage/dict.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
from dataclasses import dataclass, field
import datetime
from typing import List, Optional

from beaverhabits.storage.storage import CheckedRecord, Habit, HabitList
from beaverhabits.storage.storage import CheckedRecord, HabitStatus, Habit, HabitList
from beaverhabits.utils import generate_short_hash

DAY_MASK = "%Y-%m-%d"
Expand Down Expand Up @@ -73,6 +74,24 @@ def star(self) -> bool:
def star(self, value: int) -> None:
self.data["star"] = value

@property
def status(self) -> HabitStatus:
status_value = self.data.get("status")

if status_value is None:
return HabitStatus.ACTIVE

try:
return HabitStatus(status_value)
except ValueError:
logging.error(f"Invalid status value: {status_value}")
self.data["status"] = None
return HabitStatus.ACTIVE

@status.setter
def status(self, value: HabitStatus) -> None:
self.data["status"] = value.value

@property
def records(self) -> list[DictRecord]:
return [DictRecord(d) for d in self.data["records"]]
Expand Down Expand Up @@ -113,19 +132,7 @@ def __str__(self) -> str:
class DictHabitList(HabitList[DictHabit], DictStorage):
@property
def habits(self) -> list[DictHabit]:
habits = [DictHabit(d) for d in self.data["habits"]]

# Sort by order
if self.order:
habits.sort(
key=lambda x: (
self.order.index(str(x.id))
if str(x.id) in self.order
else float("inf")
)
)

return habits
return [DictHabit(d) for d in self.data["habits"]]

@property
def order(self) -> List[str]:
Expand Down
Loading

0 comments on commit d144fc7

Please sign in to comment.