← back to kjpatel__publicgrant

Function bodies 124 total

All specs Real LLM only Function bodies
LoginPage function · typescript · L12-L82 (71 LOC)
src/app/(auth)/login/page.tsx
export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const supabase = createClient();

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

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

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

    router.push("/dashboard");
  }

  return (
    <div className="flex min-h-screen items-center justify-center px-4">
      <Card className="w-full max-w-sm">
        <CardHeader className="text-center">
          <CardTitle className="text-2xl">Log in to PublicGrant</CardTitle>
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSubmit} className="space-y-4">
            <d
handleSubmit function · typescript · L20-L37 (18 LOC)
src/app/(auth)/login/page.tsx
  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError("");
    setLoading(true);

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

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

    router.push("/dashboard");
  }
SignupPage function · typescript · L12-L83 (72 LOC)
src/app/(auth)/signup/page.tsx
export default function SignupPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const supabase = createClient();

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

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

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

    router.push("/dashboard");
  }

  return (
    <div className="flex min-h-screen items-center justify-center px-4">
      <Card className="w-full max-w-sm">
        <CardHeader className="text-center">
          <CardTitle className="text-2xl">Create your account</CardTitle>
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSubmit} className="space-y-4">
            <div className=
handleSubmit function · typescript · L20-L37 (18 LOC)
src/app/(auth)/signup/page.tsx
  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError("");
    setLoading(true);

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

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

    router.push("/dashboard");
  }
DashboardPage function · typescript · L3-L51 (49 LOC)
src/app/(dashboard)/dashboard/page.tsx
export default function DashboardPage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Dashboard</h1>
      <div className="grid gap-4 sm:grid-cols-3">
        <Card>
          <CardHeader>
            <CardTitle className="text-sm font-medium text-muted-foreground">
              Open Grants
            </CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-3xl font-bold">—</p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader>
            <CardTitle className="text-sm font-medium text-muted-foreground">
              Matched Grants
            </CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-3xl font-bold">—</p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader>
            <CardTitle className="text-sm font-medium text-muted-foreground">
              Draft Proposals
            </CardTitle>
          </CardHea
