Commit Graph

155 Commits

Author SHA1 Message Date
jordi fita mas 992bbf32a9 Toggle filters forms
I tried this already when i started adding filters, but i tried to use
AlpineJS for that, and could not because it would reset the context each
time i submitted the filters, due to HTMx replacing the whole content.

I realized that the only thing i need is some “flag” to show and hide
the form with CSS.  I do not even need AlpineJS for that, but i used it
anyway because then i can use the x-cloak thing to hidde the toggle
button for users with JavaScript disabled.

Similarly, the body by default has that “flag” set in the markup, and is
removed when AlpineJS is initialized, thus if JavaScript is disabled the
filters form is shown nevertheless.
2023-05-24 12:13:09 +02:00
jordi fita mas 92edbdfc4d Reindent numerus.css with IntelliJ 2023-05-24 12:06:03 +02:00
jordi fita mas e68eb52578 Don’t show the “(optional)” label for filter inputs
It adds nothing, as all input fields for filters show be optional.
2023-05-24 11:41:48 +02:00
oriol carbonell pujolàs 79ec3ae4d6 Improve the CSS and general design 2023-05-23 23:13:21 +02:00
jordi fita mas bf2796190f Change the rows of the product description to 1 2023-05-23 15:25:55 +02:00
jordi fita mas d2a06dd1c0 Add class=filters to filters forms 2023-05-23 14:50:46 +02:00
jordi fita mas 9096cfe4f2 Wrap filter buttons with <noscript>
Since forms are already submitted on change, Oriol does not like the
idea of having a useless button around breaking the form grid.
2023-05-23 14:34:46 +02:00
jordi fita mas e974406870 Remove the “all” columns from products and contacts
That column was supposed to have a checkbox for batch operations, but
we do not have any operation that would like to perform to many products
or contacts at the same time.  For now, at least.
2023-05-23 14:21:04 +02:00
jordi fita mas 6c7762057c Change “Edit Invoice” button to just “Save” 2023-05-23 14:18:26 +02:00
jordi fita mas 65ee8a139c Use white-space: pre-line for invoice notes and payment instructions
I want the `white-space: pre` to preserve the newline characters that
users may have used, but this prevents line wrapping and long lines are
not confined within the page margins.

`pre-line` preserves the newlines, but collapses spaces and tabs, and
wraps long text, which is more what i want.
2023-05-22 11:23:19 +02:00
jordi fita mas eb47988464 Add a background rectangle to the chart and fix NaN when max = 0 2023-05-21 00:14:48 +02:00
jordi fita mas 39b0b801b2 Add income and expenses chart in SVG 2023-05-20 15:53:59 +02:00
oriol carbonell pujolàs 31eff5e3ab Update 'web/static/numerus.css' 2023-05-17 15:50:08 +00:00
jordi fita mas 987a99e0df Add a period filter for the dashboard
I do not yet know whether Oriol wants a YTD or MAT period, and i went
for the easiest for me: everything is MAT.
2023-05-17 12:05:30 +02:00
jordi fita mas ce42880697 Begin the dashboard with expenses, gross income, net income, and taxes
For now i use a too-long SQL query for that, but will probably replace
it with a view.  I have to check that it is correct before i do so,
however.
2023-05-16 14:56:49 +02:00
jordi fita mas ee2ed598a3 Fix invoice’s colspan for the empty index table’s row 2023-05-14 18:47:16 +02:00
jordi fita mas 3161d54aba Add expense’s file input to new and edit forms
I had to change MethodOverrider to check whether the form is encoded as
multipart/form-data or i would not be able to get the method field from
forms with files.

