Commit Graph
338 Commits
Author SHA1 Message Date
Donkie ae3208a9c3 Let an extra field's value be cleared again
Picking the empty option of a single-choice extra field did nothing --
in either client, on spools, filaments and vendors alike. Emptying an
integer, range, datetime or multi-choice field was just as stuck. There
was no way to get a value back to unset once it had one.

Nothing in the API could express it. A key left out of the map means
"leave it alone", and every value that could stand in for empty is
rejected by the field's type check: "" is not one of a choice field's
choices. So `extra` values now accept null, meaning the entity has no
value for that field -- no row is stored, which clears whatever was
there. Null was a 422 before it reached any logic, so nothing that used
to work behaves differently.

A filament's and a vendor's extra were also REPLACED wholesale by a
patch rather than merged per key, unlike a spool's since 28f6f8e
(v0.22.0). Anyone patching one field of a filament -- as client_v2 did
-- silently destroyed the rest of its custom fields; anyone relying on
the replacement to delete a key can now say so with null. All three
entities merge per key now, and the field descriptions say so instead
of saying nothing.

client_v2's saver was where the frontend half died: an emptied input
produced no patch at all. It sends the cleared key as null now, and
keeps sending only the keys that changed, which is what stops two people
editing different fields of one entity from overwriting each other. It
also captures the entity id when the edit is made -- the inspectors are
reused across selections rather than remounted, so a debounced patch
could otherwise land on whatever you clicked next.

The legacy client cleared a filament's or vendor's field by omitting it,
which the merge would now ignore, so its form sends null for a field
left empty. That also buys it clearing on spools, which merged (and so
ignored the omission) all along.

