Initial commit

This commit is contained in:
Daniel Alves Rösel
2026-04-02 18:47:14 +02:00
committed by GitHub
commit 90ad5e0260
94 changed files with 7797 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
import { type EmailOtpType } from '@supabase/supabase-js'
import { type NextRequest } from 'next/server'
import { createClient } from '@/utils/supabase/server'
import { redirect } from 'next/navigation'
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const token_hash = searchParams.get('token_hash')
const type = searchParams.get('type') as EmailOtpType | null
const next = searchParams.get('next') ?? '/'
if (token_hash && type) {
const supabase = await createClient()
const { error } = await supabase.auth.verifyOtp({
type,
token_hash,
})
if (!error) {
// redirect user to specified redirect URL or root of app
redirect(next)
}
}
// redirect the user to an error page with some instructions
redirect('/error')
}

View File

@@ -0,0 +1,10 @@
export default function BlogPage() {
return (
<main className="mx-auto max-w-3xl px-6 py-16">
<h1 className="text-3xl font-semibold tracking-tight">Blog</h1>
<p className="mt-4 text-sm text-neutral-600">
Publish product updates, engineering notes, and launch posts here.
</p>
</main>
);
}

View File

@@ -0,0 +1,12 @@
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { createClient } from '@/utils/supabase/server'
export async function logout() {
const supabase = await createClient()
await supabase.auth.signOut()
revalidatePath('/', 'layout')
redirect('/login')
}

View File

@@ -0,0 +1,14 @@
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div>
<nav>
<h1>Dashboard</h1>
</nav>
<main>{children}</main>
</div>
)
}

View File

@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'
import { createClient } from '@/utils/supabase/server'
import { logout } from './actions'
export default async function DashboardPage() {
const supabase = await createClient()
const { data, error } = await supabase.auth.getUser()
if (error || !data?.user) {
redirect('/login')
}
return (
<div>
<p>Welcome, {data.user.email}</p>
<form>
<button formAction={logout}>Logout</button>
</form>
</div>
)
}

View File

@@ -0,0 +1,20 @@
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
{/* TODO: Style this error page when implementing in your project */}
<h2>Something went wrong!</h2>
<p>{error.message || 'An unexpected error occurred'}</p>
<button onClick={() => reset()}>
Try again
</button>
</div>
);
}

View File

@@ -0,0 +1,5 @@
'use client'
export default function ErrorPage() {
return <p>Sorry, something went wrong</p>
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

View File

@@ -0,0 +1,8 @@
import { createClient } from '@/utils/supabase/server';
export default async function Instruments() {
const supabase = await createClient();
const { data: instruments } = await supabase.from("instruments").select();
return <pre>{JSON.stringify(instruments, null, 2)}</pre>
}

View File

@@ -0,0 +1,29 @@
import type { Metadata } from "next";
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const fontVariables = "font-sans";
export const metadata: Metadata = {
title: "Ultiplate - Ultimate Boilerplate",
description: "AI-native template for any project with deployment ready setup",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${fontVariables} antialiased`}>
<Header />
<main className="min-h-screen">
{children}
</main>
<Footer />
</body>
</html>
);
}

View File

@@ -0,0 +1,46 @@
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { createClient } from '@/utils/supabase/server'
export async function login(formData: FormData) {
const supabase = await createClient()
// type-casting here for convenience
// in practice, you should validate your inputs
const data = {
email: formData.get('email') as string,
password: formData.get('password') as string,
}
const { error } = await supabase.auth.signInWithPassword(data)
if (error) {
redirect('/error')
}
revalidatePath('/', 'layout')
redirect('/')
}
export async function signup(formData: FormData) {
const supabase = await createClient()
// type-casting here for convenience
// in practice, you should validate your inputs
const data = {
email: formData.get('email') as string,
password: formData.get('password') as string,
}
const { error } = await supabase.auth.signUp(data)
if (error) {
redirect('/error')
}
revalidatePath('/', 'layout')
redirect('/')
}

View File

@@ -0,0 +1,14 @@
import { login, signup } from './actions'
export default function LoginPage() {
return (
<form>
<label htmlFor="email">Email:</label>
<input id="email" name="email" type="email" required />
<label htmlFor="password">Password:</label>
<input id="password" name="password" type="password" required />
<button formAction={login}>Log in</button>
<button formAction={signup}>Sign up</button>
</form>
)
}

View File

@@ -0,0 +1,12 @@
import Link from 'next/link';
export default function NotFound() {
return (
<div>
{/* TODO: Style this 404 page when implementing in your project */}
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
</div>
);
}

View File

@@ -0,0 +1,17 @@
import Hero from "@/components/Hero";
import FeaturesGrid from "@/components/FeaturesGrid";
import Testimonials1 from "@/components/Testimonials1";
import Pricing from "@/components/Pricing";
//import FAQ from "@/components/FAQ";
//import CTA from "@/components/CTA";
export default function Home() {
return (
<div>
<Hero />
<FeaturesGrid />
<Testimonials1 />
<Pricing />
</div>
);
}

View File

@@ -0,0 +1,10 @@
export default function PrivacyPolicyPage() {
return (
<main className="mx-auto max-w-3xl px-6 py-16">
<h1 className="text-3xl font-semibold tracking-tight">Privacy Policy</h1>
<p className="mt-4 text-sm text-neutral-600">
Describe what data you collect, how it is used, and how users can request deletion.
</p>
</main>
);
}

View File

@@ -0,0 +1,10 @@
export default function TermsOfServicePage() {
return (
<main className="mx-auto max-w-3xl px-6 py-16">
<h1 className="text-3xl font-semibold tracking-tight">Terms of Service</h1>
<p className="mt-4 text-sm text-neutral-600">
Add your product terms, responsibilities, and legal limitations in this page.
</p>
</main>
);
}