v0.1.35 - Ingredient categories: created categories now available across all rows
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "hunter-backend"
|
name = "hunter-backend"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
description = "Costing platform MVP backend (API for Hunter)"
|
description = "Costing platform MVP backend (API for Hunter)"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "hunter-app",
|
"name": "hunter-app",
|
||||||
"version": "0.1.34",
|
"version": "0.1.35",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hunter-app",
|
"name": "hunter-app",
|
||||||
"version": "0.1.34",
|
"version": "0.1.35",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "^5.2.8",
|
"@fontsource/inter": "^5.2.8",
|
||||||
"lucide-svelte": "^1.0.1"
|
"lucide-svelte": "^1.0.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hunter-app",
|
"name": "hunter-app",
|
||||||
"version": "0.1.34",
|
"version": "0.1.35",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ export type ChangelogEntry = {
|
|||||||
export const APP_VERSION: string = packageInfo.version;
|
export const APP_VERSION: string = packageInfo.version;
|
||||||
|
|
||||||
export const changelog: ChangelogEntry[] = [
|
export const changelog: ChangelogEntry[] = [
|
||||||
|
{
|
||||||
|
version: '0.1.35',
|
||||||
|
date: '2026-06-21',
|
||||||
|
highlights: [
|
||||||
|
'App: Bug fixes & improvements.',
|
||||||
|
'App: General improvements.'
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: '0.1.34',
|
version: '0.1.34',
|
||||||
date: '2026-06-21',
|
date: '2026-06-21',
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
placeholder = 'Category',
|
placeholder = 'Category',
|
||||||
inputId,
|
inputId,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
ariaLabel = 'Category'
|
ariaLabel = 'Category',
|
||||||
|
oncreate
|
||||||
}: {
|
}: {
|
||||||
value?: string;
|
value?: string;
|
||||||
options?: string[];
|
options?: string[];
|
||||||
@@ -22,6 +23,9 @@
|
|||||||
inputId?: string;
|
inputId?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
|
/** Fired when the user deliberately creates a brand-new category, so the
|
||||||
|
* parent can keep it available to every other row. */
|
||||||
|
oncreate?: (category: string) => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
// `query` is the ephemeral search text; `value` is the committed category.
|
// `query` is the ephemeral search text; `value` is the committed category.
|
||||||
@@ -80,7 +84,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createNew() {
|
function createNew() {
|
||||||
value = trimmed;
|
const created = trimmed;
|
||||||
|
value = created;
|
||||||
|
oncreate?.(created);
|
||||||
closeMenu();
|
closeMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,14 +174,28 @@
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Categories the user has explicitly created via the combobox this session.
|
||||||
|
// Tracked separately from row values so a freshly-created category stays
|
||||||
|
// available to every row even before it's been saved to (or assigned on) any
|
||||||
|
// ingredient — otherwise it would vanish as soon as you moved off the row.
|
||||||
|
let createdCategories = $state<string[]>([]);
|
||||||
|
|
||||||
|
function registerCategory(category: string) {
|
||||||
|
const name = category.trim();
|
||||||
|
if (!name) return;
|
||||||
|
if (createdCategories.some((existing) => existing.toLowerCase() === name.toLowerCase())) return;
|
||||||
|
createdCategories = [...createdCategories, name];
|
||||||
|
}
|
||||||
|
|
||||||
// Existing categories, offered as autocomplete suggestions so spelling stays
|
// Existing categories, offered as autocomplete suggestions so spelling stays
|
||||||
// consistent across ingredients.
|
// consistent across ingredients.
|
||||||
const knownCategories = $derived(
|
const knownCategories = $derived(
|
||||||
Array.from(
|
Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
rows
|
[
|
||||||
.map((row) => (row.draft_category || row.category || '').trim())
|
...rows.map((row) => (row.draft_category || row.category || '').trim()),
|
||||||
.filter((value) => value.length > 0)
|
...createdCategories.map((value) => value.trim())
|
||||||
|
].filter((value) => value.length > 0)
|
||||||
)
|
)
|
||||||
).sort((a, b) => a.localeCompare(b))
|
).sort((a, b) => a.localeCompare(b))
|
||||||
);
|
);
|
||||||
@@ -298,7 +312,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Category</span>
|
<span>Category</span>
|
||||||
<CategoryCombobox bind:value={newIngredient.category} options={knownCategories} placeholder="e.g. Grains" inputId="new-ingredient-category" />
|
<CategoryCombobox bind:value={newIngredient.category} options={knownCategories} placeholder="e.g. Grains" inputId="new-ingredient-category" oncreate={registerCategory} />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Rounding</span>
|
<span>Rounding</span>
|
||||||
@@ -368,7 +382,7 @@
|
|||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<span class="cell-label">Category</span>
|
<span class="cell-label">Category</span>
|
||||||
<CategoryCombobox bind:value={row.draft_category} options={knownCategories} placeholder="—" ariaLabel={`Category for ${row.name}`} />
|
<CategoryCombobox bind:value={row.draft_category} options={knownCategories} placeholder="—" ariaLabel={`Category for ${row.name}`} oncreate={registerCategory} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
|
|||||||
Reference in New Issue
Block a user