Initial commit from Create Next App
This commit is contained in:
61
components/tutorial/code-block.tsx
Normal file
61
components/tutorial/code-block.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
const CopyIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CheckIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="20 6 9 17 4 12"></polyline>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export function CodeBlock({ code }: { code: string }) {
|
||||
const [icon, setIcon] = useState(CopyIcon);
|
||||
|
||||
const copy = async () => {
|
||||
await navigator?.clipboard?.writeText(code);
|
||||
setIcon(CheckIcon);
|
||||
setTimeout(() => setIcon(CopyIcon), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<pre className="bg-muted rounded-md p-6 my-6 relative">
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={copy}
|
||||
variant={"outline"}
|
||||
className="absolute right-2 top-2"
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
<code className="text-xs p-3">{code}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
62
components/tutorial/connect-supabase-steps.tsx
Normal file
62
components/tutorial/connect-supabase-steps.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { TutorialStep } from "./tutorial-step";
|
||||
|
||||
export default function ConnectSupabaseSteps() {
|
||||
return (
|
||||
<ol className="flex flex-col gap-6">
|
||||
<TutorialStep title="Create Supabase project">
|
||||
<p>
|
||||
Head over to{" "}
|
||||
<a
|
||||
href="https://app.supabase.com/project/_/settings/api"
|
||||
target="_blank"
|
||||
className="font-bold hover:underline text-foreground/80"
|
||||
rel="noreferrer"
|
||||
>
|
||||
database.new
|
||||
</a>{" "}
|
||||
and create a new Supabase project.
|
||||
</p>
|
||||
</TutorialStep>
|
||||
|
||||
<TutorialStep title="Declare environment variables">
|
||||
<p>
|
||||
Rename the{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
.env.example
|
||||
</span>{" "}
|
||||
file in your Next.js app to{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
.env.local
|
||||
</span>{" "}
|
||||
and populate with values from{" "}
|
||||
<a
|
||||
href="https://app.supabase.com/project/_/settings/api"
|
||||
target="_blank"
|
||||
className="font-bold hover:underline text-foreground/80"
|
||||
rel="noreferrer"
|
||||
>
|
||||
your Supabase project's API Settings
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</TutorialStep>
|
||||
|
||||
<TutorialStep title="Restart your Next.js development server">
|
||||
<p>
|
||||
You may need to quit your Next.js development server and run{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
npm run dev
|
||||
</span>{" "}
|
||||
again to load the new environment variables.
|
||||
</p>
|
||||
</TutorialStep>
|
||||
|
||||
<TutorialStep title="Refresh the page">
|
||||
<p>
|
||||
You may need to refresh the page for Next.js to load the new
|
||||
environment variables.
|
||||
</p>
|
||||
</TutorialStep>
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
96
components/tutorial/fetch-data-steps.tsx
Normal file
96
components/tutorial/fetch-data-steps.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { TutorialStep } from "./tutorial-step";
|
||||
import { CodeBlock } from "./code-block";
|
||||
|
||||
const create = `create table notes (
|
||||
id bigserial primary key,
|
||||
title text
|
||||
);
|
||||
|
||||
insert into notes(title)
|
||||
values
|
||||
('Today I created a Supabase project.'),
|
||||
('I added some data and queried it from Next.js.'),
|
||||
('It was awesome!');
|
||||
`.trim();
|
||||
|
||||
const server = `import { createClient } from '@/utils/supabase/server'
|
||||
|
||||
export default async function Page() {
|
||||
const supabase = await createClient()
|
||||
const { data: notes } = await supabase.from('notes').select()
|
||||
|
||||
return <pre>{JSON.stringify(notes, null, 2)}</pre>
|
||||
}
|
||||
`.trim();
|
||||
|
||||
const client = `'use client'
|
||||
|
||||
import { createClient } from '@/utils/supabase/client'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function Page() {
|
||||
const [notes, setNotes] = useState<any[] | null>(null)
|
||||
const supabase = createClient()
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
const { data } = await supabase.from('notes').select()
|
||||
setNotes(data)
|
||||
}
|
||||
getData()
|
||||
}, [])
|
||||
|
||||
return <pre>{JSON.stringify(notes, null, 2)}</pre>
|
||||
}
|
||||
`.trim();
|
||||
|
||||
export default function FetchDataSteps() {
|
||||
return (
|
||||
<ol className="flex flex-col gap-6">
|
||||
<TutorialStep title="Create some tables and insert some data">
|
||||
<p>
|
||||
Head over to the{" "}
|
||||
<a
|
||||
href="https://supabase.com/dashboard/project/_/editor"
|
||||
className="font-bold hover:underline text-foreground/80"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Table Editor
|
||||
</a>{" "}
|
||||
for your Supabase project to create a table and insert some example
|
||||
data. If you're stuck for creativity, you can copy and paste the
|
||||
following into the{" "}
|
||||
<a
|
||||
href="https://supabase.com/dashboard/project/_/sql/new"
|
||||
className="font-bold hover:underline text-foreground/80"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
SQL Editor
|
||||
</a>{" "}
|
||||
and click RUN!
|
||||
</p>
|
||||
<CodeBlock code={create} />
|
||||
</TutorialStep>
|
||||
|
||||
<TutorialStep title="Query Supabase data from Next.js">
|
||||
<p>
|
||||
To create a Supabase client and query data from an Async Server
|
||||
Component, create a new page.tsx file at{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
/app/notes/page.tsx
|
||||
</span>{" "}
|
||||
and add the following.
|
||||
</p>
|
||||
<CodeBlock code={server} />
|
||||
<p>Alternatively, you can use a Client Component.</p>
|
||||
<CodeBlock code={client} />
|
||||
</TutorialStep>
|
||||
|
||||
<TutorialStep title="Build in a weekend and scale to millions!">
|
||||
<p>You're ready to launch your product to the world! 🚀</p>
|
||||
</TutorialStep>
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
88
components/tutorial/sign-up-user-steps.tsx
Normal file
88
components/tutorial/sign-up-user-steps.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import Link from "next/link";
|
||||
import { TutorialStep } from "./tutorial-step";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
|
||||
export default function SignUpUserSteps() {
|
||||
return (
|
||||
<ol className="flex flex-col gap-6">
|
||||
{process.env.VERCEL_ENV === "preview" ||
|
||||
process.env.VERCEL_ENV === "production" ? (
|
||||
<TutorialStep title="Set up redirect urls">
|
||||
<p>It looks like this App is hosted on Vercel.</p>
|
||||
<p className="mt-4">
|
||||
This particular deployment is
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
"{process.env.VERCEL_ENV}"
|
||||
</span>{" "}
|
||||
on
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
https://{process.env.VERCEL_URL}
|
||||
</span>
|
||||
.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
You will need to{" "}
|
||||
<Link
|
||||
className="text-primary hover:text-foreground"
|
||||
href={
|
||||
"https://supabase.com/dashboard/project/_/auth/url-configuration"
|
||||
}
|
||||
>
|
||||
update your Supabase project
|
||||
</Link>{" "}
|
||||
with redirect URLs based on your Vercel deployment URLs.
|
||||
</p>
|
||||
<ul className="mt-4">
|
||||
<li>
|
||||
-{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
http://localhost:3000/**
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
-{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
{`https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}/**`}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
-{" "}
|
||||
<span className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-medium text-secondary-foreground border">
|
||||
{`https://${process.env.VERCEL_PROJECT_PRODUCTION_URL?.replace(".vercel.app", "")}-*-[vercel-team-url].vercel.app/**`}
|
||||
</span>{" "}
|
||||
(Vercel Team URL can be found in{" "}
|
||||
<Link
|
||||
className="text-primary hover:text-foreground"
|
||||
href="https://vercel.com/docs/accounts/create-a-team#find-your-team-id"
|
||||
target="_blank"
|
||||
>
|
||||
Vercel Team settings
|
||||
</Link>
|
||||
)
|
||||
</li>
|
||||
</ul>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/auth/redirect-urls#vercel-preview-urls"
|
||||
target="_blank"
|
||||
className="text-primary/50 hover:text-primary flex items-center text-sm gap-1 mt-4"
|
||||
>
|
||||
Redirect URLs Docs <ArrowUpRight size={14} />
|
||||
</Link>
|
||||
</TutorialStep>
|
||||
) : null}
|
||||
<TutorialStep title="Sign up your first user">
|
||||
<p>
|
||||
Head over to the{" "}
|
||||
<Link
|
||||
href="/sign-up"
|
||||
className="font-bold hover:underline text-foreground/80"
|
||||
>
|
||||
Sign up
|
||||
</Link>{" "}
|
||||
page and sign up your first user. It's okay if this is just you for
|
||||
now. Your awesome idea will have plenty of users later!
|
||||
</p>
|
||||
</TutorialStep>
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
30
components/tutorial/tutorial-step.tsx
Normal file
30
components/tutorial/tutorial-step.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Checkbox } from "../ui/checkbox";
|
||||
|
||||
export function TutorialStep({
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<li className="relative">
|
||||
<Checkbox
|
||||
id={title}
|
||||
name={title}
|
||||
className={`absolute top-[3px] mr-2 peer`}
|
||||
/>
|
||||
<label
|
||||
htmlFor={title}
|
||||
className={`relative text-base text-foreground peer-checked:line-through font-medium`}
|
||||
>
|
||||
<span className="ml-8">{title}</span>
|
||||
<div
|
||||
className={`ml-8 text-sm peer-checked:line-through font-normal text-muted-foreground`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user