diff --git a/src/App.tsx b/src/App.tsx index c2b78a7..39e8886 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,6 +24,10 @@ import axios from 'axios' const WIZARD_PORT = Number(import.meta.env.VITE_WIZARD_PORT) || 18080 const WIZARD_BASE = `http://127.0.0.1:${WIZARD_PORT}` +const getApiBase = () => { + return localStorage.getItem('HF_BACKEND_BASE_URL') || import.meta.env.VITE_API_BASE || 'http://127.0.0.1:8000' +} + type AppState = 'checking' | 'setup' | 'ready' export default function App() { @@ -35,6 +39,22 @@ export default function App() { }, []) const checkInitialized = async () => { + // First try the backend /config/status endpoint (reads from config volume directly) + try { + const res = await axios.get(`${getApiBase()}/config/status`, { timeout: 5000 }) + const cfg = res.data || {} + if (cfg.backend_url) { + localStorage.setItem('HF_BACKEND_BASE_URL', cfg.backend_url) + } + if (cfg.initialized === true) { + setAppState('ready') + return + } + } catch { + // Backend unreachable โ€” fall through to wizard check + } + + // Fallback: try the wizard directly (needed during initial setup before backend starts) try { const res = await axios.get(`${WIZARD_BASE}/api/v1/config/harborforge.json`, { timeout: 5000, @@ -49,7 +69,7 @@ export default function App() { setAppState('setup') } } catch { - // Wizard unreachable or config doesn't exist โ†’ setup needed + // Neither backend nor wizard reachable โ†’ setup needed setAppState('setup') } } diff --git a/src/pages/MilestoneDetailPage.tsx b/src/pages/MilestoneDetailPage.tsx index 0f55b52..ed6ab88 100644 --- a/src/pages/MilestoneDetailPage.tsx +++ b/src/pages/MilestoneDetailPage.tsx @@ -57,14 +57,14 @@ export default function MilestoneDetailPage() { setProjectCode(proj.project_code || '') }) api.get(`/projects/${data.project_id}/members`).then(({ data }) => setMembers(data)).catch(() => {}) - fetchPreflight(data.project_id) + fetchPreflight(data.project_id, data.id) } + api.get(`/milestones/${data.id}/progress`).then(({ data: prog }) => setProgress(prog)).catch(() => {}) }) - api.get(`/milestones/${id}/progress`).then(({ data }) => setProgress(data)).catch(() => {}) } - const fetchPreflight = (projectId: number) => { - api.get(`/projects/${projectId}/milestones/${id}/actions/preflight`) + const fetchPreflight = (projectId: number, milestoneId: number) => { + api.get(`/projects/${projectId}/milestones/${milestoneId}/actions/preflight`) .then(({ data }) => setPreflight(data)) .catch(() => setPreflight(null)) } @@ -74,23 +74,23 @@ export default function MilestoneDetailPage() { }, [id]) const refreshMilestoneItems = () => { - if (!projectCode || !id) return - api.get(`/tasks/${projectCode}/${id}`).then(({ data }) => setTasks(data)).catch(() => {}) - api.get(`/supports/${projectCode}/${id}`).then(({ data }) => setSupports(data)).catch(() => {}) - api.get(`/meetings/${projectCode}/${id}`).then(({ data }) => setMeetings(data)).catch(() => {}) + if (!projectCode || !milestone) return + api.get(`/tasks/${projectCode}/${milestone.id}`).then(({ data }) => setTasks(data)).catch(() => {}) + api.get(`/supports/${projectCode}/${milestone.id}`).then(({ data }) => setSupports(data)).catch(() => {}) + api.get(`/meetings/${projectCode}/${milestone.id}`).then(({ data }) => setMeetings(data)).catch(() => {}) } useEffect(() => { refreshMilestoneItems() - }, [projectCode, id]) + }, [projectCode, milestone?.id]) const createItem = async (type: 'supports' | 'meetings') => { - if (!newTitle.trim() || !projectCode) return + if (!newTitle.trim() || !projectCode || !milestone) return const payload = { title: newTitle, description: newDesc || null, } - await api.post(`/${type}/${projectCode}/${id}`, payload) + await api.post(`/${type}/${projectCode}/${milestone.id}`, payload) setNewTitle('') setNewDesc('') setShowCreateSupport(false) @@ -122,7 +122,7 @@ export default function MilestoneDetailPage() { await api.post(`/projects/${project.id}/milestones/${milestone.id}/actions/${action}`, body ?? {}) fetchMilestone() refreshMilestoneItems() - fetchPreflight(project.id) + fetchPreflight(project.id, milestone.id) } catch (err: any) { const detail = err?.response?.data?.detail setActionError(typeof detail === 'string' ? detail : `${action} failed`) diff --git a/src/pages/ProjectDetailPage.tsx b/src/pages/ProjectDetailPage.tsx index 8e2d8c6..1c56aa1 100644 --- a/src/pages/ProjectDetailPage.tsx +++ b/src/pages/ProjectDetailPage.tsx @@ -114,7 +114,7 @@ export default function ProjectDetailPage() { {canEditProject && } {milestones.map((ms) => ( -
navigate(`/milestones/${ms.id}`)}> +
navigate(`/milestones/${ms.milestone_code || ms.id}`)}> {ms.status} {ms.title} {ms.due_date && ยท Due {dayjs(ms.due_date).format('YYYY-MM-DD')}}