refactor: simplify theme detail page header and fix v0 open flow

- Replace dropdown menu with inline "Open in Editor" button
- Remove "Published" badge from theme name
- Make header section mobile responsive with flex-col/row
- Skip save check when opening in v0 from theme detail page since theme already exists

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sahaj Jain
2026-02-09 04:22:15 +05:30
parent 73754ccaf4
commit 3b8ab5adb3
3 changed files with 23 additions and 35 deletions
+6 -2
View File
@@ -47,7 +47,11 @@ const V0Logo = ({ className }: { className?: string }) => (
</svg>
);
const ThemePreviewPanel = ({ styles, currentMode }: ThemeEditorPreviewProps) => {
const ThemePreviewPanel = ({
styles,
currentMode,
themeId,
}: ThemeEditorPreviewProps & { themeId?: string }) => {
const { isFullscreen, toggleFullscreen } = useFullscreen();
const [activeTab, setActiveTab] = useQueryState("p", {
defaultValue: "cards",
@@ -114,7 +118,7 @@ const ThemePreviewPanel = ({ styles, currentMode }: ThemeEditorPreviewProps) =>
<div className="flex items-center gap-0.5">
<TooltipWrapper label="Open theme in v0" asChild>
<Button variant="ghost" onClick={() => handleOpenInV0()} className="group px-2.5">
<Button variant="ghost" onClick={() => handleOpenInV0(themeId)} className="group px-2.5">
<span className="flex items-center justify-center gap-1 transition-all group-hover:scale-110">
Open in <V0Logo className="mb-0.5 !size-5" />
</span>
+9 -31
View File
@@ -3,12 +3,6 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { toast } from "@/components/ui/use-toast";
import { useEditorStore } from "@/store/editor-store";
import { useToggleLike } from "@/hooks/themes";
@@ -16,7 +10,7 @@ import { useSessionGuard } from "@/hooks/use-guards";
import { usePostLoginAction } from "@/hooks/use-post-login-action";
import type { Theme } from "@/types/theme";
import { cn } from "@/lib/utils";
import { Calendar, Edit, Heart, Moon, MoreVertical, Share, Sun } from "lucide-react";
import { Calendar, Edit, Heart, Moon, Share, Sun } from "lucide-react";
import { notFound, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { CodeButton } from "./editor/action-bar/components/code-button";
@@ -96,16 +90,9 @@ export default function ThemeView({ theme, communityData }: ThemeViewProps) {
return (
<>
<div className="space-y-4">
<div className="flex items-start justify-between gap-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-3">
<div className="flex items-center gap-3">
<h1 className="text-3xl font-bold">{theme.name}</h1>
{communityData && (
<Badge variant="secondary" className="text-xs">
Published
</Badge>
)}
</div>
<h1 className="text-3xl font-bold">{theme.name}</h1>
{communityData && (
<>
<div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
@@ -134,7 +121,7 @@ export default function ThemeView({ theme, communityData }: ThemeViewProps) {
</>
)}
</div>
<div className="flex shrink-0 items-center gap-2">
<div className="flex shrink-0 flex-wrap items-center gap-2">
{communityData && <LikeButton communityData={communityData} />}
<Button variant="outline" size="icon" onClick={toggleTheme}>
{currentMode === "dark" ? (
@@ -152,26 +139,17 @@ export default function ThemeView({ theme, communityData }: ThemeViewProps) {
<Share className="size-4" />
Share
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreVertical className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem className="gap-2" onClick={handleOpenInEditor}>
<Edit className="size-4" />
Open in Editor
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button variant="outline" size="default" onClick={handleOpenInEditor}>
<Edit className="size-4" />
Open in Editor
</Button>
</div>
</div>
</div>
<DialogActionsProvider>
<div className="-m-4 mt-6 flex h-[min(80svh,900px)] flex-col">
<ThemePreviewPanel styles={theme.styles} currentMode={currentMode} />
<ThemePreviewPanel styles={theme.styles} currentMode={currentMode} themeId={theme.id} />
</div>
<CodePanelDialog
+8 -2
View File
@@ -205,7 +205,8 @@ function useDialogActionsStore(): DialogActionsContextType {
if (!presetId) return;
const currentPreset = getPreset(presetId);
const isSavedPreset = !!currentPreset && currentPreset.source === "SAVED";
// If an explicit ID is passed but not found in presets, treat as a saved/database theme
const isSavedPreset = id ? true : !!currentPreset && currentPreset.source === "SAVED";
const themeName = currentPreset?.label || presetId;
posthog.capture("OPEN_IN_V0", {
@@ -223,12 +224,17 @@ function useDialogActionsStore(): DialogActionsContextType {
};
const handleOpenInV0 = (id?: string) => {
if (id) {
openInV0(id);
return;
}
if (hasThemeChangedFromCheckpoint()) {
handleSaveClick({ openInV0AfterSave: true });
return;
}
openInV0(id);
openInV0();
};
const handleUpdateExisting = async () => {