tane/apps/app_seeds/lib/ui/quantity_kind_l10n.dart
vjrj 3942975dba feat(inventory): informal quantity scale (number + unit) + seed/plant lots
Rework quantities the way seeds are actually described — a number of an informal
unit — and let a lot hold seeds or plants (plantel).

Quantity model (commons_core):
- Quantity is now { kind, count?, label } — "3 cobs", "2 packets", "a few".
  Count is optional; uncountable vibes (a few, some, pinch) carry no number.
- QuantityKind reorganised into an ascending scale: vibes → containers
  (teaspoon/spoon/cup/jar/sack) → seed & fruit forms → plant units, each tagged
  countable/not. Stored by name; count reuses the existing numeric column.

Lot type (schema v2):
- Lots gain a `type` (seed | plant); germination stays meaningful for seeds.
  schemaVersion 2 with a from1To2 migration (adds the column), drift_schema_v2
  exported, migration test covers v1→v2.

UI:
- New QuantityPicker: a horizontal scale of large seed-glyph icons + a number
  stepper (shown only for countable units), plus a seed/plant SegmentedButton.
  Used in quick-add and add-lot. Lot lines render "3 cobs" / "a few".
- i18n: unit singular/plural (ES/EN, Weblate-friendly), lot-type labels.

Build: slang moved to its CLI (disabled in build_runner to avoid a multi-file
collision); CI runs `dart run slang` before build_runner. 38 tests green,
Linux migrates the existing v1 DB and runs.
2026-07-08 10:51:02 +02:00

50 lines
2.5 KiB
Dart

import 'package:commons_core/commons_core.dart';
import '../i18n/strings.g.dart';
/// Singular + plural label for a [QuantityKind]. For uncountable *vibe* kinds
/// (a few, some…) both are the same word.
(String, String) _unit(Translations t, QuantityKind kind) => switch (kind) {
QuantityKind.aFew => (t.unit.aFew, t.unit.aFew),
QuantityKind.some => (t.unit.some, t.unit.some),
QuantityKind.plenty => (t.unit.plenty, t.unit.plenty),
QuantityKind.pinch => (t.unit.pinch, t.unit.pinch),
QuantityKind.handful => (t.unit.handful.singular, t.unit.handful.plural),
QuantityKind.teaspoon => (t.unit.teaspoon.singular, t.unit.teaspoon.plural),
QuantityKind.spoon => (t.unit.spoon.singular, t.unit.spoon.plural),
QuantityKind.cup => (t.unit.cup.singular, t.unit.cup.plural),
QuantityKind.jar => (t.unit.jar.singular, t.unit.jar.plural),
QuantityKind.sack => (t.unit.sack.singular, t.unit.sack.plural),
QuantityKind.packet => (t.unit.packet.singular, t.unit.packet.plural),
QuantityKind.cob => (t.unit.cob.singular, t.unit.cob.plural),
QuantityKind.pod => (t.unit.pod.singular, t.unit.pod.plural),
QuantityKind.ear => (t.unit.ear.singular, t.unit.ear.plural),
QuantityKind.head => (t.unit.head.singular, t.unit.head.plural),
QuantityKind.fruit => (t.unit.fruit.singular, t.unit.fruit.plural),
QuantityKind.bulb => (t.unit.bulb.singular, t.unit.bulb.plural),
QuantityKind.tuber => (t.unit.tuber.singular, t.unit.tuber.plural),
QuantityKind.seedHead => (t.unit.seedHead.singular, t.unit.seedHead.plural),
QuantityKind.bunch => (t.unit.bunch.singular, t.unit.bunch.plural),
QuantityKind.plant => (t.unit.plant.singular, t.unit.plant.plural),
QuantityKind.pot => (t.unit.pot.singular, t.unit.pot.plural),
QuantityKind.tray => (t.unit.tray.singular, t.unit.tray.plural),
QuantityKind.grams => (t.unit.grams.singular, t.unit.grams.plural),
QuantityKind.count => (t.unit.count.singular, t.unit.count.plural),
};
/// The unit's singular name — for chips/scale items ("cob", "packet").
String unitLabel(Translations t, QuantityKind kind) => _unit(t, kind).$1;
/// A full, human quantity: "3 cobs", "2 packets", "a few".
String quantityDisplay(Translations t, Quantity q) {
final n = q.count;
final (one, other) = _unit(t, q.kind);
if (!q.kind.countable || n == null) {
// Vibe amount, or a countable unit with no number → bare unit name.
return (n != null && n != 1) ? other : one;
}
return '${_formatCount(n)} ${n == 1 ? one : other}';
}
String _formatCount(num n) =>
n == n.roundToDouble() ? n.toInt().toString() : n.toString();