For now i add the file manually, i.e., outside add_expense and
edit_expense PL/pgSQL functions, because it was faster for me, but i
will probably add an attach_to_expense function, or something like that,
to avoid having the whole ON CONFLICT logic inside Golang—this belongs
to the database.
2023-05-14 18:46:16 +02:00
jordi fita mas f639602170 Add contact’s inline form for tags 2023-05-12 11:32:39 +02:00
jordi fita mas df37583cc6 Add the actions menu to products and contacts 2023-05-11 23:32:21 +02:00
jordi fita mas 970340277d Add the contact filter form 2023-05-10 18:56:07 +02:00
jordi fita mas 856ddde00e Add the inline form for product tags 2023-05-09 12:18:31 +02:00
jordi fita mas f0f98e200c Add inline tag form for expenses 2023-05-08 12:58:54 +02:00
jordi fita mas 664088c748 Add filter form to expenses 2023-05-07 22:49:52 +02:00
jordi fita mas 1415c3ef10 Moved the link to edit expense from the invoicer’s name to a menu
This menu will also have options like delete, and whatever we like to do
to expenses, like invoices do.
2023-05-06 11:08:21 +02:00
jordi fita mas 55d650bd62 Add expenses’ index and add form 2023-05-03 12:46:25 +02:00
jordi fita mas 19bcfc29e8 Update HTMx version to 1.9.2
I was hit with a couple of bugs: hx-on not properly de-initializing,
with a workaround in 43fffb68 and properly fixed with version 1.9.2;
and elements with naked hx-trigger did not work with hx-boost, as i do
for the tag inline form, fixed in 1.9.1.

The other bug fixed in 1.9.1, play well with other libraries that also
use the window.onpopstate, did not affect me, i believe.
2023-04-29 16:20:13 +02:00
jordi fita mas d941adcdfe Trigger a recompute when price, quantity, discount, or vat changes
I had to add the correct change event to the select in order for this to
work, too; in tags it was already done, i and did something very
similar.
2023-04-28 00:22:28 +02:00
jordi fita mas 86ccbbe830 Add keyboard controls for product search
They are almost the same as for the multiselect, except that it “clicks”
the option to “select” it, as this will trigger the replacement of the
<fieldset> with the whole product.
2023-04-28 00:06:48 +02:00
jordi fita mas 43fffb6848 Fix a swapError with data-hx-on and data-hx-swap="innerHTML"
I had a lot of errors when trying to swap an element that has data-hx-on
attribute: it would tell me that it could not swap the bloody thing and
that t.onHandlers is not an iterable.  I believe it also happened for
elements that did not have data-hx-on, but i am unsure at this point.

Apparently this is a bug introduced with version 1.9.0 of HTMx that as
of today is not yet fixed[0].

It seems that the problem that they keep the handlers created by
data-hx-on in an object, to be able to remove them afterward, but they
were looping the object with for(… of …) instead of for(… in …).

They will surely fix it in time, but since they will release a new
version, i have decided to change the minified code for now, as there
is no danger of replacing it with the new version—different file names.

[0]: https://github.com/bigskysoftware/htmx/issues/1368
2023-04-28 00:03:03 +02:00
jordi fita mas b10f0dcb3f Update HTMx to version 1.9.0
I mainly did it for the new hx-on attribute, to click the update
button on recompute, but it does not seem to work as i think it does.
Anyway, there are some fixed bugs.

From the release announcement[0]:

## New Features

  * Support for view transitions, based on the experimental View
    Transitions API currently available in Chrome 111+ and coming to
    other browsers soon.
  * Support for “naked” hx-trigger attributes, where an hx-trigger is
    present on an element that does not have an hx-get, etc. defined on
    it. Instead, it will trigger the new htmx:triggered event, which
    can be responded to via your preferred scripting solution.
  * Support for generalized inline event handling via the new hx-on
    attribute, which addresses the shortcoming of limited onevent
    properties attributes in HTML.

## Improvements & Bug fixes

  * A memory leak fix by @croxton

[0]: https://htmx.org/posts/2023-04-11-htmx-1-9-0-is-released/
2023-04-26 14:30:40 +02:00
jordi fita mas a06bc3df58 Use slugs too to select invoice products without JavaScript
The product search returns a list of products using its slug as the
“external key”, because i do not want people seeing the id in links,
and the search product list is just a different rendering of the product
index table.

However, now i had two almost identical select queries for product,
one using the product_id and the other its slug, the former intended for
the form to select products using checkboxes—the one non-JavaScript
users see—and the latter for the product search.

Using the slug in both forms i can now simplify the code and have a
single query.
2023-04-26 13:50:02 +02:00
jordi fita mas f2a0cd7d94 Move back most dialogs to regular (but still boosted) pages
With Oriol agreed that adding or editing invoices, products, and
contacts is not just a “user interruption” but the main flow of the
program, and, as such, it is not correct to use dialogs for these.

