2 Commits

Author SHA1 Message Date
Mondo Diaz
54d56eae40 Update logo to curved canopy tree design
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 18:00:31 -06:00
Mondo Diaz
b7cf724e05 Fix frontend compatibility with paginated API response
- Update frontend API client to extract items from paginated response
- Fix SPA routing to serve index.html for /project/* paths on refresh

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:40:21 -06:00
3 changed files with 28 additions and 15 deletions

View File

@@ -57,11 +57,12 @@ if os.path.exists(static_dir):
# Catch-all for SPA routing (must be last) # Catch-all for SPA routing (must be last)
@app.get("/{full_path:path}") @app.get("/{full_path:path}")
async def serve_spa_routes(full_path: str): async def serve_spa_routes(full_path: str):
# Don't catch API routes # Don't catch API routes or health endpoint
if full_path.startswith("api/") or full_path.startswith("health") or full_path.startswith("project/"): if full_path.startswith("api/") or full_path.startswith("health"):
from fastapi import HTTPException from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Not found") raise HTTPException(status_code=404, detail="Not found")
# Serve SPA for all other routes (including /project/*)
index_path = os.path.join(static_dir, "index.html") index_path = os.path.join(static_dir, "index.html")
if os.path.exists(index_path): if os.path.exists(index_path):
return FileResponse(index_path) return FileResponse(index_path)

View File

@@ -10,10 +10,22 @@ async function handleResponse<T>(response: Response): Promise<T> {
return response.json(); return response.json();
} }
// Paginated response type
interface PaginatedResponse<T> {
items: T[];
pagination: {
page: number;
limit: number;
total: number;
total_pages: number;
};
}
// Project API // Project API
export async function listProjects(): Promise<Project[]> { export async function listProjects(): Promise<Project[]> {
const response = await fetch(`${API_BASE}/projects`); const response = await fetch(`${API_BASE}/projects`);
return handleResponse<Project[]>(response); const data = await handleResponse<PaginatedResponse<Project>>(response);
return data.items;
} }
export async function createProject(data: { name: string; description?: string; is_public?: boolean }): Promise<Project> { export async function createProject(data: { name: string; description?: string; is_public?: boolean }): Promise<Project> {

View File

@@ -16,18 +16,18 @@ function Layout({ children }: LayoutProps) {
<Link to="/" className="logo"> <Link to="/" className="logo">
<div className="logo-icon"> <div className="logo-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Three trees representing an orchard */} {/* Three fruit trees representing an orchard */}
{/* Left tree */} {/* Left tree - rounded canopy */}
<ellipse cx="6" cy="9" rx="4" ry="5" fill="currentColor" opacity="0.7"/> <path d="M6 14 Q6 8 3 8 Q6 4 6 4 Q6 4 9 8 Q6 8 6 14" fill="currentColor" opacity="0.6"/>
<rect x="5" y="13" width="2" height="5" fill="currentColor"/> <rect x="5.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
{/* Center tree (larger) */} {/* Center tree - larger rounded canopy */}
<ellipse cx="12" cy="7" rx="5" ry="6" fill="currentColor"/> <path d="M12 12 Q12 5 8 5 Q12 1 12 1 Q12 1 16 5 Q12 5 12 12" fill="currentColor"/>
<rect x="11" y="12" width="2" height="6" fill="currentColor"/> <rect x="11.25" y="11" width="1.5" height="5" fill="currentColor"/>
{/* Right tree */} {/* Right tree - rounded canopy */}
<ellipse cx="18" cy="9" rx="4" ry="5" fill="currentColor" opacity="0.7"/> <path d="M18 14 Q18 8 15 8 Q18 4 18 4 Q18 4 21 8 Q18 8 18 14" fill="currentColor" opacity="0.6"/>
<rect x="17" y="13" width="2" height="5" fill="currentColor"/> <rect x="17.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
{/* Ground line */} {/* Ground */}
<line x1="2" y1="18" x2="22" y2="18" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/> <ellipse cx="12" cy="19" rx="9" ry="1.5" fill="currentColor" opacity="0.3"/>
</svg> </svg>
</div> </div>
<span className="logo-text">Orchard</span> <span className="logo-text">Orchard</span>