formatCurrency function · typescript · L39-L46 (8 LOC)
src/app/(dashboard)/grants/grants-list.tsx
function formatCurrency(amount: number | null) {
  if (!amount) return "—";
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0,
  }).format(amount);
}
formatDate function · typescript · L48-L55 (8 LOC)
src/app/(dashboard)/grants/grants-list.tsx
function formatDate(dateStr: string | null) {
  if (!dateStr) return "—";
  return new Date(dateStr).toLocaleDateString("en-US", {
    month: "short",
    day: "numeric",
    year: "numeric",
  });
}
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
statusColor function · typescript · L57-L68 (12 LOC)
src/app/(dashboard)/grants/grants-list.tsx
function statusColor(status: string) {
  switch (status) {
    case "open":
      return "default";
    case "upcoming":
      return "secondary";
    case "closed":
      return "outline";
    default:
      return "outline";
  }
}
GrantsList function · typescript · L70-L201 (132 LOC)
src/app/(dashboard)/grants/grants-list.tsx
export function GrantsList({
  grants,
  initialQuery,
  initialStatus,
  initialCategory,
}: {
  grants: Grant[];
  initialQuery: string;
  initialStatus: string;
  initialCategory: string;
}) {
  const router = useRouter();
  const searchParams = useSearchParams();
  const [query, setQuery] = useState(initialQuery);

  const updateParams = useCallback(
    (key: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString());
      if (value && value !== "all") {
        params.set(key, value);
      } else {
        params.delete(key);
      }
      router.push(`/grants?${params.toString()}`);
    },
    [router, searchParams]
  );

  function handleSearch(e: React.FormEvent) {
    e.preventDefault();
    updateParams("q", query);
  }

  return (
    <div className="space-y-4">
      {/* Filters */}
      <div className="flex flex-wrap gap-3">
        <form onSubmit={handleSearch} className="flex-1">
          <Input
            placeholder="Search gran
handleSearch function · typescript · L98-L101 (4 LOC)
src/app/(dashboard)/grants/grants-list.tsx
  function handleSearch(e: React.FormEvent) {
    e.preventDefault();
    updateParams("q", query);
  }
callClaude function · typescript · L12-L43 (32 LOC)
src/app/(dashboard)/grants/[id]/actions.ts
async function callClaude(system: string, user: string) {
  const client = getAnthropicClient();

  let response;
  try {
    response = await client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 2048,
      system,
      messages: [{ role: "user", content: user }],
    });
  } catch (apiError: any) {
    if (apiError?.status === 400 && apiError?.message?.includes("credit")) {
      throw new Error("Anthropic API credits exhausted. Please add credits to your account.");
    }
    throw new Error(apiError?.message || "Failed to call Claude API");
  }

  const textBlock = response.content.find((b) => b.type === "text");
  if (!textBlock || textBlock.type !== "text") {
    throw new Error("No text response from Claude");
  }

  // Extract JSON from the response (handle markdown code blocks)
  let jsonStr = textBlock.text;
  const jsonMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
  if (jsonMatch) {
    jsonStr = jsonMatch[1];
  }

  return JSON.parse(jsonSt
analyzeGrant function · typescript · L45-L72 (28 LOC)
src/app/(dashboard)/grants/[id]/actions.ts
export async function analyzeGrant(grantId: string) {
  const supabase = await createClient();

  const { data: grant } = await supabase
    .from("grants")
    .select("*")
    .eq("id", grantId)
    .single();

  if (!grant) return { error: "Grant not found" };

  const typedGrant = grant as Grant;

  try {
    const prompt = grantSummaryPrompt(typedGrant);
    const result = await callClaude(prompt.system, prompt.user);

    // Cache the summary in the database
    await supabase
      .from("grants")
      .update({ ai_summary: JSON.stringify(result) })
      .eq("id", grantId);

    return { data: result };
  } catch (e) {
    return { error: `AI analysis failed: ${e instanceof Error ? e.message : "Unknown error"}` };
  }
}
extractEligibility function · typescript · L74-L101 (28 LOC)
src/app/(dashboard)/grants/[id]/actions.ts
export async function extractEligibility(grantId: string) {
  const supabase = await createClient();

  const { data: grant } = await supabase
    .from("grants")
    .select("*")
    .eq("id", grantId)
    .single();

  if (!grant) return { error: "Grant not found" };

  const typedGrant = grant as Grant;

  try {
    const prompt = eligibilityExtractionPrompt(typedGrant);
    const result = await callClaude(prompt.system, prompt.user);

    // Cache parsed eligibility
    await supabase
      .from("grants")
      .update({ eligibility_parsed: result })
      .eq("id", grantId);

    return { data: result };
  } catch (e) {
    return { error: `Eligibility extraction failed: ${e instanceof Error ? e.message : "Unknown error"}` };
  }
}
scoreGrantFit function · typescript · L103-L152 (50 LOC)
src/app/(dashboard)/grants/[id]/actions.ts
export async function scoreGrantFit(grantId: string) {
  const supabase = await createClient();

  // Get user's org
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) return { error: "Not authenticated" };

  const { data: org } = await supabase
    .from("organizations")
    .select("*")
    .eq("user_id", user.id)
    .single();

  if (!org) return { error: "No organization profile found. Set up your org profile first." };

  const { data: grant } = await supabase
    .from("grants")
    .select("*")
    .eq("id", grantId)
    .single();

  if (!grant) return { error: "Grant not found" };

  const typedOrg = org as Organization;
  const typedGrant = grant as Grant;

  try {
    const prompt = fitScoringPrompt(typedOrg, typedGrant);
    const result = await callClaude(prompt.system, prompt.user);

    // Save match score
    await supabase.from("grant_matches").upsert(
      {
        org_id: typedOrg.id,
        grant_id: grantId,
        fit_score: res
scoreColor function · typescript · L32-L36 (5 LOC)
src/app/(dashboard)/grants/[id]/ai-analysis.tsx
function scoreColor(score: number) {
  if (score >= 75) return "text-green-600";
  if (score >= 50) return "text-yellow-600";
  return "text-red-600";
}
Want this analysis on your repo? https://repobility.com/scan/
AIAnalysis function · typescript · L38-L299 (262 LOC)
src/app/(dashboard)/grants/[id]/ai-analysis.tsx
export function AIAnalysis({
  grantId,
  cachedSummary,
  cachedEligibility,
}: {
  grantId: string;
  cachedSummary: SummaryResult | null;
  cachedEligibility: EligibilityResult | null;
}) {
  const [summary, setSummary] = useState<SummaryResult | null>(cachedSummary);
  const [eligibility, setEligibility] = useState<EligibilityResult | null>(
    cachedEligibility
  );
  const [fitScore, setFitScore] = useState<FitResult | null>(null);
  const [loading, setLoading] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function handleAnalyze() {
    setLoading("summary");
    setError(null);
    const result = await analyzeGrant(grantId);
    if (result.error) setError(result.error);
    else setSummary(result.data);
    setLoading(null);
  }

  async function handleEligibility() {
    setLoading("eligibility");
    setError(null);
    const result = await extractEligibility(grantId);
    if (result.error) setError(result.error);
    els
handleAnalyze function · typescript · L55-L62 (8 LOC)
src/app/(dashboard)/grants/[id]/ai-analysis.tsx
  async function handleAnalyze() {
    setLoading("summary");
    setError(null);
    const result = await analyzeGrant(grantId);
    if (result.error) setError(result.error);
    else setSummary(result.data);
    setLoading(null);
  }
handleEligibility function · typescript · L64-L71 (8 LOC)
src/app/(dashboard)/grants/[id]/ai-analysis.tsx
  async function handleEligibility() {
    setLoading("eligibility");
    setError(null);
    const result = await extractEligibility(grantId);
    if (result.error) setError(result.error);
    else setEligibility(result.data);
    setLoading(null);
  }
handleFitScore function · typescript · L73-L80 (8 LOC)
src/app/(dashboard)/grants/[id]/ai-analysis.tsx
  async function handleFitScore() {
    setLoading("fit");
    setError(null);
    const result = await scoreGrantFit(grantId);
    if (result.error) setError(result.error);
    else setFitScore(result.data);
    setLoading(null);
  }
formatCurrency function · typescript · L12-L19 (8 LOC)
src/app/(dashboard)/grants/[id]/page.tsx
function formatCurrency(amount: number | null) {
  if (!amount) return "—";
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0,
  }).format(amount);
}
formatDate function · typescript · L21-L29 (9 LOC)
src/app/(dashboard)/grants/[id]/page.tsx
function formatDate(dateStr: string | null) {
  if (!dateStr) return "—";
  return new Date(dateStr).toLocaleDateString("en-US", {
    weekday: "long",
    month: "long",
    day: "numeric",
    year: "numeric",
  });
}
GrantDetailPage function · typescript · L31-L172 (142 LOC)
src/app/(dashboard)/grants/[id]/page.tsx
export default async function GrantDetailPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const supabase = await createClient();

  const { data } = await supabase
    .from("grants")
    .select("*")
    .eq("id", id)
    .single();

  if (!data) notFound();

  const grant = data as Grant;

  return (
    <div className="mx-auto max-w-3xl space-y-6">
      {/* Back link */}
      <Link
        href="/grants"
        className="text-sm text-muted-foreground hover:text-foreground"
      >
        &larr; Back to Grants
      </Link>

      {/* Header */}
      <div className="space-y-2">
        <div className="flex items-start justify-between gap-4">
          <h1 className="text-2xl font-bold">{grant.title}</h1>
          <Badge
            variant={
              grant.status === "open"
                ? "default"
                : grant.status === "upcoming"
                  ? "secondary"
                  : "outline"
            }
     
StartProposalButton function · typescript · L7-L32 (26 LOC)
src/app/(dashboard)/grants/[id]/start-proposal-button.tsx
export function StartProposalButton({ grantId }: { grantId: string }) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function handleClick() {
    setLoading(true);
    setError(null);
    const result = await createProposal(grantId);
    // If we get here, redirect didn't happen, so there was an error
    if (result?.error) {
      setError(result.error);
    }
    setLoading(false);
  }

  return (
    <div>
      <Button onClick={handleClick} disabled={loading}>
        {loading ? "Creating..." : "Start Proposal"}
      </Button>
      {error && (
        <p className="mt-1 text-sm text-destructive">{error}</p>
      )}
    </div>
  );
}
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
handleClick function · typescript · L11-L20 (10 LOC)
src/app/(dashboard)/grants/[id]/start-proposal-button.tsx
  async function handleClick() {
    setLoading(true);
    setError(null);
    const result = await createProposal(grantId);
    // If we get here, redirect didn't happen, so there was an error
    if (result?.error) {
      setError(result.error);
    }
    setLoading(false);
  }
GrantsPage function · typescript · L5-L51 (47 LOC)
src/app/(dashboard)/grants/page.tsx
export default async function GrantsPage({
  searchParams,
}: {
  searchParams: Promise<{ q?: string; status?: string; category?: string }>;
}) {
  const params = await searchParams;
  const supabase = await createClient();

  let query = supabase
    .from("grants")
    .select("*")
    .order("deadline", { ascending: true });

  if (params.status && params.status !== "all") {
    query = query.eq("status", params.status);
  }

  if (params.category && params.category !== "all") {
    query = query.contains("category", [params.category]);
  }

  if (params.q) {
    query = query.or(
      `title.ilike.%${params.q}%,agency.ilike.%${params.q}%,description.ilike.%${params.q}%`
    );
  }

  const { data } = await query;
  const grants = (data ?? []) as Grant[];

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Grants</h1>
        <p className="text-muted-foreground">
          Browse and search available funding opportunities.
        </p>
DashboardLayout function · typescript · L4-L18 (15 LOC)
src/app/(dashboard)/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex h-screen">
      <Sidebar />
      <div className="flex flex-1 flex-col overflow-hidden">
        <Header />
        <main className="flex-1 overflow-y-auto p-6">{children}</main>
      </div>
    </div>
  );
}
saveOrganization function · typescript · L16-L62 (47 LOC)
src/app/(dashboard)/org/actions.ts
export async function saveOrganization(data: OrgFormData) {
  const supabase = await createClient();

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

  if (!user) {
    return { error: "Not authenticated" };
  }

  // Check if org already exists for this user
  const { data: existing } = await supabase
    .from("organizations")
    .select("id")
    .eq("user_id", user.id)
    .single();

  const orgData = {
    name: data.name,
    type: data.type,
    mission: data.mission || null,
    location: data.location || null,
    annual_budget: data.annual_budget ? parseFloat(data.annual_budget) : null,
    focus_areas: data.focus_areas,
    updated_at: new Date().toISOString(),
  };

  if (existing) {
    const { error } = await supabase
      .from("organizations")
      .update(orgData)
      .eq("id", existing.id);

    if (error) return { error: error.message };
  } else {
    const { error } = await supabase
      .from("organizations")
      .insert({ ...orgData, 
OrgForm function · typescript · L41-L194 (154 LOC)
src/app/(dashboard)/org/org-form.tsx
export function OrgForm({ org }: { org: Organization | null }) {
  const [name, setName] = useState(org?.name ?? "");
  const [type, setType] = useState<OrgType>(org?.type ?? "nonprofit");
  const [mission, setMission] = useState(org?.mission ?? "");
  const [location, setLocation] = useState(org?.location ?? "");
  const [annualBudget, setAnnualBudget] = useState(
    org?.annual_budget?.toString() ?? ""
  );
  const [focusAreas, setFocusAreas] = useState<FocusArea[]>(
    org?.focus_areas ?? []
  );
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState("");

  function toggleFocusArea(area: FocusArea) {
    setFocusAreas((prev) =>
      prev.includes(area) ? prev.filter((a) => a !== area) : [...prev, area]
    );
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);
    setMessage("");

    const result = await saveOrganization({
      name,
      type,
      mission,
      location,
      annual_budget
toggleFocusArea function · typescript · L55-L59 (5 LOC)
src/app/(dashboard)/org/org-form.tsx
  function toggleFocusArea(area: FocusArea) {
    setFocusAreas((prev) =>
      prev.includes(area) ? prev.filter((a) => a !== area) : [...prev, area]
    );
  }
handleSubmit function · typescript · L61-L82 (22 LOC)
src/app/(dashboard)/org/org-form.tsx
  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);
    setMessage("");

    const result = await saveOrganization({
      name,
      type,
      mission,
      location,
      annual_budget: annualBudget,
      focus_areas: focusAreas,
    });

    setSaving(false);

    if (result.error) {
      setMessage(result.error);
    } else {
      setMessage("Organization saved successfully.");
    }
  }
OrgPage function · typescript · L5-L36 (32 LOC)
src/app/(dashboard)/org/page.tsx
export default async function OrgPage() {
  const supabase = await createClient();

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

  let org: Organization | null = null;

  if (user) {
    const { data } = await supabase
      .from("organizations")
      .select("*")
      .eq("user_id", user.id)
      .single();

    org = data as Organization | null;
  }

  return (
    <div className="mx-auto max-w-2xl space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Organization Profile</h1>
        <p className="text-muted-foreground">
          Tell us about your organization so we can match you with relevant
          grants.
        </p>
      </div>
      <OrgForm org={org} />
    </div>
  );
}
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
createProposal function · typescript · L11-L71 (61 LOC)
src/app/(dashboard)/proposals/actions.ts
export async function createProposal(grantId: string) {
  const supabase = await createClient();

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

  if (!user) return { error: "Not authenticated" };

  const { data: org } = await supabase
    .from("organizations")
    .select("*")
    .eq("user_id", user.id)
    .single();

  if (!org)
    return { error: "Set up your organization profile before starting a proposal." };

  const { data: grant } = await supabase
    .from("grants")
    .select("*")
    .eq("id", grantId)
    .single();

  if (!grant) return { error: "Grant not found" };

  // Check if a proposal already exists for this grant
  const { data: existing } = await supabase
    .from("proposals")
    .select("id")
    .eq("org_id", org.id)
    .eq("grant_id", grantId)
    .single();

  if (existing) {
    redirect(`/proposals/${existing.id}`);
  }

  // Create empty sections
  const sections: Record<string, string> = {};
  for (const section of PROPOSAL_SE
updateProposalSection function · typescript · L73-L100 (28 LOC)
src/app/(dashboard)/proposals/actions.ts
export async function updateProposalSection(
  proposalId: string,
  sectionKey: string,
  content: string
) {
  const supabase = await createClient();

  const { data: proposal } = await supabase
    .from("proposals")
    .select("sections")
    .eq("id", proposalId)
    .single();

  if (!proposal) return { error: "Proposal not found" };

  const sections = (proposal.sections as Record<string, string>) || {};
  sections[sectionKey] = content;

  const { error } = await supabase
    .from("proposals")
    .update({ sections, updated_at: new Date().toISOString() })
    .eq("id", proposalId);

  if (error) return { error: error.message };

  revalidatePath(`/proposals/${proposalId}`);
  return { success: true };
}
generateSection function · typescript · L102-L189 (88 LOC)
src/app/(dashboard)/proposals/actions.ts
export async function generateSection(
  proposalId: string,
  sectionKey: string
) {
  const supabase = await createClient();

  // Get proposal with grant and org
  const { data: proposal } = await supabase
    .from("proposals")
    .select("*")
    .eq("id", proposalId)
    .single();

  if (!proposal) return { error: "Proposal not found" };

  const typedProposal = proposal as Proposal;

  const { data: grant } = await supabase
    .from("grants")
    .select("*")
    .eq("id", typedProposal.grant_id)
    .single();

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

  if (!user) return { error: "Not authenticated" };

  const { data: org } = await supabase
    .from("organizations")
    .select("*")
    .eq("user_id", user.id)
    .single();

  if (!grant || !org) return { error: "Missing grant or organization data" };

  const typedGrant = grant as Grant;
  const typedOrg = org as Organization;

  const sectionDef = PROPOSAL_SECTIONS.find((s) => s.key === sectio
updateProposalStatus function · typescript · L191-L206 (16 LOC)
src/app/(dashboard)/proposals/actions.ts
export async function updateProposalStatus(
  proposalId: string,
  status: "draft" | "review" | "submitted"
) {
  const supabase = await createClient();

  const { error } = await supabase
    .from("proposals")
    .update({ status, updated_at: new Date().toISOString() })
    .eq("id", proposalId);

  if (error) return { error: error.message };

  revalidatePath(`/proposals/${proposalId}`);
  return { success: true };
}
ProposalDetailPage function · typescript · L7-L44 (38 LOC)
src/app/(dashboard)/proposals/[id]/page.tsx
export default async function ProposalDetailPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const supabase = await createClient();

  const { data } = await supabase
    .from("proposals")
    .select("*, grants(title, agency, deadline)")
    .eq("id", id)
    .single();

  if (!data) notFound();

  const proposal = data as Proposal & { grants: Pick<Grant, "title" | "agency" | "deadline"> };

  return (
    <div className="mx-auto max-w-3xl space-y-6">
      <Link
        href="/proposals"
        className="text-sm text-muted-foreground hover:text-foreground"
      >
        &larr; Back to Proposals
      </Link>

      <ProposalEditor
        proposalId={proposal.id}
        title={proposal.title}
        status={proposal.status}
        sections={proposal.sections}
        grantTitle={proposal.grants.title}
        grantAgency={proposal.grants.agency}
      />
    </div>
  );
}
statusVariant function · typescript · L24-L28 (5 LOC)
src/app/(dashboard)/proposals/[id]/proposal-editor.tsx
function statusVariant(status: string) {
  if (status === "draft") return "secondary" as const;
  if (status === "review") return "default" as const;
  return "outline" as const;
}
ProposalEditor function · typescript · L30-L216 (187 LOC)
src/app/(dashboard)/proposals/[id]/proposal-editor.tsx
export function ProposalEditor({
  proposalId,
  title,
  status: initialStatus,
  sections: initialSections,
  grantTitle,
  grantAgency,
}: ProposalEditorProps) {
  const [sections, setSections] = useState<Record<string, string>>(
    initialSections || {}
  );
  const [status, setStatus] = useState(initialStatus);
  const [activeSection, setActiveSection] = useState<string | null>(null);
  const [generating, setGenerating] = useState<string | null>(null);
  const [saving, setSaving] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const filledCount = Object.values(sections).filter(
    (v) => v && v.length > 0
  ).length;

  async function handleGenerate(sectionKey: string) {
    setGenerating(sectionKey);
    setError(null);
    const result = await generateSection(proposalId, sectionKey);
    if (result.error) {
      setError(result.error);
    } else if (result.data) {
      setSections((prev) => ({ ...prev, [sectionKey]: result.data
handleGenerate function · typescript · L51-L61 (11 LOC)
src/app/(dashboard)/proposals/[id]/proposal-editor.tsx
  async function handleGenerate(sectionKey: string) {
    setGenerating(sectionKey);
    setError(null);
    const result = await generateSection(proposalId, sectionKey);
    if (result.error) {
      setError(result.error);
    } else if (result.data) {
      setSections((prev) => ({ ...prev, [sectionKey]: result.data! }));
    }
    setGenerating(null);
  }
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
handleSave function · typescript · L63-L75 (13 LOC)
src/app/(dashboard)/proposals/[id]/proposal-editor.tsx
  async function handleSave(sectionKey: string) {
    setSaving(sectionKey);
    setError(null);
    const result = await updateProposalSection(
      proposalId,
      sectionKey,
      sections[sectionKey] || ""
    );
    if (result.error) {
      setError(result.error);
    }
    setSaving(null);
  }
handleStatusChange function · typescript · L77-L84 (8 LOC)
src/app/(dashboard)/proposals/[id]/proposal-editor.tsx
  async function handleStatusChange(newStatus: "draft" | "review" | "submitted") {
    const result = await updateProposalStatus(proposalId, newStatus);
    if (result.error) {
      setError(result.error);
    } else {
      setStatus(newStatus);
    }
  }
statusVariant function · typescript · L6-L10 (5 LOC)
src/app/(dashboard)/proposals/page.tsx
function statusVariant(status: string) {
  if (status === "draft") return "secondary" as const;
  if (status === "review") return "default" as const;
  return "outline" as const;
}
ProposalsPage function · typescript · L12-L107 (96 LOC)
src/app/(dashboard)/proposals/page.tsx
export default async function ProposalsPage() {
  const supabase = await createClient();

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

  let proposals: any[] = [];

  if (user) {
    const { data: org } = await supabase
      .from("organizations")
      .select("id")
      .eq("user_id", user.id)
      .single();

    if (org) {
      const { data } = await supabase
        .from("proposals")
        .select("*, grants(title, agency, deadline)")
        .eq("org_id", org.id)
        .order("updated_at", { ascending: false });

      proposals = data ?? [];
    }
  }

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Proposals</h1>
        <p className="text-muted-foreground">
          Your grant proposal drafts.
        </p>
      </div>

      {proposals.length === 0 ? (
        <Card className="border-dashed">
          <CardContent className="py-8">
            <p className="text-center text-sm text-muted-foreg
RootLayout function · typescript · L21-L35 (15 LOC)
src/app/layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased`}
      >
        {children}
      </body>
    </html>
  );
}
Home function · typescript · L4-L75 (72 LOC)
src/app/page.tsx
export default function Home() {
  return (
    <div className="flex min-h-screen flex-col">
      {/* Nav */}
      <header className="flex items-center justify-between border-b px-6 py-4">
        <span className="text-xl font-bold">PublicGrant</span>
        <div className="flex gap-3">
          <Link href="/login">
            <Button variant="ghost">Log in</Button>
          </Link>
          <Link href="/signup">
            <Button>Get Started</Button>
          </Link>
        </div>
      </header>

      {/* Hero */}
      <main className="flex flex-1 flex-col items-center justify-center gap-8 px-6 text-center">
        <div className="max-w-2xl space-y-4">
          <h1 className="text-4xl font-bold tracking-tight sm:text-5xl">
            Find and win grants with AI
          </h1>
          <p className="text-lg text-muted-foreground">
            PublicGrant helps nonprofits, school districts, and public agencies
            discover funding opportunities, understand eli
Header function · typescript · L7-L23 (17 LOC)
src/components/layout/header.tsx
export function Header() {
  const router = useRouter();
  const supabase = createClient();

  async function handleSignOut() {
    await supabase.auth.signOut();
    router.push("/login");
  }

  return (
    <header className="flex h-14 items-center justify-end border-b px-6">
      <Button variant="ghost" size="sm" onClick={handleSignOut}>
        Sign out
      </Button>
    </header>
  );
}
handleSignOut function · typescript · L11-L14 (4 LOC)
src/components/layout/header.tsx
  async function handleSignOut() {
    await supabase.auth.signOut();
    router.push("/login");
  }
Want this analysis on your repo? https://repobility.com/scan/
Sidebar function · typescript · L14-L42 (29 LOC)
src/components/layout/sidebar.tsx
export function Sidebar() {
  const pathname = usePathname();

  return (
    <aside className="flex w-56 shrink-0 flex-col border-r bg-muted/30">
      <div className="flex h-14 items-center border-b px-4">
        <Link href="/dashboard" className="text-lg font-bold">
          PublicGrant
        </Link>
      </div>
      <nav className="flex flex-1 flex-col gap-1 p-3">
        {navItems.map((item) => (
          <Link
            key={item.href}
            href={item.href}
            className={cn(
              "rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent",
              pathname.startsWith(item.href)
                ? "bg-accent text-accent-foreground"
                : "text-muted-foreground"
            )}
          >
            {item.label}
          </Link>
        ))}
      </nav>
    </aside>
  );
}
Avatar function · typescript · L8-L26 (19 LOC)
src/components/ui/avatar.tsx
function Avatar({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
  size?: "default" | "sm" | "lg"
}) {
  return (
    <AvatarPrimitive.Root
      data-slot="avatar"
      data-size={size}
      className={cn(
        "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
        className
      )}
      {...props}
    />
  )
}
AvatarImage function · typescript · L28-L39 (12 LOC)
src/components/ui/avatar.tsx
function AvatarImage({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
  return (
    <AvatarPrimitive.Image
      data-slot="avatar-image"
      className={cn("aspect-square size-full", className)}
      {...props}
    />
  )
}
page 1 / 3next ›