More importantly, it was harder to concentrate, specially with the more
involved form of invoices, because of all the “noise” behind the dialog.
2023-04-25 15:28:55 +02:00
jordi fita mas 7d895fe5f9 Use HTMx to add product rows “inline” in the invoice form
I actually find more comfortable to select the product from the list
presented up until now, but this is mostly because i have very few
products and the list is not too long, so the idea is that with
JavaScript we will dynamically add an empty product row to the invoice
and then use the name field to search the product by name.

I have the feeling that i am doing something wrong because i ended up
with a lot of HTMx attribute for what i feel is not that much work,
but for now it will work.

I have added the `Is` field to `InputField` in order to include the `id`
attribute to the HTML element, because the HTMLAttributes are attached
to the `input`, not the `div`, and i felt like this one should also be
a custom element based on div, like all the others.

These is not yet any keyboard control to select the search results.

I am not happy with having the search of products in a different URL
than the index, specially since they use the exact same SQL query and
ProductFilter struct, but i did not know how else ask for a different
representation without resorting to the more complicated MIME types.
2023-04-24 02:00:38 +02:00
jordi fita mas 2ced61d304 Add the product filter form
I need a way to search products by name in the invoice form, when the
user adds or changes a product.  Since this is something that i have to
add too to the product list, i added it now so the function will already
be ready.
2023-04-23 03:20:01 +02:00
jordi fita mas c2f6d299b4 Refactor the invoice product form template
I was using the exact same form from edit and new pages of invoice,
which is not too bad considering it won’t change very often, but i now
want to be able to add new empty product lines with the add product
button, and i will need to have a template for that form, which would
mean a third copy.
2023-04-20 15:37:22 +02:00
jordi fita mas 884c6dc2db Make sure the tag’s condition menu is within the limits of <body>
Otherwise, when the tag input is too close to the right side of the
screen, it may be unreadable without scrolling.
2023-04-17 11:51:10 +02:00
jordi fita mas 149557e42e “Integrate” the tags’ condition into the input field
We have reconsidered the toggle thing and instead moved the selection
into a little menu on top of the input, like the input’s label does à
la Material Design.

I just moved the checkboxes into a new details, that works as a menu,
but i had to add the type="search" to the existing input in the tags
field, or the CSS would style the checkboxes as well.

I do not do anything when the checkbox selection changes because that
already triggers a POST to the server that returns the new HTML with
the checkbox changed, and the JavaScript only has to retrieve that new
structure, exactly as it does in the initial rendering.

Since we want to add a little description to the options, i no longer
can use the same SelectOption in ToggleField, even though i could have
reused the Group element, but that felt wrong.
2023-04-16 19:01:11 +02:00
jordi fita mas b30aeb5d49 Reformat form.gohtml with IntelliJ 2023-04-15 20:43:20 +02:00
jordi fita mas 5e01965d7e Replace use of <select> for tags “and” and “or” with checkboxes
I realized that using a select for just two, short, options is overkill:
the select and its options use a lot more real state than the two
radios, which can have tooltips (not yet, though).

Since i am going to replace this field with a custom element that has
a toggle-like aspect, i already added the is="numerus-toggle" attribute
and use it for stying the non-JavaScript field.
2023-04-15 04:05:59 +02:00
jordi fita mas 8f7933ffe2 Allow to select AND or OR for tags filter
This is because Oriol thinks that there may be cases where you want to
search invoices and such that have any of the selected labels, not all
of them, so we agreed on adding an option to choose.

The idea is that it will be a toggle, but this requires JavaScript and
this commit adds it as a dropdown as a first non-JavaScript step.
2023-04-14 02:40:48 +02:00
jordi fita mas 8c592cfe5e Execute “focus out” handler in tag input when clicking any other element
Apparently i was only testing that control with tab, because clicking
on any other non-focusable element (e.g., a table row) it did not add
the new tag and would not dispatch the “numerus-tag-out” custom element,
which is why i have seen it now.

This is equivalent to AlpineJS’s @click.outside, and i was already using
it for the multiselect dropdown.  The isConnected check is because i
probably found some cases in the dropdown’s handler, but i can not
remeber now, but since AlpineJS does it too, i guess it is important.
2023-04-12 11:59:45 +02:00
jordi fita mas d20573aa99 Allow editing invoice tags inline from the index table
I use the same pattern as HTMx’s “Click to Edit” example[0], except that
my edit form is triggered by submit and by focus out of the tags input.

