diff --git a/app/student/applications/page.tsx b/app/student/applications/page.tsx index 75cb09a4..ded1c00f 100644 --- a/app/student/applications/page.tsx +++ b/app/student/applications/page.tsx @@ -16,11 +16,12 @@ import { useDbRefs } from "@/lib/db/use-refs"; import { formatTimeAgo } from "@/lib/utils"; import { Loader } from "@/components/ui/loader"; import { Card } from "@/components/ui/card"; -import { JobHead } from "@/components/shared/jobs"; +import { JobHead, SuperListingBadge } from "@/components/shared/jobs"; import { UserApplication } from "@/lib/db/db.types"; import { HeaderText, HeaderIcon } from "@/components/ui/text"; import { Separator } from "@/components/ui/separator"; import { PageError } from "@/components/ui/error"; +import { cn } from "@/lib/utils"; export default function ApplicationsPage() { const { redirectIfNotLoggedIn } = useAuthContext(); @@ -85,31 +86,40 @@ export default function ApplicationsPage() { const ApplicationCard = ({ application }: { application: UserApplication }) => { const { to_app_status_name } = useDbRefs(); + const job = application.job ?? application.jobs; + const jobRecord = job as Record | undefined; + const employer = application.employer ?? application.employers; + const challengeTitleFromJoin = ( + jobRecord?.jobs_challenge as { title?: unknown } | undefined + )?.title; + const isSuperListing = + typeof challengeTitleFromJoin === "string" && + challengeTitleFromJoin.trim().length > 0; return ( - +
- + {isSuperListing && } +
Applied {formatTimeAgo(application.applied_at ?? "")} {to_app_status_name(application.status)} - {(!application.job?.is_active || application.job?.is_deleted) && ( + {(!job?.is_active || job?.is_deleted) && ( Job no longer available. )}
- - diff --git a/app/student/fff/layout.tsx b/app/student/fff/layout.tsx new file mode 100644 index 00000000..18e797f2 --- /dev/null +++ b/app/student/fff/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "BetterInternship x FFF: Startup Accelerator Intern", + description: + "Scout top AI-native builders, network deeply, and help scale the next startup accelerator.", + openGraph: { + title: "BetterInternship x FFF: Startup Accelerator Intern", + description: + "Scout top AI-native builders, network deeply, and help scale the next startup accelerator.", + url: "/fff", + siteName: "BetterInternship", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "BetterInternship x FFF: Startup Accelerator Intern", + description: + "Scout top AI-native builders, network deeply, and help scale the next startup accelerator.", + }, +}; + +export default function FFFLayout({ children }: { children: React.ReactNode }) { + return <>{children}; +} diff --git a/app/student/fff/opengraph-image.tsx b/app/student/fff/opengraph-image.tsx new file mode 100644 index 00000000..17dce097 --- /dev/null +++ b/app/student/fff/opengraph-image.tsx @@ -0,0 +1,104 @@ +import { ImageResponse } from "next/og"; + +export const size = { + width: 1200, + height: 630, +}; + +export const contentType = "image/png"; + +export default function OpenGraphImage() { + return new ImageResponse( +
+
+ BetterInternship + x + Founders For Founders +
+ +
+
+ + Super Listing +
+ +
+ Scout. Network. Scale. +
+ +
+ Startup Accelerator Intern +
+
+ +
+ Scout top AI-native builders, network deeply, and help scale the next + startup accelerator. +
+
, + size, + ); +} diff --git a/app/student/fff/page.tsx b/app/student/fff/page.tsx new file mode 100644 index 00000000..92f73e00 --- /dev/null +++ b/app/student/fff/page.tsx @@ -0,0 +1,509 @@ +"use client"; + +import { + type ChangeEvent, + type FormEvent, + useMemo, + useRef, + useState, +} from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { ArrowUpRight, Loader2 } from "lucide-react"; +import { JetBrains_Mono, Space_Grotesk } from "next/font/google"; +import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { SuperListingBadge } from "@/components/shared/jobs"; +import { cn } from "@/lib/utils"; +import { Loader } from "@/components/ui/loader"; +import { Turnstile } from "@marsidev/react-turnstile"; +import { Badge } from "@/components/ui/badge"; + +const headingFont = Space_Grotesk({ + subsets: ["latin"], + weight: ["500", "700"], + variable: "--font-fff-heading", +}); + +const monoFont = JetBrains_Mono({ + subsets: ["latin"], + weight: ["400", "600"], + variable: "--font-fff-mono", +}); + +const RESPONSIBILITIES = [ + { + title: "Scout", + body: "Identify the top 1% of AI-native builders before anyone else.", + }, + { + title: "Network", + body: "Help build a founder community you would actually want to join.", + }, + { + title: "Scale", + body: "Solve problems that make the accelerator itself better over time.", + }, +]; + +const CHALLENGE_ITEMS = [ + { + title: "Scout Signal Early", + body: "Find AI-native builders before they become obvious. Prioritize quality of insight over quantity.", + }, + { + title: "Build Community Gravity", + body: "Design and run lightweight initiatives that attract serious founders and keep them engaged.", + }, + { + title: "Scale The Accelerator", + body: "Identify one bottleneck in the accelerator workflow and ship an improvement with measurable impact.", + }, +]; + +const SUBMISSION_REQUIREMENTS = [ + "Your scouting framework or thesis (short write-up or deck).", + "A sample list of high-signal builders you would prioritize.", + "A concrete idea you would implement to make the accelerator better.", +]; + +type FffSubmissionPayload = { + fullName: string; + email: string; + portfolioUrl: string; + linkedinUrl: string; + whyFit: string; +}; + +type FffSubmissionResponse = { + success: boolean; + message?: string; +}; + +const INITIAL_FORM_STATE: FffSubmissionPayload = { + fullName: "", + email: "", + portfolioUrl: "", + linkedinUrl: "", + whyFit: "", +}; + +export default function FFFPage() { + const [form, setForm] = useState(INITIAL_FORM_STATE); + const [isSubmitting, setIsSubmitting] = useState(false); + const [resultMessage, setResultMessage] = useState(""); + const [isError, setIsError] = useState(false); + const [submitModalOpen, setSubmitModalOpen] = useState(false); + const [token, setToken] = useState(""); + const [tokenFail, setTokenFail] = useState(false); + + const challengeRef = useRef(null); + + const endpoint = useMemo(() => { + const base = process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, ""); + if (!base) return "/api/super-listings/fff-submission"; + return `${base}/super-listings/fff-submission`; + }, []); + + const updateField = + (field: keyof FffSubmissionPayload) => + ( + event: ChangeEvent | ChangeEvent, + ) => { + setForm((previous) => ({ ...previous, [field]: event.target.value })); + }; + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + setResultMessage(""); + setIsError(false); + setIsSubmitting(true); + + try { + const response = await fetch(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ...form, "cf-token": token }), + }); + + const data = (await response.json()) as FffSubmissionResponse; + + if (!response.ok || !data.success) { + throw new Error(data.message || "Could not send your submission."); + } + + setForm(INITIAL_FORM_STATE); + setResultMessage( + "Submission sent to your email with the BetterInternship team in CC.", + ); + } catch (error) { + setIsError(true); + setResultMessage( + error instanceof Error + ? error.message + : "Something went wrong while sending your submission.", + ); + } finally { + setIsSubmitting(false); + } + }; + + const openSubmitModal = () => { + setResultMessage(""); + setIsError(false); + setSubmitModalOpen(true); + }; + + const scrollToChallenge = () => { + challengeRef.current?.scrollIntoView({ + behavior: "smooth", + block: "start", + }); + }; + + return ( +
+
+
+
+ +
+
+ + BetterInternship + + + x + + + + Founders For Founders + + +
+
+ +
+
+
+ +

+ Scout. + Network. + Scale. +

+
+ + Startup Accelerator Intern + +
+
+

+ + s16vc + + {" "} + x{" "} + + Ellipsis Ventures + + {" "} + is building a new startup accelerator designed to outbuild + legacy programs. Backed by founders from Miro, Pitch, Supercell, + and WeTransfer. +

+

+ The hard part is not building the accelerator. It is finding the + right founders. +

+
+

+ In this role, you will drive developer evangelism by SCOUT, + NETWORK, SCALE. +

+
+
+
+ +
+ {RESPONSIBILITIES.map((item, index) => ( +
+
+
+

+ {String(index + 1).padStart(2, "0")} +

+

+ {item.title} +

+

+ {item.body} +

+
+
+ ))} +
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+

+ What You Need To Ship +

+
+ +
+
+ {CHALLENGE_ITEMS.map((item) => ( +
+
+
+

+ {item.title} +

+

+ {item.body} +

+
+
+ ))} +
+ +
+
+
+

+ Submission Requirements +

+
    + {SUBMISSION_REQUIREMENTS.map((item) => ( +
  • - {item}
  • + ))} +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ + { + setSubmitModalOpen(open); + if (!open) { + setResultMessage(""); + setIsError(false); + } + }} + > + +
+
+
+ +
+

+ Candidate Submission +

+

+ Throw Your Hat In +

+
+ + {token ? ( +
+
void handleSubmit(e)} + > +
+ + +
+ +
+ + +
+ +