diff options
| author | soryu <soryu@soryu.co> | 2026-01-15 03:37:44 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-15 03:37:44 +0000 |
| commit | 764bd28d08ceaef03cd4050f9568a62d77bbcfca (patch) | |
| tree | dbd83ea7d213902f2b8021acc98798b6f3545946 /makima/frontend/src/routes | |
| parent | eeafe072bc6bb81459f7d087b48fc921afe9cc11 (diff) | |
| download | soryu-764bd28d08ceaef03cd4050f9568a62d77bbcfca.tar.gz soryu-764bd28d08ceaef03cd4050f9568a62d77bbcfca.zip | |
Add repository history feature to store and suggest previously used repositories (#18)
- Add repository_history table migration with repo_type, repo_path, use_count, last_used_at
- Add RepositoryHistoryEntry model and CRUD database functions
- Create API endpoints: GET/POST/DELETE /api/v1/repository-history, GET /api/v1/repository-history/suggestions
- Update add_remote_repository and add_local_repository handlers to automatically track history
- Update frontend API with repository history types and functions
- Add Repository History section to Settings page with list of entries and delete functionality
- Add suggestions dropdown to RepositoryPanel when entering new repository URL/path
- Suggestions filter by repo type (remote vs local) and match on user input
Test plan:
- Add a remote repository to a contract - verify it appears in Settings history
- Add a local repository to a contract - verify it appears in Settings history
- Add same repository again - verify use_count increments, not duplicate
- When adding new repository, verify suggestions appear based on history
- Delete a history entry from Settings - verify it's removed
- Verify suggestions only show matching type (remote for remote, local for local)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/routes')
| -rw-r--r-- | makima/frontend/src/routes/settings.tsx | 119 |
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 */} |
