Skip to content

Feature/form list restyle - #21

Open
tulas75 wants to merge 19 commits into
devfrom
feature/form-list-restyle
Open

Feature/form list restyle#21
tulas75 wants to merge 19 commits into
devfrom
feature/form-list-restyle

Conversation

@tulas75

@tulas75 tulas75 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

No description provided.

tulas75 and others added 16 commits August 6, 2026 18:00
…lbar

Reuse the shared <dino-search-filters-bar> + FiltersService + ListDataSource
on the form-data Map view so switching Tabella<->Mappa preserves the filter
(via the ?filters= query param) and both views apply identical filtering
semantics. Replaces the bespoke client-side #filtersContainer / applyFilters()
with a reactive marker pipeline driven by FiltersService.queryString.

Toolbar restyle (search-filters-bar):
- remove the "Search by keywords" field
- add an always-visible toolbar row: Tabella|Mappa segmented switcher (left),
  Filtri avanzati + Esporta labeled buttons (right), replacing the old
  icon-only buttons; presets stay inside the collapsible FILTRI panel
- viewList()/switchView() + queryParamsHandling:'preserve' for state transfer

Map view (forms-map):
- breadcrumb + count toolbar matching the table
- ListDataSource wiring (schema, node visibility, metric basic filters,
  data headers) mirroring FormsListComponent; export via shared ExportList
- remove now-unused text-input-autocomp

Add transloco keys: Table, Map, Advanced filters, pin (eng + ita).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l, selection action-row

Toolbar (search-filters-bar):
- move Add / Import actions into the toolbar (before Filtri/Esporta) via a
  projected [toolbarActions] slot; remove the floating FABs from forms-list
- merge the collapsible Simple filters and the Advanced dialog into one Filtri
  modal with Semplice/Avanzati tabs (Cerca commits advanced, Chiudi discards;
  simple filters apply live); 3-column layouts, padding
- Filtri button shows a count badge of applied filters (simple + advanced)
- Table/Map switcher always shown on form-data lists; Map toggle disabled when
  the schema has no location; align map toolbar + count with the table

List (dino-list):
- add a selection action-row above the table: single-selection shows row
  actions as icons that expand to labelled icons on hover; multi-selection
  shows bulk actions; a placeholder hint when nothing is selected
- remove the per-row floating actions column; drop 'actions' from displayedColumns
- relocate the Columns selector into the action-row; uniform 40px, pill-shaped buttons

i18n: add Simple, Advanced, Advanced filters, Table, Map, pin, selected, Columns,
Clear selection and the selection hint to all locales (eng, ita, fra, esp, prt,
ukr, ar, uga).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t restyle

Form-data view switcher:
- rename the Tabella toggle to Dati ('Table' -> 'Data' key) and add a third AI
  toggle routing to /forms/:id/datachat, preserving the ?filters= param
- hide Filtri, Esporta, the projected toolbar actions and the filter chips on
  the AI view; new viewSwitcherOnly input skips initFilters where no filter is
  displayed
- the AI page mounts the switcher and drops its DataChat breadcrumb

DataChat:
- remove the start-up 'Here is your data!' table and the suggested_questions
  blob: the chat opens empty
- create the PandasAI agent on the first question instead of on entering the
  chat, so that opening the AI view is not charged; keep it alive while the
  User stays inside the form section and destroy it (enddatachat) only when
  leaving it. Completion mode no longer posts enddatachat: it has no agent
- conversations are stored permanently in a dedicated IndexedDB database
  (DataChatStore), scoped by user and form schema, outside RxDB: they are
  never synced with the backend and survive the logout collections wipe
- new conversations sidebar (opt-in), welcome block with starter questions,
  and a send button beside the chat input
- feedback is available in datachat mode too, the answer entry carries its
  question so the feedback request can quote it, and the source of the
  request tells the two chats apart (dinoapp-ragai / dinoapp-datachat)
- chat entries restyled as answer cards with source cards, an actions row
  (rating, copy, regenerate) and suggested questions as rows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ports the work of 491080a onto the restyled chat, which had rebuilt the same
files: the logic is the one of that commit, its four new blocks are expressed
in the current answer card.

The DataChat API turned its silent truncation and its dropped charts into
explicit data, and the client was the only reason a user could still read a
partial answer as a complete one.

Table previews and export:
- type the reply and map the additive fields (total_rows, total_columns,
  preview_rows, truncated, download_url, download_filename, note) for every
  response type, reading the truncated flag instead of inferring it
