The <th> elements should be inside <tr> elements.
Since we're changing this code, we're also adding <thead> and <tbody>
tags because that's what we usually do and it makes it easier to select
<tr> tags from either the table head or the table body using CSS or
JavaScript.
We're also moving the tests, but we're keeping one system test in order
to test the controller and the navigation to get to this page.
Note we're slightly changing the order of the methods in the component;
the order of the instance variables was `user_`, `vote_`, `vote_`,
`user_`, which was hard to follow.
We were passing a hash of options instead of converting them to keyword
parameters, which resulted in warnings on Ruby 2.7:
```
app/components/admin/table_actions_component.html.erb:5: warning: Using
the last argument as keyword parameters is deprecated; maybe ** should
be added to the call
app/components/admin/table_actions_component.html.erb:9: warning: Using
the last argument as keyword parameters is deprecated; maybe ** should
be added to the call
app/components/admin/widget/cards/row_component.html.erb:20: warning:
Using the last argument as keyword parameters is deprecated; maybe **
should be added to the call
app/components/admin/widget/cards/table_component.html.erb:14: warning:
Using the last argument as keyword parameters is deprecated; maybe **
should be added to the call
```
We weren't showing the year when a page was created/updated, and we
were displaying the created date instead of the updated one.
Co-Authored-By: Diego Calvo <diego.calvo@enreda.coop>
Note we could use `acts_as_paranoid` with the `without_default_scope`
option, but we aren't doing so because it isn't possible to consider
deleted records in uniqueness validations with the paranoia gem [1].
I've added tests for these cases so we don't accidentally add
`acts_as_paranoid` in the future.
Also note we're extracting a `RowComponent` because, when
enabling/disabling a tenant, we're also enabling/disabling the link
pointing to its URL, and so we need to update the URL column after the
AJAX call.
[1] See issues 285 and 319 in https://github.com/rubysherpas/paranoia/
This is consistent with the way we use separate actions to hide and
restore records, which is similar to enabling and disabling a record. We
might do something similar with the `toggle_selection` actions in the
future. For now, we're only doing it with budget phases because we're
going to add a similar switch control to hide and restore tenants.
We're also making these actions idempotent, so sending many requests to
the same action will get the same result, which wasn't the case with the
`toggle` action. Although it's a low probability case, the `toggle`
action could result in disabling a phase when trying to enable it if
someone else has enabled it between the time the page loaded and the
time the admin clicked on the "enable" button.
In general, we don't use links inside admin tables because we don't know
where the link will point to, and use "view" actions/links instead.
However, in this case, we're showing a URL, so it's perfectly obvious
where the link will point to. And so it makes sense to use the URL as a
link instead of using a "view" action/link.
Some institutions using CONSUL have expressed interest in this feature
since some of their tenants might already have their own domains.
We've considered many options for the user interface to select whether
we're using a subdomain or a domain, like having two separate fields,
using a check box, ... In the end we've chosen radio buttons because
they make it easier to follow a logical sequence: first you decide
whether you're introducing a domain or subdomain, and then you enter it.
We've also considered hiding this option and assuming "if it's got a
dot, it's a domain". However, this wouldn't work with nested subdomains
and it wouldn't work with domains which are simply machine names.
Note that a group of radio buttons (or check boxes) is difficult to
style when the text of the label might expand over more than one line
(as is the case here on small screens); in this case, most solutions
result in the second line of the label appearing immediately under the
radio button, instead of being aligned with the first line of the label.
That's why I've added a container for the input+label combination.
Note we aren't allowing to delete a tenant because it would delete all
its data, so this action is a very dangerous one. We might need to add a
warning when creating a tenant, indicating the tenant cannot be
destroyed. We can also add an action to delete a tenant which forces the
admin to write the name of the tenant before deleting it and with a big
warning about the danger of this operation.
For now, we're letting administrators of the "main" (default) tenant to
create other tenants. However, we're only allowing to manage tenants
when the multitenancy configuration option is enabled. This way the
interface won't get in the way on single-tenant applications.
We've thought about creating a new role to manage tenants or a new URL
out of the admin area. We aren't doing so for simplicity purposes and
because we want to keep CONSUL working the same way it has for
single-tenant installations, but we might change it in the future.
There's also the fact that by default we create one user with a known
password, and if by default we create a new role and a new user to
handle tenants, the chances of people forgetting to change the password
of one of these users increases dramatically, particularly if they
aren't using multitenancy.
We were rendering the `new` action, but that action doesn't exist.
Before commit ec861ca8e, we were rendering the `edit` action of an
answer, which was confusing as well.
Note that, when adding an invalid document, `@answer.documents` contains
that invalid document (which is not present in the database). Since
we're rendering the index, this new document would appear in the list of
the documents that can be deleted; to avoid that, we're kind of
"reloading" the answer object in the component by finding the record in
the database. We aren't using `@answer.reload` because doing so would
remove the validation errors.
We were using the same logic in four different places, so we're creating
a new class to handle that logic.
Note that I didn't find a way to delegate the `content` method to a
`Admin::TableActionsComponent`, so we're delegating the `action` method
instead. That means we need to create a method returning an
`Admin::TableActionsComponent`. We also need to cache this object;
otherwise we were getting an error when calling `actions.action` from
the `Admin::Poll::Questions::TableActionsComponent`.
Adding, modifiying, and/or deleting questions for an already started
poll is far away from being democratic and can lead to unwanted side
effects like missing votes in the results or stats.
So, from now on, only modifiying questions will be possible only if
the poll has not started yet.
In this form, the only case where `poll` might be present without
`question.poll` being present to is going to be the `new` action. We can
assign the poll in the `new` action and get rid of the `poll` variable
in the form.
The reason why we were displaying the ending date as "one second before
the actual ending" was that, when seeing that a phase ends at a date
like "2000-12-31 00:00", we might end up thinking that the phase will
finished at the midnight between December 31st and January the 1st,
while it actually ends at the midnight between December the 30th and
December the 31st.
This is particularly important because we use a date field to select the
date of a phase and if select December the 31st, it will be stored in
the database as "2000-12-31 00:00". So, instead, in this case we display
"2000-12-30 23:59", which is less confusing.
But now we're going to add support for setting a time on polls, which
means a certain poll might end at 15:30. In this case, displaying that
it ends at 15:29 doesn't make much sense.
We were displaying dates in two different formats in the same component,
leading to strange hacks like manually calling the `call` method or not
being able to use `render_inline` in the tests.
Since we're going to reuse one of these formats outside the budgets
section, we're splitting the component. We're also removing the
mentioned hacks.
In some cases (e.g. after editing or creating a resource
with errors) the default back_link did not redirect to the
expected page.
Now we force the back_links to the index pages, so we
always get the desired redirect.
The interface of this method has changed and uses keyword arguments
instead of a hash of options. This change will be particularly
significant when upgrading to Ruby 3.
We were getting some deprecation warnings:
DEPRECATION WARNING: `Module#parent` has been renamed to
`module_parent`. `parent` is deprecated and will be removed in Rails
6.1.
DEPRECATION WARNING: `Module#parents` has been renamed to
`module_parents`. `parents` is deprecated and will be removed in Rails
6.1.
While this bug was already present in the general admin search, the
combination of both search and filters was very uncommon. I've only
found this combinations in the users section, where you've got the
"erased" filter, but in this case searching for erased users doesn't
really make sense since their username and email have been deleted and
so there's nothing to find.
So the hidden content seemed to be the only affected section. However,
we're adding the field to every section so we don't have to make sure we
add it when we need it (like we did in the SDGManagement section).
Add a help text on admin budget show page and improve text from Admin::Budgets::HelpComponent in order to
clarify its functionality when we are using the wizard.
Avoid displaying the price in admin budget headings section
and avoid fill the field 'price' in admin budget headings form
when the budget has been checked with hide_money field.
This way we fix a bug we mentioned in commit 930bb753c which caused
links to documents to be broken when editing their title because the
title was used to generate the URL of the document.
Note we're still using Paperclip to render cached attachments because
this is the only case where we store files with just Paperclip and not
Active Storage.
With Active Storage, we render attachments just like any other resource,
using `polymorphic_path`. Paperclip included the `url` method in the
model; since the model doesn't have access to the request parameters
(like the host), this was inconvenient because it wasn't possible to
generate absolute URLs with Paperclip.
In order to simplify the code and make it similar to the way we used
Paperclip, we're adding a `variant` method accepting the name of a
variant and returning the variant.
In the moderation section there's no clear indicator as to what the
"Hide" and "Block" buttons do and the difference between them.
Since we're using confirmation dialogs in all moderation actions except
these ones, we're adding them here as well, so the difference will
appear in the dialog.
This isn't a very good solution, though, since the confirmation dialog
comes after clicking the button and users have already been wondering
whether clicking that button will be the right choice. A better solution
would be making the purpose clear before the button is clicked, although
that's something we don't do anywhere in the admin/moderation sections.
Placeholders are hard to read due to their low contrast, and users might
want to read them after they're already gone, and so they cause both
accessibility and usability issues.
The "enabled" column was aligned to the center, which looked weird when
seen on small screens because the `table-for-mobile` class (which I
think was added by accident) shows `th` and `td` elements using
`display: block`. Besides, using `display: block` on these elements
means browsers don't identify them as table headings/cells anymore,
causing accessibility issues.
There was an edge case where we could access the headings index without
sending the mode parameter in the URL. That meant when sending the
headings form we could send a form with the mode hidden field set to an
empty string. When that happened, the returned text was
`t("admin.budgets.help.#{i18n_namespace}.`, which returned a hash.
Using `multiple` when an empty strin is received solves the issue.
Since the message might appear several times on the same page, it's
useful to give a bit more context. Besides, usability tests show that
when there's a group with no headings, there's no clear indication on
the page that the group is actually a group and not a heading.
We're also adding some emphasis to the group name in the "Showing
headings" message, to be consistent with the emphasis we've
added the the group name in the "No headings" message.
Captions benefit blind screen reader users who navigate through tables,
particularly in this case because we potentially have several tables
with headings (one table per group), so when navigating through tables
it might be hard to know which group the headings belong to.
In this case they also benefit sighted users. Usability tests have shown
the "Groups and headings" section is a bit confusing, so adding a
caption like "Headings in Districts" helps clarifying Districts is a
group and the table refers to headings in that group.
The very same table is rendered in the "headings" step of the budget
creation wizard. However, in that case the information of the caption is
redundant because the page is specific for headings belonging to a
certain group. We're making the element invisible but still keeping it
for screen reader users in order to ease their navigation through
tables.
The population field is optional and only used for statistic purposes,
and the content block feature is also secondary, so IMHO it's OK if we
don't display it in the index; if administrators need this information,
they can see it by going to the "edit heading" page.
With this change it's easier to navigate the table on small and medium
screens. Actually, the whole page is easier to navigate, since we
greatly reduce the cases where a horizontal scrollbar is present.
The buttons to create polls associated with a budget were too prominent,
appearing on the table as if they were as used as the link to manage
investments. Most CONSUL installations don't use physical booths, and
would probably wonder what that button is about.
We're moving it to a more discrete place, at the bottom of the budget
page. This way we can also split the action in two: on budgets not
having a poll, we display the button in a not-so-accessible position (at
the bottom of the page), since this button will only be used once per
budget at most. Once the poll has been created, it means this feature is
going to be used, so we display a link to manage ballots more
prominently at the top of the page. If the budget has finished the final
voting stage without creating a poll, we don't show either the link or
the button because this feature can no longer be used.
We're also adding some texts indicating what this feature is about,
since it's probably one of the least understood features in CONSUL
(probably because the interface is very confusing... but that's a
different story).
Since now from the budget page we can access every feature related to
the budget, we can remove the "preview" action from the budgets index
table, since this feature isn't that useful for budgets once they're
published.
Now the budgets table doesn't take as much space as it used to, although
it's still too wide to be handled properly on devices with a small
screen.
Since managing investments is a very common action, with this link
administrators won't have to go back to the index page to manage
investments; they can access it from either the budgets index page or
the budget page.
Since now the links we've got on the budget page are similar to the ones
we've got in the index page table, we're styling them in a similar way.
We're also fixing a small typo en the investments path; it works exactly
the same way as it used to, but passing `budget` instead of `budget_id:
budget.id` is shorter and more consistent with what we do in other
places.