stack/examples/convex/app/page.tsx
BilalG1 91d8c16ffc
convex example testing (#943)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR fixes dependency management issues by adding the missing
`wait-on` package to the Convex example's dependencies, reorganizing the
dependency order in `package.json` for consistency, and regenerating the
`pnpm-lock.yaml` file to ensure proper dependency resolution across the
monorepo.

⏱️ Estimated Review Time: 5-15 minutes

<details>
<summary>💡 Review Order Suggestion</summary>

| Order | File Path |
|-------|-----------|
| 1 | `examples/convex/package.json` |
| 2 | `pnpm-lock.yaml` |
</details>



[![Need help? Join our
Discord](https://img.shields.io/badge/Need%20help%3F%20Join%20our%20Discord-5865F2?style=plastic&logo=discord&logoColor=white)](https://discord.gg/n3SsVDAW6U)


[![Analyze latest
changes](c932fc0941/?repo_owner=stack-auth&repo_name=stack-auth&pr_number=943)
<!-- RECURSEML_SUMMARY:END -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added UI buttons to view user info via different clients, a
server-side user info section, and an Action page to view/submit updates
to user metadata.
- Added a server-side action to update a user's client-read-only
metadata.

- **Documentation**
  - In-app link and guidance to the Action route for updating user data.

- **Chores**
- Updated project dependencies/devDependencies and added .env.local to
.gitignore.

- **Bug Fixes**
  - Token-missing scenario now handled gracefully instead of throwing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
2025-10-15 15:50:04 -07:00

167 lines
5.3 KiB
TypeScript

"use client";
import { UserButton, useUser } from "@stackframe/stack";
import { useMutation, useQuery } from "convex/react";
import Link from "next/link";
import { api } from "../convex/_generated/api";
import { getUserInfoConvexClient, getUserInfoConvexHttpClient } from "./user-info";
export default function Home() {
const user = useUser();
return (
<>
<header className="sticky top-0 z-10 bg-background p-4 border-b-2 border-slate-200 dark:border-slate-800 flex flex-row justify-between items-center">
Convex + Next.js
<UserButton />
</header>
<main className="p-8 flex flex-col gap-16">
<h1 className="text-4xl font-bold text-center">Convex + Next.js</h1>
<p className="text-center">User (client): {user ? user?.primaryEmail ?? "null" : "Not logged in"}</p>
<Content />
</main>
</>
);
}
function Content() {
const { viewer, numbers } =
useQuery(api.myFunctions.listNumbers, {
count: 10,
}) ?? {};
const addNumber = useMutation(api.myFunctions.addNumber);
const getUserInfo = useQuery(api.myFunctions.getUserInfo, {});
if (viewer === undefined || numbers === undefined) {
return (
<div className="mx-auto">
<p>loading... (consider a loading skeleton)</p>
</div>
);
}
return (
<div className="flex flex-col gap-8 max-w-lg mx-auto">
<p>Welcome {viewer ?? "Anonymous"}!</p>
<div className="flex flex-col gap-2">
<button className="bg-foreground text-background text-sm px-4 py-2 rounded-md" onClick={() => {
alert(getUserInfo);
}}>
View user info
</button>
<button className="bg-foreground text-background text-sm px-4 py-2 rounded-md" onClick={() => {
getUserInfoConvexHttpClient().then(userInfo => {
alert(userInfo);
});
}}>
View user info (http client)
</button>
<button className="bg-foreground text-background text-sm px-4 py-2 rounded-md" onClick={() => {
getUserInfoConvexClient().then(userInfo => {
alert(userInfo);
});
}}>
View user info (js client)
</button>
</div>
<p>
Click the button below and open this page in another window - this data
is persisted in the Convex cloud database!
</p>
<p>
<button
className="bg-foreground text-background text-sm px-4 py-2 rounded-md"
onClick={() => {
void addNumber({ value: Math.floor(Math.random() * 10) });
}}
>
Add a random number
</button>
</p>
<p>
Numbers:{" "}
{numbers?.length === 0
? "Click the button!"
: numbers?.join(", ") ?? "..."}
</p>
<p>
Edit{" "}
<code className="text-sm font-bold font-mono bg-slate-200 dark:bg-slate-800 px-1 py-0.5 rounded-md">
convex/myFunctions.ts
</code>{" "}
to change your backend
</p>
<p>
Edit{" "}
<code className="text-sm font-bold font-mono bg-slate-200 dark:bg-slate-800 px-1 py-0.5 rounded-md">
app/page.tsx
</code>{" "}
to change your frontend
</p>
<p>
See the{" "}
<Link href="/server" className="underline hover:no-underline">
/server route
</Link>{" "}
for an example of loading data in a server component
</p>
<p>
See the{" "}
<Link href="/action" className="underline hover:no-underline">
/action route
</Link>{" "}
for an example of using a convex action to update user data in stack auth
</p>
<div className="flex flex-col">
<p className="text-lg font-bold">Useful resources:</p>
<div className="flex gap-2">
<div className="flex flex-col gap-2 w-1/2">
<ResourceCard
title="Convex docs"
description="Read comprehensive documentation for all Convex features."
href="https://docs.convex.dev/home"
/>
<ResourceCard
title="Stack articles"
description="Learn about best practices, use cases, and more from a growing
collection of articles, videos, and walkthroughs."
href="https://www.typescriptlang.org/docs/handbook/2/basic-types.html"
/>
</div>
<div className="flex flex-col gap-2 w-1/2">
<ResourceCard
title="Templates"
description="Browse our collection of templates to get started quickly."
href="https://www.convex.dev/templates"
/>
<ResourceCard
title="Discord"
description="Join our developer community to ask questions, trade tips & tricks,
and show off your projects."
href="https://www.convex.dev/community"
/>
</div>
</div>
</div>
</div>
);
}
function ResourceCard({
title,
description,
href,
}: {
title: string;
description: string;
href: string;
}) {
return (
<div className="flex flex-col gap-2 bg-slate-200 dark:bg-slate-800 p-4 rounded-md h-28 overflow-auto">
<a href={href} className="text-sm underline hover:no-underline">
{title}
</a>
<p className="text-xs">{description}</p>
</div>
);
}