Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# AGENTS.md

## Cursor Cloud specific instructions

This is a **Next.js 14** marketing website for UseEfficiently (an Airtable consulting company). It is a single-package project — not a monorepo.

### Key facts

- **Package manager**: npm (lockfile: `package-lock.json`)
- **No database, no environment variables, no Docker** — all content is hardcoded in `use.js` and `information.json`.
- **No automated test suite** — there are no unit/integration tests configured. The only check is ESLint via `npm run lint`.

### Common commands

See `package.json` scripts. Summary:

| Task | Command |
|------|---------|
| Install deps | `npm install` |
| Dev server | `npm run dev` (runs on port 3000) |
| Production build | `npm run build` |
| Lint | `npm run lint` |

### Architecture

- `components/ui/` — shadcn primitives (Button, Card). Do not hand-edit; regenerate via shadcn CLI.
- `components/custom/` — custom components (FadeIn scroll animation, Navbar, SiteFooter).
- `components/fillout.tsx` — Fillout form integration (third-party embed, kept at root level).
- `use.js` / `information.json` — all content and company metadata (hardcoded, no CMS).
- Design: warm stone-based light palette with CSS variables (see `globals.css`). Dieter Rams inspired — minimal, functional, generous whitespace.

### Non-obvious notes

- The dev server (`npm run dev`) supports hot reload. No restart needed after code changes.
- Remote images from `cdn.useefficiently.com`, `dl.airtable.com`, `github.com`, and `picsum.photos` are configured in `next.config.mjs`. They require network access to load but the app still renders without them.
- The `/schedule-meeting` page embeds a Fillout form that requires external network access to function.
- `tsconfig.json` includes a likely typo entry `config.tss` in the `include` array — this does not break the build.
- When killing the dev server, use `fuser -k 3000/tcp` rather than `lsof`/`kill` — the next-server child processes sometimes linger on the port.
122 changes: 93 additions & 29 deletions app/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Metadata } from "next";
import information from "@/information.json";
import Link from "next/link";
import { customers } from "@/use";
import { notFound, redirect } from "next/navigation";
import { redirect } from "next/navigation";
import { FilloutButton } from "@/components/fillout";

