Files
stack/apps/web/src/components/chat/BackendStatusBanner.tsx
Jason Woltje d54714ea06 feat: add chat components from jarvis frontend
- Migrated Chat.tsx with message handling and UI structure
- Migrated ChatInput.tsx with character limits and keyboard shortcuts
- Migrated MessageList.tsx with thinking/reasoning display
- Migrated ConversationSidebar.tsx (simplified placeholder)
- Migrated BackendStatusBanner.tsx (simplified placeholder)
- Created components/chat/index.ts barrel export
- Created app/chat/page.tsx placeholder route

These components are adapted from jarvis-fe but not yet fully functional:
- API calls placeholder (need to wire up /api/brain/query)
- Auth hooks stubbed (need useAuth implementation)
- Project/conversation hooks stubbed (need implementation)
- Imports changed from @jarvis/* to @mosaic/*

Next steps:
- Implement missing hooks (useAuth, useProjects, useConversations, useApi)
- Wire up backend API endpoints
- Add proper TypeScript types
- Implement full conversation management
2026-01-29 21:47:00 -06:00

99 lines
2.9 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
/**
* Banner that displays when the backend is unavailable.
* Shows error message, countdown to next retry, and manual retry button.
*
* TODO: Integrate with actual backend status checking hook
*/
export function BackendStatusBanner() {
const [isAvailable, setIsAvailable] = useState(true);
const [error, setError] = useState<string | null>(null);
const [retryIn, setRetryIn] = useState(0);
// TODO: Replace with actual useBackendStatus hook
// const { isAvailable, error, retryIn, manualRetry } = useBackendStatus();
const manualRetry = () => {
// TODO: Implement manual retry logic
console.log("Manual retry triggered");
};
const handleSignOut = async () => {
try {
// TODO: Implement signOut
// await signOut();
} catch (error) {
console.warn("Sign-out failed during backend unavailability:", error);
}
window.location.href = "/login";
};
// Don't render if backend is available
if (isAvailable) {
return null;
}
return (
<div
className="flex items-center justify-between gap-4 px-4 py-2 text-sm"
style={{
backgroundColor: "#fef3c7", // amber-100
borderBottom: "1px solid #fcd34d", // amber-300
color: "#92400e", // amber-800
}}
role="alert"
aria-live="polite"
>
<div className="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5 flex-shrink-0"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
<span>
{error || "Backend temporarily unavailable."}
{retryIn > 0 && (
<span className="ml-1">
Retrying in {retryIn}s...
</span>
)}
</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={manualRetry}
className="rounded px-3 py-1 text-xs font-medium transition-colors hover:opacity-80"
style={{
backgroundColor: "#fcd34d", // amber-300
color: "#92400e", // amber-800
}}
>
Retry Now
</button>
<button
onClick={handleSignOut}
className="rounded px-3 py-1 text-xs font-medium transition-colors hover:opacity-80"
style={{
backgroundColor: "transparent",
color: "#92400e", // amber-800
border: "1px solid #fcd34d", // amber-300
}}
>
Sign in again
</button>
</div>
</div>
);
}