diff --git a/.env.example b/.env.example index dedca96..ba4fc7d 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,10 @@ # Visit https://dashboard.clerk.com/last-active?path=api-keys to find your API Keys NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= + +# The route at which your authentication flow lives, for more information see: https://clerk.com/docs/deployments/clerk-environment-variables +NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in + +# Redirect URLs for after authenticating, for more information see: https://clerk.com/docs/guides/custom-redirects +NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/dashboard +NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/dashboard diff --git a/app/[[...home]]/page.tsx b/app/[[...home]]/page.tsx new file mode 100644 index 0000000..c7663cb --- /dev/null +++ b/app/[[...home]]/page.tsx @@ -0,0 +1,49 @@ +import Image from "next/image"; +import Link from "next/link"; +import { + SignIn, + SignInButton, + SignUp, + SignOutButton, + SignedIn, + SignedOut, +} from "@clerk/nextjs"; +import { ClerkLogo } from "../components/clerk-logo"; + +export default function Page() { + return ( +
+
+
+ +
+ +

+ Welcome to Clerk +

+
+ +

+ Get started by signing up as your first user +

+ +
+ + + + + + + + Dashboard + + + + +
+
+ ); +} diff --git a/app/components/clerk-logo.tsx b/app/components/clerk-logo.tsx new file mode 100644 index 0000000..9ef9915 --- /dev/null +++ b/app/components/clerk-logo.tsx @@ -0,0 +1,30 @@ +export function ClerkLogo() { + return ( + + + + + + + + ); +} diff --git a/app/components/code-switcher.tsx b/app/components/code-switcher.tsx new file mode 100644 index 0000000..a41a414 --- /dev/null +++ b/app/components/code-switcher.tsx @@ -0,0 +1,60 @@ +"use client"; + +import { useOrganization, useSession, useUser } from "@clerk/nextjs"; +import clsx from "clsx"; +import { useState } from "react"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import theme from "./theme"; + +const TYPES = ["user", "session", "organization"]; + +export function CodeSwitcher() { + const [selectedType, setSelectedType] = useState(TYPES[0]); + const { user } = useUser(); + const { session } = useSession(); + const { organization } = useOrganization(); + + const selectedCode = JSON.stringify( + { + user, + session, + organization, + }[selectedType], + null, + 2 + ); + + const typesToShow = organization + ? TYPES + : TYPES.filter((type) => type !== "organization"); + + return ( +
+
+ {typesToShow.map((type) => ( + + ))} +
+
+
+ + {selectedCode} + +
+
+
+
+
+ ); +} diff --git a/app/components/footer.tsx b/app/components/footer.tsx new file mode 100644 index 0000000..31f034a --- /dev/null +++ b/app/components/footer.tsx @@ -0,0 +1,146 @@ +export function Footer() { + return ( + + ); +} diff --git a/app/components/theme.tsx b/app/components/theme.tsx new file mode 100644 index 0000000..f6d0651 --- /dev/null +++ b/app/components/theme.tsx @@ -0,0 +1,154 @@ +export default { + 'code[class*="language-"]': { + color: "#c5c8c6", + fontFamily: "var(--font-geist-mono)", + direction: "ltr", + textAlign: "left", + whiteSpace: "pre", + wordSpacing: "normal", + wordBreak: "normal", + lineHeight: "1.5", + MozTabSize: "4", + OTabSize: "4", + tabSize: "4", + WebkitHyphens: "none", + MozHyphens: "none", + msHyphens: "none", + hyphens: "none", + fontSize: 12, + }, + 'pre[class*="language-"]': { + color: "#c5c8c6", + fontFamily: "var(--font-geist-mono)", + direction: "ltr", + textAlign: "left", + whiteSpace: "pre", + wordSpacing: "normal", + wordBreak: "normal", + lineHeight: "1.5", + MozTabSize: "4", + OTabSize: "4", + tabSize: "4", + WebkitHyphens: "none", + MozHyphens: "none", + msHyphens: "none", + hyphens: "none", + padding: "16px 0px 32px", + margin: ".5em 0", + overflow: "auto", + borderRadius: "0.3em", + background: "white", + fontSize: 12, + height: "100%", + }, + ':not(pre) > code[class*="language-"]': { + background: "white", + padding: ".1em", + borderRadius: ".3em", + }, + comment: { + color: "#7C7C7C", + }, + prolog: { + color: "#7C7C7C", + }, + doctype: { + color: "#7C7C7C", + }, + cdata: { + color: "#7C7C7C", + }, + punctuation: { + color: "#c5c8c6", + }, + ".namespace": { + Opacity: ".7", + }, + property: { + color: "#676767", + }, + keyword: { + color: "#676767", + }, + tag: { + color: "#676767", + }, + "class-name": { + color: "#FFFFB6", + textDecoration: "underline", + }, + boolean: { + color: "#DB3FB9", + }, + constant: { + color: "#DB3FB9", + }, + symbol: { + color: "#f92672", + }, + deleted: { + color: "#f92672", + }, + number: { + color: "#FF73FD", + }, + selector: { + color: "#4248DA", + }, + "attr-name": { + color: "#4248DA", + }, + string: { + color: "#4248DA", + }, + char: { + color: "#4248DA", + }, + builtin: { + color: "#4248DA", + }, + inserted: { + color: "#4248DA", + }, + variable: { + color: "#C6C5FE", + }, + operator: { + color: "#EDEDED", + }, + entity: { + color: "#FFFFB6", + cursor: "help", + }, + url: { + color: "#676767", + }, + ".language-css .token.string": { + color: "#C1C1C1", + }, + ".style .token.string": { + color: "#C1C1C1", + }, + atrule: { + color: "#F9EE98", + }, + "attr-value": { + color: "#F9EE98", + }, + function: { + color: "#DAD085", + }, + regex: { + color: "#E9C062", + }, + important: { + color: "#fd971f", + fontWeight: "bold", + }, + bold: { + fontWeight: "bold", + }, + italic: { + fontStyle: "italic", + }, +}; diff --git a/app/components/user-details.tsx b/app/components/user-details.tsx new file mode 100644 index 0000000..af50edb --- /dev/null +++ b/app/components/user-details.tsx @@ -0,0 +1,167 @@ +"use client"; + +import { useOrganization, useSession, useUser } from "@clerk/nextjs"; + +function Row({ + desc, + value, + children, +}: { + desc: string; + value: string; + children: React.ReactNode; +}) { + return ( +
+ {desc} + + {value} + {children} + +
+ ); +} + +function PointerC({ label }: { label: string }) { + return ( +
+
+
+
+
+
+ {label} +
+
+ ); +} + +function formatDate(date: Date) { + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); +} + +function formatDateWithNumbers(date: Date): string { + return date.toLocaleString("en-US", { + month: "numeric", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + hour12: true, + }); +} + +export function UserDetails() { + const { user } = useUser(); + const { session } = useSession(); + const { organization } = useOrganization(); + + if (!user || !session) return null; + + return ( +
+
+
+
+ +
+
+
+
+
+
+ user.imageUrl +
+
+
+ {user.firstName && user.lastName ? ( +

+ {user.firstName} {user.lastName} +
+
+
+
+
+
+ user.firstName +
+
+ user.lastName +
+
+

+ ) : ( +
+ )} +
+ +
+ + + + + + + + + + + + +
+

+ Session details +

+
+ + + + + + + + + + + + +
+ {organization ? ( + <> +

+ Organization detail +

+
+ + + + + + + + + + + + +
+ + ) : null} +
+
+ ); +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..f3c90ad --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,60 @@ +import { UserButton } from "@clerk/nextjs"; +import { auth } from "@clerk/nextjs/server"; +import { ClerkLogo } from "../components/clerk-logo"; +import Link from "next/link"; +import { CodeSwitcher } from "../components/code-switcher"; +import { UserDetails } from "../components/user-details"; + +export default async function Page() { + await auth.protect(); + + return ( + <> +
+
+
+
+
+
+ +
+ + + + + Back to Home + +
+
+ +
+
+ +
+
+ +
+
+
+ + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index e9e5c37..a51e97c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,46 +1,50 @@ -import type { Metadata } from 'next' -import { ClerkProvider, SignInButton, SignUpButton, SignedIn, SignedOut, UserButton } from '@clerk/nextjs' -import { Geist, Geist_Mono } from 'next/font/google' -import './globals.css' +import type { Metadata } from "next"; +import { + ClerkProvider, + SignInButton, + SignUpButton, + SignedIn, + SignedOut, + UserButton, +} from "@clerk/nextjs"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; +import { Footer } from "./components/footer"; const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], -}) + variable: "--font-geist-sans", + subsets: ["latin"], +}); const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], -}) + variable: "--font-geist-mono", + subsets: ["latin"], +}); export const metadata: Metadata = { - title: 'Clerk Next.js Quickstart', - description: 'Generated by create next app', -} + title: "Clerk Next.js Quickstart", + description: "Generated by create next app", +}; export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode + children: React.ReactNode; }>) { return ( - + - -
- - - - - - - -
+ {children} +