feat(frontend): add Front/ — Vite/React frontend with backend pipeline integration
Mirror of design_agent_front/design-agent/ for shipping alongside backend.
Vite plugin (vitePluginPhaseZApi) endpoints :
- POST /api/run — spawn `python -m src.phase_z2_pipeline` with overrides
- GET /api/sample-mdx?mdx=03/04/05 — fixed sample MDX
- GET /frame-preview/{n} — figma preview thumbnails
- GET /data/runs/{run_id}/{path} — pipeline artifacts (final.html, step*.json, ...)
Env toggle forward (보고용) :
PHASE_Z_ALLOW_RESTRUCTURE / PHASE_Z_ALLOW_REJECT / PHASE_Z_MAX_RANK=32
Components :
- LeftMdxPanel (03/04/05 fix list + section tree)
- SlideCanvas (iframe + slideOverrideCss prop for inline CSS inject)
- FramePanel (label priority + confidence sort)
- LayoutPanel
README with mermaid diagrams covering the 5-step demo flow.
node_modules / dist / .manus-logs / .env excluded via .gitignore.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* BottomActions - 하단 액션 버튼 영역
|
||||
*
|
||||
* 생성하기, 다운로드, 연동하기 버튼 컴포넌트
|
||||
*/
|
||||
|
||||
import { Sparkles, Download, Link2, Loader2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "sonner";
|
||||
import type { SlidePlan, UserSelection } from "../types/designAgent";
|
||||
import { serializeSlidePlan } from "../utils/slidePlanUtils";
|
||||
|
||||
interface BottomActionsProps {
|
||||
slidePlan: SlidePlan | null;
|
||||
userSelection: UserSelection;
|
||||
isLoading: boolean;
|
||||
onGenerate: () => void;
|
||||
}
|
||||
|
||||
export default function BottomActions({
|
||||
slidePlan,
|
||||
userSelection,
|
||||
isLoading,
|
||||
onGenerate,
|
||||
}: BottomActionsProps) {
|
||||
const handleDownload = () => {
|
||||
if (!slidePlan) {
|
||||
toast.error("슬라이드 플랜이 없습니다. 먼저 생성하기를 눌러주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
const json = serializeSlidePlan(slidePlan, userSelection);
|
||||
console.log("[Download] SlidePlan JSON:", json);
|
||||
|
||||
// JSON 파일 다운로드
|
||||
const blob = new Blob([json], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `slide-plan-${Date.now()}.json`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
toast.success("SlidePlan JSON이 다운로드되었습니다.");
|
||||
};
|
||||
|
||||
const handleConnect = () => {
|
||||
toast.info("연동하기 기능은 파이프라인 연결 후 활성화됩니다.");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-3 px-6 py-3 bg-white border-t border-slate-200">
|
||||
<div className="flex items-center gap-1.5 mr-2">
|
||||
<span className="w-5 h-5 rounded-full bg-blue-600 text-white text-xs flex items-center justify-center font-bold">4</span>
|
||||
<span className="text-xs text-slate-500 font-medium">액션</span>
|
||||
</div>
|
||||
{/* 생성하기 */}
|
||||
<Button
|
||||
onClick={onGenerate}
|
||||
disabled={isLoading}
|
||||
className="gap-2 min-w-[120px]"
|
||||
size="default"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
생성 중...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Sparkles className="w-4 h-4" />
|
||||
생성하기
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* 다운로드 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleDownload}
|
||||
disabled={!slidePlan || isLoading}
|
||||
className="gap-2 min-w-[120px]"
|
||||
size="default"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
다운로드
|
||||
</Button>
|
||||
|
||||
{/* 연동하기 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleConnect}
|
||||
className="gap-2 min-w-[120px] text-slate-500"
|
||||
size="default"
|
||||
>
|
||||
<Link2 className="w-4 h-4" />
|
||||
연동하기
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user