I could not, however, use the standard focus out event because it would
also trigger when removing a tag with the mouse, as for a moment the
remove button has the focus and the search input dispatches a bubbling
focusout.  I had to resort to a custom event for that, but i am not
happy with it.

The autofocus attribute seems to do nothing in this case, so i need to
manually change the focus to the new input with JavaScript.  However,
this means that i can not use the same input ID for all the forms
because getElementById would always return the first in document order,
changing the focus to that same element and automatically submit the
form due to focus out.  That’s why in this form i append the invoice’s
slug to the input’s ID.

Finally, this is the first time i am using an HTMx-only solution and i
needed a way to return back just the HTML for the <td>, without <title>,
breadcrumbs, or <dialog>.  In principle, the template would be the
“layout”, but then i would need to modify everything to check whether
the template file is empty, or something to that effect, so instead i
created a “standalone” template for these cases.

[0]: https://htmx.org/examples/click-to-edit/
2023-04-11 10:46:27 +02:00
jordi fita mas 1290fc7283 Set the focus back to the search input when removing a tag
The idea is that if they removed a tag it is more that possible that
they want to continue editing tags.
2023-04-11 10:24:40 +02:00
jordi fita mas 3b568b013f Fix adding empty tag on focus out from element
For some reason, i was looking at the value of the focus’ **target**,
which is not my search field at all, but whatever control the focus
changes **to**.  It that new control is an input with value, then it
created a new tag with whatever my search field had, which could be the
empty string.
2023-04-11 10:23:32 +02:00
jordi fita mas 33277454fa Try to remove as many leaky references from event listeners as possible 2023-04-10 23:04:16 +02:00
jordi fita mas f945051f4a Remove document and window event handlers when removing custom elements
I realized that the event handlers that i was setting when creating the
tags input and the multi-select controls were not removed just because
these elements are no longer in the document, and kept firing again and
again.

I no longer can use an anonymous function, because removeEventListener
would not match it with the one passed to addEventListener.  I also have
to bind the handler to `this` in order to keep having access to the
object, and, again, can not do it in the call to addEventListener, or
i would get a different function each time.

I added the check to see if the element is connected inside the
connectedCallback because the documentation warns that this callback
“may be called once your element is no longer connected”[0], and i
understood it to mean that the connected and disconnected callbacks
could be called our of order, thus it would be possible to add event
listeners that would not be removed—again.

I am not actually sure where i have to do the same for the rest of the
“internal” events.

[0]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks
2023-04-10 00:05:29 +02:00
jordi fita mas ba880c6560 Create the tag when focusing out of the input
This is mainly because i sometimes think that the tag is accepted just
because it is there in the input, but actually it is not being used at
all.  I fear more people would do the same mistake.
2023-04-08 21:27:40 +02:00
jordi fita mas 233e7723c3 Use HX-Location instead of HX-Refresh when editing invoices
This makes reload only the <main> portion of the page, instead of the
whole thing, which to me looks faster; haven’t really measured it.

Like with duplicate, i had to add the location query argument when
inside the view page in order to return back to the same page, not the
index.
2023-04-05 10:29:03 +02:00
jordi fita mas dbfa58699c Show the duplicate invoice form in a dialog
Had to add a new hidden field to the form to know whether, when the
request is HTMx-triggered, to refresh the page, as i do when duplicating
from the index, or redirect the client to the new invoice’s view page,
but only if i was duplicating from that same page, not the index.

Since i now have to target main when redirecting to the view page, so
i had to add a location structure with the required json fields and all
that, when “refreshing” i actually tell HTMx to open the index page
again, which seems faster, now that i am used to boosted links.
2023-04-04 14:39:55 +02:00
jordi fita mas b6668e72ef Trigger filter form on change and search, as well as submit as before
Changed the invoice number field’s type to search to add the delete icon
on Chromium.  Firefox does not add that icon, but i do not care; it is
still better that type="text".

Had to emit the change event to the numerus-tag field, otherwise the
form would not detect the change.

I also can not use keyup as a trigger because the changed modifier can
not be used in the <form>, as nothing ever changes, i do not know how to
trigger the form from children (i.e., data-hx-trigger on the <input>
does nothing), and i can not trigger for just any keyup, or i would
make the request even if they only moved the cursor with the arrow keys,
which is very confusing as Firefox resets the position (this may be due
the fact that i reload the whole <main>, but still).
2023-04-03 12:45:15 +02:00