"use client"; import { useState, useCallback, useMemo } from "react"; import { IconPlus, IconTrash, IconRefresh } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { CardContent } from "@kandev/ui/card"; import { Input } from "@kandev/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kandev/ui/select"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@kandev/ui/tabs"; import { useToast } from "@/components/toast-provider"; import { SettingsSection } from "@/components/settings/settings-section"; import { SettingsCard } from "@/components/settings/settings-card"; import { useSettingsSaveContributor } from "@/components/settings/settings-save-provider"; import { useDefaultQueryPresets, toStored, type StoredQueryPreset, } from "@/components/github/my-github/use-default-query-presets"; import { PR_PRESETS as BUILTIN_PR_PRESETS, ISSUE_PRESETS as BUILTIN_ISSUE_PRESETS, } from "@/components/github/my-github/search-bar"; import { useTranslation } from "react-i18next"; import { controlSizingClassName } from "@kandev/ui/control-sizing"; import { settingsActionClassName, settingsControlClassName, } from "@/components/settings/settings-control"; // `label` is PERSISTED as part of `StoredQueryPreset` (github_default_query_presets) // and is editable in the row below, so it must stay locale-neutral — see // `newPreset` in action-presets-section.tsx for the same contract. function newPreset(): StoredQueryPreset { // i18n-exempt: persisted, editable preset label. See the comment above. return { value: `q_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`, label: "New query", filter: "", group: "inbox", }; } function QueryRow({ preset, baseline, onPatch, onRemove, }: { preset: StoredQueryPreset; baseline?: StoredQueryPreset; onPatch: (patch: Partial) => void; onRemove: () => void; }) { const { t } = useTranslation(); return (
{t("github:label")} onPatch({ label: e.target.value })} />
{t("github:query")} onPatch({ filter: e.target.value })} />
{t("github:group")}
); } function QueryEditor({ presets, baseline, onChange, addLabel, }: { presets: StoredQueryPreset[]; baseline: StoredQueryPreset[]; onChange: (presets: StoredQueryPreset[]) => void; addLabel: string; }) { const patch = useCallback( (index: number, change: Partial) => { onChange(presets.map((p, i) => (i === index ? { ...p, ...change } : p))); }, [presets, onChange], ); const remove = useCallback( (index: number) => onChange(presets.filter((_, i) => i !== index)), [presets, onChange], ); const add = useCallback(() => onChange([...presets, newPreset()]), [presets, onChange]); return (
{presets.map((preset, index) => ( candidate.value === preset.value)} onPatch={(p) => patch(index, p)} onRemove={() => remove(index)} /> ))}
); } function useDefaultQueryDrafts(workspaceId?: string) { const { t } = useTranslation(); const { toast } = useToast(); const { prPresets, issuePresets, save, reset, isCustomized, isReady } = useDefaultQueryPresets( workspaceId ?? null, ); const [prDraft, setPrDraft] = useState(prPresets); const [issueDraft, setIssueDraft] = useState(issuePresets); const [prBaseline, setPrBaseline] = useState(prPresets); const [issueBaseline, setIssueBaseline] = useState(issuePresets); const [resetRequested, setResetRequested] = useState(false); const dirty = useMemo( () => resetRequested || JSON.stringify(prBaseline) !== JSON.stringify(prDraft) || JSON.stringify(issueBaseline) !== JSON.stringify(issueDraft), [prBaseline, issueBaseline, prDraft, issueDraft, resetRequested], ); const [syncedPr, setSyncedPr] = useState(prPresets); const [syncedIssue, setSyncedIssue] = useState(issuePresets); if (prPresets !== syncedPr && !dirty) { setSyncedPr(prPresets); setPrBaseline(prPresets); setPrDraft(prPresets); setResetRequested(false); } if (issuePresets !== syncedIssue && !dirty) { setSyncedIssue(issuePresets); setIssueBaseline(issuePresets); setIssueDraft(issuePresets); setResetRequested(false); } const handleSave = useCallback(async () => { const submittedPr = prDraft; const submittedIssue = issueDraft; try { if (resetRequested) await reset(); else await save({ pr: submittedPr, issue: submittedIssue }); setPrBaseline(submittedPr); setIssueBaseline(submittedIssue); setPrDraft((current) => JSON.stringify(current) === JSON.stringify(submittedPr) ? submittedPr : current, ); setIssueDraft((current) => JSON.stringify(current) === JSON.stringify(submittedIssue) ? submittedIssue : current, ); setResetRequested(false); toast({ description: t("github:defaultQueriesSaved"), variant: "success" }); } catch { toast({ description: t("github:failedToSaveDefaultQueries"), variant: "error" }); throw new Error("Failed to save default queries"); } }, [issueDraft, prDraft, reset, resetRequested, save, t, toast]); const handleReset = useCallback(() => { setPrDraft(toStored(BUILTIN_PR_PRESETS)); setIssueDraft(toStored(BUILTIN_ISSUE_PRESETS)); setResetRequested(true); }, []); const discard = useCallback(() => { setPrDraft(prBaseline); setIssueDraft(issueBaseline); setResetRequested(false); }, [prBaseline, issueBaseline]); const defaultPr = toStored(BUILTIN_PR_PRESETS); const defaultIssue = toStored(BUILTIN_ISSUE_PRESETS); const atDefaults = JSON.stringify(prDraft) === JSON.stringify(defaultPr) && JSON.stringify(issueDraft) === JSON.stringify(defaultIssue); useSettingsSaveContributor({ id: `github-default-queries:${workspaceId ?? "global"}`, revision: JSON.stringify([prDraft, issueDraft]), isDirty: dirty, canSave: isReady, invalidReason: isReady ? undefined : t("github:defaultQueriesAreStillLoading"), save: handleSave, discard, }); return { prDraft, issueDraft, prBaseline, issueBaseline, dirty, setPrDraft: (next: StoredQueryPreset[]) => { setResetRequested(false); setPrDraft(next); }, setIssueDraft: (next: StoredQueryPreset[]) => { setResetRequested(false); setIssueDraft(next); }, reset: handleReset, atDefaults, resetDisabled: atDefaults && !isCustomized, }; } export function DefaultQueriesSection({ workspaceId }: { workspaceId?: string }) { const { t } = useTranslation(); const drafts = useDefaultQueryDrafts(workspaceId); return ( } > {t("github:pullRequests")} {t("github:issues")} ); }