Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions caloreazi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.23.1

- שמונת מקורות הוספת הארוחה מוצגים באריחים גדולים וברורים בפריסה של שתי עמודות וארבע שורות.
- גובה האריחים מותאם אוטומטית למסכים נמוכים כדי לשמור את כל מסך ההוספה ללא גלילה אנכית.

## 1.23.0

- חלון הוספת הארוחה עוצב מחדש כמסך אחד: חיפוש, שמונה מקורות הוספה בשתי שורות וקרוסלת ארוחות אחרונות או נפוצות.
Expand Down
2 changes: 1 addition & 1 deletion caloreazi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN chmod +x /etc/services.d/app/run /etc/services.d/nginx/run /etc/services.d/p
&& rm -f /etc/nginx/http.d/default.conf

LABEL \
io.hass.version="1.23.0" \
io.hass.version="1.23.1" \
io.hass.type="app" \
io.hass.arch="amd64"

Expand Down
2 changes: 1 addition & 1 deletion caloreazi/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CALOREAZI 1.23.0
# CALOREAZI 1.23.1

Private Home Assistant nutrition coach with persistent onboarding, daily tracking, global Admin-managed AI, and AI meal-photo analysis.

Expand Down
2 changes: 1 addition & 1 deletion caloreazi/app/api/admin/health/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ export async function GET(request: Request) {
const saveAudits = (state.auditLog || []).filter((item: any) => item.action === "meal.created").slice(-500).map((item: any) => { try { return JSON.parse(item.details || "{}"); } catch { return {}; } });
const timedSaveAudits = saveAudits.filter((item: any) => Number(item.interactionDurationMs || item.durationMs || 0) > 0);
const quality = { averageAddSeconds: timedSaveAudits.length ? Math.round(timedSaveAudits.reduce((sum: number, item: any) => sum + Number(item.interactionDurationMs || item.durationMs), 0) / timedSaveAudits.length / 100) / 10 : null, recognitionApprovalRate: recognized.length ? Math.round(recognized.filter((meal: any) => !meal.updatedAt).length / recognized.length * 100) : null, correctionRate: recognized.length ? Math.round(recognized.filter((meal: any) => meal.updatedAt).length / recognized.length * 100) : null, highReliabilityRate: allMeals.length ? Math.round(allMeals.filter((meal: any) => Number(meal.nutritionReliability?.score || 0) >= 88).length / allMeals.length * 100) : null, duplicateBlocks: (state.auditLog || []).filter((item: any) => item.action === "meal.duplicate_blocked").length, failedAnalyses: (state.analysisJobs || []).filter((item: any) => item.status === "failed").length, aiCostPerMeal: monthlyMeals.length ? monthlyAiCost / monthlyMeals.length : 0, aiCostPerActiveUser: activeUserIds.size ? monthlyAiCost / activeUserIds.size : 0 };
return Response.json({ version: process.env.CALOREAZI_VERSION || "1.23.0", build: process.env.CALOREAZI_BUILD_COMMIT || "development", application: "ok", database, storage: "ok", ai: state.ai.encryptedKey ? "configured" : "attention", users: state.users.length, activeUsers: state.users.filter((user) => !user.disabled).length, activeSessions: (state.sessions || []).filter((item) => !item.revokedAt && new Date(item.expiresAt).getTime() > Date.now()).length, analysisJobs: { pending: (state.analysisJobs || []).filter((item) => ["pending", "processing"].includes(item.status)).length, failed: quality.failedAnalyses }, meals, quality, trashItems: state.trash.length, auditEvents: state.auditLog.length, aiRequests: usage.length, estimatedAiCost: usage.reduce((sum, item) => sum + Number(item.cost || 0), 0), byFeature });
return Response.json({ version: process.env.CALOREAZI_VERSION || "1.23.1", build: process.env.CALOREAZI_BUILD_COMMIT || "development", application: "ok", database, storage: "ok", ai: state.ai.encryptedKey ? "configured" : "attention", users: state.users.length, activeUsers: state.users.filter((user) => !user.disabled).length, activeSessions: (state.sessions || []).filter((item) => !item.revokedAt && new Date(item.expiresAt).getTime() > Date.now()).length, analysisJobs: { pending: (state.analysisJobs || []).filter((item) => ["pending", "processing"].includes(item.status)).length, failed: quality.failedAnalyses }, meals, quality, trashItems: state.trash.length, auditEvents: state.auditLog.length, aiRequests: usage.length, estimatedAiCost: usage.reduce((sum, item) => sum + Number(item.cost || 0), 0), byFeature });
}
7 changes: 7 additions & 0 deletions caloreazi/app/globals.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion caloreazi/app/health/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export function GET() {
return Response.json({ status: "ok", service: "caloreazi", version: "1.23.0", build: process.env.CALOREAZI_BUILD_COMMIT || "development" });
return Response.json({ status: "ok", service: "caloreazi", version: "1.23.1", build: process.env.CALOREAZI_BUILD_COMMIT || "development" });
}
20 changes: 14 additions & 6 deletions caloreazi/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ export default function Home() {
const [forgottenMeals, setForgottenMeals] = useState<any[]>([]);
const [forgottenStatus, setForgottenStatus] = useState("");
const [quickCategory, setQuickCategory] = useState("");
const [produceFilter, setProduceFilter] = useState<"vegetables" | "fruits">("vegetables");
const [quickSearch, setQuickSearch] = useState("");
const [onlineFoodResults, setOnlineFoodResults] = useState<any[]>([]);
const [onlineFoodStatus, setOnlineFoodStatus] = useState("");
Expand Down Expand Up @@ -4351,34 +4352,41 @@ export default function Home() {
{ label: "הקלט ארוחה", icon: "mic", action: () => { setQuickAddOpen(false); setVoiceOpen(true); } },
{ label: "מועדפים", icon: "star", action: () => { setQuickAddOpen(false); setFoodLibraryOpen(true); } },
{ label: "משקאות", icon: "drink", action: () => setQuickCategory("drinks") },
{ label: "ירקות ופירות", icon: "produce", action: () => setQuickCategory("produce") },
{ label: "ירקות ופירות", icon: "produce", action: () => { setProduceFilter("vegetables"); setQuickCategory("produce"); } },
].map((entry: any, index) => <button type="button" className={`quick-source source-${index + 1}`} onClick={entry.action} key={entry.label}><span><AppIcon name={entry.icon} /></span><strong>{entry.label}</strong></button>)}
</div>{quickRepeatMeals.length > 0 && <section className="quick-add-recents"><header><strong>ארוחות אחרונות ונפוצות</strong><small>להוספה חוזרת בלחיצה</small></header><div>{quickRepeatMeals.slice(0, 8).map(({ meal, count }) => <button type="button" key={meal.id || meal.name} disabled={busy} onClick={() => { setQuickAddOpen(false); repeatRecentMeal(meal); }}><span>+</span><strong>{meal.name}</strong><small>{Math.round(Number(meal.kcal))} קלוריות{count > 1 ? ` · ${count} פעמים` : ""}</small></button>)}</div></section>}</>
) : (
<>
<button
className="category-back"
onClick={() => { setQuickCategory(""); setQuickSearch(""); }}
aria-label="חזרה למסך הוספת ארוחה"
>
→ חזרה לקטגוריות
← חזרה להוספת ארוחה
</button>
{quickCategory === "produce" && (
<div className="produce-filter" role="tablist" aria-label="בחירת ירקות או פירות">
<button type="button" role="tab" aria-selected={produceFilter === "vegetables"} className={produceFilter === "vegetables" ? "active" : ""} onClick={() => setProduceFilter("vegetables")}>ירקות</button>
<button type="button" role="tab" aria-selected={produceFilter === "fruits"} className={produceFilter === "fruits" ? "active" : ""} onClick={() => setProduceFilter("fruits")}>פירות</button>
</div>
)}
<div className={`quick-food-grid ${quickCategory}`}>
{(quickCategory === "produce" ? [...quickFoods.vegetables.map((item: any) => ({ ...item, spriteCategory: "vegetables" })), ...quickFoods.fruits.map((item: any) => ({ ...item, spriteCategory: "fruits" }))] : quickFoods[quickCategory]).filter((item) => !quickSearch.trim() || `${item.name} ${item.portion}`.toLocaleLowerCase("he").includes(quickSearch.trim().toLocaleLowerCase("he"))).map((item: any, index: number) => (
{(quickCategory === "produce" ? quickFoods[produceFilter].map((item: any) => ({ ...item, spriteCategory: produceFilter })) : quickFoods[quickCategory]).filter((item) => !quickSearch.trim() || `${item.name} ${item.portion}`.toLocaleLowerCase("he").includes(quickSearch.trim().toLocaleLowerCase("he"))).map((item: any, index: number) => (
<button
key={`${item.name}-${item.portion}`}
onClick={() => selectQuickFood(item)}
>
<span
className="food-sprite"
style={foodSpriteStyle(item.spriteCategory || quickCategory, item.spriteCategory === "fruits" ? index - quickFoods.vegetables.length : index)}
style={foodSpriteStyle(item.spriteCategory || quickCategory, index)}
/>
<strong>{item.name}</strong>
<small>{item.portion}</small>
<b>{item.kcal} kcal</b>
</button>
))}
{(state.foods || [])
.filter((food) => (quickCategory === "produce" ? ["vegetables", "fruits"].includes(food.category) : food.category === quickCategory) && (!quickSearch.trim() || `${food.name} ${food.category || ""}`.toLocaleLowerCase("he").includes(quickSearch.trim().toLocaleLowerCase("he"))))
.filter((food) => (quickCategory === "produce" ? food.category === produceFilter : food.category === quickCategory) && (!quickSearch.trim() || `${food.name} ${food.category || ""}`.toLocaleLowerCase("he").includes(quickSearch.trim().toLocaleLowerCase("he"))))
.map((food) => (
<button
key={food.id}
Expand All @@ -4405,7 +4413,7 @@ export default function Home() {
onClick={() => {
setSaveToLibrary(true);
setGenerateFoodArtwork(true);
openManualMeal(quickCategory === "produce" ? "vegetables" : quickCategory);
openManualMeal(quickCategory === "produce" ? produceFilter : quickCategory);
}}
>
<span>+</span>
Expand Down
2 changes: 1 addition & 1 deletion caloreazi/config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "CALOREAZI"
version: "1.23.0"
version: "1.23.1"
slug: "caloreazi"
description: "AI-assisted nutrition tracking, made easy"
url: "https://github.com/r11a/CALOREAZI"
Expand Down
4 changes: 2 additions & 2 deletions caloreazi/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading