diff --git a/hamstertools/app.py b/hamstertools/app.py index e429a41..25eba38 100644 --- a/hamstertools/app.py +++ b/hamstertools/app.py @@ -8,7 +8,6 @@ from textual.events import DescendantBlur from textual.widgets import Header, Footer, DataTable, Input, Button, Label, Checkbox from textual.containers import Horizontal, Vertical from textual.coordinate import Coordinate -from textual.reactive import reactive from textual.screen import Screen, ModalScreen from textual_autocomplete import AutoComplete, Dropdown, DropdownItem @@ -75,21 +74,65 @@ class ListScreen(Screen): self._refresh() - class ActivityEditScreen(ModalScreen): - BINDINGS = [("escape", "cancel", "Cancel")] + BINDINGS = [ + ("escape", "cancel", "Cancel"), + ("ctrl+s", "save", "Save"), + ] + + category_id = None + category_name = '' + + def _get_categories(self, input_state): + categories = [DropdownItem(c.name, str(c.id)) for c in HamsterCategory.select()] + return ActivityMappingScreen._filter_dropdowns(categories, input_state.value) + + def __init__(self, category, activity): + if category is not None: + self.category_id = category.id + self.category_name = category.name + self.activity_name = activity.name + super().__init__() def compose(self) -> ComposeResult: - yield Grid( - Label("Are you sure you want to quit?", id="question"), - Button("Quit", variant="error", id="quit"), - Button("Cancel", variant="primary", id="cancel"), - id="dialog", + yield Vertical( + Horizontal(Label("Category:"), + AutoComplete( + Input(placeholder="Type to search...", id="category", + value=self.category_name), + Dropdown(items=self._get_categories), + ), + ), + Horizontal(Label("Activity:"), Input(value=self.activity_name, + id='activity')), ) + @on(Input.Submitted, "#category") + def category_submitted(self, event): + if event.control.parent.dropdown.selected_item is not None: + self.category_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) + self.query_one("#activity").focus() + + @on(DescendantBlur, "#category") + def category_blur(self, event): + if event.control.parent.dropdown.selected_item is not None: + self.category_id = str( + event.control.parent.dropdown.selected_item.left_meta + ) + def action_cancel(self): self.dismiss(None) + def action_save(self): + self.dismiss( + { + "category": self.category_id, + "activity": self.query_one('#activity').value, + } + ) + class ActivityMappingScreen(ModalScreen): BINDINGS = [ @@ -376,10 +419,27 @@ class ActivityListScreen(ListScreen): del self.move_from_activity def action_edit(self): - def handle_edit(properties): - print(properties) + row_idx: int = self.table.cursor_row + row_cells = self.table.get_row_at(row_idx) - self.app.push_screen(ActivityEditScreen(), handle_edit) + try: + category = HamsterCategory.get(id=row_cells[0]) + except HamsterCategory.DoesNotExist: + category = None + activity = HamsterActivity.get(id=row_cells[2]) + + def handle_edit(properties): + if properties is None: + return + activity.name = properties['activity'] + activity.category_id = properties['category'] + activity.save() + self._refresh() + + self.app.push_screen(ActivityEditScreen( + category=category, + activity=activity + ), handle_edit) def action_mapping(self): selected_activity = ( diff --git a/hamstertools/app.tcss b/hamstertools/app.tcss index e36a803..3f74269 100644 --- a/hamstertools/app.tcss +++ b/hamstertools/app.tcss @@ -1,50 +1,64 @@ DataTable { - height: 90%; + height: 90%; } DataTable .datatable--cursor { - background: grey; + background: grey; } DataTable:focus .datatable--cursor { - background: orange; + background: orange; } #filter { - display: none; + display: none; } #filter Input { - width: 50%; + width: 50%; } ActivityEditScreen, ActivityMappingScreen { align: center middle; } +ActivityEditScreen > Vertical, ActivityMappingScreen > Vertical { padding: 0 1; - width: auto; + width: 80; height: 30; border: thick $background 80%; background: $surface; } -ActivityMappingScreen Horizontal { - align: left middle; - width: auto; +ActivityEditScreen > Vertical { + height: 10; } +ActivityMappingScreen Horizontal { + align: left middle; + width: auto; +} + +ActivityEditScreen Horizontal { + width: 80; +} + +ActivityEditScreen Label, ActivityMappingScreen Label { - padding: 0 1; - width: auto; - border: blank; + padding: 0 1; + width: auto; + border: blank; } ActivityMappingScreen AutoComplete { - width: 80; + width: 80; } #description, #tags { - width: 30; + width: 30; +} + +ActivityEditScreen Input { + width: 60; } diff --git a/hamstertools/kimai.py b/hamstertools/kimai.py index a36aeff..d2b08da 100644 --- a/hamstertools/kimai.py +++ b/hamstertools/kimai.py @@ -17,7 +17,7 @@ class KimaiAPI(object): auth_headers = {"X-AUTH-USER": KIMAI_USERNAME, "X-AUTH-TOKEN": KIMAI_API_KEY} def __init__(self): - requests_cache.install_cache("kimai", backend="sqlite", expire_after=1800) + # requests_cache.install_cache("kimai", backend="sqlite", expire_after=1800) self.customers_json = requests.get( f"{self.KIMAI_API_URL}/customers?visible=3", headers=self.auth_headers ).json()