- show how large the result really is before the table, say when columns were
  dropped, and display a note verbatim, whether or not the result is truncated
- download the complete csv through HttpClient with the api key and user email
  headers, and hand the file to the host app, which saves it natively on device
  and through an anchor on the web
- request the export outside the interceptors: JWTInterceptor reads any 400 as
  an expired token, and the export endpoint answers 400 when its agent is gone
- map an export failure by status only: a blob response type leaves the body
  unparsed and a 403 answers with html. An expired token is expected, not an
  error worth reporting
- render a null cell as an em dash: not analyzed is not a value

Charts:
- render the charts of any answer, prompted or not, in a new DataChatChart
  drawn directly on chart.js, with a color vision safe palette per theme
- resolve the api plot kinds, draw a horizontal bar as chart.js 2 horizontalBar
  and keep the categories in the order they arrive
- validate a specification before drawing it, and say that a chart is missing
  rather than throwing or leaving a blank canvas
- never drop a malformed series: a chart missing a series looks complete
- keep the further charts of a chart answer: its value is the primary chart and
  its charts are the others, so overwriting them lost the answer's own extras
- accept a correctly encoded image, so that the api can stop wrapping its
  images in a python bytes repr independently of this client

Stored conversations:
- store the rows of a tabular answer and build its table again when the
  conversation is displayed again: a component instance cannot be stored
- never store an export link: an export lives as long as the chat session

Rating an answer:
- rate a table with its rows and a chart with its title: a tabular answer has
  no prose of its own, and the api rejects an empty answer
- copy a table as tab separated text, which a spreadsheet understands

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- color every snackbar with the theme's primary, its message white and its
  action label white and bold. The snackbar styles are injected at runtime by
  the component and read the material tokens, which the theme sets on html:
  overriding them on the container is what recolors the action label. The
  import wizard keeps its own success and error panels
- give the buttons of the form data section the radius of the other sections,
  i.e. the one of the import wizard: the view switcher and the toolbar buttons,
  the selection actions and the columns button of the list, and the buttons of
  the chat. A chip stays a pill

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The selection action row replaced them with the actions of the selected row,
which costs a click and a checkbox to reach what used to be one hover away.

- restore the actions column and its 'actions' displayed column: the main
  actions of a row, revealed when the row is hovered or focused
- drop the three dots opening the actions dialog: every action of a row is
  still available from the selection row above, which lists them all
- center the icons on the right edge of the row. The cell has no width of its
  own, so the icons are taken out of the flow as a group instead of being laid
  out one slot each, which left them at the bottom of the row
- give them the icon size of the actions of the selection row

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nged

Opening the chat validated the stored api key and asked the api for the
credits of the user, which cannot have changed: nothing is spent until the
agent is created, on the first question. Both the agent creation and every
answer already refresh them.

The credits of a key entered for the first time are still read once: without a
stored key the api is not asked for them at all, so they would stay unknown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every column of a list takes the same share of the width, so a column of long
answers is as wide as one of dates and the User cannot trade one for the other.
Angular Material does not resize columns: only the experimental package does,
which this drags a grip of its own instead of depending on.

- add a grip on the right edge of a header cell. It swallows its own pointer
  events: that cell is the drag source reordering the columns, and the button
  sorting them
- size the resized columns through a stylesheet of the list, rather than a
  binding on every cell: a cell is rendered by the table, in a view of its own,
  and a width must follow the pointer without waiting for a change detection
  and hold for the rows rendered later. The rules are marked important: the
  width of a column is otherwise given by selectors of a higher specificity,
  which would keep a column from being made narrower than the default
- store the width of a column in its header, which the columns preset of the
  list already saves and reads back
- let the text of a cell use the width of its column, instead of ellipsizing at
  200px whatever the column: without this, widening a column shows nothing more
- reset the columns from their dialog: the ones displayed, their order and
  their widths, dropping the preset of the User

Two bugs found on the way, both older than this:

- a column dropped in the wrong place. Only the columns of the data are
  dragged, so the indexes of the event count those alone, while they were
  applied to the displayed columns, which the checkbox and the actions belong
  to as well
- the headers of the section were overwritten by the preferences of the User,
  as the list set them on itself through their own input. The input alone is a
  default now, the list applies its preferences through _applyHeaders, and a
  width is stored replacing a header instead of writing into it: with no
  preference stored, those are the very objects the section holds

Narrow the checkbox column, which used to hold the bulk actions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ns into the toolbar

