Skip to content

Commit 693ffbc

Browse files
committed
Release 2.11.0: household toggle to hide AI summaries and debt suggestion
1 parent 3532bd3 commit 693ffbc

6 files changed

Lines changed: 64 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 2.11.0: 2026-05-29
2+
3+
* Add household setting to hide AI summaries and debt suggestion everywhere via settings toggle
4+
15
### 2.10.0: 2026-05-29
26

37
* Add automatic daily YNAB sync via cron with a configurable hour setting, default 6

src/app/(app)/debts/page.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default function DebtsPage() {
8989
const [extraPayment, setExtraPayment] = useState(50);
9090
const [aiSuggestion, setAiSuggestion] = useState<string | null>(null);
9191
const [aiLoading, setAiLoading] = useState(false);
92+
const [aiHidden, setAiHidden] = useState(false);
9293
const [saving, setSaving] = useState<string | null>(null);
9394
const [dragIdx, setDragIdx] = useState<number | null>(null);
9495

@@ -105,14 +106,23 @@ export default function DebtsPage() {
105106
.catch((err) => console.error("[debts] Load error:", err))
106107
.finally(() => setLoading(false));
107108

108-
// Load cached suggestion separately (don't block page, cache only)
109-
fetch("/api/debts/suggestion?cache_only=1")
109+
// Honor household AI-summaries off-switch and load cached suggestion if not disabled
110+
fetch("/api/household")
110111
.then((r) => r.json())
111-
.then((data) => {
112-
if (data.suggestion) {
113-
console.info("[debts] Loaded cached AI suggestion");
114-
setAiSuggestion(data.suggestion);
112+
.then((h) => {
113+
if (h.settings?.ai_summaries_disabled === "1") {
114+
setAiHidden(true);
115+
return;
115116
}
117+
return fetch("/api/debts/suggestion?cache_only=1")
118+
.then((r) => r.json())
119+
.then((data) => {
120+
if (data.disabled) { setAiHidden(true); return; }
121+
if (data.suggestion) {
122+
console.info("[debts] Loaded cached AI suggestion");
123+
setAiSuggestion(data.suggestion);
124+
}
125+
});
116126
})
117127
.catch(() => {});
118128
}, []);
@@ -233,6 +243,7 @@ export default function DebtsPage() {
233243
</div>
234244

235245
{/* AI suggestion */}
246+
{!aiHidden && (
236247
<Card className="ai-summary-card">
237248
<div className="ai-summary-header">
238249
<div className="ai-summary-icon"><Sparkles /></div>
@@ -252,6 +263,7 @@ export default function DebtsPage() {
252263
</p>
253264
)}
254265
</Card>
266+
)}
255267

256268
{/* Debt list with editable fields */}
257269
{debts.length > 0 && (

src/app/(app)/settings/page.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export default function SettingsPage() {
6060
const [reserveSaved, setReserveSaved] = useState(false);
6161
const [ynabSyncHour, setYnabSyncHour] = useState("6");
6262
const [syncHourSaved, setSyncHourSaved] = useState(false);
63+
const [aiSummariesDisabled, setAiSummariesDisabled] = useState(false);
64+
const [aiSummariesSaved, setAiSummariesSaved] = useState(false);
6365
const [billsModeSaved, setBillsModeSaved] = useState(false);
6466
const [thresholds, setThresholds] = useState({ tight: "20", normal: "30", good: "50" });
6567
const [thresholdsSaved, setThresholdsSaved] = useState(false);
@@ -133,6 +135,9 @@ export default function SettingsPage() {
133135
if (householdData.settings?.ynab_sync_hour !== undefined) {
134136
setYnabSyncHour(String(householdData.settings.ynab_sync_hour));
135137
}
138+
if (householdData.settings?.ai_summaries_disabled !== undefined) {
139+
setAiSummariesDisabled(householdData.settings.ai_summaries_disabled === "1");
140+
}
136141
if (householdData.settings?.budget_threshold_tight) setThresholds((p) => ({ ...p, tight: householdData.settings.budget_threshold_tight }));
137142
if (householdData.settings?.budget_threshold_normal) setThresholds((p) => ({ ...p, normal: householdData.settings.budget_threshold_normal }));
138143
if (householdData.settings?.budget_threshold_good) setThresholds((p) => ({ ...p, good: householdData.settings.budget_threshold_good }));
@@ -1088,6 +1093,30 @@ export default function SettingsPage() {
10881093
</CardTitle>
10891094
</CardHeader>
10901095
<CardContent className="form-stack">
1096+
<div className="form-field">
1097+
<Label>{locale === "fi" ? "Piilota AI-yhteenvedot" : "Hide AI summaries"}</Label>
1098+
<div className="settings-row">
1099+
<Switch
1100+
checked={aiSummariesDisabled}
1101+
onCheckedChange={async (v) => {
1102+
setAiSummariesDisabled(v);
1103+
await fetch("/api/household", {
1104+
method: "POST",
1105+
headers: { "Content-Type": "application/json" },
1106+
body: JSON.stringify({ ai_summaries_disabled: v ? "1" : "0" }),
1107+
});
1108+
setAiSummariesSaved(true);
1109+
setTimeout(() => setAiSummariesSaved(false), 2000);
1110+
}}
1111+
/>
1112+
{aiSummariesSaved && <span className="settings-saved">{t.common.saved}</span>}
1113+
</div>
1114+
<p className="settings-help">
1115+
{locale === "fi"
1116+
? "Piilottaa AI-yhteenvedot ja velkasuosituksen kaikkialta sovelluksessa. Asetus on jaettu kotitalouden kaikille käyttäjille."
1117+
: "Hides AI summaries and the debt suggestion across the app. Setting is shared with all household users."}
1118+
</p>
1119+
</div>
10911120
{([
10921121
{ key: "chat" as const, dbKey: "prompt_chat_guidelines", label: locale === "fi" ? "Keskustelun ohjeet" : "Chat guidelines" },
10931122
{ key: "summary" as const, dbKey: "prompt_summary_instructions", label: locale === "fi" ? "Yhteenvedon ohjeet" : "Summary instructions" },

src/app/api/debts/suggestion/route.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { NextResponse } from "next/server";
33
import { getSession } from "@/lib/auth";
44
import { getDb } from "@/lib/db";
5+
import { getHouseholdSetting } from "@/lib/household";
56
import { spawn } from "child_process";
67

78
export async function GET(request: Request) {
@@ -13,6 +14,11 @@ export async function GET(request: Request) {
1314
const cacheOnly = url.searchParams.get("cache_only") === "1";
1415
const refresh = url.searchParams.get("refresh") === "1";
1516

17+
// Household-wide off switch for AI summaries
18+
if (getHouseholdSetting("ai_summaries_disabled") === "1") {
19+
return NextResponse.json({ suggestion: null, disabled: true });
20+
}
21+
1622
const db = getDb();
1723

1824
// Check for cached suggestion
@@ -55,7 +61,6 @@ export async function GET(request: Request) {
5561
return `${a.name}: ${balance.toFixed(0)} euros${rate > 0 ? ` (${rate}% APR)` : ""}${payment > 0 ? `, ${payment} euros/month` : ""}${dueDay > 0 ? ` (due ${dueDay}th)` : ""}`;
5662
});
5763

58-
const { getHouseholdSetting } = await import("@/lib/household");
5964
const householdProfile = getHouseholdSetting("household_profile") || "";
6065
const { DEFAULT_DEBT_INSTRUCTIONS } = await import("@/lib/ai/default-prompts");
6166
const debtInstructions = getHouseholdSetting("prompt_debt_instructions") || DEFAULT_DEBT_INSTRUCTIONS;

src/app/api/household/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export async function GET() {
2929
reserve_next_month_saving: settings.reserve_next_month_saving || "0",
3030
last_reservation_month: settings.last_reservation_month || "",
3131
ynab_sync_hour: settings.ynab_sync_hour || "6",
32+
ai_summaries_disabled: settings.ai_summaries_disabled || "0",
3233
budget_threshold_tight: settings.budget_threshold_tight || "20",
3334
budget_threshold_normal: settings.budget_threshold_normal || "30",
3435
budget_threshold_good: settings.budget_threshold_good || "50",

src/app/api/summary/route.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextResponse } from "next/server";
22
import { getSession } from "@/lib/auth";
33
import { getDb } from "@/lib/db";
4-
import { getYnabToken, getYnabBudgetId } from "@/lib/household";
4+
import { getYnabToken, getYnabBudgetId, getHouseholdSetting } from "@/lib/household";
55
import { DEFAULT_SUMMARY_INSTRUCTIONS } from "@/lib/ai/default-prompts";
66
import { spawn } from "child_process";
77

@@ -17,6 +17,11 @@ export async function GET(request: Request) {
1717
const url = new URL(request.url);
1818
const forceRefresh = url.searchParams.get("refresh") === "1";
1919

20+
// Household-wide off switch for AI summaries
21+
if (getHouseholdSetting("ai_summaries_disabled") === "1") {
22+
return NextResponse.json({ summary: null, disabled: true });
23+
}
24+
2025
const db = getDb();
2126

2227
// Ensure table exists
@@ -112,7 +117,6 @@ export async function GET(request: Request) {
112117
? "Respond in Finnish."
113118
: "Respond in English.";
114119

115-
const { getHouseholdSetting } = await import("@/lib/household");
116120
const householdProfile = getHouseholdSetting("household_profile") || "";
117121

118122
const daysPassed = now.getDate();

0 commit comments

Comments
 (0)