feat: add incident delete and clarify AI assistant settings
Release APK / build (push) Has been cancelled

Wire deleteIncident() into IncidentDetailScreen with a destructive confirm
dialog. Expand the AI settings section with base URL + model fields and an
explanatory card documenting the OpenAI-compatible use case (Ollama / OpenAI).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 13:24:56 +02:00
parent fee283b5d6
commit 7e42d3a26f
2 changed files with 88 additions and 10 deletions
+37 -1
View File
@@ -10,7 +10,7 @@ import {
import { useLocalSearchParams, router, useNavigation } from "expo-router";
import * as Clipboard from "expo-clipboard";
import * as SecureStore from "expo-secure-store";
import { getIncidentById, updateIncident } from "@/lib/db";
import { getIncidentById, updateIncident, deleteIncident } from "@/lib/db";
import { renderMarkdown } from "@/lib/markdown";
import { pushIncidentToGit } from "@/lib/git";
import type { GitProvider } from "@/lib/git";
@@ -54,6 +54,25 @@ export default function IncidentDetailScreen() {
}
}, [id]);
async function handleDelete() {
if (!incident) return;
Alert.alert(
"Delete incident?",
"This action cannot be undone.",
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: async () => {
await deleteIncident(incident.id);
router.replace("/");
},
},
]
);
}
async function handleResolve() {
if (!incident) return;
await updateIncident(incident.id, { status: "resolved" });
@@ -291,6 +310,23 @@ export default function IncidentDetailScreen() {
Edit
</Text>
</Pressable>
<Pressable
onPress={handleDelete}
style={{
backgroundColor: "#ef444410",
borderWidth: 1,
borderColor: "#ef4444",
borderRadius: 10,
padding: 14,
alignItems: "center",
marginTop: 8,
}}
>
<Text style={{ color: "#ef4444", fontWeight: "600", fontSize: 15 }}>
Delete Incident
</Text>
</Pressable>
</View>
</ScrollView>
);
+45 -3
View File
@@ -17,6 +17,8 @@ const KEYS = {
HOME_VIEW: "pref_home_view",
AI_ENABLED: "pref_ai_enabled",
AI_KEY: "pref_ai_key",
AI_BASE_URL: "pref_ai_base_url",
AI_MODEL: "pref_ai_model",
MD_TEMPLATE: "md_template",
GIT_PROVIDER: "git_provider",
// Gitea
@@ -75,6 +77,8 @@ export default function SettingsScreen() {
const [homeView, setHomeView] = useState<"dashboard" | "capture">("dashboard");
const [aiEnabled, setAiEnabled] = useState(false);
const [aiKey, setAiKey] = useState("");
const [aiBaseUrl, setAiBaseUrl] = useState("");
const [aiModel, setAiModel] = useState("");
const [mdTemplate, setMdTemplate] = useState(DEFAULT_TEMPLATE);
const [saved, setSaved] = useState(false);
@@ -97,12 +101,14 @@ export default function SettingsScreen() {
useEffect(() => {
(async () => {
const [im, hv, ai, key, tpl, gp, gu, gt, go, gr, ghu, ghown, ghrepo, glu, glt, glo, glr] =
const [im, hv, ai, key, aiUrl, aiMdl, tpl, gp, gu, gt, go, gr, ghu, ghown, ghrepo, glu, glt, glo, glr] =
await Promise.all([
SecureStore.getItemAsync(KEYS.INPUT_MODE),
SecureStore.getItemAsync(KEYS.HOME_VIEW),
SecureStore.getItemAsync(KEYS.AI_ENABLED),
SecureStore.getItemAsync(KEYS.AI_KEY),
SecureStore.getItemAsync(KEYS.AI_BASE_URL),
SecureStore.getItemAsync(KEYS.AI_MODEL),
SecureStore.getItemAsync(KEYS.MD_TEMPLATE),
SecureStore.getItemAsync(KEYS.GIT_PROVIDER),
SecureStore.getItemAsync(KEYS.GITEA_URL),
@@ -121,6 +127,8 @@ export default function SettingsScreen() {
if (hv) setHomeView(hv as "dashboard" | "capture");
if (ai) setAiEnabled(ai === "true");
if (key) setAiKey(key);
if (aiUrl) setAiBaseUrl(aiUrl);
if (aiMdl) setAiModel(aiMdl);
if (tpl) setMdTemplate(tpl);
if (gp) setGitProvider(gp as GitProvider);
if (gu) setGiteaUrl(gu);
@@ -143,6 +151,8 @@ export default function SettingsScreen() {
SecureStore.setItemAsync(KEYS.HOME_VIEW, homeView),
SecureStore.setItemAsync(KEYS.AI_ENABLED, String(aiEnabled)),
SecureStore.setItemAsync(KEYS.AI_KEY, aiKey),
SecureStore.setItemAsync(KEYS.AI_BASE_URL, aiBaseUrl),
SecureStore.setItemAsync(KEYS.AI_MODEL, aiModel),
SecureStore.setItemAsync(KEYS.MD_TEMPLATE, mdTemplate),
SecureStore.setItemAsync(KEYS.GIT_PROVIDER, gitProvider),
SecureStore.setItemAsync(KEYS.GITEA_URL, giteaUrl),
@@ -300,20 +310,52 @@ export default function SettingsScreen() {
]}
/>
<Text style={SECTION_TITLE}>AI (optional)</Text>
<Text style={SECTION_TITLE}>AI Root Cause Assistant</Text>
<View
style={{
backgroundColor: Colors.surface,
borderRadius: 8,
padding: 12,
marginBottom: 14,
borderLeftWidth: 3,
borderLeftColor: Colors.text2,
}}
>
<Text style={{ color: Colors.text2, fontSize: 13, lineHeight: 20 }}>
When enabled, a button appears on the capture form to suggest a root cause and fix
based on the incident title and symptom. Uses any OpenAI-compatible API
set the base URL below to point at a local Ollama instance or leave empty
for OpenAI (api.openai.com).
</Text>
</View>
<ToggleRow
label="Enable AI assistance"
value={aiEnabled}
onValueChange={setAiEnabled}
/>
{aiEnabled && (
<>
<Field
label="API Base URL"
value={aiBaseUrl}
onChange={setAiBaseUrl}
placeholder="http://192.168.x.x:11434/v1 (empty = OpenAI)"
url
/>
<Field
label="Model"
value={aiModel}
onChange={setAiModel}
placeholder="gpt-4o-mini or llama3.2"
/>
<Field
label="API Key"
value={aiKey}
onChange={setAiKey}
placeholder="sk-..."
placeholder="sk-... (leave empty for Ollama)"
secure
/>
</>
)}
<Text style={SECTION_TITLE}>Git Repository</Text>