The toolbar restyle of 0906a50 removed the keyword field of the filters bar,
which the form data does not need, its filters being in the Filters modal. That
bar is shared by every list, so the search went away from the sections that
have nothing else: users, groups, notifications, reports, the aggregation list
and every metric section.

- display the keyword field again, beside the view switcher, through a
  keywordSearch input which defaults to true: the form list and the form map
  turn it off, everything else has it back. The AI view never displays it: it
  initializes no filter, so a field of its own would search nothing
- only the field had been removed: the control of the bar, the filter item the
  FiltersService builds from it and the query the ListDataSource makes of it
  were all still there, so nothing else was needed
- move the floating buttons of these sections into the toolbar, as the form
  list already does: Add, Import, Filters, Export. The credits a report costs
  are displayed on its button, as the floating one displayed them
- the toolbar gives the keyword field all the width its actions leave

The tour anchor of the report button is on a button of the section now, so its
directive is always imported, as in the form list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The filters lived in the url alone: leaving a section dropped them, and coming
back displayed everything again. Whoever works with the same filters every day
had to apply them every time, the only alternative being the named presets,
which are global and have to be saved and loaded by hand.

- store the filters of a section as they are applied, and apply them again when
  it is opened. What is stored is what the url carries, so the whole mechanism
  is the one already there: the filters are encoded where they were encoded,
  and applied through loadPreset, where a preset is applied
- put the filters back in the url, replacing the entry: the section then reads,
  and is shared, exactly as if they had just been applied. A url carrying
  filters wins over the stored ones: a link must display what it says
- forget a section when its last filter is removed: filtering nothing is a
  choice of its own
- drop a stored value that cannot be read, which would otherwise break the
  loading of the filters of every section from then on
- give the sections one way of identifying themselves, sectionStorageKey, which
  the columns preset already defined and now shares: the two cannot drift apart,
  and the map, which is not a list, can identify its form as its table does
- azzera filtri: a button of the Filters modal dropping every filter of the
  section, the applied ones and the ones staged in the Advanced tab

The FiltersService is a singleton holding the root route, so it cannot tell
which section is displayed: the list and the map tell it.
The filters of the sections are cleared on logout, as the columns and the
presets already were.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The export dialog was the last screen of the list area still on the old look:
two stacked toolbars and a tab per slide, each tab a one-column selection list.
On a form with many slides there was no way to search a field, no idea of how
much was selected, no confirm row, and the four fields-and-formats options sat
in a single multi-select where two of them are mutually exclusive and were kept
apart by a disabled expression in the template.

- lay the dialog out as header, dropdown, body and footer: the scope and the
  format as segmented controls in the header, the sections in a sidebar driving
  a searchable grid of field cards, the selected count and Annulla/Esporta in
  the footer
- turn the fields and formats multi-select into a menu: the two checkboxes stay
  checkboxes, the value format becomes a radio group with a Default option, so
  the exclusion is the shape of the control instead of a rule on top of it. It
  still drives the same _dataAnalysis$ and _separateColumns$ the engine reads
- move the field selection out of the DOM and into the component, one Set of
  names per section. It had to move: only the active section is rendered now,
  and the tabs were the only thing keeping the others alive. _getFieldsFromTabs
  becomes _getSelectedFields and _getSectionFields, its two overloads meaning
  two different things at the five places that called it
- open the dialog large, 1200px by 85vh, under its own panel class

The export engine is untouched: the same fields go in and the same file comes
out. Metrics keeps the same component, its schema having no slides: the body
and the dropdown are absent and the dialog is header and footer alone.

The colours come from an --exp-* token set with a dark override, as datachat
and the import wizard already do, in place of a hardcoded purple; the global
dialog padding rule this file leaked to every dialog of the app is gone, scoped
to the export panel.

The bottom sheet and the toggle button go away with the old template: the
bottom sheet had no caller, its opener in the filters bar removed with it.

The four fields-and-formats labels were missing from every translation file and
rendered in English everywhere, Italian included: they are translated now,
along with the fifteen strings of the new layout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…port dialog

The two modals open from the same toolbar and read as one screen, but their
headers had drifted: the Filtri title was lighter and a hair smaller than
Esporta dati, and its Semplice/Avanzati toggles kept the Material defaults,
taller than the segmented controls of the export header and grey where those
are tinted with the primary colour.

- give the heading the same 18px 600 of the export title
- give the tabs the same segmented control: 40px tall, 8px radius, the selected
  one tinted with the primary colour

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ented controls

