summaryrefslogtreecommitdiff
path: root/makima/frontend
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-16 15:45:42 +0000
committersoryu <soryu@soryu.co>2026-02-16 15:45:42 +0000
commit7d2079d7c13804766405af8044574bfc93a86897 (patch)
tree05212cb5cd472eff75ed54b9805a1d6ef5c7d922 /makima/frontend
parent29ec8e53f2acf56fe4a2cd02d352144c697a6afc (diff)
downloadsoryu-7d2079d7c13804766405af8044574bfc93a86897.tar.gz
soryu-7d2079d7c13804766405af8044574bfc93a86897.zip
Add PR button to directives
Diffstat (limited to 'makima/frontend')
-rw-r--r--makima/frontend/src/components/directives/DirectiveDetail.tsx16
-rw-r--r--makima/frontend/src/hooks/useDirectives.ts8
-rw-r--r--makima/frontend/src/lib/api.ts6
-rw-r--r--makima/frontend/src/routes/directives.tsx3
4 files changed, 32 insertions, 1 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx
index 9305e20..c8da7a0 100644
--- a/makima/frontend/src/components/directives/DirectiveDetail.tsx
+++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx
@@ -27,6 +27,7 @@ interface DirectiveDetailProps {
onRefresh: () => void;
onCleanupTasks: () => void;
onPickUpOrders: () => Promise<{ message: string; orderCount: number; taskId: string | null } | null>;
+ onCreatePR: () => Promise<void>;
}
export function DirectiveDetail({
@@ -43,12 +44,14 @@ export function DirectiveDetail({
onRefresh,
onCleanupTasks,
onPickUpOrders,
+ onCreatePR,
}: DirectiveDetailProps) {
const [editingGoal, setEditingGoal] = useState(false);
const [goalText, setGoalText] = useState(directive.goal);
const [visibleTaskIds, setVisibleTaskIds] = useState<Set<string> | null>(null);
const [pickingUpOrders, setPickingUpOrders] = useState(false);
const [pickUpResult, setPickUpResult] = useState<string | null>(null);
+ const [creatingPR, setCreatingPR] = useState(false);
// Sync goalText and reset editing state when directive changes
useEffect(() => {
@@ -333,6 +336,19 @@ export function DirectiveDetail({
Clean up tasks
</button>
)}
+ {completedSteps > 0 && !directive.completionTaskId && (
+ <button
+ type="button"
+ onClick={async () => {
+ setCreatingPR(true);
+ try { await onCreatePR(); } catch (e) { console.error("Failed to create PR:", e); } finally { setCreatingPR(false); }
+ }}
+ disabled={creatingPR}
+ className="text-[10px] font-mono text-emerald-400 hover:text-emerald-300 border border-emerald-800 rounded px-2 py-1 disabled:opacity-50"
+ >
+ {creatingPR ? "Creating..." : directive.prUrl ? "Update PR" : "Create PR"}
+ </button>
+ )}
<button
type="button"
onClick={handlePickUpOrders}
diff --git a/makima/frontend/src/hooks/useDirectives.ts b/makima/frontend/src/hooks/useDirectives.ts
index 7e26ec4..18544da 100644
--- a/makima/frontend/src/hooks/useDirectives.ts
+++ b/makima/frontend/src/hooks/useDirectives.ts
@@ -21,6 +21,7 @@ import {
updateDirectiveGoal,
cleanupDirectiveTasks,
pickUpOrders as pickUpOrdersApi,
+ createDirectivePR,
} from "../lib/api";
export function useDirectives() {
@@ -185,6 +186,12 @@ export function useDirective(id: string | undefined) {
return result;
}, [id, refresh]);
+ const createPR = useCallback(async () => {
+ if (!id) return;
+ await createDirectivePR(id);
+ await refresh();
+ }, [id, refresh]);
+
return {
directive, loading, error, refresh,
update, addStep, removeStep,
@@ -192,5 +199,6 @@ export function useDirective(id: string | undefined) {
completeStep, failStep, skipStep,
updateGoal, cleanupTasks,
pickUpOrders: pickUpOrdersFn,
+ createPR,
};
}
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index 467ee22..a496412 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -3262,6 +3262,12 @@ export interface PickUpOrdersResponse {
taskId: string | null;
}
+export async function createDirectivePR(id: string): Promise<DirectiveWithSteps> {
+ const res = await authFetch(`${API_BASE}/api/v1/directives/${id}/create-pr`, { method: "POST" });
+ if (!res.ok) throw new Error(`Failed to create PR: ${res.statusText}`);
+ return res.json();
+}
+
export async function pickUpOrders(directiveId: string): Promise<PickUpOrdersResponse> {
const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/pick-up-orders`, {
method: "POST",
diff --git a/makima/frontend/src/routes/directives.tsx b/makima/frontend/src/routes/directives.tsx
index b4ed0cc..2bb673c 100644
--- a/makima/frontend/src/routes/directives.tsx
+++ b/makima/frontend/src/routes/directives.tsx
@@ -12,7 +12,7 @@ export default function DirectivesPage() {
const navigate = useNavigate();
const { id: selectedId } = useParams<{ id: string }>();
const { directives, loading: listLoading, create, remove } = useDirectives();
- const { directive, refresh: refreshDetail, update, start, pause, advance, completeStep, failStep, skipStep, updateGoal, cleanupTasks, pickUpOrders } = useDirective(selectedId);
+ const { directive, refresh: refreshDetail, update, start, pause, advance, completeStep, failStep, skipStep, updateGoal, cleanupTasks, pickUpOrders, createPR } = useDirective(selectedId);
const [showCreate, setShowCreate] = useState(false);
const [newTitle, setNewTitle] = useState("");
@@ -212,6 +212,7 @@ export default function DirectivesPage() {
onRefresh={refreshDetail}
onCleanupTasks={cleanupTasks}
onPickUpOrders={pickUpOrders}
+ onCreatePR={createPR}
/>
) : (
<div className="flex-1 flex items-center justify-center h-full">