Skip to content

Commit 001312b

Browse files
author
Miriad
committed
feat: build Sponsors pipeline page + Settings page with real Sanity data
1 parent 94970b3 commit 001312b

5 files changed

Lines changed: 699 additions & 105 deletions

File tree

Lines changed: 259 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,261 @@
1+
import {
2+
Card,
3+
CardContent,
4+
CardDescription,
5+
CardHeader,
6+
CardTitle,
7+
} from "@/components/ui/card";
8+
import { Input } from "@/components/ui/input";
9+
import { Label } from "@/components/ui/label";
10+
import { Badge } from "@/components/ui/badge";
11+
import { Separator } from "@/components/ui/separator";
12+
13+
const PUBLISH_DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
14+
const DEFAULT_PUBLISH_DAYS = ["Mon", "Wed", "Fri"];
15+
16+
const CONTENT_CATEGORIES = [
17+
"JavaScript",
18+
"TypeScript",
19+
"React",
20+
"Next.js",
21+
"Angular",
22+
"Svelte",
23+
"Node.js",
24+
"CSS",
25+
"DevOps",
26+
"AI / ML",
27+
"Web Performance",
28+
"Tooling",
29+
];
30+
31+
const RATE_CARD_TIERS = [
32+
{
33+
name: "Pre-roll Mention",
34+
description: "15-second sponsor mention at the start of the video",
35+
price: "$200",
36+
},
37+
{
38+
name: "Mid-roll Segment",
39+
description: "60-second dedicated sponsor segment mid-video",
40+
price: "$500",
41+
},
42+
{
43+
name: "Dedicated Video",
44+
description: "Full sponsored video with product deep-dive",
45+
price: "$1,500",
46+
},
47+
];
48+
49+
const INTEGRATIONS = [
50+
{
51+
name: "Sanity",
52+
envVar: "SANITY_API_TOKEN",
53+
description: "Content management and data store",
54+
},
55+
{
56+
name: "YouTube",
57+
envVar: "YOUTUBE_API_KEY",
58+
description: "Video publishing and analytics",
59+
},
60+
{
61+
name: "Supabase",
62+
envVar: "NEXT_PUBLIC_SUPABASE_URL",
63+
description: "Authentication and database",
64+
},
65+
{
66+
name: "ElevenLabs",
67+
envVar: "ELEVENLABS_API_KEY",
68+
description: "AI voice generation for videos",
69+
},
70+
{
71+
name: "OpenAI",
72+
envVar: "OPENAI_API_KEY",
73+
description: "Script generation and content AI",
74+
},
75+
{
76+
name: "Stripe",
77+
envVar: "STRIPE_SECRET_KEY",
78+
description: "Sponsor payment processing",
79+
},
80+
{
81+
name: "Resend",
82+
envVar: "RESEND_API_KEY",
83+
description: "Transactional email delivery",
84+
},
85+
];
86+
87+
function IntegrationDot({ connected }: { connected: boolean }) {
88+
return (
89+
<span
90+
className={`inline-block h-2.5 w-2.5 rounded-full ${
91+
connected
92+
? "bg-green-500"
93+
: "bg-gray-300 dark:bg-gray-600"
94+
}`}
95+
/>
96+
);
97+
}
98+
199
export default function SettingsPage() {
2-
return (
3-
<div className="flex flex-col gap-6">
4-
<div>
5-
<h1 className="text-3xl font-bold tracking-tight">Settings</h1>
6-
<p className="text-muted-foreground">
7-
Configure your content engine — cadence, categories, and rate card.
8-
</p>
9-
</div>
10-
11-
<div className="grid gap-4 md:grid-cols-2">
12-
<div className="rounded-lg border p-6">
13-
<h2 className="text-lg font-semibold">Publishing Cadence</h2>
14-
<p className="mt-2 text-sm text-muted-foreground">
15-
Set the publishing schedule — how many videos per week, preferred
16-
publish days, and time slots. Controls the automated pipeline
17-
pacing.
18-
</p>
19-
<div className="mt-4 rounded-md bg-muted p-4 text-sm text-muted-foreground">
20-
📅 Cadence settings coming in Phase 1b
21-
</div>
22-
</div>
23-
24-
<div className="rounded-lg border p-6">
25-
<h2 className="text-lg font-semibold">Categories</h2>
26-
<p className="mt-2 text-sm text-muted-foreground">
27-
Manage content categories and tags — used for content idea
28-
classification and YouTube metadata.
29-
</p>
30-
<div className="mt-4 rounded-md bg-muted p-4 text-sm text-muted-foreground">
31-
🏷️ Category management coming in Phase 1b
32-
</div>
33-
</div>
34-
35-
<div className="rounded-lg border p-6">
36-
<h2 className="text-lg font-semibold">Sponsor Rate Card</h2>
37-
<p className="mt-2 text-sm text-muted-foreground">
38-
Define sponsorship tiers, pricing, and deliverables. Used by the
39-
sponsor portal and pipeline.
40-
</p>
41-
<div className="mt-4 rounded-md bg-muted p-4 text-sm text-muted-foreground">
42-
💳 Rate card editor coming in Phase 1c
43-
</div>
44-
</div>
45-
46-
<div className="rounded-lg border p-6">
47-
<h2 className="text-lg font-semibold">Integrations</h2>
48-
<p className="mt-2 text-sm text-muted-foreground">
49-
API keys and connections — YouTube API, Sanity webhooks, and
50-
notification channels.
51-
</p>
52-
<div className="mt-4 rounded-md bg-muted p-4 text-sm text-muted-foreground">
53-
🔌 Integration settings coming in Phase 2
54-
</div>
55-
</div>
56-
</div>
57-
</div>
58-
)
100+
// Check which env vars are likely set (server component has access)
101+
const integrationStatus = INTEGRATIONS.map((integration) => ({
102+
...integration,
103+
connected: !!process.env[integration.envVar],
104+
}));
105+
106+
return (
107+
<div className="flex flex-col gap-6">
108+
<div>
109+
<h1 className="text-3xl font-bold tracking-tight">Settings</h1>
110+
<p className="text-muted-foreground">
111+
Configure your content engine — cadence, categories, and rate card.
112+
</p>
113+
</div>
114+
115+
<div className="rounded-lg border border-dashed bg-muted/50 p-4 text-sm text-muted-foreground">
116+
<strong>Note:</strong> Settings are currently read-only. Editing will be
117+
enabled in a future phase once a settings schema is added to Sanity.
118+
</div>
119+
120+
<div className="grid gap-6 md:grid-cols-2">
121+
{/* Publishing Cadence */}
122+
<Card>
123+
<CardHeader>
124+
<CardTitle>Publishing Cadence</CardTitle>
125+
<CardDescription>
126+
Control how often videos are published and on which days.
127+
</CardDescription>
128+
</CardHeader>
129+
<CardContent className="space-y-4">
130+
<div className="space-y-2">
131+
<Label htmlFor="videos-per-week">Videos per week</Label>
132+
<Input
133+
id="videos-per-week"
134+
type="number"
135+
defaultValue={3}
136+
min={1}
137+
max={7}
138+
disabled
139+
className="w-24"
140+
/>
141+
</div>
142+
<Separator />
143+
<div className="space-y-2">
144+
<Label>Preferred publish days</Label>
145+
<div className="flex flex-wrap gap-2">
146+
{PUBLISH_DAYS.map((day) => (
147+
<Badge
148+
key={day}
149+
variant={
150+
DEFAULT_PUBLISH_DAYS.includes(day)
151+
? "default"
152+
: "outline"
153+
}
154+
className="cursor-default"
155+
>
156+
{day}
157+
</Badge>
158+
))}
159+
</div>
160+
</div>
161+
<p className="text-xs text-muted-foreground">
162+
Settings will be stored in Sanity once a settings schema is
163+
created.
164+
</p>
165+
</CardContent>
166+
</Card>
167+
168+
{/* Content Categories */}
169+
<Card>
170+
<CardHeader>
171+
<CardTitle>Content Categories</CardTitle>
172+
<CardDescription>
173+
Categories used for content idea classification and YouTube
174+
metadata.
175+
</CardDescription>
176+
</CardHeader>
177+
<CardContent className="space-y-4">
178+
<div className="flex flex-wrap gap-2">
179+
{CONTENT_CATEGORIES.map((category) => (
180+
<Badge key={category} variant="secondary">
181+
{category}
182+
</Badge>
183+
))}
184+
</div>
185+
<p className="text-xs text-muted-foreground">
186+
Custom category management will be available in a future phase.
187+
</p>
188+
</CardContent>
189+
</Card>
190+
191+
{/* Sponsor Rate Card */}
192+
<Card>
193+
<CardHeader>
194+
<CardTitle>Sponsor Rate Card</CardTitle>
195+
<CardDescription>
196+
Sponsorship tiers and pricing used by the sponsor portal and
197+
pipeline.
198+
</CardDescription>
199+
</CardHeader>
200+
<CardContent>
201+
<div className="space-y-3">
202+
{RATE_CARD_TIERS.map((tier) => (
203+
<div
204+
key={tier.name}
205+
className="flex items-center justify-between rounded-lg border p-3"
206+
>
207+
<div>
208+
<p className="text-sm font-medium">{tier.name}</p>
209+
<p className="text-xs text-muted-foreground">
210+
{tier.description}
211+
</p>
212+
</div>
213+
<span className="text-sm font-semibold">{tier.price}</span>
214+
</div>
215+
))}
216+
</div>
217+
<p className="mt-4 text-xs text-muted-foreground">
218+
Rate card editing will be available once the sponsor rate card
219+
schema is finalized.
220+
</p>
221+
</CardContent>
222+
</Card>
223+
224+
{/* Integrations Status */}
225+
<Card>
226+
<CardHeader>
227+
<CardTitle>Integrations Status</CardTitle>
228+
<CardDescription>
229+
Connection status for external services. Green indicates the
230+
environment variable is configured.
231+
</CardDescription>
232+
</CardHeader>
233+
<CardContent>
234+
<div className="space-y-3">
235+
{integrationStatus.map((integration) => (
236+
<div
237+
key={integration.name}
238+
className="flex items-center gap-3"
239+
>
240+
<IntegrationDot connected={integration.connected} />
241+
<div className="flex-1">
242+
<p className="text-sm font-medium">{integration.name}</p>
243+
<p className="text-xs text-muted-foreground">
244+
{integration.description}
245+
</p>
246+
</div>
247+
<Badge
248+
variant={integration.connected ? "default" : "outline"}
249+
className="text-xs"
250+
>
251+
{integration.connected ? "Connected" : "Not configured"}
252+
</Badge>
253+
</div>
254+
))}
255+
</div>
256+
</CardContent>
257+
</Card>
258+
</div>
259+
</div>
260+
);
59261
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use server";
2+
3+
import { revalidatePath } from "next/cache";
4+
import { dashboardClient } from "@/lib/sanity/dashboard";
5+
6+
export async function updateLeadStatus(id: string, status: string) {
7+
await dashboardClient.patch(id).set({ status }).commit();
8+
revalidatePath("/dashboard/sponsors");
9+
}

0 commit comments

Comments
 (0)