diff --git a/boilerplate-chakra-pro-main/.env.local.example b/boilerplate-chakra-pro-main/.env.local.example new file mode 100644 index 00000000..aa56306f --- /dev/null +++ b/boilerplate-chakra-pro-main/.env.local.example @@ -0,0 +1,26 @@ +NEXT_PUBLIC_SUPABASE_URL=https://********************.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=**************************************************************************************************************************************************************************************************************** + +NEXT_PUBLIC_OPENAI_API_KEY=sk-************************************************ +NEXT_PUBLIC_OPENAI_ASSISTANT_KEY=asst_************************ + +# Update these with your Supabase details from your project settings > API + SUPABASE_SERVICE_ROLE_KEY=*************************************************************************************************************************************************************************************************************************** + +# Update these with your Stripe credentials from https://dashboard.stripe.com/apikeys +# NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_*************************************************************************************************** +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_*************************************************************************************************** +# STRIPE_SECRET_KEY=sk_live_*************************************************************************************************** +STRIPE_SECRET_KEY=sk_test_*************************************************************************************************** +# The commented variable is usually for production webhook key. This you get in the Stripe dashboard and is usually shorter. +# STRIPE_WEBHOOK_SECRET=whsec_******************************** +STRIPE_WEBHOOK_SECRET=whsec_**************************************************************** + +# Update this with your stable site URL only for the production environment. +# NEXT_PUBLIC_SITE_URL=https://horizon-ui.com/shadcn-nextjs-boilerplate +# NEXT_PUBLIC_SITE_URL=https://******************.com + +NEXT_PUBLIC_AWS_S3_REGION=eu-north-1 +NEXT_PUBLIC_AWS_S3_ACCESS_KEY_ID=******************** +NEXT_PUBLIC_AWS_S3_SECRET_ACCESS_KEY=**************************************** +NEXT_PUBLIC_AWS_S3_BUCKET_NAME=mybucket \ No newline at end of file diff --git a/boilerplate-chakra-pro-main/.eslintrc.json b/boilerplate-chakra-pro-main/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/boilerplate-chakra-pro-main/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/src/views/AgentChat/neuratalk/.gitignore b/boilerplate-chakra-pro-main/.gitignore similarity index 64% rename from src/views/AgentChat/neuratalk/.gitignore rename to boilerplate-chakra-pro-main/.gitignore index 5ef6a520..52813db0 100644 --- a/src/views/AgentChat/neuratalk/.gitignore +++ b/boilerplate-chakra-pro-main/.gitignore @@ -3,12 +3,7 @@ # dependencies /node_modules /.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions +.pnp.js # testing /coverage @@ -30,8 +25,14 @@ yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local +.env.local.production +.env.local.new +.env.local.non-docker # vercel .vercel @@ -39,3 +40,8 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +yarn.lock + +# editors +.vscode diff --git a/boilerplate-chakra-pro-main/.npmignore b/boilerplate-chakra-pro-main/.npmignore new file mode 100644 index 00000000..e69de29b diff --git a/boilerplate-chakra-pro-main/.npmrc b/boilerplate-chakra-pro-main/.npmrc new file mode 100644 index 00000000..5c6c9587 --- /dev/null +++ b/boilerplate-chakra-pro-main/.npmrc @@ -0,0 +1,3 @@ +legacy-peer-deps=true +auto-install-peers=true +strict-peer-dependencies=false \ No newline at end of file diff --git a/boilerplate-chakra-pro-main/README.md b/boilerplate-chakra-pro-main/README.md new file mode 100644 index 00000000..b7695cf5 --- /dev/null +++ b/boilerplate-chakra-pro-main/README.md @@ -0,0 +1,106 @@ +  +

+ + Horizon UI Boilerplate - NextJS Template for Startups & Companies + +

+ +

+ Website • + Documentation • + Admin Template • + AI Template • + Twitter +

+ Launch your SaaS startup within a few days with the all-in-one NextJS boilerplate that you always searched for. + +

+ +  + +

+Horizon UI Free Tailwind CSS Landing Page Kit +

