design(dashboard): remove unused routes, rename existing routes to match Base44 menu structure
This commit is contained in:
@@ -1,66 +0,0 @@
|
||||
import { Suspense } from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card";
|
||||
import { Loader2, CreditCard, ArrowRight, ShieldCheck, Zap } from "lucide-react";
|
||||
|
||||
export default async function BillingPage(props: { params: Promise<{ projectId: string }> }) {
|
||||
const { projectId } = await props.params;
|
||||
|
||||
return (
|
||||
<div style={{ padding: "40px 48px", maxWidth: 1000, margin: "0 auto", fontFamily: "var(--font-inter), sans-serif" }}>
|
||||
<div style={{ marginBottom: 32 }}>
|
||||
<h1 style={{ fontFamily: "var(--font-lora), serif", fontSize: "1.8rem", color: "#1a1a1a", marginBottom: 8 }}>
|
||||
Payments & Billing
|
||||
</h1>
|
||||
<p style={{ color: "#6b6560", fontSize: "0.95rem" }}>
|
||||
Connect your bank account to start charging customers for this project.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr", gap: "24px" }}>
|
||||
|
||||
{/* Onboarding Card */}
|
||||
<Card style={{ border: "1px solid #6366f1", boxShadow: "0 4px 14px rgba(99, 102, 241, 0.08)" }}>
|
||||
<CardHeader>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 8 }}>
|
||||
<div style={{ background: "#e0e7ff", padding: 8, borderRadius: 8, color: "#4f46e5" }}>
|
||||
<CreditCard style={{ width: 24, height: 24 }} />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle style={{ fontSize: "1.2rem" }}>Accept Payments with Stripe</CardTitle>
|
||||
<CardDescription>Setup takes 3 minutes. Vibn handles the code.</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ background: "#f8fafc", padding: 20, borderRadius: 8, marginBottom: 24 }}>
|
||||
<h4 style={{ fontWeight: 600, fontSize: "0.9rem", color: "#111827", marginBottom: 12 }}>What you get immediately:</h4>
|
||||
<ul style={{ display: "flex", flexDirection: "column", gap: 12, margin: 0, padding: 0, listStyle: "none" }}>
|
||||
<li style={{ display: "flex", alignItems: "flex-start", gap: 8, fontSize: "0.85rem", color: "#4b5563" }}>
|
||||
<Zap style={{ width: 16, height: 16, color: "#eab308", flexShrink: 0 }} />
|
||||
<span><strong>AI Auto-Wiring:</strong> The Vibn AI will automatically inject your secure Stripe keys into your live Coolify application.</span>
|
||||
</li>
|
||||
<li style={{ display: "flex", alignItems: "flex-start", gap: 8, fontSize: "0.85rem", color: "#4b5563" }}>
|
||||
<ShieldCheck style={{ width: 16, height: 16, color: "#22c55e", flexShrink: 0 }} />
|
||||
<span><strong>Instant Compliance:</strong> Securely accept Apple Pay, Google Pay, and credit cards with PCI compliance handled automatically.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p style={{ fontSize: "0.85rem", color: "#6b7280", lineHeight: 1.5 }}>
|
||||
By connecting, you agree to Stripe's Services Agreement. Vibn takes a small 1% platform fee on successful transactions to keep the AI platform running.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter style={{ background: "#f9fafb", borderTop: "1px solid #f3f4f6", padding: "16px 24px" }}>
|
||||
<button
|
||||
className="bg-indigo-600 hover:bg-indigo-700 text-white transition-colors"
|
||||
style={{ padding: "10px 20px", borderRadius: 6, fontSize: "0.9rem", fontWeight: 500, display: "flex", alignItems: "center", gap: 8 }}
|
||||
>
|
||||
Connect with Stripe <ArrowRight style={{ width: 16, height: 16 }} />
|
||||
</button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ type Selection =
|
||||
| { type: "image"; uuid: string }
|
||||
| null;
|
||||
|
||||
export default function ProductTab() {
|
||||
export default function CodeTab() {
|
||||
const params = useParams();
|
||||
const projectId = params.projectId as string;
|
||||
const { anatomy, loading, error } = useAnatomy(projectId);
|
||||
@@ -1,351 +0,0 @@
|
||||
import { BigQuery } from '@google-cloud/bigquery';
|
||||
import { Suspense } from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Loader2, Users, Target, Search, Database } from "lucide-react";
|
||||
|
||||
async function getMarketData(projectId: string) {
|
||||
let bqOptions: any = { projectId: process.env.GCP_PROJECT_ID || 'master-ai-484822' };
|
||||
if (process.env.GOOGLE_SERVICE_ACCOUNT_KEY_B64) {
|
||||
try {
|
||||
const saStr = Buffer.from(process.env.GOOGLE_SERVICE_ACCOUNT_KEY_B64, 'base64').toString('utf8');
|
||||
bqOptions.credentials = JSON.parse(saStr);
|
||||
bqOptions.projectId = bqOptions.credentials.project_id;
|
||||
} catch (e) {}
|
||||
}
|
||||
const bigquery = new BigQuery(bqOptions);
|
||||
|
||||
try {
|
||||
const [leads] = await bigquery.query({
|
||||
query: `SELECT * FROM \`master-ai-484822.vibn_market_data.market_leads\` WHERE project_id = @projectId OR project_id = 'SYSTEM_BACKFILL' LIMIT 50`,
|
||||
params: { projectId }
|
||||
});
|
||||
|
||||
const [aggregations] = await bigquery.query({
|
||||
query: `SELECT * FROM \`master-ai-484822.vibn_market_data.market_aggregations\` ORDER BY last_updated DESC LIMIT 1`
|
||||
});
|
||||
|
||||
const [competitors] = await bigquery.query({
|
||||
query: `SELECT * FROM \`master-ai-484822.vibn_market_data.software_providers_seo\` ORDER BY last_updated DESC LIMIT 10`
|
||||
});
|
||||
|
||||
return { leads, aggregations: aggregations[0], competitors };
|
||||
} catch (err) {
|
||||
console.error("BigQuery Error:", err);
|
||||
return { leads: [], aggregations: null, competitors: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export default async function MarketPage(props: { params: Promise<{ projectId: string }> }) {
|
||||
const { projectId } = await props.params;
|
||||
|
||||
return (
|
||||
<div style={{ padding: "40px 48px", maxWidth: 1200, margin: "0 auto", fontFamily: "var(--font-inter), sans-serif" }}>
|
||||
<div style={{ marginBottom: 32 }}>
|
||||
<h1 style={{ fontFamily: "var(--font-lora), serif", fontSize: "1.8rem", color: "#1a1a1a", marginBottom: 8 }}>
|
||||
Market Intelligence
|
||||
</h1>
|
||||
<p style={{ color: "#6b6560", fontSize: "0.95rem" }}>
|
||||
Real-time TAM, verified leads, and competitor teardowns from the Vibn Data Co-op.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Suspense fallback={<div className="flex justify-center p-12"><Loader2 className="animate-spin w-8 h-8 text-gray-400" /></div>}>
|
||||
<MarketDataDisplay projectId={projectId} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function MarketDataDisplay({ projectId }: { projectId: string }) {
|
||||
const data = await getMarketData(projectId);
|
||||
|
||||
if (!data.aggregations && data.leads.length === 0) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="py-16 text-center" style={{ paddingTop: '4rem', paddingBottom: '4rem', textAlign: 'center' }}>
|
||||
<Database style={{ width: 48, height: 48, margin: '0 auto 16px', color: '#d0ccc4' }} />
|
||||
<h3 style={{ fontSize: '1.125rem', fontWeight: 500, color: '#111827', marginBottom: '8px' }}>No Market Data Yet</h3>
|
||||
<p style={{ color: '#6b7280', maxWidth: '28rem', margin: '0 auto' }}>
|
||||
Ask the Vibn AI to run market research for your niche to populate this dashboard with leads, competitors, and SEO insights.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "32px" }}>
|
||||
{/* Overview Cards */}
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: "24px" }}>
|
||||
<Card>
|
||||
<CardHeader style={{ paddingBottom: '8px' }}>
|
||||
<CardTitle style={{ fontSize: '0.875rem', fontWeight: 500, color: '#6b7280', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<Users style={{ width: 16, height: 16 }} /> Total Addressable Market
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ fontSize: '1.875rem', fontWeight: 600, color: '#111827' }}>
|
||||
{data.aggregations?.total_market_size?.toLocaleString() || "..."}
|
||||
</div>
|
||||
<p style={{ fontSize: '0.75rem', color: '#6b7280', marginTop: '4px' }}>Verified businesses in selected region</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader style={{ paddingBottom: '8px' }}>
|
||||
<CardTitle style={{ fontSize: '0.875rem', fontWeight: 500, color: '#6b7280', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<Target style={{ width: 16, height: 16 }} /> Qualified Leads Captured
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ fontSize: '1.875rem', fontWeight: 600, color: '#111827' }}>
|
||||
{data.leads.length}
|
||||
</div>
|
||||
<p style={{ fontSize: '0.75rem', color: '#6b7280', marginTop: '4px' }}>Ready for cold outreach</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader style={{ paddingBottom: '8px' }}>
|
||||
<CardTitle style={{ fontSize: '0.875rem', fontWeight: 500, color: '#6b7280', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<Search style={{ width: 16, height: 16 }} /> Tech Debt Indicator
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ fontSize: '1.875rem', fontWeight: 600, color: '#111827' }}>
|
||||
{data.aggregations ? Math.round((data.aggregations.websites_count / data.aggregations.total_market_size) * 100) : 0}%
|
||||
</div>
|
||||
<p style={{ fontSize: '0.75rem', color: '#6b7280', marginTop: '4px' }}>Of TAM have a website</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "32px" }}>
|
||||
{/* Pain Points */}
|
||||
{data.aggregations && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Customer Pain Points</CardTitle>
|
||||
<CardDescription>Extracted from Google Reviews</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: "8px" }}>
|
||||
{Object.entries(typeof data.aggregations.customer_pain_points === 'string' ? JSON.parse(data.aggregations.customer_pain_points) : data.aggregations.customer_pain_points || {})
|
||||
.sort(([, a], [, b]) => (b as number) - (a as number))
|
||||
.slice(0, 15)
|
||||
.map(([topic, count]) => (
|
||||
<span key={topic} style={{ padding: "4px 12px", background: "#f0ede8", color: "#6b6560", fontSize: "0.75rem", fontWeight: 500, borderRadius: "9999px" }}>
|
||||
{topic} ({(count as number).toLocaleString()})
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Sub-niches */}
|
||||
{data.aggregations && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Market Sub-Niches</CardTitle>
|
||||
<CardDescription>Breakdown of primary category</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||
{Object.entries(typeof data.aggregations.sub_niches === 'string' ? JSON.parse(data.aggregations.sub_niches) : data.aggregations.sub_niches || {})
|
||||
.sort(([, a], [, b]) => (b as number) - (a as number))
|
||||
.slice(0, 6)
|
||||
.map(([topic, count]) => (
|
||||
<div key={topic} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "0.875rem" }}>
|
||||
<span style={{ color: "#374151", textTransform: "capitalize" }}>{topic.replace(/_/g, ' ')}</span>
|
||||
<span style={{ fontWeight: 500 }}>{(count as number).toLocaleString()}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Competitors */}
|
||||
{data.competitors.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>SaaS Competitors & Ad Spend</CardTitle>
|
||||
<CardDescription>Top incumbents and their Google Ads budget</CardDescription>
|
||||
</CardHeader>
|
||||
<div style={{ overflowX: "auto" }}>
|
||||
<table style={{ width: "100%", fontSize: "0.875rem", textAlign: "left" }}>
|
||||
<thead style={{ fontSize: "0.75rem", color: "#6b7280", textTransform: "uppercase", background: "#f9fafb", borderBottom: "1px solid #e5e7eb" }}>
|
||||
<tr>
|
||||
<th style={{ padding: "12px 24px" }}>Domain</th>
|
||||
<th style={{ padding: "12px 24px" }}>Monthly Ad Spend</th>
|
||||
<th style={{ padding: "12px 24px" }}>Organic Traffic</th>
|
||||
<th style={{ padding: "12px 24px" }}>Top Paid Keywords</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.competitors.map((comp: any) => {
|
||||
const paidKw = typeof comp.top_paid_keywords === 'string' ? JSON.parse(comp.top_paid_keywords) : comp.top_paid_keywords;
|
||||
return (
|
||||
<tr key={comp.domain} style={{ background: "#fff", borderBottom: "1px solid #e5e7eb" }}>
|
||||
<td style={{ padding: "16px 24px", fontWeight: 500, color: "#111827" }}>{comp.domain}</td>
|
||||
<td style={{ padding: "16px 24px", color: "#dc2626", fontWeight: 500 }}>
|
||||
${Math.round(comp.ad_spend_usd).toLocaleString()}
|
||||
</td>
|
||||
<td style={{ padding: "16px 24px" }}>
|
||||
{Math.round(comp.organic_traffic).toLocaleString()} /mo
|
||||
</td>
|
||||
<td style={{ padding: "16px 24px" }}>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: "4px" }}>
|
||||
{(paidKw || []).slice(0, 3).map((kw: string) => (
|
||||
<span key={kw} style={{ padding: "2px 8px", background: "#eff6ff", color: "#1d4ed8", fontSize: "0.625rem", borderRadius: "4px" }}>
|
||||
{kw}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Leads Table */}
|
||||
{data.leads.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Verified Leads</CardTitle>
|
||||
<CardDescription>First {data.leads.length} contacts matching your target market</CardDescription>
|
||||
</CardHeader>
|
||||
<div style={{ overflowX: "auto" }}>
|
||||
<table style={{ width: "100%", fontSize: "0.875rem", textAlign: "left" }}>
|
||||
<thead style={{ fontSize: "0.75rem", color: "#6b7280", textTransform: "uppercase", background: "#f9fafb", borderBottom: "1px solid #e5e7eb" }}>
|
||||
<tr>
|
||||
<th style={{ padding: "12px 24px" }}>Business Name</th>
|
||||
<th style={{ padding: "12px 24px" }}>Location</th>
|
||||
<th style={{ padding: "12px 24px" }}>Rating</th>
|
||||
<th style={{ padding: "12px 24px" }}>Contact</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.leads.map((lead: any) => {
|
||||
const emails = typeof lead.emails === 'string' ? JSON.parse(lead.emails) : lead.emails;
|
||||
return (
|
||||
<tr key={lead.place_id} style={{ background: "#fff", borderBottom: "1px solid #e5e7eb" }}>
|
||||
<td style={{ padding: "16px 24px", fontWeight: 500, color: "#111827" }}>
|
||||
{lead.name}
|
||||
{lead.website && (
|
||||
<a href={lead.website.startsWith('http') ? lead.website : `https://${lead.website}`} target="_blank" rel="noreferrer" style={{ display: "block", color: "#2563eb", fontSize: "0.75rem", marginTop: "4px", textDecoration: "none" }}>
|
||||
{lead.website.replace(/^https?:\/\//, '')}
|
||||
</a>
|
||||
)}
|
||||
</td>
|
||||
<td style={{ padding: "16px 24px" }}>
|
||||
{lead.city}, {lead.region}
|
||||
</td>
|
||||
<td style={{ padding: "16px 24px" }}>
|
||||
{lead.rating ? `${lead.rating} ⭐ (${lead.reviews_count})` : 'N/A'}
|
||||
</td>
|
||||
<td style={{ padding: "16px 24px" }}>
|
||||
<div style={{ fontSize: "0.75rem", color: "#4b5563" }}>
|
||||
{lead.phone && <div style={{ marginBottom: "4px" }}>{lead.phone}</div>}
|
||||
{(emails || []).map((e: string) => (
|
||||
<a key={e} href={`mailto:${e}`} style={{ display: "block", color: "#2563eb", textDecoration: "none" }}>
|
||||
{e}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* ───────────────────────────────────────────────────────────── */}
|
||||
{/* GO-TO-MARKET (GTM) STRATEGY ENGINE */}
|
||||
{/* ───────────────────────────────────────────────────────────── */}
|
||||
<div style={{ marginTop: "48px", borderTop: "1px solid #e5e7eb", paddingTop: "32px" }}>
|
||||
<div style={{ marginBottom: 24, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<div>
|
||||
<h2 style={{ fontFamily: "var(--font-lora), serif", fontSize: "1.5rem", color: "#111827", marginBottom: 4 }}>
|
||||
Go-To-Market Strategy
|
||||
</h2>
|
||||
<p style={{ color: "#6b7280", fontSize: "0.875rem" }}>
|
||||
Synthesize market data into an actionable marketing and positioning plan.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
style={{
|
||||
background: "linear-gradient(to bottom right, #4f46e5, #312e81)",
|
||||
color: "#fff",
|
||||
padding: "8px 16px",
|
||||
borderRadius: 6,
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: 500,
|
||||
boxShadow: "0 2px 4px rgba(79, 70, 229, 0.2)",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8
|
||||
}}
|
||||
onClick={() => alert("This will deduct 500 AI Credits and generate the GTM strategy.")}
|
||||
>
|
||||
<span style={{ fontSize: "1.1rem", lineHeight: 1 }}>✨</span> Generate GTM Plan (500 Credits)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "24px", opacity: 0.5, pointerEvents: "none" }}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Brand Positioning</CardTitle>
|
||||
<CardDescription>Value prop, target persona, and wedge strategy.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ background: "#f9fafb", padding: 24, borderRadius: 8, textAlign: "center", border: "1px dashed #d1d5db" }}>
|
||||
<p style={{ fontSize: "0.875rem", color: "#6b7280" }}>Generate a plan to reveal the positioning strategy.</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>SEO & Content Engine</CardTitle>
|
||||
<CardDescription>Keyword gaps and initial blog architecture.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ background: "#f9fafb", padding: 24, borderRadius: 8, textAlign: "center", border: "1px dashed #d1d5db" }}>
|
||||
<p style={{ fontSize: "0.875rem", color: "#6b7280" }}>Generate a plan to reveal keyword targets.</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: "24px", opacity: 0.5, pointerEvents: "none" }}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Social Media Automation
|
||||
<span style={{ fontSize: "0.65rem", background: "#f3f4f6", padding: "2px 6px", borderRadius: 4, fontWeight: 600, color: "#4b5563" }}>POWERED BY MISSINGLETTR</span>
|
||||
</CardTitle>
|
||||
<CardDescription>A 3-month automated drip campaign based on your positioning.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ background: "#f9fafb", padding: 48, borderRadius: 8, textAlign: "center", border: "1px dashed #d1d5db" }}>
|
||||
<p style={{ fontSize: "0.875rem", color: "#6b7280" }}>Generate a plan to automatically orchestrate your social media strategy via Missinglettr.</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ type Preview = Anatomy["hosting"]["previews"][number];
|
||||
// Main component
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
export default function HostingTab() {
|
||||
export default function OverviewTab() {
|
||||
const params = useParams();
|
||||
const projectId = params.projectId as string;
|
||||
const { anatomy, loading, error } = useAnatomy(projectId, { pollMs: 8000 });
|
||||
@@ -121,7 +121,7 @@ export default function PlanTab() {
|
||||
<div style={railItems}>
|
||||
<RailItem
|
||||
id="objective"
|
||||
label="Project Objective"
|
||||
label="Product Brief"
|
||||
icon={<Target />}
|
||||
selectedId={selectedId}
|
||||
onClick={setSelectedId}
|
||||
@@ -282,7 +282,7 @@ function ObjectivePanel({
|
||||
<div style={panel}>
|
||||
<div style={panelHeader}>
|
||||
<div>
|
||||
<h2 style={panelTitle}>Project Objective</h2>
|
||||
<h2 style={panelTitle}>Product Brief</h2>
|
||||
<p style={panelDesc}>
|
||||
The high-level business case and elevator pitch.
|
||||
</p>
|
||||
|
||||
@@ -112,7 +112,7 @@ function categoryDef(key: CategoryKey): CategoryDef {
|
||||
return CATEGORIES.find(c => c.key === key)!;
|
||||
}
|
||||
|
||||
export default function InfrastructureTab() {
|
||||
export default function SecurityTab() {
|
||||
const params = useParams();
|
||||
const projectId = params.projectId as string;
|
||||
const { anatomy, loading, error } = useAnatomy(projectId);
|
||||
@@ -6,18 +6,14 @@ import { usePathname } from "next/navigation";
|
||||
import {
|
||||
Search,
|
||||
LayoutGrid,
|
||||
Users,
|
||||
ClipboardList,
|
||||
Database,
|
||||
BarChart2,
|
||||
TrendingUp,
|
||||
Globe,
|
||||
Plug,
|
||||
ShieldCheck,
|
||||
Code2,
|
||||
Bot,
|
||||
Zap,
|
||||
Terminal,
|
||||
FileJson,
|
||||
Settings,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
@@ -56,29 +52,19 @@ export function DashboardSidebar({
|
||||
|
||||
const menuItems = [
|
||||
{ segment: "overview", label: "Overview", Icon: LayoutGrid },
|
||||
{ segment: "users", label: "Users", Icon: Users },
|
||||
{ segment: "plan", label: "Plan & Specs", Icon: ClipboardList },
|
||||
{ segment: "data", label: "Data", Icon: Database },
|
||||
{
|
||||
segment: "data",
|
||||
label: "Data",
|
||||
Icon: Database,
|
||||
hasChildren: true,
|
||||
},
|
||||
{ segment: "analytics", label: "Analytics", Icon: BarChart2 },
|
||||
{
|
||||
segment: "marketing",
|
||||
label: "Marketing",
|
||||
Icon: TrendingUp,
|
||||
badge: "New",
|
||||
hasChildren: true,
|
||||
segment: "analytics",
|
||||
label: "Analytics",
|
||||
Icon: BarChart2,
|
||||
badge: "Soon",
|
||||
},
|
||||
{ segment: "domains", label: "Domains", Icon: Globe },
|
||||
{ segment: "integrations", label: "Integrations", Icon: Plug },
|
||||
{ segment: "security", label: "Security", Icon: ShieldCheck },
|
||||
{ segment: "code", label: "Code", Icon: Code2 },
|
||||
{ segment: "agents", label: "Agents", Icon: Bot, badge: "New" },
|
||||
{ segment: "automations", label: "Automations", Icon: Zap },
|
||||
{ segment: "logs", label: "Logs", Icon: Terminal },
|
||||
{ segment: "api", label: "API", Icon: FileJson },
|
||||
{
|
||||
segment: "settings",
|
||||
label: "Settings",
|
||||
@@ -87,7 +73,6 @@ export function DashboardSidebar({
|
||||
children: [
|
||||
{ segment: "settings/app", label: "App Settings" },
|
||||
{ segment: "settings/auth", label: "Authentication" },
|
||||
{ segment: "settings/template", label: "App Template" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user