type Props = {
params: {
Expand All @@ -13,55 +14,118 @@ type Props = {
export const generateMetadata = async ({
params,
}: Props): Promise<Metadata> => {
const { metaTitle, description } = customers.find(
(content) => content.slug === params.slug
) || { metaTitle: "", description: "" };
const customer = customers.find((c) => c.slug === params.slug);
const metaTitle = customer?.metaTitle ?? "";
const description = customer?.description ?? "";

const metadata = {
return {
title: metaTitle,
description: description,
description,
openGraph: {
title: metaTitle,
description: description,
description,
url: information.website,
type: "website",
images: [
{
url: information.website + "/api/og?title=" + metaTitle,
alt: information.company + " Logo",
url: `${information.website}/api/og?title=${metaTitle}`,
alt: `${information.company} Logo`,
},
],
},

twitter: {
card: "summary_large_image",
site: "@" + information.slug,
site: `@${information.slug}`,
title: metaTitle,
description: description,
images: information.website + "/api/og?title=" + metaTitle,
description,
images: `${information.website}/api/og?title=${metaTitle}`,
},
};

return metadata;
};

export default function CustomerStory({ params }: Props) {
const { title, detail } = customers.find(
(content) => content.slug === params.slug
) || redirect("/");
return (
<>
<div className="mx-auto container p-4 sm:p-6">
<Link href="/customer-stories" className="w-[250px]"></Link>
const customer = customers.find((c) => c.slug === params.slug);
if (!customer) redirect("/");

const hasContent = customer.detail && customer.detail.trim().length > 0;

if (!hasContent) {
return (
<div className="py-24 px-6">
<div className="max-w-3xl mx-auto">
<Link
href="/stories"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
&larr; Back to stories
</Link>

<h1 className="text-4xl sm:text-5xl font-medium tracking-tight mt-8">
{customer.name}
</h1>

<p className="text-muted-foreground mt-6 leading-relaxed">
We worked with {customer.name} on their Airtable system. Full story
coming soon.
</p>

<div className="mt-10 p-8 rounded-xl border border-border bg-secondary/30">
<p className="font-medium">Want to hear more about this project?</p>
<p className="text-sm text-muted-foreground mt-2">
We&rsquo;re happy to walk you through it on a call.
</p>
<div className="mt-4">
<FilloutButton className="bg-foreground text-background px-5 py-2.5 rounded-md text-sm font-medium hover:bg-foreground/90 transition-colors">
Book a call
</FilloutButton>
</div>
</div>

<div className="mt-16 pt-8 border-t border-border">
<Link
href="/stories"
className="text-sm font-medium hover:underline underline-offset-4"
>
&larr; Back to all stories
</Link>
</div>
</div>
</div>
);
}

return (
<div className="py-24 px-6">
<div className="max-w-3xl mx-auto">
<Link
href="/stories"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
&larr; Back to stories
</Link>

<h1 className="text-4xl sm:text-5xl font-medium tracking-tight mt-8">
{customer.name}
</h1>

<article
className="mt-12 prose prose-stone prose-lg max-w-none
prose-headings:font-medium prose-headings:tracking-tight
prose-a:text-foreground prose-a:underline-offset-4
prose-p:text-muted-foreground prose-p:leading-relaxed
prose-li:text-muted-foreground prose-strong:text-foreground"
dangerouslySetInnerHTML={{ __html: customer.detail }}
/>

<article
className="prose sm:prose-xl prose-zinc article"
dangerouslySetInnerHTML={{ __html: title + detail }}
/>
<div className="flex p-8">
<Link href="/customer-stories" className="w-[250px] mx-auto"></Link>
<div className="mt-16 pt-8 border-t border-border">
<Link
href="/stories"
className="text-sm font-medium hover:underline underline-offset-4"
>
&larr; Back to all stories
</Link>
</div>
</div>
</>
</div>
);
}
139 changes: 57 additions & 82 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,111 +1,86 @@
import Link from "next/link";
import { SiLinkedin } from "react-icons/si";
import Image from "next/image";
import Link from "next/link";
import { team } from "@/use";
import { Button } from "@/components/button";
import information from "@/information.json";
import FadeIn from "@/components/custom/fade-in";
import { Metadata } from "next";

export async function generateMetadata(): Promise<Metadata> {
const title = "About";
const description = "Contact us for any questions or feedback you may have.";
const description = "Meet the team behind UseEfficiently.";

const metadata = {
title: title,
description: description,
return {
title,
description,
openGraph: {
title: title,
description: description,
title,
description,
url: information.website,
type: "website",
images: [
{
url:
information.website +
"/api/og?title=" +
title +
" | UseEfficiently",
alt: information.company + " Logo",
url: `${information.website}/api/og?title=${title} | UseEfficiently`,
alt: `${information.company} Logo`,
},
],
},

twitter: {
card: "summary_large_image",
site: "@" + information.slug,
title: title,
description: description,
images: information.website + "/api/og?title=" + title,
site: `@${information.slug}`,
title,
description,
images: `${information.website}/api/og?title=${title}`,
},
};

return metadata;
}

export default function Page() {
return (
<div className="grid place-content-center grid-cols-1 pt-20 items-center justify-center gap-16">
<h1 className="text-center text-4xl sm:text-6xl font-medium leading-tight content-center ">
Team UseEfficiently
</h1>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{team.map((member) => (
<Button
key={member.name}
title={member.name}
description={member.quote}
link={member.linkedin}
className="flex text-start cursor-pointer duration-300 transition-all hover:scale-105 items-center relative w-full max-w-lg mx-auto"
>
<Image
className="rounded-full z-40"
style={{ width: "150px" }}
width={200}
loading="lazy"
height={200}
src={member.pic}
alt={member.name}
/>
<div className="border border-zinc-700 flex bg-zinc-800 p-2 w-full rounded-full absolute">
<div style={{ width: "150px" }}></div>
<div>
<h2 className="text-3xl font-medium">{member.name}</h2>
{/* <h3 className="text-lg leading-tight text-violet-200 mb-2">
{member.title}
</h3> */}
<div className="w-min flex gap-2 text-violet-200 text-2xl items-center jus">
<Link href={member.linkedin} target="_blank">
<SiLinkedin />
<div className="py-24 px-6">
<div className="max-w-3xl mx-auto">
<FadeIn>
<h1 className="text-4xl sm:text-5xl font-medium tracking-tight">
Our Team
</h1>
<p className="text-muted-foreground text-lg mt-4 max-w-xl leading-relaxed">
A small team of specialists helping businesses work smarter with
Airtable.
</p>
</FadeIn>

<div className="mt-16 space-y-16">
{team.map((member, i) => (
<FadeIn key={member.name} delay={i * 0.1}>
<div className="flex flex-col sm:flex-row gap-6 items-start">
<Image
src={member.pic}
width={120}
height={120}
alt={member.name}
className="rounded-full shrink-0"
/>
<div>
<h2 className="text-2xl font-medium">{member.name}</h2>
<p className="text-sm text-muted-foreground mt-0.5">
{member.title} &middot; {member.address}
</p>
{member.quote ? (
<p className="text-muted-foreground mt-4 leading-relaxed max-w-lg">
{member.quote}
</p>
) : null}
<Link
href={member.linkedin}
target="_blank"
className={`text-sm font-medium inline-block hover:underline underline-offset-4 ${member.quote ? "mt-4" : "mt-2"}`}
>
LinkedIn &rarr;
</Link>
</div>
</div>
</div>
</Button>
))}
{/* <Button
key="Your Name"
title="Your Name"
description="You can be here!"
link=""
className="flex text-start cursor-pointer duration-300 transition-all hover:scale-105 items-center relative w-full max-w-lg mx-auto"
>
<Image
className="rounded-full z-40"
style={{ width: "150px" }}
width={200}
height={200}
src="/images/you.jpg"
alt="Your Name"
/>
<div className="border border-zinc-700 flex bg-zinc-800 p-6 w-full rounded-full absolute">
<div style={{ width: "140px" }}></div>
<div>
<h2 className="text-3xl font-medium">Your Name</h2>
<h3 className="text-lg leading-tight text-violet-200 mb-2">
You can be here!
</h3>
</div>
</div>
</Button> */}
</FadeIn>
))}
</div>
</div>
</div>
);
Expand Down
Loading