← back to jiwon706878-glitch__bossboard

Function bodies 164 total

All specs Real LLM only Function bodies
AdminOverviewPage function · typescript · L7-L97 (91 LOC)
src/app/(admin)/admin/page.tsx
export default async function AdminOverviewPage() {
  const supabase = createAdminClient();

  const [profilesRes, businessesRes, usageRes, subsRes] = await Promise.all([
    supabase.from("profiles").select("id, plan_id, created_at", { count: "exact" }),
    supabase.from("businesses").select("id", { count: "exact", head: true }),
    supabase.from("ai_usage").select("credits_used"),
    supabase.from("subscriptions").select("plan_id, status").eq("status", "active"),
  ]);

  const totalUsers = profilesRes.count ?? 0;
  const totalBusinesses = businessesRes.count ?? 0;
  const totalAiCalls = usageRes.data?.reduce((s, r) => s + r.credits_used, 0) ?? 0;

  // MRR calculation
  const activeSubs = subsRes.data ?? [];
  const mrr = activeSubs.reduce((sum, sub) => {
    const plan = plans[sub.plan_id as keyof typeof plans];
    return sum + (plan?.monthlyPrice ?? 0);
  }, 0);

  // Plan distribution
  const planCounts: Record<string, number> = {};
  for (const p of profilesRes.data ?? []) {
AdminRevenuePage function · typescript · L7-L175 (169 LOC)
src/app/(admin)/admin/revenue/page.tsx
export default async function AdminRevenuePage() {
  const supabase = createAdminClient();

  const { data: profiles } = await supabase
    .from("profiles")
    .select("plan_id");

  const { data: subscriptions } = await supabase
    .from("subscriptions")
    .select("plan_id, status, current_period_end, cancel_at_period_end");

  // Count users per plan
  const planCounts: Record<string, number> = {};
  for (const p of profiles ?? []) {
    planCounts[p.plan_id] = (planCounts[p.plan_id] || 0) + 1;
  }

  // Active paid subscribers
  const activeSubs = (subscriptions ?? []).filter(
    (s) => s.status === "active" && s.plan_id !== "free"
  );

  // MRR
  const mrr = activeSubs.reduce((sum, sub) => {
    const plan = plans[sub.plan_id as PlanId];
    return sum + (plan?.monthlyPrice ?? 0);
  }, 0);

  // ARR
  const arr = mrr * 12;

  // Churn risk (cancel_at_period_end = true)
  const churning = (subscriptions ?? []).filter(
    (s) => s.cancel_at_period_end
  ).length;

  // Past d
AdminUsagePage function · typescript · L9-L286 (278 LOC)
src/app/(admin)/admin/usage/page.tsx
export default async function AdminUsagePage() {
  const supabase = createAdminClient();

  // All usage data
  const { data: usage } = await supabase
    .from("ai_usage")
    .select("user_id, credits_used, feature, created_at")
    .order("created_at", { ascending: false });

  const allUsage = usage ?? [];

  // Total calls and credits
  const totalCalls = allUsage.length;
  const totalCredits = allUsage.reduce((s, r) => s + r.credits_used, 0);
  const estimatedCost = totalCredits * COST_PER_CALL;

  // This month
  const startOfMonth = new Date(
    new Date().getFullYear(),
    new Date().getMonth(),
    1
  ).toISOString();
  const thisMonth = allUsage.filter((u) => u.created_at >= startOfMonth);
  const monthlyCredits = thisMonth.reduce((s, r) => s + r.credits_used, 0);
  const monthlyCost = monthlyCredits * COST_PER_CALL;

  // By feature
  const byFeature: Record<string, number> = {};
  for (const row of allUsage) {
    byFeature[row.feature] = (byFeature[row.feature] || 0) +
AdminUsersPage function · typescript · L4-L45 (42 LOC)
src/app/(admin)/admin/users/page.tsx
export default async function AdminUsersPage() {
  const supabase = createAdminClient();

  // Fetch all profiles
  const { data: profiles } = await supabase
    .from("profiles")
    .select("id, full_name, plan_id, created_at, updated_at")
    .order("created_at", { ascending: false });

  // Fetch auth users for emails and last sign-in
  const {
    data: { users: authUsers },
  } = await supabase.auth.admin.listUsers({ perPage: 1000 });

  // Merge profile data with auth data
  const users = (profiles ?? []).map((profile) => {
    const authUser = authUsers?.find((u) => u.id === profile.id);
    return {
      id: profile.id,
      email: authUser?.email ?? "unknown",
      full_name: profile.full_name,
      plan_id: profile.plan_id,
      created_at: profile.created_at,
      last_sign_in: authUser?.last_sign_in_at ?? null,
      banned: authUser?.banned_until
        ? new Date(authUser.banned_until) > new Date()
        : false,
    };
  });

  return (
    <div className="spac
AdminLayout function · typescript · L8-L36 (29 LOC)
src/app/(admin)/layout.tsx
export default async function AdminLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user || user.email !== ADMIN_EMAIL) {
    redirect("/login");
  }

  return (
    <div className="flex h-screen overflow-hidden">
      <AdminSidebar className="hidden lg:flex" />
      <div className="flex flex-1 flex-col overflow-hidden">
        <header className="flex h-16 items-center justify-between border-b bg-card px-4 lg:px-6">
          <h2 className="text-sm font-medium text-muted-foreground">
            Admin Panel
          </h2>
          <ThemeToggle />
        </header>
        <main className="flex-1 overflow-y-auto p-4 lg:p-6">{children}</main>
      </div>
    </div>
  );
}
PATCH function · typescript · L7-L87 (81 LOC)
src/app/api/admin/users/route.ts
export async function PATCH(req: Request) {
  // Verify admin
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user || user.email !== ADMIN_EMAIL) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const { userId, action, plan_id } = await req.json();

  if (!userId || !action) {
    return NextResponse.json(
      { error: "userId and action are required" },
      { status: 400 }
    );
  }

  const admin = createAdminClient();

  switch (action) {
    case "change_plan": {
      if (!plan_id) {
        return NextResponse.json(
          { error: "plan_id is required" },
          { status: 400 }
        );
      }

      const { error } = await admin
        .from("profiles")
        .update({ plan_id })
        .eq("id", userId);

      if (error) {
        return NextResponse.json({ error: error.message }, { status: 500 });
      }

      // Also update subscriptions table if exists
   
POST function · typescript · L6-L56 (51 LOC)
src/app/api/ai/caption/route.ts
export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    return new Response("Unauthorized", { status: 401 });
  }

  const { businessId, description, tone, platform } = await req.json();

  // Get user plan
  const { data: profile } = await supabase
    .from("profiles")
    .select("plan_id")
    .eq("id", user.id)
    .single();

  const planId = (profile?.plan_id as "free" | "starter" | "pro" | "agency") ?? "free";

  const creditCheck = await checkCredits(user.id, planId);
  if (!creditCheck.allowed) {
    return new Response("AI credit limit reached. Please upgrade your plan.", {
      status: 429,
    });
  }

  const { data: business } = await supabase
    .from("businesses")
    .select("name, type")
    .eq("id", businessId)
    .single();

  const result = streamText({
    model: anthropic("claude-sonnet-4-20250514"),
    system: `You are a social media caption w
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
POST function · typescript · L6-L63 (58 LOC)
src/app/api/ai/review-reply/route.ts
export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    return new Response("Unauthorized", { status: 401 });
  }

  const { businessId, reviewerName, rating, reviewText, tone } =
    await req.json();

  // Get user plan
  const { data: profile } = await supabase
    .from("profiles")
    .select("plan_id")
    .eq("id", user.id)
    .single();

  const planId = (profile?.plan_id as "free" | "starter" | "pro" | "agency") ?? "free";

  // Check credits
  const creditCheck = await checkCredits(user.id, planId);
  if (!creditCheck.allowed) {
    return new Response("AI credit limit reached. Please upgrade your plan.", {
      status: 429,
    });
  }

  // Get business info
  const { data: business } = await supabase
    .from("businesses")
    .select("name, type")
    .eq("id", businessId)
    .single();

  const result = streamText({
    model: anthropic("claude-sonnet-4-
POST function · typescript · L6-L75 (70 LOC)
src/app/api/ai/script/route.ts
export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    return new Response("Unauthorized", { status: 401 });
  }

  const { businessId, format, topic, audience } = await req.json();

  const { data: profile } = await supabase
    .from("profiles")
    .select("plan_id")
    .eq("id", user.id)
    .single();

  const planId = (profile?.plan_id as "free" | "starter" | "pro" | "agency") ?? "free";

  const creditCheck = await checkCredits(user.id, planId);
  if (!creditCheck.allowed) {
    return new Response("AI credit limit reached. Please upgrade your plan.", {
      status: 429,
    });
  }

  const { data: business } = await supabase
    .from("businesses")
    .select("name, type")
    .eq("id", businessId)
    .single();

  const formatGuide: Record<string, string> = {
    tiktok: "TikTok video (15-60 seconds, fast-paced, trendy)",
    reel: "Instagram Reel (15-90 sec
POST function · typescript · L6-L149 (144 LOC)
src/app/api/paddle/webhook/route.ts
export async function POST(req: Request) {
  const body = await req.text();

  // TODO: verify webhook signature once PADDLE_WEBHOOK_SECRET is configured
  // const signature = req.headers.get("paddle-signature");

  let event: {
    event_type: string;
    data: Record<string, unknown>;
  };

  try {
    event = JSON.parse(body);
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }

  const supabase = createAdminClient();

  switch (event.event_type) {
    case "subscription.created":
    case "subscription.activated": {
      const data = event.data;
      const customData = (data.custom_data ?? {}) as Record<string, string>;
      const userId = customData.user_id;
      if (!userId) break;

      const items = data.items as Array<{ price: { id: string } }>;
      const priceId = items?.[0]?.price?.id;
      const plan = priceId ? getPlanByPaddlePriceId(priceId) : undefined;

      const currentPeriod = data.current_billing_period as {
        st
GET function · typescript · L4-L18 (15 LOC)
src/app/(auth)/auth/callback/route.ts
export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/dashboard";

  if (code) {
    const supabase = await createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  return NextResponse.redirect(`${origin}/login?error=auth`);
}
Confetti function · typescript · L18-L72 (55 LOC)
src/app/(auth)/auth/confirmed/page.tsx
function Confetti() {
  const [particles, setParticles] = useState<Particle[]>([]);

  useEffect(() => {
    const colors = [
      "#4F46E5", "#F59E0B", "#10B981", "#EF4444", "#8B5CF6",
      "#EC4899", "#06B6D4", "#F97316",
    ];
    const generated: Particle[] = Array.from({ length: 60 }, (_, i) => ({
      id: i,
      x: Math.random() * 100,
      color: colors[Math.floor(Math.random() * colors.length)],
      delay: Math.random() * 0.8,
      size: Math.random() * 6 + 4,
      drift: (Math.random() - 0.5) * 40,
    }));
    setParticles(generated);
  }, []);

  return (
    <div className="pointer-events-none fixed inset-0 z-50 overflow-hidden">
      {particles.map((p) => (
        <div
          key={p.id}
          className="absolute animate-confetti"
          style={{
            left: `${p.x}%`,
            top: "-10px",
            width: `${p.size}px`,
            height: `${p.size}px`,
            backgroundColor: p.color,
            borderRadius: Math.random() > 0.5 
ConfirmedPage function · typescript · L74-L123 (50 LOC)
src/app/(auth)/auth/confirmed/page.tsx
export default function ConfirmedPage() {
  const router = useRouter();
  const [countdown, setCountdown] = useState(3);

  const goToDashboard = useCallback(() => {
    router.push("/dashboard");
  }, [router]);

  useEffect(() => {
    const timer = setInterval(() => {
      setCountdown((c) => {
        if (c <= 1) {
          clearInterval(timer);
          goToDashboard();
          return 0;
        }
        return c - 1;
      });
    }, 1000);

    return () => clearInterval(timer);
  }, [goToDashboard]);

  return (
    <>
      <Confetti />
      <Card className="text-center">
        <CardContent className="pt-10 pb-8 space-y-6">
          <div className="flex justify-center">
            <div className="flex h-16 w-16 items-center justify-center rounded-full bg-green-100 dark:bg-green-900">
              <CheckCircle2 className="h-9 w-9 text-green-600 dark:text-green-400" />
            </div>
          </div>
          <div className="space-y-2">
            <h1 className
GET function · typescript · L4-L22 (19 LOC)
src/app/(auth)/auth/confirm/route.ts
export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const token_hash = searchParams.get("token_hash");
  const type = searchParams.get("type");

  if (token_hash && type) {
    const supabase = await createClient();
    const { error } = await supabase.auth.verifyOtp({
      token_hash,
      type: type as "signup" | "email",
    });

    if (!error) {
      return NextResponse.redirect(`${origin}/auth/confirmed`);
    }
  }

  return NextResponse.redirect(`${origin}/login?error=invalid_token`);
}
ForgotPasswordPage function · typescript · L19-L94 (76 LOC)
src/app/(auth)/forgot-password/page.tsx
export default function ForgotPasswordPage() {
  const [email, setEmail] = useState("");
  const [loading, setLoading] = useState(false);
  const [sent, setSent] = useState(false);
  const supabase = createClient();

  async function handleReset(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.resetPasswordForEmail(email, {
      redirectTo: `${window.location.origin}/reset-password`,
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    setSent(true);
    setLoading(false);
  }

  if (sent) {
    return (
      <Card>
        <CardHeader className="text-center">
          <CardTitle className="text-2xl">Check your email</CardTitle>
          <CardDescription>
            We sent a password reset link to <strong>{email}</strong>.
          </CardDescription>
        </CardHeader>
        <CardFooter className="justify-center">
          <Link href="/login" className="te
Want this analysis on your repo? https://repobility.com/scan/
handleReset function · typescript · L25-L41 (17 LOC)
src/app/(auth)/forgot-password/page.tsx
  async function handleReset(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.resetPasswordForEmail(email, {
      redirectTo: `${window.location.origin}/reset-password`,
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    setSent(true);
    setLoading(false);
  }
AuthLayout function · typescript · L3-L19 (17 LOC)
src/app/(auth)/layout.tsx
export default function AuthLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center bg-background px-4">
      <Link href="/" className="mb-8 flex items-center gap-2">
        <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-primary text-primary-foreground font-bold text-lg">
          B
        </div>
        <span className="text-xl font-bold">BossBoard</span>
      </Link>
      <div className="w-full max-w-md">{children}</div>
    </div>
  );
}
LoginPage function · typescript · L20-L141 (122 LOC)
src/app/(auth)/login/page.tsx
export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const supabase = createClient();

  async function handleLogin(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    router.push("/dashboard");
    router.refresh();
  }

  async function handleOAuth(provider: "google") {
    const { error } = await supabase.auth.signInWithOAuth({
      provider,
      options: { redirectTo: `${window.location.origin}/auth/callback` },
    });
    if (error) toast.error(error.message);
  }

  return (
    <Card>
      <CardHeader className="text-center">
        <CardTitle className="text-2xl">Welcome back</CardTitle>
        <CardDes
handleLogin function · typescript · L27-L44 (18 LOC)
src/app/(auth)/login/page.tsx
  async function handleLogin(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    router.push("/dashboard");
    router.refresh();
  }
handleOAuth function · typescript · L46-L52 (7 LOC)
src/app/(auth)/login/page.tsx
  async function handleOAuth(provider: "google") {
    const { error } = await supabase.auth.signInWithOAuth({
      provider,
      options: { redirectTo: `${window.location.origin}/auth/callback` },
    });
    if (error) toast.error(error.message);
  }
ResetPasswordPage function · typescript · L18-L67 (50 LOC)
src/app/(auth)/reset-password/page.tsx
export default function ResetPasswordPage() {
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const supabase = createClient();

  async function handleUpdate(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.updateUser({ password });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    toast.success("Password updated successfully!");
    router.push("/dashboard");
  }

  return (
    <Card>
      <CardHeader className="text-center">
        <CardTitle className="text-2xl">Set new password</CardTitle>
        <CardDescription>Enter your new password below.</CardDescription>
      </CardHeader>
      <CardContent>
        <form onSubmit={handleUpdate} className="space-y-4">
          <div className="space-y-2">
            <Label htmlFor="password">New password</Label>
            <Input
            
handleUpdate function · typescript · L24-L38 (15 LOC)
src/app/(auth)/reset-password/page.tsx
  async function handleUpdate(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.updateUser({ password });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    toast.success("Password updated successfully!");
    router.push("/dashboard");
  }
SignupPage function · typescript · L19-L122 (104 LOC)
src/app/(auth)/signup/page.tsx
export default function SignupPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [fullName, setFullName] = useState("");
  const [loading, setLoading] = useState(false);
  const [sent, setSent] = useState(false);
  const supabase = createClient();

  async function handleSignup(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.signUp({
      email,
      password,
      options: {
        data: { full_name: fullName },
        emailRedirectTo: `${window.location.origin}/auth/callback`,
      },
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    setSent(true);
    setLoading(false);
  }

  if (sent) {
    return (
      <Card>
        <CardHeader className="text-center">
          <CardTitle className="text-2xl">Check your email</CardTitle>
          <CardDescription>
            We sent a confirmation link to <st
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
handleSignup function · typescript · L27-L48 (22 LOC)
src/app/(auth)/signup/page.tsx
  async function handleSignup(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.signUp({
      email,
      password,
      options: {
        data: { full_name: fullName },
        emailRedirectTo: `${window.location.origin}/auth/callback`,
      },
    });

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    setSent(true);
    setLoading(false);
  }
BillingPage function · typescript · L13-L184 (172 LOC)
src/app/(dashboard)/dashboard/billing/page.tsx
export default function BillingPage() {
  const [currentPlan, setCurrentPlan] = useState<PlanId>("free");
  const [userId, setUserId] = useState<string | null>(null);
  const [userEmail, setUserEmail] = useState<string | null>(null);
  const [loading, setLoading] = useState<string | null>(null);
  const searchParams = useSearchParams();
  const router = useRouter();
  const supabase = createClient();

  useEffect(() => {
    if (searchParams.get("success")) {
      toast.success("Subscription activated!");
    }
    if (searchParams.get("canceled")) {
      toast.info("Checkout canceled");
    }
  }, [searchParams]);

  useEffect(() => {
    async function loadPlan() {
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;

      setUserId(user.id);
      setUserEmail(user.email ?? null);

      const { data: profile } = await supabase
        .from("profiles")
        .select("plan_id")
        .eq("id", user.id)
        .single();

   
loadPlan function · typescript · L32-L50 (19 LOC)
src/app/(dashboard)/dashboard/billing/page.tsx
    async function loadPlan() {
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;

      setUserId(user.id);
      setUserEmail(user.email ?? null);

      const { data: profile } = await supabase
        .from("profiles")
        .select("plan_id")
        .eq("id", user.id)
        .single();

      if (profile?.plan_id) {
        setCurrentPlan(profile.plan_id as PlanId);
      }
    }
DashboardLoading function · typescript · L3-L17 (15 LOC)
src/app/(dashboard)/dashboard/loading.tsx
export default function DashboardLoading() {
  return (
    <div className="space-y-6">
      <div>
        <Skeleton className="h-9 w-48" />
        <Skeleton className="mt-2 h-4 w-72" />
      </div>
      <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
        {Array.from({ length: 4 }).map((_, i) => (
          <Skeleton key={i} className="h-32 rounded-xl" />
        ))}
      </div>
    </div>
  );
}
DashboardPage function · typescript · L6-L111 (106 LOC)
src/app/(dashboard)/dashboard/page.tsx
export default async function DashboardPage() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  // Check if user has businesses
  const { data: businesses } = await supabase
    .from("businesses")
    .select("id")
    .eq("user_id", user.id)
    .limit(1);

  if (!businesses || businesses.length === 0) {
    redirect("/onboarding");
  }

  const businessId = businesses[0].id;

  // Fetch stats
  const [reviewsResult, postsResult, scriptsResult, usageResult] =
    await Promise.all([
      supabase
        .from("reviews")
        .select("id", { count: "exact", head: true })
        .eq("business_id", businessId),
      supabase
        .from("social_posts")
        .select("id", { count: "exact", head: true })
        .eq("business_id", businessId),
      supabase
        .from("scripts")
        .select("id", { count: "exact", head: true })
        .eq("business_id", businessId),
      
ReviewsPage function · typescript · L5-L40 (36 LOC)
src/app/(dashboard)/dashboard/reviews/page.tsx
export default async function ReviewsPage() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  const { data: businesses } = await supabase
    .from("businesses")
    .select("id")
    .eq("user_id", user.id)
    .limit(1);

  if (!businesses || businesses.length === 0) redirect("/onboarding");

  const businessId = businesses[0].id;

  const { data: reviews } = await supabase
    .from("reviews")
    .select("*")
    .eq("business_id", businessId)
    .order("review_date", { ascending: false });

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-3xl font-bold">Reviews</h1>
        <p className="text-muted-foreground">
          Manage and respond to your customer reviews with AI.
        </p>
      </div>
      <ReviewsList reviews={reviews ?? []} businessId={businessId} />
    </div>
  );
}
parseScript function · typescript · L31-L53 (23 LOC)
src/app/(dashboard)/dashboard/scripts/new/page.tsx
function parseScript(text: string) {
  const sections: Record<string, string> = {};
  const markers = ["---HOOK---", "---BODY---", "---CTA---", "---FILMING GUIDE---"];

  let current = "";
  for (const line of text.split("\n")) {
    const trimmed = line.trim();
    if (markers.includes(trimmed)) {
      current = trimmed.replace(/---/g, "").trim().toLowerCase();
      continue;
    }
    if (current) {
      sections[current] = (sections[current] || "") + line + "\n";
    }
  }

  return {
    hook: sections["hook"]?.trim() || "",
    body: sections["body"]?.trim() || "",
    cta: sections["cta"]?.trim() || "",
    filmingGuide: sections["filming guide"]?.trim() || "",
  };
}
NewScriptPage function · typescript · L55-L257 (203 LOC)
src/app/(dashboard)/dashboard/scripts/new/page.tsx
export default function NewScriptPage() {
  const [format, setFormat] = useState("tiktok");
  const [topic, setTopic] = useState("");
  const [audience, setAudience] = useState("");
  const [title, setTitle] = useState("");
  const [parsedScript, setParsedScript] = useState<{
    hook: string;
    body: string;
    cta: string;
    filmingGuide: string;
  } | null>(null);
  const [fullScript, setFullScript] = useState("");
  const [saving, setSaving] = useState(false);

  const router = useRouter();
  const supabase = createClient();
  const currentBusiness = useBusinessStore((s) => s.currentBusiness);

  const { complete, isLoading } = useCompletion({
    api: "/api/ai/script",
    body: {
      businessId: currentBusiness?.id,
      format,
      topic,
      audience,
    },
    onFinish: (_prompt, completion) => {
      setFullScript(completion);
      setParsedScript(parseScript(completion));
      if (!title) {
        setTitle(
          topic.length > 50 ? topic.substring(0, 50
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
handleGenerate function · typescript · L92-L102 (11 LOC)
src/app/(dashboard)/dashboard/scripts/new/page.tsx
  async function handleGenerate() {
    if (!currentBusiness) {
      toast.error("No business selected");
      return;
    }
    if (!topic) {
      toast.error("Please enter a topic");
      return;
    }
    await complete("");
  }
handleSave function · typescript · L104-L130 (27 LOC)
src/app/(dashboard)/dashboard/scripts/new/page.tsx
  async function handleSave() {
    if (!currentBusiness || !fullScript) return;
    setSaving(true);

    const { error } = await supabase.from("scripts").insert({
      business_id: currentBusiness.id,
      title: title || topic,
      format,
      topic,
      audience: audience || null,
      hook: parsedScript?.hook || null,
      body: parsedScript?.body || null,
      cta: parsedScript?.cta || null,
      filming_guide: parsedScript?.filmingGuide || null,
      full_script: fullScript,
    });

    if (error) {
      toast.error(error.message);
      setSaving(false);
      return;
    }

    toast.success("Script saved!");
    router.push("/dashboard/scripts");
    router.refresh();
  }
ScriptsPage function · typescript · L10-L53 (44 LOC)
src/app/(dashboard)/dashboard/scripts/page.tsx
export default async function ScriptsPage() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  const { data: businesses } = await supabase
    .from("businesses")
    .select("id")
    .eq("user_id", user.id)
    .limit(1);

  if (!businesses || businesses.length === 0) redirect("/onboarding");

  const businessId = businesses[0].id;

  const { data: scripts } = await supabase
    .from("scripts")
    .select("*")
    .eq("business_id", businessId)
    .order("created_at", { ascending: false });

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-3xl font-bold">Content Studio</h1>
          <p className="text-muted-foreground">
            Generate camera-ready scripts for short-form video content.
          </p>
        </div>
        <Link href="/dashboard/scripts/new">
          <Button>
        
SettingsPage function · typescript · L14-L151 (138 LOC)
src/app/(dashboard)/dashboard/settings/page.tsx
export default function SettingsPage() {
  const [fullName, setFullName] = useState("");
  const [businessName, setBusinessName] = useState("");
  const [businessAddress, setBusinessAddress] = useState("");
  const [loading, setLoading] = useState(false);
  const supabase = createClient();
  const router = useRouter();
  const currentBusiness = useBusinessStore((s) => s.currentBusiness);
  const setCurrentBusiness = useBusinessStore((s) => s.setCurrentBusiness);

  useEffect(() => {
    async function load() {
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;

      const { data: profile } = await supabase
        .from("profiles")
        .select("full_name")
        .eq("id", user.id)
        .single();

      if (profile?.full_name) setFullName(profile.full_name);

      if (currentBusiness) {
        setBusinessName(currentBusiness.name);
        setBusinessAddress(currentBusiness.address || "");
      }
    }
    load();
  }, [
load function · typescript · L25-L43 (19 LOC)
src/app/(dashboard)/dashboard/settings/page.tsx
    async function load() {
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;

      const { data: profile } = await supabase
        .from("profiles")
        .select("full_name")
        .eq("id", user.id)
        .single();

      if (profile?.full_name) setFullName(profile.full_name);

      if (currentBusiness) {
        setBusinessName(currentBusiness.name);
        setBusinessAddress(currentBusiness.address || "");
      }
    }
handleSaveProfile function · typescript · L47-L67 (21 LOC)
src/app/(dashboard)/dashboard/settings/page.tsx
  async function handleSaveProfile(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const {
      data: { user },
    } = await supabase.auth.getUser();
    if (!user) return;

    const { error } = await supabase
      .from("profiles")
      .update({ full_name: fullName })
      .eq("id", user.id);

    if (error) {
      toast.error(error.message);
    } else {
      toast.success("Profile updated");
    }
    setLoading(false);
  }
handleSaveBusiness function · typescript · L69-L92 (24 LOC)
src/app/(dashboard)/dashboard/settings/page.tsx
  async function handleSaveBusiness(e: React.FormEvent) {
    e.preventDefault();
    if (!currentBusiness) return;
    setLoading(true);

    const { data, error } = await supabase
      .from("businesses")
      .update({
        name: businessName,
        address: businessAddress || null,
      })
      .eq("id", currentBusiness.id)
      .select()
      .single();

    if (error) {
      toast.error(error.message);
    } else {
      toast.success("Business updated");
      if (data) setCurrentBusiness(data);
      router.refresh();
    }
    setLoading(false);
  }
NewSocialPostPage function · typescript · L24-L229 (206 LOC)
src/app/(dashboard)/dashboard/social/new/page.tsx
export default function NewSocialPostPage() {
  const [description, setDescription] = useState("");
  const [tone, setTone] = useState("casual");
  const [platform, setPlatform] = useState("Instagram");
  const [scheduledAt, setScheduledAt] = useState("");
  const [caption, setCaption] = useState("");
  const [hashtags, setHashtags] = useState<string[]>([]);
  const [copied, setCopied] = useState(false);
  const [saving, setSaving] = useState(false);

  const router = useRouter();
  const supabase = createClient();
  const currentBusiness = useBusinessStore((s) => s.currentBusiness);

  const { complete, isLoading } = useCompletion({
    api: "/api/ai/caption",
    body: {
      businessId: currentBusiness?.id,
      description,
      tone,
      platform,
    },
    onFinish: (_prompt, completion) => {
      // Parse caption and hashtags
      const parts = completion.split("---HASHTAGS---");
      setCaption(parts[0]?.trim() || completion);
      if (parts[1]) {
        const tags =
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
handleGenerate function · typescript · L61-L71 (11 LOC)
src/app/(dashboard)/dashboard/social/new/page.tsx
  async function handleGenerate() {
    if (!currentBusiness) {
      toast.error("No business selected");
      return;
    }
    if (!description) {
      toast.error("Please describe your post");
      return;
    }
    await complete("");
  }
handleSave function · typescript · L73-L95 (23 LOC)
src/app/(dashboard)/dashboard/social/new/page.tsx
  async function handleSave(status: "draft" | "scheduled") {
    if (!currentBusiness || !caption) return;
    setSaving(true);

    const { error } = await supabase.from("social_posts").insert({
      business_id: currentBusiness.id,
      caption,
      hashtags,
      tone,
      status,
      scheduled_at: status === "scheduled" && scheduledAt ? scheduledAt : null,
    });

    if (error) {
      toast.error(error.message);
      setSaving(false);
      return;
    }

    toast.success(status === "draft" ? "Saved as draft!" : "Post scheduled!");
    router.push("/dashboard/social");
    router.refresh();
  }
copyCaption function · typescript · L97-L104 (8 LOC)
src/app/(dashboard)/dashboard/social/new/page.tsx
  async function copyCaption() {
    const fullText =
      caption + (hashtags.length > 0 ? "\n\n" + hashtags.map((h) => `#${h}`).join(" ") : "");
    await navigator.clipboard.writeText(fullText);
    setCopied(true);
    toast.success("Copied!");
    setTimeout(() => setCopied(false), 2000);
  }
SocialPage function · typescript · L10-L53 (44 LOC)
src/app/(dashboard)/dashboard/social/page.tsx
export default async function SocialPage() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  const { data: businesses } = await supabase
    .from("businesses")
    .select("id")
    .eq("user_id", user.id)
    .limit(1);

  if (!businesses || businesses.length === 0) redirect("/onboarding");

  const businessId = businesses[0].id;

  const { data: posts } = await supabase
    .from("social_posts")
    .select("*")
    .eq("business_id", businessId)
    .order("created_at", { ascending: false });

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-3xl font-bold">Social Media</h1>
          <p className="text-muted-foreground">
            Create AI-powered captions and manage your social posts.
          </p>
        </div>
        <Link href="/dashboard/social/new">
          <Button>
            
UsagePage function · typescript · L11-L229 (219 LOC)
src/app/(dashboard)/dashboard/usage/page.tsx
export default async function UsagePage() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  const { data: profile } = await supabase
    .from("profiles")
    .select("plan_id")
    .eq("id", user.id)
    .single();

  const planId = (profile?.plan_id ?? "free") as PlanId;
  const plan = plans[planId];
  const limit = plan.limits.aiCredits;

  // Get monthly usage
  const startOfMonth = new Date(
    new Date().getFullYear(),
    new Date().getMonth(),
    1
  );

  const { data: usageData } = await supabase
    .from("ai_usage")
    .select("credits_used, feature, created_at")
    .eq("user_id", user.id)
    .gte("created_at", startOfMonth.toISOString())
    .order("created_at");

  const totalUsed =
    usageData?.reduce((sum, r) => sum + r.credits_used, 0) ?? 0;
  const remaining = limit === -1 ? Infinity : limit - totalUsed;
  const percentage = limit === -1 ? 0 : Math.round((totalUsed 
DashboardLayout function · typescript · L6-L36 (31 LOC)
src/app/(dashboard)/layout.tsx
export default async function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  // Check if user has a business (skip on onboarding page)
  const { data: businesses } = await supabase
    .from("businesses")
    .select("id")
    .eq("user_id", user.id)
    .limit(1);

  return (
    <div className="flex h-screen overflow-hidden">
      <DashboardSidebar className="hidden lg:flex" />
      <div className="flex flex-1 flex-col overflow-hidden">
        <DashboardTopbar />
        <main className="flex-1 overflow-y-auto p-4 lg:p-6">
          {children}
        </main>
      </div>
    </div>
  );
}
OnboardingPage function · typescript · L39-L151 (113 LOC)
src/app/(dashboard)/onboarding/page.tsx
export default function OnboardingPage() {
  const [name, setName] = useState("");
  const [type, setType] = useState("");
  const [address, setAddress] = useState("");
  const [googlePlaceId, setGooglePlaceId] = useState("");
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const supabase = createClient();
  const setCurrentBusiness = useBusinessStore((s) => s.setCurrentBusiness);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const {
      data: { user },
    } = await supabase.auth.getUser();
    if (!user) {
      toast.error("Not authenticated");
      setLoading(false);
      return;
    }

    const { data, error } = await supabase
      .from("businesses")
      .insert({
        user_id: user.id,
        name,
        type,
        address: address || null,
        google_place_id: googlePlaceId || null,
      })
      .select()
      .single();

    if (error) {
      toast.error(error.me
handleSubmit function · typescript · L49-L84 (36 LOC)
src/app/(dashboard)/onboarding/page.tsx
  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    const {
      data: { user },
    } = await supabase.auth.getUser();
    if (!user) {
      toast.error("Not authenticated");
      setLoading(false);
      return;
    }

    const { data, error } = await supabase
      .from("businesses")
      .insert({
        user_id: user.id,
        name,
        type,
        address: address || null,
        google_place_id: googlePlaceId || null,
      })
      .select()
      .single();

    if (error) {
      toast.error(error.message);
      setLoading(false);
      return;
    }

    setCurrentBusiness(data);
    toast.success("Business created!");
    router.push("/dashboard");
    router.refresh();
  }
Want this analysis on your repo? https://repobility.com/scan/
RootLayout function · typescript · L25-L46 (22 LOC)
src/app/layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ThemeProvider>
          <PaddleProvider>
            <TooltipProvider>
              {children}
              <Toaster richColors position="bottom-right" />
            </TooltipProvider>
          </PaddleProvider>
        </ThemeProvider>
      </body>
    </html>
  );
}
Loading function · typescript · L3-L17 (15 LOC)
src/app/loading.tsx
export default function Loading() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <div className="space-y-4 w-full max-w-md px-4">
        <Skeleton className="h-8 w-48" />
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-3/4" />
        <div className="grid grid-cols-2 gap-4 pt-4">
          <Skeleton className="h-24" />
          <Skeleton className="h-24" />
        </div>
      </div>
    </div>
  );
}
MarketingLayout function · typescript · L4-L16 (13 LOC)
src/app/(marketing)/layout.tsx
export default function MarketingLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex min-h-screen flex-col">
      <MarketingNavbar />
      <main className="flex-1">{children}</main>
      <MarketingFooter />
    </div>
  );
}
page 1 / 4next ›