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.
This commit is contained in:
parent
48e9d15772
commit
3942975dba
26 changed files with 4220 additions and 315 deletions
|
|
@ -2,46 +2,49 @@ import 'package:commons_core/commons_core.dart';
|
|||
|
||||
import '../i18n/strings.g.dart';
|
||||
|
||||
/// Localized display label for a [QuantityKind]. The enum name is the stable
|
||||
/// storage key; the label is always resolved through i18n (never hardcoded).
|
||||
String quantityKindLabel(Translations t, QuantityKind kind) {
|
||||
final q = t.quantityKind;
|
||||
switch (kind) {
|
||||
case QuantityKind.aFew:
|
||||
return q.aFew;
|
||||
case QuantityKind.some:
|
||||
return q.some;
|
||||
case QuantityKind.plenty:
|
||||
return q.plenty;
|
||||
case QuantityKind.handful:
|
||||
return q.handful;
|
||||
case QuantityKind.pinch:
|
||||
return q.pinch;
|
||||
case QuantityKind.jar:
|
||||
return q.jar;
|
||||
case QuantityKind.packet:
|
||||
return q.packet;
|
||||
case QuantityKind.cob:
|
||||
return q.cob;
|
||||
case QuantityKind.head:
|
||||
return q.head;
|
||||
case QuantityKind.pod:
|
||||
return q.pod;
|
||||
case QuantityKind.ear:
|
||||
return q.ear;
|
||||
case QuantityKind.fruit:
|
||||
return q.fruit;
|
||||
case QuantityKind.bulb:
|
||||
return q.bulb;
|
||||
case QuantityKind.tuber:
|
||||
return q.tuber;
|
||||
case QuantityKind.seedHead:
|
||||
return q.seedHead;
|
||||
case QuantityKind.bunch:
|
||||
return q.bunch;
|
||||
case QuantityKind.grams:
|
||||
return q.grams;
|
||||
case QuantityKind.count:
|
||||
return q.count;
|
||||
/// 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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue