design(preview): match toggle button sizes to device toggles
This commit is contained in:
@@ -18,8 +18,12 @@ import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Users,
|
||||
HardDrive,
|
||||
Blocks,
|
||||
} from "lucide-react";
|
||||
|
||||
import { useAnatomy } from "@/components/project/use-anatomy";
|
||||
|
||||
export function DashboardSidebar({
|
||||
workspace,
|
||||
projectId,
|
||||
@@ -39,23 +43,40 @@ export function DashboardSidebar({
|
||||
Record<string, boolean>
|
||||
>({
|
||||
settings: true,
|
||||
data: true,
|
||||
});
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const { anatomy } = useAnatomy(projectId);
|
||||
const databases = anatomy?.infrastructure?.databases ?? [];
|
||||
|
||||
if (isPreview) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
const toggleSection = (section: string) => {
|
||||
setExpandedSections((prev) => ({ ...prev, [section]: !prev[section] }));
|
||||
const handleSectionClick = (segment: string) => {
|
||||
if (!expandedSections[segment]) {
|
||||
setExpandedSections((prev) => ({ ...prev, [segment]: true }));
|
||||
}
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{ segment: "overview", label: "Overview", Icon: LayoutGrid },
|
||||
{ segment: "plan", label: "Plan & Specs", Icon: ClipboardList },
|
||||
{ segment: "code", label: "Code", Icon: Code2 },
|
||||
{ segment: "data", label: "Data", Icon: Database },
|
||||
{
|
||||
segment: "data",
|
||||
label: "Data",
|
||||
Icon: Database,
|
||||
hasChildren: true,
|
||||
children: databases.map((db) => ({
|
||||
segment: `data/tables?db=${db.uuid}`,
|
||||
label: db.name,
|
||||
})),
|
||||
},
|
||||
{ segment: "storage", label: "Storage", Icon: HardDrive },
|
||||
{ segment: "services", label: "Services", Icon: Blocks },
|
||||
{ segment: "users", label: "Auth / Users", Icon: Users },
|
||||
{ segment: "integrations", label: "Integrations", Icon: Plug },
|
||||
{ segment: "security", label: "Security", Icon: ShieldCheck },
|
||||
@@ -67,6 +88,17 @@ export function DashboardSidebar({
|
||||
Icon: BarChart2,
|
||||
badge: "Soon",
|
||||
},
|
||||
{
|
||||
segment: "marketing",
|
||||
label: "Marketing",
|
||||
Icon: BarChart2,
|
||||
badge: "New",
|
||||
hasChildren: true,
|
||||
children: [
|
||||
{ segment: "marketing/seo", label: "SEO & GEO" },
|
||||
{ segment: "marketing/social", label: "Social content" },
|
||||
],
|
||||
},
|
||||
{
|
||||
segment: "settings",
|
||||
label: "Settings",
|
||||
@@ -170,9 +202,10 @@ export function DashboardSidebar({
|
||||
}}
|
||||
onClick={() => {
|
||||
if (item.hasChildren) {
|
||||
toggleSection(item.segment);
|
||||
} else {
|
||||
// Navigate via link logic would happen here, we'll wrap the icon/label in a link
|
||||
setExpandedSections((prev) => ({
|
||||
...prev,
|
||||
[item.segment]: !prev[item.segment],
|
||||
}));
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -245,28 +278,41 @@ export function DashboardSidebar({
|
||||
}}
|
||||
>
|
||||
{item.children.map((child) => {
|
||||
const isChildActive =
|
||||
pathname === `${projectBase}/${child.segment}`;
|
||||
const href = child.segment.includes("?")
|
||||
? `${projectBase}/${child.segment.split("?")[0]}?${child.segment.split("?")[1]}`
|
||||
: `${projectBase}/${child.segment}`;
|
||||
let isChildActive = false;
|
||||
if (child.segment.includes("?")) {
|
||||
const [basePath, searchStr] = child.segment.split("?");
|
||||
isChildActive =
|
||||
pathname === `${projectBase}/${basePath}` &&
|
||||
(typeof window !== "undefined"
|
||||
? window.location.search.includes(searchStr)
|
||||
: false);
|
||||
} else {
|
||||
isChildActive =
|
||||
pathname === `${projectBase}/${child.segment}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={child.segment}
|
||||
href={`${projectBase}/${child.segment}`}
|
||||
href={href}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "6px 10px 6px 36px",
|
||||
borderRadius: 8,
|
||||
padding: "6px 10px 6px 14px",
|
||||
marginLeft: "18px",
|
||||
borderRadius: "0 8px 8px 0",
|
||||
fontSize: "0.8rem",
|
||||
fontWeight: 500,
|
||||
textDecoration: "none",
|
||||
color: isChildActive ? "#18181b" : "#52525b",
|
||||
background: isChildActive
|
||||
? "#f4f4f5"
|
||||
: "transparent",
|
||||
background: "transparent",
|
||||
transition: "all 0.1s ease",
|
||||
border: isChildActive
|
||||
? "1px solid #e4e4e7"
|
||||
: "1px solid transparent",
|
||||
borderLeft: isChildActive
|
||||
? "2px solid #18181b"
|
||||
: "2px solid transparent",
|
||||
}}
|
||||
>
|
||||
{child.label}
|
||||
|
||||
@@ -37,6 +37,9 @@ export function ProjectIconRail({ workspace, projectId, actions }: Props) {
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: 500,
|
||||
borderRadius: 6,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: 24, // Explicitly match device toggles height
|
||||
textDecoration: "none",
|
||||
background: isPreviewActive ? "#ffffff" : "transparent",
|
||||
color: isPreviewActive ? "#18181b" : "#71717a",
|
||||
@@ -53,6 +56,9 @@ export function ProjectIconRail({ workspace, projectId, actions }: Props) {
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: 500,
|
||||
borderRadius: 6,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: 24, // Explicitly match device toggles height
|
||||
textDecoration: "none",
|
||||
background: !isPreviewActive ? "#ffffff" : "transparent",
|
||||
color: !isPreviewActive ? "#18181b" : "#71717a",
|
||||
@@ -401,12 +407,11 @@ const bar: React.CSSProperties = {
|
||||
alignItems: "center",
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
height: "56px", // Explicitly set height
|
||||
height: "100%", // Inherit height from parent
|
||||
padding: "0 16px",
|
||||
gap: 12,
|
||||
boxSizing: "border-box",
|
||||
background: "#fafafa",
|
||||
borderBottom: "1px solid #e4e4e7",
|
||||
background: "#faf8f5",
|
||||
fontFamily: '"Outfit", "Inter", ui-sans-serif, sans-serif',
|
||||
};
|
||||
|
||||
|
||||
@@ -1027,16 +1027,11 @@ export function ChatPanel({
|
||||
);
|
||||
const [showThreads, setShowThreads] = useState(false);
|
||||
const [mcpToken, setMcpToken] = useState<string | null>(null);
|
||||
const [isChatMinimized, setIsChatMinimized] = useState<boolean>(() => {
|
||||
if (typeof window === "undefined") return false;
|
||||
return !window.location.pathname.includes("/preview");
|
||||
});
|
||||
const [isChatMinimized, setIsChatMinimized] = useState<boolean>(false);
|
||||
|
||||
// Auto-minimize when navigating to dashboard, auto-open when navigating to preview
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
setIsChatMinimized(!window.location.pathname.includes("/preview"));
|
||||
}
|
||||
setIsChatMinimized(!pathname.includes("/preview"));
|
||||
}, [pathname]);
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
@@ -2101,12 +2096,9 @@ export function ChatPanel({
|
||||
}}
|
||||
onInput={(e) => {
|
||||
const el = e.currentTarget;
|
||||
const newlines = (el.value.match(/\n/g) || []).length;
|
||||
if ((el as any).lastNewlines !== newlines) {
|
||||
(el as any).lastNewlines = newlines;
|
||||
el.style.height = "auto";
|
||||
el.style.height = Math.min(el.scrollHeight, 240) + "px";
|
||||
}
|
||||
// Only resize if height actually changed
|
||||
el.style.height = "auto";
|
||||
el.style.height = Math.min(el.scrollHeight, 240) + "px";
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -2332,7 +2324,7 @@ export function ChatPanel({
|
||||
alignItems: "stretch",
|
||||
flexShrink: 0,
|
||||
height: 48,
|
||||
borderBottom: "1px solid #e8e4dc",
|
||||
borderBottom: "1px solid #e4e4e7",
|
||||
background: "#faf8f5",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
@@ -2347,7 +2339,7 @@ export function ChatPanel({
|
||||
padding: "0 12px",
|
||||
gap: 6,
|
||||
boxSizing: "border-box",
|
||||
borderRight: "1px solid #e8e4dc",
|
||||
borderRight: "1px solid #e4e4e7",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user