feat: add incident delete and clarify AI assistant settings
Release APK / build (push) Has been cancelled
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:
+37
-1
@@ -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>
|
||||
);
|
||||
|
||||
+51
-9
@@ -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 Key"
|
||||
value={aiKey}
|
||||
onChange={setAiKey}
|
||||
placeholder="sk-..."
|
||||
secure
|
||||
/>
|
||||
<>
|
||||
<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-... (leave empty for Ollama)"
|
||||
secure
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text style={SECTION_TITLE}>Git Repository</Text>
|
||||
|
||||
Reference in New Issue
Block a user