+ + +  + +# Quickstart install + +Install our product by running either of the following: + +- Open the .zip archive file you got when you bought Horizon +  +- Install NodeJS LTS from [NodeJs Official Page](https://nodejs.org/en/) (NOTE: Product only works with LTS version). +  + +Run in the terminal this command: + +``` +npm install +``` + +
+ +Then run this command to start your local server: + +``` +npm run dev +``` +  + +# Documentation + +View full documentation here + + +--- + +# Example Sections + +If you want to get inspiration for your startup project or just show something directly to your clients, you can jump-start your development with our pre-built example sections. You will be able to quickly set up the basic structure for your web project. + + View example sections here + + + +--- + +# Reporting Issues + +We use GitHub Issues as the official bug tracker for the Horizon UI. Here are +some advice for our users who want to report an issue: + +1. Make sure that you are using the latest version of the Horizon UI Boilerplate. Check the CHANGELOG for your dashboard on our [CHANGE LOG File](https://github.com/horizon-ui/boilerplate-issues/blob/main/CHANGELOG.md). +
+ +1. Providing us with reproducible steps for the issue will shorten the time it takes for it to be fixed. +
+ + +3. Some issues may be browser-specific, so specifying in what browser you encountered the issue might help. + +--- + +# Community + +Connect with the community! Feel free to ask questions, report issues, and meet new people who already use Horizon UI! + +💬 [Join the #HorizonUI Discord Community!](https://discord.gg/f6tEKFBd4m) + + +### Copyright and license + +⭐️ [Copyright 2024 Horizon UI](https://www.horizon-ui.com/?ref=readme-horizon) + +📄 [Horizon UI License](https://horizon-ui.notion.site/End-User-License-Agreement-8fb09441ea8c4c08b60c37996195a6d5) + + +--- + +# Credits + +Special thanks to the open-source resources that helped us create this awesome boilerplate package, including: + +- [NextJS Subscription Payments](https://github.com/vercel/nextjs-subscription-payments) +- [ChatBot UI by mckaywrigley](https://github.com/mckaywrigley/chatbot-ui) \ No newline at end of file diff --git a/boilerplate-chakra-pro-main/app-essay-builder.code-workspace b/boilerplate-chakra-pro-main/app-essay-builder.code-workspace new file mode 100644 index 00000000..a9ad457d --- /dev/null +++ b/boilerplate-chakra-pro-main/app-essay-builder.code-workspace @@ -0,0 +1,17 @@ +{ + "folders": [ + { + "name": "project-root", + "path": "./" + }, + { + "name": "supabase-functions", + "path": "supabase/functions" + } + ], + "settings": { + "files.exclude": { + "supabase/functions/": true + } + } +} diff --git a/boilerplate-chakra-pro-main/app/api/chatAPI/route.ts b/boilerplate-chakra-pro-main/app/api/chatAPI/route.ts new file mode 100644 index 00000000..9992bcb4 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/api/chatAPI/route.ts @@ -0,0 +1,43 @@ +import { ChatBody } from '@/types/types'; +import { OpenAIStream } from '@/utils/streams/chatStream'; + +export const runtime = 'edge'; + +export async function GET(req: Request): Promise { + try { + const { inputMessage, model, apiKey } = (await req.json()) as ChatBody; + + let apiKeyFinal; + if (apiKey) { + apiKeyFinal = apiKey; + } else { + apiKeyFinal = process.env.NEXT_PUBLIC_OPENAI_API_KEY; + } + + const stream = await OpenAIStream(inputMessage, model, apiKeyFinal); + + return new Response(stream); + } catch (error) { + console.error(error); + return new Response('Error', { status: 500 }); + } +} +export async function POST(req: Request): Promise { + try { + const { inputMessage, model, apiKey } = (await req.json()) as ChatBody; + + let apiKeyFinal; + if (apiKey) { + apiKeyFinal = apiKey; + } else { + apiKeyFinal = process.env.NEXT_PUBLIC_OPENAI_API_KEY; + } + + const stream = await OpenAIStream(inputMessage, model, apiKeyFinal); + + return new Response(stream); + } catch (error) { + console.error(error); + return new Response('Error', { status: 500 }); + } +} diff --git a/boilerplate-chakra-pro-main/app/api/essayAPI/route.ts b/boilerplate-chakra-pro-main/app/api/essayAPI/route.ts new file mode 100644 index 00000000..bb4a722a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/api/essayAPI/route.ts @@ -0,0 +1,29 @@ +import { EssayBody } from '@/types/types'; +import { OpenAIStream } from '@/utils/streams/essayStream'; + +export const runtime = 'edge'; + +const handler = async (req: Request): Promise => { + try { + const { + topic, + words, + essayType, + model, + apiKey + } = (await req.json()) as EssayBody; + + if (!apiKey) { + return new Response('API key not found', { status: 500 }); + } + + const stream = await OpenAIStream(topic, essayType, words, model, apiKey); + + return new Response(stream); + } catch (error) { + console.error(error); + return new Response('Error', { status: 500 }); + } +}; + +export default handler; diff --git a/boilerplate-chakra-pro-main/app/api/premiumEssayAPI/route.ts b/boilerplate-chakra-pro-main/app/api/premiumEssayAPI/route.ts new file mode 100644 index 00000000..a4e7ed1a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/api/premiumEssayAPI/route.ts @@ -0,0 +1,43 @@ +import { PremiumEssayBody } from '@/types/types'; +import { OpenAIStream } from '@/utils/streams/premiumEssayStream'; + +export const runtime = 'edge'; + +const handler = async (req: Request): Promise => { + try { + const { + words, + topic, + essayType, + tone, + citation, + level, + citations, + model, + apiKey + } = (await req.json()) as PremiumEssayBody; + + if (!apiKey) { + return new Response('API key not found', { status: 500 }); + } + + const stream = await OpenAIStream( + words, + topic, + essayType, + tone, + citation, + level, + citations, + model, + apiKey + ); + + return new Response(stream); + } catch (error) { + console.error(error); + return new Response('Error', { status: 500 }); + } +}; + +export default handler; diff --git a/boilerplate-chakra-pro-main/app/api/webhooks/route.ts b/boilerplate-chakra-pro-main/app/api/webhooks/route.ts new file mode 100644 index 00000000..322593c1 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/api/webhooks/route.ts @@ -0,0 +1,81 @@ +import { stripe } from '@/utils/stripe/config'; +import { + manageSubscriptionStatusChange, + upsertPriceRecord, + upsertProductRecord +} from '@/utils/supabase/admin'; +import { headers } from 'next/headers'; +import Stripe from 'stripe'; + +const relevantEvents = new Set([ + 'product.created', + 'product.updated', + 'price.created', + 'price.updated', + 'checkout.session.completed', + 'customer.subscription.created', + 'customer.subscription.updated', + 'customer.subscription.deleted' +]); + +export async function POST(req: Request) { + const body = await req.text(); + const sig = headers().get('Stripe-Signature') as string; + const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; + let event: Stripe.Event; + + try { + if (!sig || !webhookSecret) return; + event = stripe.webhooks.constructEvent(body, sig, webhookSecret); + } catch (err) { + console.log(`❌ Error message: ${err.message}`); + return new Response(`Webhook Error: ${err.message}`, { status: 400 }); + } + + if (relevantEvents.has(event.type)) { + try { + switch (event.type) { + case 'product.created': + case 'product.updated': + await upsertProductRecord(event.data.object as Stripe.Product); + break; + case 'price.created': + case 'price.updated': + await upsertPriceRecord(event.data.object as Stripe.Price); + break; + case 'customer.subscription.created': + case 'customer.subscription.updated': + case 'customer.subscription.deleted': + const subscription = event.data.object as Stripe.Subscription; + await manageSubscriptionStatusChange( + subscription.id, + subscription.customer as string, + event.type === 'customer.subscription.created' + ); + break; + case 'checkout.session.completed': + const checkoutSession = event.data.object as Stripe.Checkout.Session; + if (checkoutSession.mode === 'subscription') { + const subscriptionId = checkoutSession.subscription; + await manageSubscriptionStatusChange( + subscriptionId as string, + checkoutSession.customer as string, + true + ); + } + break; + default: + throw new Error('Unhandled relevant event!'); + } + } catch (error) { + console.log(error); + return new Response( + 'Webhook handler failed. View your nextjs function logs.', + { + status: 400 + } + ); + } + } + return new Response(JSON.stringify({ received: true })); +} diff --git a/boilerplate-chakra-pro-main/app/auth/callback/route.ts b/boilerplate-chakra-pro-main/app/auth/callback/route.ts new file mode 100644 index 00000000..1bb56afc --- /dev/null +++ b/boilerplate-chakra-pro-main/app/auth/callback/route.ts @@ -0,0 +1,35 @@ +import { getErrorRedirect, getStatusRedirect } from '@/utils/helpers'; +import { createClient } from '@/utils/supabase/server'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(request: NextRequest) { + // The `/auth/callback` route is required for the server-side auth flow implemented + // by the `@supabase/ssr` package. It exchanges an auth code for the user's session. + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get('code'); + + if (code) { + const supabase = createClient(); + + const { error } = await supabase.auth.exchangeCodeForSession(code); + + if (error) { + return NextResponse.redirect( + getErrorRedirect( + `${requestUrl.origin}/dashboard/signin`, + error.name, + "Sorry, we weren't able to log you in. Please try again." + ) + ); + } + } + + // URL to redirect to after sign in process completes + return NextResponse.redirect( + getStatusRedirect( + `${requestUrl.origin}/dashboard/main`, + 'Success!', + 'You are now signed in.' + ) + ); +} diff --git a/boilerplate-chakra-pro-main/app/auth/reset_password/route.ts b/boilerplate-chakra-pro-main/app/auth/reset_password/route.ts new file mode 100644 index 00000000..f1b0147a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/auth/reset_password/route.ts @@ -0,0 +1,36 @@ +import { createClient } from '@/utils/supabase/server'; +import { NextResponse } from 'next/server'; +import { NextRequest } from 'next/server'; +import { getErrorRedirect, getStatusRedirect } from '@/utils/helpers'; + +export async function GET(request: NextRequest) { + // The `/auth/callback` route is required for the server-side auth flow implemented + // by the `@supabase/ssr` package. It exchanges an auth code for the user's session. + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get('code'); + + if (code) { + const supabase = createClient(); + + const { error } = await supabase.auth.exchangeCodeForSession(code); + + if (error) { + return NextResponse.redirect( + getErrorRedirect( + `${requestUrl.origin}/signin/forgot_password`, + error.name, + "Sorry, we weren't able to log you in. Please try again." + ) + ); + } + } + + // URL to redirect to after sign in process completes + return NextResponse.redirect( + getStatusRedirect( + `${requestUrl.origin}/signin/update_password`, + 'You are now signed in.', + 'Please enter a new password for your account.' + ) + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/ai-assistant/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/ai-assistant/page.tsx new file mode 100644 index 00000000..623438da --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/ai-assistant/page.tsx @@ -0,0 +1,35 @@ +import Assistant from '@/components/dashboard/ai-assistant'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function AiAssistant() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/ai-chat/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/ai-chat/page.tsx new file mode 100644 index 00000000..e6eaace3 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/ai-chat/page.tsx @@ -0,0 +1,36 @@ +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; + +import Chat from '@/components/dashboard/ai-chat'; +import { Providers } from '@/components/providers'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function AiChat() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/ai-generator/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/ai-generator/page.tsx new file mode 100644 index 00000000..3a299346 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/ai-generator/page.tsx @@ -0,0 +1,35 @@ +import Generator from '@/components/dashboard/ai-generator'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function AiGenerator() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/main/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/main/page.tsx new file mode 100644 index 00000000..bf6f4240 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/main/page.tsx @@ -0,0 +1,35 @@ +import { Providers } from '@/components/providers'; + +import Main from '@/components/dashboard/main'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function MainPage() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + +
+ + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/page.tsx new file mode 100644 index 00000000..be0e66c8 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/page.tsx @@ -0,0 +1,14 @@ +import { getUser } from '@/utils/supabase/queries'; +import { redirect } from 'next/navigation'; +import { createClient } from '@/utils/supabase/server'; + +export default async function Dashboard() { + const supabase = createClient(); + const [user] = await Promise.all([getUser(supabase)]); + + if (!user) { + return redirect('/dashboard/signin'); + } else { + redirect('/dashboard/main'); + } +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/premium-essays/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/premium-essays/page.tsx new file mode 100644 index 00000000..6db79c4a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/premium-essays/page.tsx @@ -0,0 +1,42 @@ +import PremiumGenerator from '@/components/dashboard/premium-generator'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function PremiumGeneratorPage() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + if (!subscription) { + redirect('/dashboard/main'); + } + return ( + + {subscription ? ( + + ) : ( +

NICE TRY BUDDY

+ )} +
+ ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/settings/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/settings/page.tsx new file mode 100644 index 00000000..70cf03ec --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/settings/page.tsx @@ -0,0 +1,35 @@ +import Settings from '@/components/dashboard/settings'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function SettingsPage() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/signin/[id]/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/signin/[id]/page.tsx new file mode 100644 index 00000000..a3ead599 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/signin/[id]/page.tsx @@ -0,0 +1,71 @@ +import DefaultAuth from '@/components/auth'; +import AuthUI from '@/components/auth/AuthUI'; +import { Providers } from '@/components/providers'; +import { + getAuthTypes, + getDefaultSignInView, + getRedirectMethod, + getViewTypes +} from '@/utils/auth-helpers/settings'; +import { createClient } from '@/utils/supabase/server'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; +import illustration from '/public/img/auth/auth.png'; + +export default async function SignIn({ + params, + searchParams +}: { + params: { id: string }; + searchParams: { disable_button: boolean }; +}) { + const { allowOauth, allowEmail, allowPassword } = getAuthTypes(); + const viewTypes = getViewTypes(); + const redirectMethod = getRedirectMethod(); + + // Declare 'viewProp' and initialize with the default value + let viewProp: string; + + // Assign url id to 'viewProp' if it's a valid string and ViewTypes includes it + if (typeof params.id === 'string' && viewTypes.includes(params.id)) { + viewProp = params.id; + } else { + const preferredSignInView = + cookies().get('preferredSignInView')?.value || null; + viewProp = getDefaultSignInView(preferredSignInView); + return redirect(`/dashboard/signin/${viewProp}`); + } + + // Check if the user is already logged in and redirect to the account page if so + const supabase = createClient(); + + const { + data: { user } + } = await supabase.auth.getUser(); + + if (user && viewProp !== 'update_password') { + return redirect('/dashboard/main'); + } else if (!user && viewProp === 'update_password') { + return redirect('/dashboard/signin'); + } + return ( + + +
+ +
+
+
+ ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/signin/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/signin/page.tsx new file mode 100644 index 00000000..7c660c56 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/signin/page.tsx @@ -0,0 +1,11 @@ +import { getDefaultSignInView } from '@/utils/auth-helpers/settings'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; + +export default function SignIn() { + const preferredSignInView = + cookies().get('preferredSignInView')?.value || null; + const defaultView = getDefaultSignInView(preferredSignInView); + + return redirect(`/dashboard/signin/${defaultView}`); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/subscription/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/subscription/page.tsx new file mode 100644 index 00000000..3d8abc70 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/subscription/page.tsx @@ -0,0 +1,34 @@ +import Subscription from '@/components/dashboard/subscription'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function SubscriptionPage() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/dashboard/users-list/page.tsx b/boilerplate-chakra-pro-main/app/dashboard/users-list/page.tsx new file mode 100644 index 00000000..22a66a28 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/dashboard/users-list/page.tsx @@ -0,0 +1,35 @@ +import UsersList from '@/components/dashboard/users-list'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getSubscription, + getUser, + getUserDetails +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; +import { redirect } from 'next/navigation'; + +export default async function UserList() { + const supabase = createClient(); + const [user, userDetails, products, subscription] = await Promise.all([ + getUser(supabase), + getUserDetails(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + if (!user) { + return redirect('/dashboard/signin'); + } + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/essayAPI.ts b/boilerplate-chakra-pro-main/app/essayAPI.ts new file mode 100644 index 00000000..bb4a722a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/essayAPI.ts @@ -0,0 +1,29 @@ +import { EssayBody } from '@/types/types'; +import { OpenAIStream } from '@/utils/streams/essayStream'; + +export const runtime = 'edge'; + +const handler = async (req: Request): Promise => { + try { + const { + topic, + words, + essayType, + model, + apiKey + } = (await req.json()) as EssayBody; + + if (!apiKey) { + return new Response('API key not found', { status: 500 }); + } + + const stream = await OpenAIStream(topic, essayType, words, model, apiKey); + + return new Response(stream); + } catch (error) { + console.error(error); + return new Response('Error', { status: 500 }); + } +}; + +export default handler; diff --git a/boilerplate-chakra-pro-main/app/layout.tsx b/boilerplate-chakra-pro-main/app/layout.tsx new file mode 100644 index 00000000..2c41d506 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/layout.tsx @@ -0,0 +1,81 @@ +import SupabaseProvider from './supabase-provider'; +import Script from 'next/script'; +import { PropsWithChildren } from 'react'; +import '@/styles/globals.css'; + +export const dynamic = 'force-dynamic'; + +export default function RootLayout({ + // Layouts must accept a children prop. + // This will be populated with nested layouts or pages + children, +}: PropsWithChildren) { + return ( + + + + Horizon UI Boilerplate - Launch your startup project 10X in a few + moments - The best NextJS Boilerplate (This is an example) + + + {/* */} + + + {/* */} + + + + {/* */} + + + + + {/* */} + + + + + + + + + + + + {/* @ts-ignore */} +
{children}
+
+ + + ); +} diff --git a/boilerplate-chakra-pro-main/app/page.tsx b/boilerplate-chakra-pro-main/app/page.tsx new file mode 100644 index 00000000..3e9a3799 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/page.tsx @@ -0,0 +1,10 @@ +import Landing from '@/components/landing'; +import { Providers } from '@/components/providers'; + +export default async function PricingPage() { + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/pricing/page.tsx b/boilerplate-chakra-pro-main/app/pricing/page.tsx new file mode 100644 index 00000000..665be946 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/pricing/page.tsx @@ -0,0 +1,23 @@ +import Pricing from '@/components/pricing'; +import { Providers } from '@/components/providers'; +import { + getProducts, + getUser, + getSubscription +} from '@/utils/supabase/queries'; +import { createClient } from '@/utils/supabase/server'; + +export default async function PricingPage() { + const supabase = createClient(); + const [user, products, subscription] = await Promise.all([ + getUser(supabase), + getProducts(supabase), + getSubscription(supabase) + ]); + + return ( + + + + ); +} diff --git a/boilerplate-chakra-pro-main/app/supabase-provider.tsx b/boilerplate-chakra-pro-main/app/supabase-provider.tsx new file mode 100644 index 00000000..84feff27 --- /dev/null +++ b/boilerplate-chakra-pro-main/app/supabase-provider.tsx @@ -0,0 +1,50 @@ +'use client'; + +import type { Database } from '@/types_db'; +import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'; +import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'; +import { useRouter } from 'next/navigation'; +import { createContext, useContext, useEffect, useState } from 'react'; + +type SupabaseContext = { + supabase: SupabaseClient; +}; + +const Context = createContext(undefined); + +export default function SupabaseProvider({ + children +}: { + children: React.ReactNode; +}) { + const [supabase] = useState(() => createPagesBrowserClient()); + const router = useRouter(); + + useEffect(() => { + const { + data: { subscription } + } = supabase.auth.onAuthStateChange((event) => { + if (event === 'SIGNED_IN') router.refresh(); + }); + + return () => { + subscription.unsubscribe(); + }; + }, [router, supabase]); + + return ( + + <>{children} + + ); +} + +export const useSupabase = () => { + const context = useContext(Context); + + if (context === undefined) { + throw new Error('useSupabase must be used inside SupabaseProvider'); + } + + return context; +}; diff --git a/boilerplate-chakra-pro-main/app/supabase-server.ts b/boilerplate-chakra-pro-main/app/supabase-server.ts new file mode 100644 index 00000000..7b6b9f8a --- /dev/null +++ b/boilerplate-chakra-pro-main/app/supabase-server.ts @@ -0,0 +1,54 @@ +import { Database } from '@/types_db'; +import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'; +import { cookies } from 'next/headers'; +import { cache } from 'react'; + +export const createServerSupabaseClient = cache(() => + createServerComponentClient({ cookies }) +); + +export async function getUserDetails() { + const supabase = createServerSupabaseClient(); + try { + const { data: userDetails } = await supabase + .from('users') + .select('*') + .single(); + return userDetails; + } catch (error) { + console.error('Error:', error); + return null; + } +} + +export async function getSubscription() { + const supabase = createServerSupabaseClient(); + try { + const { data: subscription } = await supabase + .from('subscriptions') + .select('*, prices(*, products(*))') + .in('status', ['trialing', 'active']) + .maybeSingle() + .throwOnError(); + return subscription; + } catch (error) { + console.error('Error:', error); + return null; + } +} + +export const getActiveProductsWithPrices = async () => { + const supabase = createServerSupabaseClient(); + const { data, error } = await supabase + .from('products') + .select('*, prices(*)') + .eq('active', true) + .eq('prices.active', true) + .order('metadata->index') + .order('unit_amount', { foreignTable: 'prices' }); + + if (error) { + console.log(error.message); + } + return data ?? []; +}; diff --git a/boilerplate-chakra-pro-main/components/MessageBox/index.tsx b/boilerplate-chakra-pro-main/components/MessageBox/index.tsx new file mode 100644 index 00000000..d47cc278 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/MessageBox/index.tsx @@ -0,0 +1,26 @@ +import { Flex, useColorModeValue } from '@chakra-ui/react'; +import ReactMarkdown from 'react-markdown'; + +export default function MessageBox(props: { output: string }) { + const { output } = props; + const textColor = useColorModeValue('#120F43', 'white'); + const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200'); + return ( + + + {output ? output : 'Your generated response will appear here...'} + + + ); +} diff --git a/boilerplate-chakra-pro-main/components/MessageBoxChat/index.tsx b/boilerplate-chakra-pro-main/components/MessageBoxChat/index.tsx new file mode 100644 index 00000000..d5582ce9 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/MessageBoxChat/index.tsx @@ -0,0 +1,28 @@ +import Card from '@/components/card/Card'; +import { useColorModeValue } from '@chakra-ui/system'; +import ReactMarkdown from 'react-markdown'; + +export default function MessageBox(props: { output: string }) { + const { output } = props; + const bgColor = useColorModeValue('white', 'whiteAlpha.100'); + const textColor = useColorModeValue('#120F43', 'white'); + const borderWidth = useColorModeValue('1px', '0px'); + return ( + + + {output ? output : ''} + + + ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/EmailSignIn.tsx b/boilerplate-chakra-pro-main/components/auth-ui/EmailSignIn.tsx new file mode 100644 index 00000000..897117e7 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/EmailSignIn.tsx @@ -0,0 +1,100 @@ +'use client'; + +import { signInWithEmail } from '@/utils/auth-helpers/server'; +import { handleRequest } from '@/utils/auth-helpers/client'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { + Box, + Button, + Flex, + FormLabel, + Input, + useColorModeValue, + Link +} from '@chakra-ui/react'; + +// Define prop type with allowPassword boolean +interface EmailSignInProps { + allowPassword: boolean; + redirectMethod: string; + disableButton?: boolean; +} + +export default function EmailSignIn({ + allowPassword, + redirectMethod +}: EmailSignInProps) { + const router = redirectMethod === 'client' ? useRouter() : null; + const [isSubmitting, setIsSubmitting] = useState(false); + const textColor = useColorModeValue('navy.700', 'white'); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await handleRequest(e, signInWithEmail, router); + setIsSubmitting(false); + }; + + return ( + +
handleSubmit(e)}> + + Email + + + +
+ + {allowPassword && ( + + + Sign in with email and password + + + + Don't have an account? Sign up + + + )} +
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/ForgotPassword.tsx b/boilerplate-chakra-pro-main/components/auth-ui/ForgotPassword.tsx new file mode 100644 index 00000000..d7975fbe --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/ForgotPassword.tsx @@ -0,0 +1,106 @@ +'use client'; +import { requestPasswordUpdate } from '@/utils/auth-helpers/server'; +import { handleRequest } from '@/utils/auth-helpers/client'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { + Box, + Button, + Flex, + FormLabel, + Input, + useColorModeValue, + Link +} from '@chakra-ui/react'; + +// Define prop type with allowEmail boolean +interface ForgotPasswordProps { + allowEmail: boolean; + redirectMethod: string; + disableButton?: boolean; +} + +export default function ForgotPassword({ + allowEmail, + redirectMethod +}: ForgotPasswordProps) { + const router = redirectMethod === 'client' ? useRouter() : null; + + const textColor = useColorModeValue('navy.700', 'white'); + const [isSubmitting, setIsSubmitting] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await handleRequest(e, requestPasswordUpdate, router); + setIsSubmitting(false); + }; + + return ( + +
handleSubmit(e)}> + + Email + + + +
+ + + + Sign in with email and password + + {allowEmail && ( + + Sign in via magic link + + )} + + Don't have an account? Sign up + + +
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/OauthSignIn.tsx b/boilerplate-chakra-pro-main/components/auth-ui/OauthSignIn.tsx new file mode 100644 index 00000000..cee976d0 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/OauthSignIn.tsx @@ -0,0 +1,77 @@ +'use client'; + + +import { signInWithOAuth } from '@/utils/auth-helpers/client'; +import { type Provider } from '@supabase/supabase-js'; +import { FcGoogle } from "react-icons/fc"; +import { useState } from 'react'; +import { useColorModeValue } from '@chakra-ui/system'; +import { Box, Button, Icon, Input } from '@chakra-ui/react'; +import { IconType } from 'react-icons'; + +type OAuthProviders = { + name: Provider; + displayName: string; + icon: IconType; +}; + +export default function OauthSignIn() { + // Chakra color mode + const googleBg = useColorModeValue('secondaryGray.300', 'whiteAlpha.200'); + const googleText = useColorModeValue('navy.700', 'white'); + const googleHover = useColorModeValue( + { bg: 'gray.200' }, + { bg: 'whiteAlpha.300' }, + ); + const googleActive = useColorModeValue( + { bg: 'secondaryGray.300' }, + { bg: 'whiteAlpha.200' }, + ); + const oAuthProviders: OAuthProviders[] = [ + { + name: 'google', + displayName: 'Google', + icon: FcGoogle + } + /* Add desired OAuth providers here */ + ]; + const [isSubmitting, setIsSubmitting] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await signInWithOAuth(e); + setIsSubmitting(false); + }; + + return ( + + {oAuthProviders.map((provider) => ( +
handleSubmit(e)} + > + + +
+ ))} +
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/PasswordSignIn.tsx b/boilerplate-chakra-pro-main/components/auth-ui/PasswordSignIn.tsx new file mode 100644 index 00000000..aad753b9 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/PasswordSignIn.tsx @@ -0,0 +1,132 @@ +'use client'; + +import { signInWithPassword } from '@/utils/auth-helpers/server'; +import { handleRequest } from '@/utils/auth-helpers/client'; +import { useRouter } from 'next/navigation'; +import React, { useState } from 'react'; +import { + Box, + Button, + Flex, + FormLabel, + Input, + Link, + useColorModeValue +} from '@chakra-ui/react'; + +// Define prop type with allowEmail boolean +interface PasswordSignInProps { + allowEmail: boolean; + redirectMethod: string; +} + +export default function PasswordSignIn({ + allowEmail, + redirectMethod +}: PasswordSignInProps) { + const router = redirectMethod === 'client' ? useRouter() : null; + const [isSubmitting, setIsSubmitting] = useState(false); + const textColor = useColorModeValue('navy.700', 'white'); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await handleRequest(e, signInWithPassword, router); + setIsSubmitting(false); + }; + + return ( + + +
handleSubmit(e)}> + + Email + + + + Password + + + +
+
+ + + Forgot your password? + + {allowEmail && ( + + Sign in via magic link + + )} + + Don't have an account? Sign up + + +
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/Signup.tsx b/boilerplate-chakra-pro-main/components/auth-ui/Signup.tsx new file mode 100644 index 00000000..75579628 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/Signup.tsx @@ -0,0 +1,131 @@ +'use client'; + +import React from 'react'; +import { signUp } from '@/utils/auth-helpers/server'; +import { handleRequest } from '@/utils/auth-helpers/client'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { + Flex, + useColorModeValue, + Link, + FormLabel, + Box, + Input, + Button +} from '@chakra-ui/react'; + +// Define prop type with allowEmail boolean +interface SignUpProps { + allowEmail: boolean; + redirectMethod: string; +} + +export default function SignUp({ allowEmail, redirectMethod }: SignUpProps) { + const router = redirectMethod === 'client' ? useRouter() : null; + const [isSubmitting, setIsSubmitting] = useState(false); + const textColor = useColorModeValue('navy.700', 'white'); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await handleRequest(e, signUp, router); + setIsSubmitting(false); + }; + + return ( + + +
handleSubmit(e)}> + + Email + + + + Password + + + +
+
+ + + + Forgot your password? + + + Already have an account? + + {allowEmail && ( + + Sign in via magic link + + )} + +
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth-ui/UpdatePassword.tsx b/boilerplate-chakra-pro-main/components/auth-ui/UpdatePassword.tsx new file mode 100644 index 00000000..cdaf4d30 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth-ui/UpdatePassword.tsx @@ -0,0 +1,100 @@ +'use client'; + +import { updatePassword } from '@/utils/auth-helpers/server'; +import { handleRequest } from '@/utils/auth-helpers/client'; +import { useRouter } from 'next/navigation'; +import React, { useState } from 'react'; +import { + Box, + Button, + FormLabel, + Input, + useColorModeValue +} from '@chakra-ui/react'; + +interface UpdatePasswordProps { + redirectMethod: string; +} + +export default function UpdatePassword({ + redirectMethod +}: UpdatePasswordProps) { + const router = redirectMethod === 'client' ? useRouter() : null; + const [isSubmitting, setIsSubmitting] = useState(false); + const textColor = useColorModeValue('navy.700', 'white'); + + const handleSubmit = async (e: React.FormEvent) => { + setIsSubmitting(true); // Disable the button while the request is being handled + await handleRequest(e, updatePassword, router); + setIsSubmitting(false); + }; + + return ( + +
handleSubmit(e)}> + + New Password + + + + Confirm New Password + + + +
+
+ ); +} diff --git a/boilerplate-chakra-pro-main/components/auth/AuthUI.tsx b/boilerplate-chakra-pro-main/components/auth/AuthUI.tsx new file mode 100644 index 00000000..d673c705 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth/AuthUI.tsx @@ -0,0 +1,81 @@ +'use client'; + +import PasswordSignIn from '@/components/auth-ui/PasswordSignIn'; +import EmailSignIn from '@/components/auth-ui/EmailSignIn'; +import OauthSignIn from '@/components/auth-ui/OauthSignIn'; +import ForgotPassword from '@/components/auth-ui/ForgotPassword'; +import UpdatePassword from '@/components/auth-ui/UpdatePassword'; +import SignUp from '@/components/auth-ui/Signup'; +import { Flex, Text } from '@chakra-ui/react'; +import { HSeparator } from '../separator/Separator'; + +export default function AuthUI(props: any) { + return ( + + + {props.viewProp === 'signup' + ? 'Sign Up' + : props.viewProp === 'forgot_password' + ? 'Forgot Password' + : props.viewProp === 'update_password' + ? 'Update Password' + : props.viewProp === 'email_signin' + ? 'Email Sign In' + : 'Sign In'} + + + {props.viewProp === 'signup' + ? 'Enter your email and password to sign up!' + : props.viewProp === 'forgot_password' + ? 'Enter your email to get a passoword reset link!' + : props.viewProp === 'update_password' + ? 'Choose a new password for your account!' + : props.viewProp === 'email_signin' + ? 'Enter your email to get a magic link!' + : 'Enter your email and password to sign in!'} + + {props.viewProp !== 'update_password' && + props.viewProp !== 'signup' && + props.allowOauth && ( + <> + + + + )} + {props.viewProp === 'password_signin' && ( + + )} + {props.viewProp === 'email_signin' && ( + + )} + {props.viewProp === 'forgot_password' && ( + + )} + {props.viewProp === 'update_password' && ( + + )} + {props.viewProp === 'signup' && ( + + )} + + ); +} diff --git a/boilerplate-chakra-pro-main/components/auth/index.tsx b/boilerplate-chakra-pro-main/components/auth/index.tsx new file mode 100644 index 00000000..b149ae15 --- /dev/null +++ b/boilerplate-chakra-pro-main/components/auth/index.tsx @@ -0,0 +1,79 @@ +'use client'; + +import Footer from '@/components/footer/FooterAuthDefault'; +import NavLink from '@/components/link/NavLink'; +import { Box, Flex, Icon, Link, Text } from '@chakra-ui/react'; +import { PropsWithChildren } from 'react'; +import { FaChevronLeft } from 'react-icons/fa'; +interface DefaultAuthLayoutProps extends PropsWithChildren { + children: JSX.Element; + illustrationBackground: string; + viewProp: any; +} + +export default function DefaultAuthLayout(props: DefaultAuthLayoutProps) { + const { children, illustrationBackground } = props; + return ( + + + + + + + Back to the website + + + + {children} + + + + + +