VIBN Frontend for Coolify deployment
This commit is contained in:
171
components/ai/project-config-generator.tsx
Normal file
171
components/ai/project-config-generator.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Download, Search, CheckCircle2, FileText } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface ProjectConfigGeneratorProps {
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export function ProjectConfigGenerator({ projectId, projectName }: ProjectConfigGeneratorProps) {
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [fileFound, setFileFound] = useState<boolean | null>(null);
|
||||
|
||||
const vibnConfig = {
|
||||
projectId,
|
||||
projectName,
|
||||
version: "1.0",
|
||||
};
|
||||
|
||||
const configContent = JSON.stringify(vibnConfig, null, 2);
|
||||
|
||||
const handleSearchFile = async () => {
|
||||
setSearching(true);
|
||||
setFileFound(null);
|
||||
|
||||
try {
|
||||
// Prompt user to select the .vibn file
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.vibn,.json';
|
||||
|
||||
input.onchange = async (e: any) => {
|
||||
const file = e.target?.files?.[0];
|
||||
if (!file) {
|
||||
setSearching(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const text = await file.text();
|
||||
const config = JSON.parse(text);
|
||||
|
||||
if (config.projectId === projectId) {
|
||||
setFileFound(true);
|
||||
toast.success("File verified!", {
|
||||
description: "Your .vibn file is correctly configured for this project",
|
||||
});
|
||||
} else {
|
||||
setFileFound(false);
|
||||
toast.error("Project ID mismatch", {
|
||||
description: "This .vibn file is for a different project",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setFileFound(false);
|
||||
toast.error("Invalid file", {
|
||||
description: "Could not read or parse the .vibn file",
|
||||
});
|
||||
}
|
||||
|
||||
setSearching(false);
|
||||
};
|
||||
|
||||
input.click();
|
||||
} catch (error) {
|
||||
toast.error("Failed to search for file");
|
||||
setSearching(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
const blob = new Blob([configContent], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = ".vibn";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
toast.success("Configuration downloaded!", {
|
||||
description: "Save it to your project root and restart Cursor",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start h-auto py-2 px-3"
|
||||
onClick={() => setShowDialog(true)}
|
||||
>
|
||||
<FileText className="h-4 w-4 mr-2 flex-shrink-0" />
|
||||
<span className="text-xs">Link Workspace</span>
|
||||
</Button>
|
||||
|
||||
<Dialog open={showDialog} onOpenChange={setShowDialog}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<FileText className="h-5 w-5" />
|
||||
Link Your Workspace
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Add a <code className="text-xs bg-muted px-1 py-0.5 rounded">.vibn</code> file to your project root so the Cursor extension automatically tracks sessions.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Primary Action */}
|
||||
<Button onClick={handleDownload} className="w-full" size="lg">
|
||||
<Download className="h-5 w-5 mr-2" />
|
||||
Download .vibn
|
||||
</Button>
|
||||
|
||||
{/* Instructions */}
|
||||
<div className="text-sm space-y-3 bg-muted/50 p-3 rounded-md">
|
||||
<p className="font-medium">Setup Steps:</p>
|
||||
<ol className="text-xs text-muted-foreground space-y-1 list-decimal ml-4">
|
||||
<li>Download the file above</li>
|
||||
<li>Save it to your project root as <code className="bg-background px-1 rounded">.vibn</code></li>
|
||||
<li>Commit and push to GitHub</li>
|
||||
<li>Extension will auto-link sessions to this project</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
{/* Verification */}
|
||||
<div className="pt-2 space-y-2">
|
||||
<Button
|
||||
onClick={handleSearchFile}
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
size="sm"
|
||||
disabled={searching}
|
||||
>
|
||||
{searching ? (
|
||||
<>
|
||||
<Search className="h-4 w-4 mr-2 animate-spin" />
|
||||
Verifying...
|
||||
</>
|
||||
) : fileFound === true ? (
|
||||
<>
|
||||
<CheckCircle2 className="h-4 w-4 mr-2 text-green-600" />
|
||||
✓ File Found
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Search className="h-4 w-4 mr-2" />
|
||||
Verify File Added
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{fileFound === false && (
|
||||
<p className="text-xs text-red-600 text-center">File not found or incorrect projectId</p>
|
||||
)}
|
||||
{fileFound === true && (
|
||||
<p className="text-xs text-green-600 text-center">Ready! Push to GitHub and you're all set.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user