Claude-Session: https://claude.ai/code/session_01UB5KocsTBm9GL5x3Uuup5F
2026-07-30 18:11:02 +02:00
470d8f3bcc Add filtering and sorting for custom fields (#773)
* Add filtering and sorting for custom fields

* Fix tests and make code postgres compatible

* Fix bugs and expand test coverage for custom field filter/sort

* trigger CI

* fix: address extra field filter review feedback

* fix: clean up custom field table state handling

* docs: update extra field table view description

* fix: narrow boolean custom field filter syntax

* Expand test coverage for all custom field types/entities and fix range filter/sort

- Add comprehensive tests covering all 9 field types (text, integer, float,
  boolean, single-choice, multi-choice, datetime, integer_range, float_range)
  for filter and sort on spool, filament, and vendor entities
- Add invalid-filter 400 tests for float, integer_range, and float_range
- Fix integer_range/float_range filter to use LIKE pattern matching against
  Python's deterministic json.dumps output instead of fragile .contains()
- Add integer_range/float_range sort support via a @compiles helper
  (_JsonArrayFirstElement) that emits CAST(col AS JSON)->>0 on PostgreSQL
  and JSON_EXTRACT(col, '$[0]') on SQLite/MariaDB
- Add logger and __all__ to extra_fields.py (aligns with PR #893)
- All tests verified passing on postgres, sqlite, and mariadb

* Add filter UI for all custom field types and fix range filters

- Add text input filter dropdown for text fields (substring search)
- Add range input filter dropdowns (min/max) for integer, float, integer_range, and float_range fields
- Add datetime picker filter dropdown for datetime fields
- Fix boolean "No" filter to use empty value so it correctly matches unset/false fields
- Fix integer_range/float_range filter to use numeric comparisons instead of LIKE-based exact matching (stored_min >= filter_min, stored_max <= filter_max)
- Add range filter support for integer/float fields (min:max format)
- Add _JsonArraySecondElement cross-database helper for extracting second JSON array element

* Range filter for integer/float fields and fix range_field filter semantics

Backend:
- Add _JsonArraySecondElement cross-database helper (mirrors _JsonArrayFirstElement)
- integer_range/float_range filter now uses numeric comparisons:
  stored_min >= filter_min and stored_max <= filter_max (was LIKE exact match)
- integer/float filter now supports min:max range format in addition to exact match:
  stored_value >= min and/or stored_value <= max

Frontend:
- integer and float fields now use a range filter dropdown (min/max inputs)
  instead of a single exact-value input

Tests:
- Fix integer_range/float_range spool and vendor tests broken by new >= semantics
  (filter values updated to discriminate between test entries)
- Add range filter tests (min only, max only, both) for integer and float fields

* Replace 12 copy-pasted numeric field tests with 4 parametrized tests

Introduce a _create_entity helper and @pytest.mark.parametrize("entity_type",
["spool", "filament", "vendor"]) to run each numeric field type test against
all three entity types from a single test function.

- Removes 12 individual tests (integer/float/integer_range/float_range × 3 entities)
- Adds 4 parametrized tests covering the same ground plus the previously missing
  min-only / max-only range filter cases for filament and vendor
- Uses try/finally for cleanup so entities are always deleted even on assertion failure

* Add datetime range filter and restore integer/float range filter support

- Backend: add datetime range filter using '|' separator (ISO dates contain ':')
- Backend: restore integer/float range filter support (lost during attribution rewrite)
- Frontend: replace single DateTimePicker with From/To range pickers for datetime fields

* Use local timezone in filter picker; add range filter tests

The DateTimeRangeFilterDropdown was initializing pickers with dayjs.utc(),
causing them to display and accept UTC times directly. The entry form shows
local time and converts to UTC, so the filter was inconsistent: entering
the same displayed time would produce a filter value 2h offset from what
was stored (in UTC+2), making exact boundaries fail unexpectedly.

Fix: initialize picker values with dayjs() (local mode) so the filter picker
behaves the same as the entry form — shows local times and converts to UTC.

Tests: replace 3 separate entity-specific datetime tests with one parametrized
test covering spool/filament/vendor, and add range filter cases (start|, |end,
start|end).

* Replace all entity-specific tests with parametrized tests

Replace 16 separate spool/filament/vendor tests for text, boolean,
single-choice, multi-choice, and empty-filter with 5 parametrized tests
that run against all three entity types. Each test is now defined once
and executed 3 times, eliminating copy-paste and ensuring consistent
coverage across all entity types.

Parametrize invalid-filter-value 400 tests across all entity types

The non-numeric-value error uses "Invalid integer/float range filter value"
while the missing-colon error uses "Invalid range filter value". Both contain
"range filter value", so use that as the assertion substring.

* Align custom filter dropdowns with Ant Design choice filter style

All three custom filter dropdowns (text, number range, datetime range) now
match the built-in choice filter footer: a border-top separator, Reset as a
link button on the left (disabled when no filter is active), and OK as a
primary button on the right.

* fix(fields): correct custom-field filter edge cases across databases

- Float exact-match filters now compare numerically (cast to Float) instead of
  by JSON string, so float fields stored as integer JSON (e.g. "2") or
  non-canonical decimals (e.g. "2.50") match an equivalent filter.
- integer_range/float_range filter and sort no longer return HTTP 500 on
  CockroachDB: the PostgreSQL '->>' JSON extraction is now also registered for
  the cockroachdb dialect, which previously fell back to the unsupported
  JSON_EXTRACT function.
- Text exact-match and single-choice equality use ensure_ascii=False so
  non-ASCII values match how the frontend's JSON.stringify stores them.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* test(fields): expand custom-field filter/sort coverage

Add tests covering filter composition (multiple extra fields, extra + built-in,
filter one field / sort another), pagination total-count, unknown-field
handling, text case-insensitivity/substring/non-ASCII, numeric multi-value OR,
boolean empty semantics and token rejection, multi-choice substring collisions,
empty filters on numeric fields, null-bounded ranges, and additional
invalid-value 400 paths.

Verified against sqlite, postgres, mariadb and cockroachdb.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* style(client): format column.tsx with prettier

The custom-field filter dropdowns' Reset buttons were written as one-line
onClick handlers that prettier wraps across multiple lines. Apply prettier
so the CI style check passes.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* fix(fields): decode JSON scalars in-DB for text/choice/datetime custom-field filters

Match text, single-choice and datetime custom-field filters against the
database-decoded JSON scalar (new _JsonScalarText cross-dialect helper:
#>> '{}' on postgres/cockroachdb, JSON_UNQUOTE(JSON_EXTRACT) on mysql,
json_extract on sqlite) instead of reconstructing the exact JSON
serialization the client wrote. This decouples matching from
json.dumps/JSON.stringify encoding quirks (non-ASCII escaping, surrounding
quotes). Also escape LIKE wildcards so '%' and '_' in text and multi-choice
queries match literally rather than acting as wildcards.

Verified against sqlite, postgres, mariadb and cockroachdb.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* test(fields): cover LIKE-wildcard escaping in custom-field filters

Add parametrized tests (spool/filament/vendor) asserting that '%' and '_'
are matched literally in text and multi-choice filters, that a literal '/'
(the internal LIKE escape char) matches literally, and that single-choice
equality handles a '%' value exactly.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

---------

Co-authored-by: Donkie <daniel.cf.hultgren@gmail.com>
Co-authored-by: akira69 <akira69@gmail.com>
2026-07-08 10:14:27 +02:00
Donkie 92da7fd110 feat(i18n): add Lithuanian and Turkish locales
Rework check-i18n to measure real translation coverage against en
(non-empty and differing from the reference) instead of file size,
gating inclusion at 50%. Add lt and tr, both ~96% translated.

Claude-Session: https://claude.ai/code/session_01BQjJiwEbu6bHbeDjPPDm3V
2026-07-07 11:40:48 +02:00
Donkie e2ab72b5d0 feat(theme): add 'System' option that follows OS/browser theme (#947)
Replace the light/dark switch with a 3-way System/Light/Dark control. The new
'System' preference follows prefers-color-scheme live via a matchMedia listener.
New installs default to System; existing users keep their stored light/dark
choice (legacy values remain valid preferences).

Claude-Session: https://claude.ai/code/session_01BQjJiwEbu6bHbeDjPPDm3V
2026-07-07 11:28:51 +02:00
Donkie 8c262a640d feat(scanner): accept Data Matrix and other 2D codes (#887)
Widen the code scanner's accepted formats beyond qr_code to the common 2D
matrix codes (micro/rm QR, Data Matrix, Aztec, PDF417) so manually-generated
labels using those symbologies can be scanned. Payloads that don't match the
spoolman spool format are ignored, so this is a safe superset.

Claude-Session: https://claude.ai/code/session_01BQjJiwEbu6bHbeDjPPDm3V
2026-07-07 11:28:51 +02:00
Donkie 025f18f914 Fix spool price disappearing from the list on live updates (#814)
The REST endpoints serialize with response_model_exclude_none=True (unset fields
are omitted), but websocket events were serialized with a plain .json() that
includes unset fields as explicit null. The spool list fills a missing price
from the filament price only when it is `undefined`, so a live update carrying
`price: null` (e.g. after a quick weight adjust) blanked the price column until
a full page reload.

Fixed on both sides so the two serialization paths can no longer drift:
- Server: websocket payloads now use exclude_none=True (spoolman/ws.py), giving
  live updates and REST responses an identical shape. Note this is a small
  websocket wire-format change external consumers will see: unset fields are now
  omitted instead of sent as null, matching REST.
- Client: collapseSpool and the price column now fall back for both `null` and
  `undefined`, keeping the list resilient regardless of serialization.
2026-07-06 11:50:07 +02:00
nyanotech 34c09d3f55 explicitly disable pagination for useSelect 2026-01-27 03:39:48 -08:00
Donkie 251f7e2ede Removed max height from locations page
Fixes #816
2026-01-19 20:36:11 +01:00
Donkie f137535c1e Merge pull request #811 from kaufi95/master
Bugfix for issue #803 and #805
2026-01-18 23:02:37 +01:00
Donkie fb83220739 Merge pull request #777 from beikeland/AplhanumericQR
All caps short QR codes for alphanumerical encoding
2026-01-18 22:54:10 +01:00
kaufi95 c179a38f03 fix for issue #803 2026-01-11 00:01:23 +01:00
kaufi95 6e9f6e5af4 fix for issue #805 2026-01-11 00:00:11 +01:00
Donkie 0f5775c2d4 Enforced eslint and prettier, added lefthook config 2026-01-07 23:02:32 +01:00
Donkie cda756557d Updated react qr scanner 2026-01-06 13:05:54 +01:00
Donkie b627e151a8 Updated client packages 2026-01-06 01:54:09 +01:00
Donkie 9baf91380a Merge pull request #717 from ajford/spool_edit_weight_iss477
Fix Spool Edit page Empty Weight and Initial Weight bug
2026-01-05 22:58:42 +01:00
JakobGue 185c8b07e0 remove show spool count option from settings 2026-01-04 00:54:19 +01:00
beikeland 967b9d5b31 Change short QR codes to be uppercase to allow for alphanumerical encodingm and make QR code scanner case insensitive 2025-11-04 16:42:33 +01:00
ajford 25f78e90e6 Fixes #477 by defaulting to spool values first 2025-06-27 00:29:23 -05:00
JakobGue d3181382ae add spool count in locations with a setting to enable / disable the display 2025-06-21 19:26:39 +02:00
Donkie 8bfb263858 Merge branch 'master' into master 2025-05-31 19:52:01 +02:00
Nícolas Pastorello b6ed0cb6e9 Update index.tsx 2025-05-05 16:37:34 -03:00
Matt Gerega 4e10f1019d Add Measured Weight option to the filament adjustment modal #560 2025-03-20 16:52:14 -04:00
Charalampos Tsachakis dbdad84120 Merge branch 'master' into adjust-spool-button-on-show 2025-03-19 09:07:08 +02:00
Donkie dcb21f71cb Added auto focus to adjust spool input field
Resolves #585
2025-03-18 21:36:57 +01:00
Donkie aa839b08e5 Handle input number formatting better
Now it doesn't interfer with the user writing until the user leaves the element

Resolves #633
Resolves #632
2025-03-18 21:27:00 +01:00
Donkie 3d1bb70913 Fixed not being able to change color picker format
Resolves #610
2025-03-18 21:09:19 +01:00
Donkie 6f01f372d6 Remove debug logging from numberFormatter function 2025-03-18 21:03:19 +01:00
Donkie a775beb5bf Fixed wonky trailing zero removal
Resolves #623
2025-03-18 20:59:57 +01:00
Donkie a94163dc1a Fixed empty default values extra field range breaking page
Resolves #652
2025-03-18 20:51:26 +01:00
Donkie 3fc2f712e3 Fixed a deprecation 2025-03-18 20:44:28 +01:00
Donkie f041207db8 Fixed broken antd elements
Resolves #655
2025-03-18 20:38:48 +01:00
Donkie dde468ed69 Add Japanese, Brazilian Portuguese, Tamil, and Thai locales to i18n.ts 2025-03-18 20:32:43 +01:00
Donkie 53d5fdf7e3 Remove country code flags in language selector 2025-03-18 20:31:53 +01:00
Pedro Lamas 3963ec6203 Reset multi-color when single color is selected 2025-03-12 18:10:26 +00:00
Donkie cd274a9968 Updated client dependencies 2025-02-24 21:46:56 +01:00
Donkie 4b08f27614 Merge pull request #624 from bdchik/fix
Bug Fix:  Ensure the QR code favicon.svg honors the base path, when configured.
2025-02-24 21:04:19 +01:00
Donkie 58a6233e7b Merge pull request #582 from ErikMichelson/feat/round-prices-setting
Add setting to toggle rounding of prices
2025-02-24 20:56:44 +01:00
Donkie 03815687ff Merge pull request #526 from Pantastisch/feature/hex-color-code-in-filament-page
Add Color Code Display for Single Color Filaments
2025-02-24 20:48:31 +01:00
Charalampos Tsachakis 53da38e396 Added adjust spool button
Added adjust spool button to spools/show page
2025-02-12 11:31:11 +02:00
Brett Chik c5878883a8 Link to print label from spool details page missing getBasePath() 2025-02-07 20:24:03 -08:00
Brett Chik 21befc2993 Ensure the QR code icon honors the base path, when configured. 2025-02-07 20:09:29 -08:00
Erik Michelson e93f89aa51 Add setting to toggle rounding of prices 2024-12-30 01:07:38 +01:00
Panagiotis Doulgeris 6b2dbda354 Add possibility to exclude margin in spool icon for overview sites 2024-12-22 15:38:05 +01:00
Donkie a3be91b89c Fixed not being able to clear price field
Resolves #525
2024-11-25 20:39:24 +01:00
Donkie 04920e463a Fixed number precision in locations page spool card 2024-11-25 19:07:24 +01:00
Donkie 46a9097491 Final locations updates 2024-11-24 15:04:20 +01:00
Donkie d142b0eaec Include the custom locations in spool edit page 2024-11-24 14:51:15 +01:00
Donkie 0827cc8a5a Improvements to default location 2024-11-24 14:45:16 +01:00
Donkie 755e06c989 Now goes back to loc page on edit submit 2024-11-24 12:31:38 +01:00