128 lines
4.6 KiB
TypeScript
128 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useParams } from "next/navigation";
|
|
import { useSession } from "next-auth/react";
|
|
import { AtlasChat } from "@/components/AtlasChat";
|
|
import { OrchestratorChat } from "@/components/OrchestratorChat";
|
|
import { Loader2 } from "lucide-react";
|
|
|
|
function MobileQRButton({ projectId, workspace }: { projectId: string; workspace: string }) {
|
|
const [show, setShow] = useState(false);
|
|
const url = typeof window !== "undefined"
|
|
? `${window.location.origin}/${workspace}/project/${projectId}/overview`
|
|
: "";
|
|
const qrSrc = `https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=${encodeURIComponent(url)}&bgcolor=f6f4f0&color=1a1a1a&margin=2`;
|
|
|
|
return (
|
|
<div style={{ position: "relative" }}>
|
|
<button
|
|
onClick={() => setShow(s => !s)}
|
|
title="Open on your phone"
|
|
style={{
|
|
display: "flex", alignItems: "center", gap: 6,
|
|
padding: "6px 12px", borderRadius: 7,
|
|
background: "none", border: "1px solid #e0dcd4",
|
|
fontSize: "0.72rem", fontFamily: "Outfit, sans-serif",
|
|
color: "#8a8478", cursor: "pointer",
|
|
transition: "border-color 0.12s",
|
|
}}
|
|
onMouseEnter={e => (e.currentTarget.style.borderColor = "#b5b0a6")}
|
|
onMouseLeave={e => (e.currentTarget.style.borderColor = "#e0dcd4")}
|
|
>
|
|
📱 Open on phone
|
|
</button>
|
|
{show && (
|
|
<div style={{
|
|
position: "absolute", top: "calc(100% + 8px)", right: 0,
|
|
background: "#fff", borderRadius: 12,
|
|
border: "1px solid #e8e4dc",
|
|
boxShadow: "0 8px 24px #1a1a1a12",
|
|
padding: "16px", zIndex: 50,
|
|
display: "flex", flexDirection: "column", alignItems: "center", gap: 10,
|
|
minWidth: 220,
|
|
}}>
|
|
<img src={qrSrc} alt="QR code" width={180} height={180} style={{ borderRadius: 8 }} />
|
|
<p style={{ fontSize: "0.72rem", color: "#8a8478", textAlign: "center", margin: 0, fontFamily: "Outfit, sans-serif" }}>
|
|
Scan to open Atlas on your phone
|
|
</p>
|
|
<p style={{ fontSize: "0.65rem", color: "#b5b0a6", textAlign: "center", margin: 0, fontFamily: "IBM Plex Mono, monospace", wordBreak: "break-all" }}>
|
|
{url}
|
|
</p>
|
|
<button onClick={() => setShow(false)} style={{ fontSize: "0.68rem", color: "#a09a90", background: "none", border: "none", cursor: "pointer" }}>
|
|
Close
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface Project {
|
|
id: string;
|
|
productName: string;
|
|
stage?: "discovery" | "architecture" | "building" | "active";
|
|
}
|
|
|
|
export default function ProjectOverviewPage() {
|
|
const params = useParams();
|
|
const projectId = params.projectId as string;
|
|
const workspace = params.workspace as string;
|
|
const { status: authStatus } = useSession();
|
|
const [project, setProject] = useState<Project | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (authStatus !== "authenticated") {
|
|
if (authStatus === "unauthenticated") setLoading(false);
|
|
return;
|
|
}
|
|
fetch(`/api/projects/${projectId}`)
|
|
.then((r) => r.json())
|
|
.then((d) => setProject(d.project))
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false));
|
|
}, [authStatus, projectId]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", fontFamily: "Outfit, sans-serif" }}>
|
|
<Loader2 style={{ width: 24, height: 24, color: "#a09a90" }} className="animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!project) {
|
|
return (
|
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", fontFamily: "Outfit, sans-serif", color: "#a09a90", fontSize: "0.88rem" }}>
|
|
Project not found.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
|
|
{/* Desktop-only: Open on phone button */}
|
|
<style>{`@media (max-width: 768px) { .vibn-phone-btn { display: none !important; } }`}</style>
|
|
<div className="vibn-phone-btn" style={{
|
|
position: "absolute", top: 14, right: 248,
|
|
zIndex: 20,
|
|
}}>
|
|
<MobileQRButton projectId={projectId} workspace={workspace} />
|
|
</div>
|
|
|
|
{(!project.stage || project.stage === "discovery") ? (
|
|
<AtlasChat
|
|
projectId={projectId}
|
|
projectName={project.productName}
|
|
/>
|
|
) : (
|
|
<OrchestratorChat
|
|
projectId={projectId}
|
|
projectName={project.productName}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|