summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes/settings.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/routes/settings.tsx')
-rw-r--r--makima/frontend/src/routes/settings.tsx119
1 files changed, 119 insertions, 0 deletions
diff --git a/makima/frontend/src/routes/settings.tsx b/makima/frontend/src/routes/settings.tsx
index 7ca40ba..d3f4c1b 100644
--- a/makima/frontend/src/routes/settings.tsx
+++ b/makima/frontend/src/routes/settings.tsx
@@ -11,9 +11,12 @@ import {
changeEmail,
deleteAccount,
listDaemons,
+ listRepositoryHistory,
+ deleteRepositoryHistory,
type ApiKeyInfo,
type CreateApiKeyResponse,
type Daemon,
+ type RepositoryHistoryEntry,
} from "../lib/api";
// =============================================================================
@@ -304,9 +307,16 @@ export default function SettingsPage() {
const [daemonsLoading, setDaemonsLoading] = useState(true);
const [daemonsError, setDaemonsError] = useState<string | null>(null);
+ // Repository history state
+ const [repoHistory, setRepoHistory] = useState<RepositoryHistoryEntry[]>([]);
+ const [repoHistoryLoading, setRepoHistoryLoading] = useState(true);
+ const [repoHistoryError, setRepoHistoryError] = useState<string | null>(null);
+ const [deletingRepoId, setDeletingRepoId] = useState<string | null>(null);
+
useEffect(() => {
loadApiKey();
loadDaemons();
+ loadRepoHistory();
}, []);
// Auto-refresh daemons every 30 seconds
@@ -342,6 +352,30 @@ export default function SettingsPage() {
}
};
+ const loadRepoHistory = async () => {
+ try {
+ setRepoHistoryError(null);
+ const response = await listRepositoryHistory();
+ setRepoHistory(response.entries);
+ } catch (err) {
+ setRepoHistoryError(err instanceof Error ? err.message : "Failed to load repository history");
+ } finally {
+ setRepoHistoryLoading(false);
+ }
+ };
+
+ const handleDeleteRepoHistory = async (id: string) => {
+ try {
+ setDeletingRepoId(id);
+ await deleteRepositoryHistory(id);
+ setRepoHistory((prev) => prev.filter((entry) => entry.id !== id));
+ } catch (err) {
+ setRepoHistoryError(err instanceof Error ? err.message : "Failed to delete entry");
+ } finally {
+ setDeletingRepoId(null);
+ }
+ };
+
const handleCreate = async () => {
try {
setActionLoading(true);
@@ -692,6 +726,91 @@ export default function SettingsPage() {
</div>
)}
</section>
+
+ {/* Repository History */}
+ <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4">
+ <div className="flex items-center justify-between mb-3 pb-2 border-b border-[rgba(117,170,252,0.15)]">
+ <div className="flex items-center gap-2">
+ <h2 className="text-[11px] font-mono uppercase tracking-wide text-[#8899aa]">
+ Repository History
+ </h2>
+ {repoHistory.length > 0 && (
+ <span className="text-[10px] font-mono text-[#556677]">
+ ({repoHistory.length} saved)
+ </span>
+ )}
+ </div>
+ <button
+ onClick={loadRepoHistory}
+ disabled={repoHistoryLoading}
+ className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] disabled:opacity-50"
+ title="Refresh"
+ >
+ {repoHistoryLoading ? "..." : "↻"}
+ </button>
+ </div>
+ <p className="text-[#7788aa] font-mono text-[10px] mb-3">
+ Previously used repositories will appear as suggestions when adding repos to contracts.
+ </p>
+
+ {repoHistoryError && <ErrorAlert>{repoHistoryError}</ErrorAlert>}
+
+ {repoHistoryLoading && repoHistory.length === 0 ? (
+ <p className="text-[#7788aa] font-mono text-xs">Loading...</p>
+ ) : repoHistory.length === 0 ? (
+ <div className="text-center py-4">
+ <p className="text-[#7788aa] font-mono text-xs mb-2">No repository history</p>
+ <p className="text-[#556677] font-mono text-[10px]">
+ Add repositories to contracts to build history
+ </p>
+ </div>
+ ) : (
+ <div className="space-y-2 max-h-[300px] overflow-y-auto">
+ {repoHistory.map((entry) => (
+ <div
+ key={entry.id}
+ className="border border-[rgba(117,170,252,0.15)] bg-[#0a1525] p-3"
+ >
+ <div className="flex items-start justify-between gap-2">
+ <div className="flex-1 min-w-0">
+ <div className="flex items-center gap-2 mb-1">
+ <span className="font-mono text-xs text-[#9bc3ff] truncate">
+ {entry.name}
+ </span>
+ <span
+ className={`text-[10px] font-mono uppercase px-1.5 py-0.5 border ${
+ entry.sourceType === "remote"
+ ? "text-cyan-400 border-cyan-700/50 bg-cyan-900/20"
+ : "text-green-400 border-green-700/50 bg-green-900/20"
+ }`}
+ >
+ {entry.sourceType}
+ </span>
+ </div>
+ <div className="font-mono text-[10px] text-[#556677] truncate mb-1">
+ {entry.repositoryUrl || entry.localPath}
+ </div>
+ <div className="flex items-center gap-3 font-mono text-[10px] text-[#7788aa]">
+ <span>Used {entry.useCount}×</span>
+ <span>
+ Last: {new Date(entry.lastUsedAt).toLocaleDateString()}
+ </span>
+ </div>
+ </div>
+ <button
+ onClick={() => handleDeleteRepoHistory(entry.id)}
+ disabled={deletingRepoId === entry.id}
+ className="p-1 font-mono text-[10px] text-[#555] hover:text-red-400 transition-colors disabled:opacity-50"
+ title="Delete from history"
+ >
+ {deletingRepoId === entry.id ? "..." : "×"}
+ </button>
+ </div>
+ </div>
+ ))}
+ </div>
+ )}
+ </section>
</div>
{/* Right Column */}