diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 15b1d6ad..04c72285 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -25,6 +25,7 @@ import { } from 'lucide-react' import { Navbar } from './components/Navbar' import EmptyState from './components/EmptyState' +import { BACKEND_URL } from './config' import { CuratedTips } from './components/CuratedTips' import { StepProgress } from './components/StepProgress' import { OnboardingTour } from './components/OnboardingTour' @@ -124,10 +125,9 @@ function ResumePreview({ text, skills }: { text: string; skills: string[] }) { interface SuggestionCardProps { text: string index: number - backendUrl?: string } -const SuggestionCard: React.FC = ({ text, index, backendUrl = '' }) => { +const SuggestionCard: React.FC = ({ text, index }) => { const [copied, setCopied] = React.useState(false) const [voted, setVoted] = React.useState<'up' | 'down' | null>(null) const [isVoting, setIsVoting] = React.useState(false) @@ -142,7 +142,7 @@ const SuggestionCard: React.FC = ({ text, index, backendUrl if (voted !== null || isVoting) return setIsVoting(true) try { - await axios.post(`${backendUrl}/api/suggestion-feedback/`, { + await axios.post(`${BACKEND_URL}/api/suggestion-feedback/`, { suggestion: text, vote, index, @@ -362,12 +362,10 @@ function App() { setEntries, } = useAnalysisHistory() - const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://127.0.0.1:8000' - const handleDeleteEntry = async (id: string) => { if (user) { try { - await axios.delete(`${backendUrl}/api/history/${id}/`, { + await axios.delete(`${BACKEND_URL}/api/history/${id}/`, { headers: { Authorization: `Bearer ${user.token}` }, }) } catch (error) { @@ -384,7 +382,7 @@ function App() { const handleClearAll = async () => { if (user) { try { - await axios.delete(`${backendUrl}/api/history/clear/`, { + await axios.delete(`${BACKEND_URL}/api/history/clear/`, { headers: { Authorization: `Bearer ${user.token}` }, }) } catch (error) { @@ -397,7 +395,7 @@ function App() { const fetchDbHistory = useCallback( async (token: string) => { try { - const res = await axios.get(`${backendUrl}/api/history/`, { + const res = await axios.get(`${BACKEND_URL}/api/history/`, { headers: { Authorization: `Bearer ${token}` }, }) const dbEntries: AnalysisEntry[] = res.data.map( @@ -433,7 +431,7 @@ function App() { /* silently ignore */ } }, - [backendUrl, setEntries] + [setEntries] ) useEffect(() => { @@ -602,7 +600,7 @@ function App() { }, 1000) const headers = user ? { Authorization: `Bearer ${user.token}` } : {} - const res = await axios.post(`${backendUrl}/api/upload/`, formData, { headers }) + const res = await axios.post(`${BACKEND_URL}/api/upload/`, formData, { headers }) clearTimeout(stageTimer1) clearTimeout(stageTimer2) @@ -1626,7 +1624,6 @@ function App() { key={index} text={suggestion} index={index} - backendUrl={backendUrl} /> ))} diff --git a/frontend/src/components/AccountSettingsModal.tsx b/frontend/src/components/AccountSettingsModal.tsx index b9174cff..f3377bbd 100644 --- a/frontend/src/components/AccountSettingsModal.tsx +++ b/frontend/src/components/AccountSettingsModal.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { X, Trash2, Loader2, ShieldAlert } from "lucide-react"; +import { BACKEND_URL } from "../config"; import type { AuthUser } from "../hooks/useAuth"; import axios from "axios"; @@ -20,8 +21,6 @@ export const AccountSettingsModal: React.FC = ({ const [loading, setLoading] = useState(false); const [showDeleteZone, setShowDeleteZone] = useState(false); - const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://127.0.0.1:8000"; - const handleDelete = async (e: React.FormEvent) => { e.preventDefault(); if (!user) return; @@ -35,7 +34,7 @@ export const AccountSettingsModal: React.FC = ({ setLoading(true); try { - await axios.delete(`${backendUrl}/api/auth/delete-account/`, { + await axios.delete(`${BACKEND_URL}/api/auth/delete-account/`, { headers: { Authorization: `Bearer ${user.token}` }, data: { password, confirm_text: confirmText }, }); diff --git a/frontend/src/components/Navbar.test.tsx b/frontend/src/components/Navbar.test.tsx index fc8e166b..2da350cb 100644 --- a/frontend/src/components/Navbar.test.tsx +++ b/frontend/src/components/Navbar.test.tsx @@ -11,7 +11,7 @@ describe('Navbar Component (#241)', () => { user: null, onLogin: vi.fn(), onLogout: vi.fn(), - onHistoryClick: vi.fn(), + onSettingsClick: vi.fn(), onHistoryClick: vi.fn(), } it('renders the header brand with emoji and "AI Resume Analyzer" title text', () => { @@ -36,7 +36,7 @@ describe('Navbar Component right-side cluster (#244)', () => { user={null} onLogin={() => {}} onLogout={() => {}} - onHistoryClick={() => {}} + onSettingsClick={() => {}} onHistoryClick={() => {}} /> ) @@ -58,7 +58,7 @@ describe('Navbar Component right-side cluster (#244)', () => { user={user} onLogin={() => {}} onLogout={() => {}} - onHistoryClick={() => {}} + onSettingsClick={() => {}} onHistoryClick={() => {}} /> ) @@ -76,7 +76,7 @@ describe('Navbar responsive hamburger (#245)', () => { user={null} onLogin={() => {}} onLogout={() => {}} - onHistoryClick={() => {}} + onSettingsClick={() => {}} onHistoryClick={() => {}} /> ) @@ -94,7 +94,7 @@ describe('Navbar responsive hamburger (#245)', () => { user={null} onLogin={() => {}} onLogout={() => {}} - onHistoryClick={() => {}} + onSettingsClick={() => {}} onHistoryClick={() => {}} /> ) @@ -121,6 +121,7 @@ describe('Navbar responsive hamburger (#245)', () => { user={null} onLogin={() => {}} onLogout={() => {}} + onSettingsClick={() => {}} onHistoryClick={onHistoryClick} /> ) @@ -145,7 +146,7 @@ describe('Navbar responsive hamburger (#245)', () => { user={null} onLogin={() => {}} onLogout={() => {}} - onHistoryClick={() => {}} + onSettingsClick={() => {}} onHistoryClick={() => {}} /> ) diff --git a/frontend/src/config.ts b/frontend/src/config.ts new file mode 100644 index 00000000..dd0fc489 --- /dev/null +++ b/frontend/src/config.ts @@ -0,0 +1,24 @@ +export const ENV_CONFIG = { + development: { + BACKEND_URL: 'http://127.0.0.1:8000', + }, + staging: { + BACKEND_URL: 'https://staging-api.airesumeanalyzer.com', + }, + production: { + BACKEND_URL: 'https://api.airesumeanalyzer.com', + }, +} + +const getEnvMode = () => { + const mode = import.meta.env.MODE || 'development' + return mode as keyof typeof ENV_CONFIG +} + +const currentConfig = ENV_CONFIG[getEnvMode()] || ENV_CONFIG.development + +/** + * BACKEND_URL prioritizes the explicit VITE_BACKEND_URL environment variable. + * If not provided, it falls back to the configured URL for the current environment mode (dev/staging/prod). + */ +export const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || currentConfig.BACKEND_URL diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index 53787827..cd41d600 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -1,7 +1,7 @@ import { useState, useCallback } from 'react' import axios from 'axios' -const BACKEND = import.meta.env.VITE_BACKEND_URL || 'http://127.0.0.1:8000' +import { BACKEND_URL } from '../config' export interface AuthUser { username: string @@ -31,13 +31,13 @@ export function useAuth() { } const signup = useCallback(async (username: string, password: string) => { - await axios.post(`${BACKEND}/api/auth/signup/`, { username, password }) - const res = await axios.post(`${BACKEND}/api/auth/login/`, { username, password }) + await axios.post(`${BACKEND_URL}/api/auth/signup/`, { username, password }) + const res = await axios.post(`${BACKEND_URL}/api/auth/login/`, { username, password }) persist({ username, token: res.data.access }) }, []) const login = useCallback(async (username: string, password: string) => { - const res = await axios.post(`${BACKEND}/api/auth/login/`, { username, password }) + const res = await axios.post(`${BACKEND_URL}/api/auth/login/`, { username, password }) persist({ username, token: res.data.access }) }, []) diff --git a/frontend/src/hooks/useCompareVersions.ts b/frontend/src/hooks/useCompareVersions.ts index c1f4879b..1cd2491d 100644 --- a/frontend/src/hooks/useCompareVersions.ts +++ b/frontend/src/hooks/useCompareVersions.ts @@ -23,7 +23,7 @@ export interface VersionComparison { insights: string[] } -const BACKEND = import.meta.env.VITE_BACKEND_URL || 'http://127.0.0.1:8000' +import { BACKEND_URL } from '../config' export function useCompareVersions(token: string | undefined) { const [comparison, setComparison] = useState(null) @@ -39,7 +39,7 @@ export function useCompareVersions(token: string | undefined) { setLoading(true) setError(null) try { - const res = await axios.get(`${BACKEND}/api/compare/`, { + const res = await axios.get(`${BACKEND_URL}/api/compare/`, { headers: { Authorization: `Bearer ${token}` }, params: { older: olderId, newer: newerId }, })