The Tutti/Qualsiasi toggle of the Advanced tab was the last one left on the
Material defaults, taller and grey beside the Semplice/Avanzati tabs right
above it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ght it needs

Metrics have no slides, so the dialog is its header and its footer alone, but
it was opened at the same fixed 85vh as the form export: below the two rows lay
an empty grey area taller than the whole modal.

- open the dialog at auto height when there are no fields to pick, keeping 85vh
  as the ceiling of the form one, which fills it with the sections and the grid

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A field card is one cell of the grid: a long label is ellipsized, and long
labels are the rule in a form of any size, so choosing what to export meant
guessing between two fields cut at the same word.

- give every field card the tooltip of its label, as the sections of the
  sidebar already had, and let both wrap up to 420px instead of being
  ellipsized in turn

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The checkbox column was cut to 76px, and a cell is `box-sizing: border-box`:
after its 3px border and its padding that left 65px of content. A checkbox is
40px and each status icon is a 34px box - 24px plus the `padding: 5px` that
`_table-base.scss` gives every icon in a cell - so the invalid form icon did
not fit even on its own, and a cell is `overflow: hidden`: it was cut in half.

Widening the column back would have been paying, on every row, for the two
icons that only some rows carry - a column has one width for all of them. So
the status leaves the flow instead: `.dino-row-status` stacks the badges on
the left edge of the row, out of the flow, and the column goes down to 61px,
the width of its checkbox. A row with nothing to report now has no gap at all,
and one with both has room for both.

The bar at the start of a flagged row takes the warn colour, so the status is
legible the whole way down the list and not only once the eye is on the row.
The conditions behind it move into `isInvalidRow` / `hasFilesToUploadRow` /
`hasStatusRow`, so the bar and the badges cannot disagree on what counts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tulas75 and others added 3 commits September 8, 2026 11:19
…imple ones included

The chips under the toolbar only stood for the advanced filters. The simple
ones - date, metric, user, group, status - live in fields the Filters modal
hides as soon as it is closed, so the only trace left of them was the count on
the Filters button: the list showed fewer rows than the section holds and
nothing said why.

- the chips row reads the whole applied set ('all'), hiding only `keyword`,
  the one field the bar keeps visible with a value and a button to clear it
- a basic chip is labelled after the field it comes from ('From date', 'User',
  'Form status', following the placeholders) and carries the value that field
  displays: the status label, the user's full name, the group name, the metric
  name with its secondary attribute, the date in the locale format. No
  operator and no quotes: `$regex` is an implementation detail of a field the
  user filled in by picking from a list.
- a basic filter keeps its place in the list once its field has been used, so
  only the ones actually carrying a value get a chip
- removing a basic chip empties the field behind it, which clears the multiple
  selection and the metric checkboxes with it, instead of dropping the filter
  and leaving the field looking as if it were still applied

The field a chip filters on now reads in semibold, in the advanced chips too,
so the eye separates it from the value at a glance. The listbox also loses the
`aria-label` "Fish selection" it had carried over from the Material docs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lection read as a list

`multipleName.join()` separates with a bare comma, so a field with several
metrics chosen - and its chip - ran the names together:
`AFRUZA BOICHUK,AMIN,Angela KURLOU`. Joining with ', ' tells them apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…chosen list its options again

Reopening the Filters modal after applying a metric, status, user or group
filter left that field dead: the panel listed nothing, so nothing could be
clicked and the only way out was Reset filters.

Two things met. The option lists are built once, in `initFilters`, as cold
streams fed by the field `valueChanges`, and the modal lives in an
`ng-template`: every open builds a fresh view whose `async` subscribes to those
streams from scratch, and a cold `valueChanges` emits nothing on subscribe, so
the panel started empty and waited for the field to be edited. Meanwhile the
field held the option chosen, which is an object and not a search term, and the
`switchMap` answers a non-term with `[]`: no query ever ran. `showOptions`
could not help either - it turns the value back into a term only when it is
null, which is why resetting the filters was the one thing that worked.

- `_searchTerms` wraps the `valueChanges` of a field in a `defer`, so every
  subscription starts with the term the field holds right then: the text typed
  in it, or '' when it carries an option already chosen, which searches the
  whole list. The four option fields share it, and a field displaying a
  selection now lists all the options with the checkboxes of the selected ones
  already ticked, `_setupMetricsCheckboxes` running on the query result.
- the 800ms of the metric fields move inside the helper, debouncing the typing
  alone: the panel is ready as the modal opens instead of 800ms later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant