update pay function
This commit is contained in:
@@ -1,26 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "next/core-web-vitals"
|
|
||||||
}
|
|
||||||
47
boilerplate-chakra-pro-main/.gitignore
vendored
47
boilerplate-chakra-pro-main/.gitignore
vendored
@@ -1,47 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
next-env.d.ts
|
|
||||||
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
# editors
|
|
||||||
.vscode
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
legacy-peer-deps=true
|
|
||||||
auto-install-peers=true
|
|
||||||
strict-peer-dependencies=false
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://horizon-ui.com/boilerplate" target="_blank">
|
|
||||||
<img src="https://i.ibb.co/LYGWM0W/top-boilerplate-readme.png" alt="Horizon UI Boilerplate - NextJS Template for Startups & Companies" width="400px" max-width="400px">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a style="color: #4318FF;" target="_blank" href="https://horizon-ui.com/boilerplate">Website</a> •
|
|
||||||
<a style="color: #4318FF;" target="_blank" href="https://horizon-ui.com/docs-boilerplate/">Documentation</a> •
|
|
||||||
<a style="color: #4318FF;" target="_blank" href="https://horizon-ui.com/pro">Admin Template</a> •
|
|
||||||
<a style="color: #4318FF;" target="_blank" href="https://horizon-ui.com/ai-template">AI Template</a> •
|
|
||||||
<a style="color: #4318FF;" target="_blank" href="https://twitter.com/horizon_ui">Twitter</a>
|
|
||||||
<p align="center" style="max-width: 500px; margin: auto;">
|
|
||||||
Launch your SaaS startup within a few days with the all-in-one NextJS boilerplate that you always searched for.
|
|
||||||
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center" style="width: 100%;">
|
|
||||||
<a style="display:flex; justify-content: center; width: 100%;" href="https://horizon-ui.com/boilerplate" target="_blank"><img style="border-radius: 10px; width: 100%;" src="https://i.ibb.co/G0xrhsk/horizon-boilerplate-image-readme.png" alt="Horizon UI Free Tailwind CSS Landing Page Kit" /></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
Then run this command to start your local server:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
View <a href="https://horizon-ui.com/docs-boilerplate/" target="_blank">full documentation here</a>
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 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 <a href="https://horizon-ui.com/boilerplate#pages" target="_blank">example sections here</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 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).
|
|
||||||
<br />
|
|
||||||
|
|
||||||
1. Providing us with reproducible steps for the issue will shorten the time it takes for it to be fixed.
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"name": "project-root",
|
|
||||||
"path": "./"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "supabase-functions",
|
|
||||||
"path": "supabase/functions"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"files.exclude": {
|
|
||||||
"supabase/functions/": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { ChatBody } from '@/types/types';
|
|
||||||
import { OpenAIStream } from '@/utils/streams/chatStream';
|
|
||||||
|
|
||||||
export const runtime = 'edge';
|
|
||||||
|
|
||||||
export async function GET(req: Request): Promise<Response> {
|
|
||||||
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<Response> {
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { EssayBody } from '@/types/types';
|
|
||||||
import { OpenAIStream } from '@/utils/streams/essayStream';
|
|
||||||
|
|
||||||
export const runtime = 'edge';
|
|
||||||
|
|
||||||
const handler = async (req: Request): Promise<Response> => {
|
|
||||||
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;
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { PremiumEssayBody } from '@/types/types';
|
|
||||||
import { OpenAIStream } from '@/utils/streams/premiumEssayStream';
|
|
||||||
|
|
||||||
export const runtime = 'edge';
|
|
||||||
|
|
||||||
const handler = async (req: Request): Promise<Response> => {
|
|
||||||
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;
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
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 }));
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Assistant
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Chat
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Generator
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Main
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
{subscription ? (
|
|
||||||
<PremiumGenerator
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<p>NICE TRY BUDDY</p>
|
|
||||||
)}
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Settings
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<DefaultAuth
|
|
||||||
viewProp={viewProp}
|
|
||||||
illustrationBackground={illustration.src}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<AuthUI
|
|
||||||
viewProp={viewProp}
|
|
||||||
user={user}
|
|
||||||
allowPassword={allowPassword}
|
|
||||||
allowEmail={allowEmail}
|
|
||||||
redirectMethod={redirectMethod}
|
|
||||||
disableButton={searchParams.disable_button}
|
|
||||||
allowOauth={allowOauth}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DefaultAuth>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
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}`);
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Subscription
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<UsersList
|
|
||||||
userDetails={userDetails}
|
|
||||||
user={user}
|
|
||||||
products={products}
|
|
||||||
subscription={subscription}
|
|
||||||
/>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { EssayBody } from '@/types/types';
|
|
||||||
import { OpenAIStream } from '@/utils/streams/essayStream';
|
|
||||||
|
|
||||||
export const runtime = 'edge';
|
|
||||||
|
|
||||||
const handler = async (req: Request): Promise<Response> => {
|
|
||||||
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;
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
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 (
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>
|
|
||||||
Horizon UI Boilerplate - Launch your startup project 10X in a few
|
|
||||||
moments - The best NextJS Boilerplate (This is an example)
|
|
||||||
</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
{/* <!-- Social tags --> */}
|
|
||||||
<meta
|
|
||||||
name="keywords"
|
|
||||||
content="Add here your main keywords and separate them with a comma"
|
|
||||||
/>
|
|
||||||
<meta name="description" content="Add here your website description" />
|
|
||||||
{/* <!-- Schema.org markup for Google+ --> */}
|
|
||||||
<meta itemProp="name" content="Add here your website name / title" />
|
|
||||||
<meta
|
|
||||||
itemProp="description"
|
|
||||||
content="Add here your website description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
itemProp="image"
|
|
||||||
content="Add here the link for your website SEO image"
|
|
||||||
/>
|
|
||||||
{/* <!-- Twitter Card data --> */}
|
|
||||||
<meta name="twitter:card" content="product" />
|
|
||||||
<meta
|
|
||||||
name="twitter:title"
|
|
||||||
content="Add here your website name / title"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="twitter:description"
|
|
||||||
content="Add here your website description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="twitter:image"
|
|
||||||
content="Add here the link for your website SEO image"
|
|
||||||
/>
|
|
||||||
{/* <!-- Open Graph data --> */}
|
|
||||||
<meta
|
|
||||||
property="og:title"
|
|
||||||
content="Add here your website name / title"
|
|
||||||
/>
|
|
||||||
<meta property="og:type" content="product" />
|
|
||||||
<meta property="og:url" content="https://your-website.com" />
|
|
||||||
<meta
|
|
||||||
property="og:image"
|
|
||||||
content="Add here the link for your website SEO image"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Add here your website description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
property="og:site_name"
|
|
||||||
content="Add here your website name / title"
|
|
||||||
/>
|
|
||||||
<link rel="canonical" href="https://your-website.com" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
</head>
|
|
||||||
<body style={{ background: 'white' }}>
|
|
||||||
<SupabaseProvider>
|
|
||||||
{/* @ts-ignore */}
|
|
||||||
<main id="skip">{children}</main>
|
|
||||||
</SupabaseProvider>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import Landing from '@/components/landing';
|
|
||||||
import { Providers } from '@/components/providers';
|
|
||||||
|
|
||||||
export default async function PricingPage() {
|
|
||||||
return (
|
|
||||||
<Providers>
|
|
||||||
<Landing />
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Providers>
|
|
||||||
<Pricing user={user} products={products} subscription={subscription} />
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
'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<Database>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Context = createContext<SupabaseContext | undefined>(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 (
|
|
||||||
<Context.Provider value={{ supabase }}>
|
|
||||||
<>{children}</>
|
|
||||||
</Context.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useSupabase = () => {
|
|
||||||
const context = useContext(Context);
|
|
||||||
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error('useSupabase must be used inside SupabaseProvider');
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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<Database>({ 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 ?? [];
|
|
||||||
};
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
p="15px 20px"
|
|
||||||
border="1px solid"
|
|
||||||
color={textColor}
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="10px"
|
|
||||||
minH="564px"
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="500"
|
|
||||||
mb="28px"
|
|
||||||
>
|
|
||||||
<ReactMarkdown className="font-medium">
|
|
||||||
{output ? output : 'Your generated response will appear here...'}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
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 (
|
|
||||||
<Card
|
|
||||||
display={output ? 'flex' : 'none'}
|
|
||||||
p="22px"
|
|
||||||
maxH="max-content"
|
|
||||||
color={textColor}
|
|
||||||
bg={bgColor}
|
|
||||||
backdropBlur="xl"
|
|
||||||
borderWidth={borderWidth}
|
|
||||||
fontSize={{ base: 'sm', md: '16px' }}
|
|
||||||
lineHeight={{ base: '24px', md: '26px' }}
|
|
||||||
fontWeight={'500'}
|
|
||||||
>
|
|
||||||
<ReactMarkdown className="font-medium">
|
|
||||||
{output ? output : ''}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await handleRequest(e, signInWithEmail, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mb={8}>
|
|
||||||
<form onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="email"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Email
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="email"
|
|
||||||
placeholder="name@example.com"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
variant="brand"
|
|
||||||
fontWeight="500"
|
|
||||||
w="100%"
|
|
||||||
h="50"
|
|
||||||
mb="24px"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{allowPassword && (
|
|
||||||
<Flex direction="column">
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/password_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Sign in with email and password
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/signup"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Don't have an account? Sign up
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await handleRequest(e, requestPasswordUpdate, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mb={8}>
|
|
||||||
<form onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="email"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Email
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="email"
|
|
||||||
placeholder="name@example.com"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
variant="brand"
|
|
||||||
fontWeight="500"
|
|
||||||
w="100%"
|
|
||||||
h="50"
|
|
||||||
mb="24px"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Flex direction="column">
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/password_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Sign in with email and password
|
|
||||||
</Link>
|
|
||||||
{allowEmail && (
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/email_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Sign in via magic link
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/signup"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Don't have an account? Sign up
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await signInWithOAuth(e);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mt="40px">
|
|
||||||
{oAuthProviders.map((provider) => (
|
|
||||||
<form
|
|
||||||
key={provider.name}
|
|
||||||
className="pb-2"
|
|
||||||
onSubmit={(e) => handleSubmit(e)}
|
|
||||||
>
|
|
||||||
<Input type="hidden" name="provider" value={provider.name} />
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
me="0px"
|
|
||||||
py="15px"
|
|
||||||
h="50px"
|
|
||||||
w="100%"
|
|
||||||
borderRadius="16px"
|
|
||||||
bg={googleBg}
|
|
||||||
color={googleText}
|
|
||||||
fontWeight="500"
|
|
||||||
_hover={googleHover}
|
|
||||||
_active={googleActive}
|
|
||||||
_focus={googleActive}
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
<Icon as={ provider.icon} w="20px" h="20px" me="10px" />
|
|
||||||
{provider.displayName}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await handleRequest(e, signInWithPassword, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mb="auto">
|
|
||||||
<Box mb="8px">
|
|
||||||
<form noValidate={true} onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="email"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Email
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="email"
|
|
||||||
placeholder="name@example.com"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
autoCapitalize="none"
|
|
||||||
autoComplete="email"
|
|
||||||
autoCorrect="off"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="password"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Password
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="password"
|
|
||||||
placeholder="Password"
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
variant="brand"
|
|
||||||
fontWeight="500"
|
|
||||||
w="100%"
|
|
||||||
h="50"
|
|
||||||
mb="24px"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Box>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/forgot_password"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Forgot your password?
|
|
||||||
</Link>
|
|
||||||
{allowEmail && (
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/email_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Sign in via magic link
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/signup"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Don't have an account? Sign up
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await handleRequest(e, signUp, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mb="auto" mt="20px">
|
|
||||||
<Box mb="8px">
|
|
||||||
<form noValidate={true} onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="email"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Email
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="email"
|
|
||||||
placeholder="name@example.com"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
autoCapitalize="none"
|
|
||||||
autoComplete="email"
|
|
||||||
autoCorrect="off"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="password"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Password
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
id="password"
|
|
||||||
placeholder="Password"
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
variant="brand"
|
|
||||||
fontWeight="500"
|
|
||||||
w="100%"
|
|
||||||
h="50"
|
|
||||||
mb="24px"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Flex direction="column">
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/forgot_password"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Forgot your password?
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/password_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Already have an account?
|
|
||||||
</Link>
|
|
||||||
{allowEmail && (
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin/email_signin"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
Sign in via magic link
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
'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<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true); // Disable the button while the request is being handled
|
|
||||||
await handleRequest(e, updatePassword, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box mb={8}>
|
|
||||||
<form onSubmit={(e) => handleSubmit(e)}>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="password"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
New Password
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
id="password"
|
|
||||||
placeholder="Password"
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
/>
|
|
||||||
<FormLabel
|
|
||||||
htmlFor="password"
|
|
||||||
display="flex"
|
|
||||||
ms="4px"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
mb="8px"
|
|
||||||
>
|
|
||||||
Confirm New Password
|
|
||||||
</FormLabel>
|
|
||||||
<Input
|
|
||||||
isRequired={true}
|
|
||||||
variant="auth"
|
|
||||||
fontSize="sm"
|
|
||||||
ms={{ base: '0px', md: '0px' }}
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
size="lg"
|
|
||||||
id="passwordConfirm"
|
|
||||||
placeholder="Password"
|
|
||||||
type="password"
|
|
||||||
name="passwordConfirm"
|
|
||||||
autoComplete="current-password"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
fontSize="sm"
|
|
||||||
variant="brand"
|
|
||||||
fontWeight="500"
|
|
||||||
w="100%"
|
|
||||||
h="50"
|
|
||||||
mb="24px"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
'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 (
|
|
||||||
<Flex
|
|
||||||
direction={'column'}
|
|
||||||
my="auto"
|
|
||||||
mt={{ base: '30px', md: '70px', lg: 'auto' }}
|
|
||||||
maxW={{ md: 'full', lg: '420px' }}
|
|
||||||
>
|
|
||||||
<Text fontSize="32px" fontWeight={'bold'} color="gray.900">
|
|
||||||
{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'}
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="16px" color="gray.t00">
|
|
||||||
{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!'}
|
|
||||||
</Text>
|
|
||||||
{props.viewProp !== 'update_password' &&
|
|
||||||
props.viewProp !== 'signup' &&
|
|
||||||
props.allowOauth && (
|
|
||||||
<>
|
|
||||||
<OauthSignIn />
|
|
||||||
<HSeparator my="20px" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{props.viewProp === 'password_signin' && (
|
|
||||||
<PasswordSignIn
|
|
||||||
allowEmail={props.allowEmail}
|
|
||||||
redirectMethod={props.redirectMethod}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{props.viewProp === 'email_signin' && (
|
|
||||||
<EmailSignIn
|
|
||||||
allowPassword={props.allowPassword}
|
|
||||||
redirectMethod={props.redirectMethod}
|
|
||||||
disableButton={props.disableButton}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{props.viewProp === 'forgot_password' && (
|
|
||||||
<ForgotPassword
|
|
||||||
allowEmail={props.allowEmail}
|
|
||||||
redirectMethod={props.redirectMethod}
|
|
||||||
disableButton={props.disableButton}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{props.viewProp === 'update_password' && (
|
|
||||||
<UpdatePassword redirectMethod={props.redirectMethod} />
|
|
||||||
)}
|
|
||||||
{props.viewProp === 'signup' && (
|
|
||||||
<SignUp
|
|
||||||
allowEmail={props.allowEmail}
|
|
||||||
redirectMethod={props.redirectMethod}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
'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 (
|
|
||||||
<Flex position="relative" h="max-content">
|
|
||||||
<Flex
|
|
||||||
minH="100vh"
|
|
||||||
w="100%"
|
|
||||||
maxW={{ base: '90%', md: '66%', lg: '1313px' }}
|
|
||||||
mx="auto"
|
|
||||||
pt={{ sm: '0px', md: '0px' }}
|
|
||||||
px={{ lg: '30px', xl: '0px' }}
|
|
||||||
ps={{ xl: '70px' }}
|
|
||||||
justifyContent="start"
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
width={'fit-content'}
|
|
||||||
mt={10}
|
|
||||||
mb={{ base: '', md: '', lg: '120px', xl: '150px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
ps={{ base: '25px', lg: '0px' }}
|
|
||||||
pt={{ lg: '0px', xl: '0px' }}
|
|
||||||
w="fit-content"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
as={FaChevronLeft}
|
|
||||||
me="12px"
|
|
||||||
h="13px"
|
|
||||||
w="8px"
|
|
||||||
color="gray.500"
|
|
||||||
/>
|
|
||||||
<Text ms="0px" fontSize="sm" color="gray.500">
|
|
||||||
Back to the website
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
{children}
|
|
||||||
<Box
|
|
||||||
display={{ base: 'none', md: 'block' }}
|
|
||||||
h="100%"
|
|
||||||
minH="100vh"
|
|
||||||
w={{ lg: '50vw', '2xl': '44vw' }}
|
|
||||||
position="absolute"
|
|
||||||
right="0px"
|
|
||||||
>
|
|
||||||
<Link href="/">
|
|
||||||
<Flex
|
|
||||||
bg={`url(${illustrationBackground})`}
|
|
||||||
justify="center"
|
|
||||||
align="end"
|
|
||||||
w="100%"
|
|
||||||
h="100%"
|
|
||||||
bgSize="cover"
|
|
||||||
bgPosition="50%"
|
|
||||||
position="absolute"
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</Box>
|
|
||||||
<Footer />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import { useStyleConfig, chakra, forwardRef } from '@chakra-ui/react';
|
|
||||||
import { CustomCardProps } from '@/theme/theme';
|
|
||||||
const CustomCard = forwardRef<CustomCardProps, 'div'>((props, ref) => {
|
|
||||||
const { size, variant, ...rest } = props;
|
|
||||||
const styles = useStyleConfig('Card', { size, variant });
|
|
||||||
|
|
||||||
return <chakra.div ref={ref} __css={styles} {...rest} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CustomCard;
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Stat,
|
|
||||||
StatLabel,
|
|
||||||
StatNumber,
|
|
||||||
useColorModeValue,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export default function Default(props: {
|
|
||||||
startContent?: JSX.Element;
|
|
||||||
endContent?: JSX.Element;
|
|
||||||
name: string;
|
|
||||||
growth?: string | number;
|
|
||||||
value: string | number;
|
|
||||||
}) {
|
|
||||||
const { startContent, endContent, name, growth, value } = props;
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = 'gray.500';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<Flex
|
|
||||||
my="auto"
|
|
||||||
h="100%"
|
|
||||||
align={{ base: 'center', xl: 'start' }}
|
|
||||||
justify={{ base: 'center', xl: 'center' }}
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
{startContent}
|
|
||||||
|
|
||||||
<Stat my="auto" ms={startContent ? '18px' : '0px'}>
|
|
||||||
<StatLabel
|
|
||||||
lineHeight="100%"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontSize="sm"
|
|
||||||
mb="4px"
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</StatLabel>
|
|
||||||
<StatNumber color={textColor} fontWeight="700" fontSize="lg">
|
|
||||||
{value}
|
|
||||||
</StatNumber>
|
|
||||||
{growth ? (
|
|
||||||
<Flex align="center">
|
|
||||||
<Text color="green.500" fontSize="xs" fontWeight="700" me="5px">
|
|
||||||
{growth}
|
|
||||||
</Text>
|
|
||||||
<Text color="gray.500" fontSize="xs" fontWeight="400">
|
|
||||||
since last month
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : null}
|
|
||||||
</Stat>
|
|
||||||
<Flex ms="auto" w="max-content">
|
|
||||||
{endContent}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import NavLink from '../link/NavLink';
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
useColorModeValue,
|
|
||||||
Text,
|
|
||||||
Icon,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { MdEdit } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function Default(props: {
|
|
||||||
illustration: string | JSX.Element;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
link: string;
|
|
||||||
edit?: string;
|
|
||||||
action?: any;
|
|
||||||
admin?: boolean;
|
|
||||||
}) {
|
|
||||||
const { illustration, name, description, link, edit, admin } = props;
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const gray = useColorModeValue('gray.500', 'white');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NavLink href={link}>
|
|
||||||
<Card h="100%" py="24px" px="24px">
|
|
||||||
<Flex
|
|
||||||
my="auto"
|
|
||||||
h="100%"
|
|
||||||
direction={'column'}
|
|
||||||
align={{ base: 'center', xl: 'start' }}
|
|
||||||
justify={{ base: 'center', xl: 'center' }}
|
|
||||||
>
|
|
||||||
<Flex align="start" w="100%" mb="30px">
|
|
||||||
<Text fontSize="34px" lineHeight={'120%'}>
|
|
||||||
{illustration}
|
|
||||||
</Text>
|
|
||||||
{admin ? (
|
|
||||||
<Flex ms="auto">
|
|
||||||
<NavLink href={edit ? edit : '/admin/edit-template'}>
|
|
||||||
<Button
|
|
||||||
w="24px"
|
|
||||||
h="24px"
|
|
||||||
_hover={{}}
|
|
||||||
_focus={{}}
|
|
||||||
_active={{}}
|
|
||||||
bg="none"
|
|
||||||
>
|
|
||||||
<Icon w="24px" h="24px" as={MdEdit} color={gray} />
|
|
||||||
</Button>
|
|
||||||
</NavLink>
|
|
||||||
</Flex>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
<Box>
|
|
||||||
<Text fontSize="lg" color={textColor} fontWeight="700" mb="8px">
|
|
||||||
{name}
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="sm" color={gray} fontWeight="500">
|
|
||||||
{description}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
</NavLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
// import Chart from 'react-apexcharts';
|
|
||||||
const Chart = dynamic(() => import('react-apexcharts'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const BarChart = (props) => {
|
|
||||||
const { chartData, chartOptions } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
// @ts-ignore
|
|
||||||
<Chart
|
|
||||||
options={chartOptions}
|
|
||||||
type="bar"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
series={chartData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BarChart;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
// import Chart from 'react-apexcharts';
|
|
||||||
const Chart = dynamic(() => import('react-apexcharts'), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const LineChart = (props) => {
|
|
||||||
const { chartData, chartOptions } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
// @ts-ignore
|
|
||||||
<Chart
|
|
||||||
options={chartOptions}
|
|
||||||
type="line"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
series={chartData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LineChart;
|
|
||||||
@@ -1,457 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/*eslint-disable*/
|
|
||||||
import MessageBoxChat from '@/components/MessageBoxChat';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import Bg from '@/public/img/ai-chat/bg-image.png';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Input,
|
|
||||||
Text,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import endent from 'endent';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { MdAutoAwesome, MdEdit, MdPerson } from 'react-icons/md';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
export default function AiAssistant(props: Props) {
|
|
||||||
// *** If you use .env.local variable for your API key, method which we recommend, use the apiKey variable commented below
|
|
||||||
// Input States
|
|
||||||
const [inputMessage, setInputMessage] = useState<string>('');
|
|
||||||
const [submitMessage, setSubmitMessage] = useState<string>('');
|
|
||||||
// Loading state
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [assistant, setAssistant] = useState(Object);
|
|
||||||
const [thread, setThread] = useState(Object);
|
|
||||||
const [res_message, setResMessage] = useState(Object);
|
|
||||||
const gray = useColorModeValue('gray.500', 'white');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
|
|
||||||
const gradientBg2 = useColorModeValue(
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)',
|
|
||||||
'linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.31) 100%)'
|
|
||||||
);
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const placeholderColor = useColorModeValue(
|
|
||||||
{ color: 'gray.500' },
|
|
||||||
{ color: 'whiteAlpha.600' }
|
|
||||||
);
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
|
|
||||||
|
|
||||||
const createPrompt = (inputMessage: string) => {
|
|
||||||
const data = (inputMessage: string) => {
|
|
||||||
return endent` do me this:
|
|
||||||
${inputMessage}
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (inputMessage) {
|
|
||||||
return data(inputMessage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAssistant = async () => {
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/assistants/' +
|
|
||||||
process.env.NEXT_PUBLIC_OPENAI_ASSISTANT_KEY,
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const assistant = await gptResponse.json();
|
|
||||||
return assistant;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createThread = async () => {
|
|
||||||
const gptResponse = await fetch('https://api.openai.com/v1/threads', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const thread = await gptResponse.json();
|
|
||||||
return thread;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMessage = async (thread_id: string) => {
|
|
||||||
const prompt = createPrompt(inputMessage);
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/threads/' + thread_id + '/messages',
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
role: 'user',
|
|
||||||
// content: topic,
|
|
||||||
content: prompt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = await gptResponse.json();
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMessage = async (thread_id: string, message_id: string) => {
|
|
||||||
// https://api.openai.com/v1/threads/{thread_id}/messages/{message_id}
|
|
||||||
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/threads/' + thread_id + '/messages',
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = await gptResponse.json();
|
|
||||||
console.log('I get the message.');
|
|
||||||
console.log(message);
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
const runAssistant = async (thread_id: string, assistant_id: string) => {
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/threads/' + thread_id + '/runs',
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
assistant_id: assistant_id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const run_res = await gptResponse.json();
|
|
||||||
return run_res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRunAssistant = async (run_id: string, thread_id: string) => {
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/threads/' + thread_id + '/runs/' + run_id,
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const run_res = await gptResponse.json();
|
|
||||||
console.log('I get the status.');
|
|
||||||
console.log(run_res);
|
|
||||||
return run_res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteThread = async (thread_id: string) => {
|
|
||||||
if (thread === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const gptResponse = await fetch(
|
|
||||||
'https://api.openai.com/v1/threads/' + thread_id,
|
|
||||||
{
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'OpenAI-Beta': 'assistants=v1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const thread_res = await gptResponse.json();
|
|
||||||
console.log(thread_res);
|
|
||||||
return thread_res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: any) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
// save the keys in storage browser
|
|
||||||
// @ts-ignore
|
|
||||||
localStorage.setItem('open_ai_key', process.env.NEXT_PUBLIC_OPENAI_API_KEY);
|
|
||||||
localStorage.setItem(
|
|
||||||
'assistant_key',
|
|
||||||
// @ts-ignore
|
|
||||||
process.env.NEXT_PUBLIC_OPENAI_ASSISTANT_KEY
|
|
||||||
);
|
|
||||||
|
|
||||||
const assistant_res = await getAssistant();
|
|
||||||
setAssistant(assistant_res);
|
|
||||||
const thread_res = await createThread();
|
|
||||||
setThread(thread_res);
|
|
||||||
|
|
||||||
const message = await createMessage(thread_res.id);
|
|
||||||
let runAssistantResponse = await runAssistant(
|
|
||||||
thread_res.id,
|
|
||||||
assistant_res.id
|
|
||||||
);
|
|
||||||
console.log(runAssistantResponse);
|
|
||||||
|
|
||||||
while (runAssistantResponse.status !== 'completed') {
|
|
||||||
runAssistantResponse = await getRunAssistant(
|
|
||||||
runAssistantResponse.id,
|
|
||||||
thread_res.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (runAssistantResponse.status === 'completed') {
|
|
||||||
console.log('Message is : ');
|
|
||||||
const call_response = await getMessage(thread_res.id, message.id);
|
|
||||||
setResMessage(call_response);
|
|
||||||
console.log(await deleteThread(thread_res.id));
|
|
||||||
} else {
|
|
||||||
// sleep for 2 second
|
|
||||||
await new Promise((r) => setTimeout(r, 2000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(assistant);
|
|
||||||
console.log(thread);
|
|
||||||
console.log(message);
|
|
||||||
console.log(runAssistantResponse);
|
|
||||||
|
|
||||||
setSubmitMessage(inputMessage);
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Copy Response --------------
|
|
||||||
// const copyToClipboard = (text: string) => {
|
|
||||||
// const el = document.createElement('textarea');
|
|
||||||
// el.value = text;
|
|
||||||
// document.body.appendChild(el);
|
|
||||||
// el.select();
|
|
||||||
// document.execCommand('copy');
|
|
||||||
// document.body.removeChild(el);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleChange = (Event: any) => {
|
|
||||||
setInputMessage(Event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="AI Generator"
|
|
||||||
description="AI Generator"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
position="relative"
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
pt={{ base: '20px', md: 0 }}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
width={{ base: '340px', xl: '350px' }}
|
|
||||||
src={Bg.src}
|
|
||||||
position="absolute"
|
|
||||||
left={{ base: '-20%', md: '35%', lg: '38%' }}
|
|
||||||
top={{ base: '50%' }}
|
|
||||||
zIndex="0"
|
|
||||||
w="200px"
|
|
||||||
transform="translate(0, -50%)"
|
|
||||||
alt=" "
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
direction={'column'}
|
|
||||||
mx="auto"
|
|
||||||
minH={{ base: '75vh', xl: '85vh' }}
|
|
||||||
w="full"
|
|
||||||
maxW="1000px"
|
|
||||||
>
|
|
||||||
{/* Model Change */}
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction={'column'}
|
|
||||||
mb={
|
|
||||||
res_message?.data?.[0]?.content?.[0].text?.value ? '20px' : 'auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
textAlign={'center'}
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'500'}
|
|
||||||
color="secondaryGray.500"
|
|
||||||
>
|
|
||||||
Please make sure that you have set the environmental variable for
|
|
||||||
the Assistant Key.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* Main Box */}
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w="100%"
|
|
||||||
direction={'column'}
|
|
||||||
display={
|
|
||||||
res_message?.data?.[0]?.content?.[0].text?.value ? 'flex' : 'none'
|
|
||||||
}
|
|
||||||
mb="auto"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mb="10px"
|
|
||||||
display={'flex'}
|
|
||||||
w="100%"
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
me="20px"
|
|
||||||
h="40px"
|
|
||||||
minH="40px"
|
|
||||||
minW="40px"
|
|
||||||
align="center"
|
|
||||||
borderRadius="full"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
>
|
|
||||||
<Icon as={MdPerson} color={brandColor} h="20px" w="20px" />
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
zIndex={2}
|
|
||||||
borderRadius="14px"
|
|
||||||
w="100%"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
p="20px"
|
|
||||||
backdropFilter="blur(24px)"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: 'sm', md: '16px' }}
|
|
||||||
fontWeight={'600'}
|
|
||||||
>
|
|
||||||
{submitMessage}
|
|
||||||
</Text>
|
|
||||||
<Icon
|
|
||||||
as={MdEdit}
|
|
||||||
color={'gray.500'}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
ms="auto"
|
|
||||||
cursor={'pointer'}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex w="100%">
|
|
||||||
<Flex
|
|
||||||
me="20px"
|
|
||||||
h="40px"
|
|
||||||
minW="40px"
|
|
||||||
align="center"
|
|
||||||
justify={'center'}
|
|
||||||
borderRadius="full"
|
|
||||||
background={gradientBg2}
|
|
||||||
>
|
|
||||||
<Icon as={MdAutoAwesome} color={'white'} h="20px" w="20px" />
|
|
||||||
</Flex>
|
|
||||||
<MessageBoxChat
|
|
||||||
output={res_message?.data?.[0]?.content?.[0].text?.value}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{/* Chat Input */}
|
|
||||||
<Flex mt="20px" justifyContent={'flex-center'} mx="auto">
|
|
||||||
<Input
|
|
||||||
color={textColor}
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'45px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
w={{ md: '100%', xl: '45vw' }}
|
|
||||||
h="60px"
|
|
||||||
id="email"
|
|
||||||
fontSize={'sm'}
|
|
||||||
fontWeight="500"
|
|
||||||
placeholder="Type your message here..."
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb={{ base: '14px', md: '16px' }}
|
|
||||||
me="20px"
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
h="54px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg: 'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
minW="150px"
|
|
||||||
onClick={handleSubmit}
|
|
||||||
isLoading={loading}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
mt="10px"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
>
|
|
||||||
<Text color={gray} textAlign="center" fontSize="xs">
|
|
||||||
Free Research Preview. ChatGPT may produce inaccurate information
|
|
||||||
about people, places, or facts. Consider checking important
|
|
||||||
information.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,439 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/*eslint-disable*/
|
|
||||||
import MessageBoxChat from '@/components/MessageBoxChat';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import Bg from '@/public/img/ai-chat/bg-image.png';
|
|
||||||
import { ChatBody } from '@/types/types';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionButton,
|
|
||||||
AccordionIcon,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionPanel,
|
|
||||||
useColorModeValue,
|
|
||||||
Box,
|
|
||||||
Icon,
|
|
||||||
Flex,
|
|
||||||
Text,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Image
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { MdAutoAwesome, MdBolt, MdEdit, MdPerson } from 'react-icons/md';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
export default function AiChat(props: Props) {
|
|
||||||
// *** If you use .env.local variable for your API key, method which we recommend, use the apiKey variable commented below
|
|
||||||
// Input States
|
|
||||||
const [inputOnSubmit, setInputOnSubmit] = useState<string>('');
|
|
||||||
const [inputMessage, setInputMessage] = useState<string>('');
|
|
||||||
// Response message
|
|
||||||
const [outputCode, setOutputCode] = useState<string>('');
|
|
||||||
// ChatGPT model
|
|
||||||
const [model, setModel] = useState('gpt-3.5-turbo');
|
|
||||||
// Loading state
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const gray = useColorModeValue('gray.500', 'white');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
const opacityBg = useColorModeValue('white', 'whiteAlpha.100');
|
|
||||||
const gradientBg = useColorModeValue(
|
|
||||||
'linear-gradient(180deg, #FBFBFF 0%, #CACAFF 100%)',
|
|
||||||
'linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.31) 100%)'
|
|
||||||
);
|
|
||||||
|
|
||||||
const gradientBg2 = useColorModeValue(
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)',
|
|
||||||
'linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.31) 100%)'
|
|
||||||
);
|
|
||||||
|
|
||||||
const shadow = useColorModeValue(
|
|
||||||
'14px 27px 45px rgba(112, 144, 176, 0.2)',
|
|
||||||
'unset'
|
|
||||||
);
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const placeholderColor = useColorModeValue(
|
|
||||||
{ color: 'gray.500' },
|
|
||||||
{ color: 'whiteAlpha.600' }
|
|
||||||
);
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
|
|
||||||
|
|
||||||
// API Key
|
|
||||||
const handleTranslate = async () => {
|
|
||||||
const apiKey = localStorage.getItem('apiKey');
|
|
||||||
setInputOnSubmit(inputMessage);
|
|
||||||
|
|
||||||
// Chat post conditions(maximum number of characters, valid message etc.)
|
|
||||||
const maxCodeLength = model === 'gpt-3.5-turbo' ? 700 : 700;
|
|
||||||
|
|
||||||
if (!apiKey?.includes('sk-')) {
|
|
||||||
alert('Please enter an API key.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inputMessage) {
|
|
||||||
alert('Please enter your subject.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputMessage.length > maxCodeLength) {
|
|
||||||
alert(
|
|
||||||
`Please enter code less than ${maxCodeLength} characters. You are currently at ${inputMessage.length} characters.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOutputCode(' ');
|
|
||||||
setLoading(true);
|
|
||||||
const controller = new AbortController();
|
|
||||||
const body: ChatBody = {
|
|
||||||
inputMessage,
|
|
||||||
model,
|
|
||||||
apiKey
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Fetch --------------
|
|
||||||
const response = await fetch('/api/chatAPI', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
body: JSON.stringify(body)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
setLoading(false);
|
|
||||||
if (response) {
|
|
||||||
alert(
|
|
||||||
'Something went wrong went fetching from the API. Make sure to use a valid API key.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = response.body;
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
setLoading(false);
|
|
||||||
alert('Something went wrong');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = data.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
let done = false;
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
setLoading(true);
|
|
||||||
const { value, done: doneReading } = await reader.read();
|
|
||||||
done = doneReading;
|
|
||||||
const chunkValue = decoder.decode(value);
|
|
||||||
setOutputCode((prevCode) => prevCode + chunkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
// -------------- Copy Response --------------
|
|
||||||
// const copyToClipboard = (text: string) => {
|
|
||||||
// const el = document.createElement('textarea');
|
|
||||||
// el.value = text;
|
|
||||||
// document.body.appendChild(el);
|
|
||||||
// el.select();
|
|
||||||
// document.execCommand('copy');
|
|
||||||
// document.body.removeChild(el);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleChange = (Event: any) => {
|
|
||||||
setInputMessage(Event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Essay Generator"
|
|
||||||
description="Essay Generator"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction={'column'}
|
|
||||||
pt={{ base: 5, md: 0 }}
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
width={{ base: '340px', xl: '350px' }}
|
|
||||||
src={Bg.src}
|
|
||||||
position="absolute"
|
|
||||||
left={{ base: '-20%', md: '35%', lg: '38%' }}
|
|
||||||
top={{ base: '50%' }}
|
|
||||||
zIndex="0"
|
|
||||||
w="200px"
|
|
||||||
transform="translate(0, -50%)"
|
|
||||||
alt=" "
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w="100%"
|
|
||||||
maxW="100%"
|
|
||||||
direction="column"
|
|
||||||
minH={{ base: '75vh', xl: '85vh' }}
|
|
||||||
>
|
|
||||||
{/* Model Change */}
|
|
||||||
<Flex w="100%" direction={'column'} mb={outputCode ? '20px' : 'auto'}>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
mb="20px"
|
|
||||||
w="max-content"
|
|
||||||
borderRadius="60px"
|
|
||||||
zIndex={2}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
cursor={'pointer'}
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="8px"
|
|
||||||
align={'center'}
|
|
||||||
py="16px"
|
|
||||||
transitionDuration={'0.3s'}
|
|
||||||
h="70px"
|
|
||||||
w="174px"
|
|
||||||
bg={model === 'gpt-3.5-turbo' ? opacityBg : 'transparent'}
|
|
||||||
boxShadow={model === 'gpt-3.5-turbo' ? shadow : 'unset'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="700"
|
|
||||||
fontSize="18px"
|
|
||||||
onClick={() => setModel('gpt-3.5-turbo')}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
me="10px"
|
|
||||||
w="39px"
|
|
||||||
h="39px"
|
|
||||||
justify={'center'}
|
|
||||||
alignItems="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg={gradientBg}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
as={MdAutoAwesome}
|
|
||||||
color={brandColor}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
GPT-3.5
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
cursor={'pointer'}
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="8px"
|
|
||||||
align={'center'}
|
|
||||||
py="16px"
|
|
||||||
transitionDuration={'0.3s'}
|
|
||||||
h="70px"
|
|
||||||
w="174px"
|
|
||||||
bg={model === 'gpt-4-1106-preview' ? opacityBg : 'transparent'}
|
|
||||||
boxShadow={model === 'gpt-4-1106-preview' ? shadow : 'unset'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="700"
|
|
||||||
fontSize="18px"
|
|
||||||
onClick={() => setModel('gpt-4-1106-preview')}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
me="10px"
|
|
||||||
w="39px"
|
|
||||||
h="39px"
|
|
||||||
justify={'center'}
|
|
||||||
alignItems="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg={gradientBg}
|
|
||||||
>
|
|
||||||
<Icon as={MdBolt} color={brandColor} h="20px" w="20px" />
|
|
||||||
</Flex>
|
|
||||||
GPT-4
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Accordion zIndex={10} mx="auto" my="0px" color={gray} allowToggle>
|
|
||||||
<AccordionItem border="none">
|
|
||||||
<AccordionButton
|
|
||||||
borderBottom="0px solid"
|
|
||||||
maxW="max-content"
|
|
||||||
mx="auto"
|
|
||||||
_hover={{ border: '0px solid', bg: 'none' }}
|
|
||||||
_focus={{ border: '0px solid', bg: 'none' }}
|
|
||||||
>
|
|
||||||
<Box textAlign={'center'}>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'500'}
|
|
||||||
color="secondaryGray.500"
|
|
||||||
>
|
|
||||||
No plugins added
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<AccordionIcon color="secondaryGray.500" />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel mx="auto" w="max-content" p="0px 0px 10px 0px">
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'500'}
|
|
||||||
color="secondaryGray.500"
|
|
||||||
textAlign={'center'}
|
|
||||||
>
|
|
||||||
This is a cool text example.
|
|
||||||
</Text>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</Flex>
|
|
||||||
{/* Main Box */}
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w="100%"
|
|
||||||
direction={'column'}
|
|
||||||
display={outputCode ? 'flex' : 'none'}
|
|
||||||
mb="auto"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mb="10px"
|
|
||||||
display={'flex'}
|
|
||||||
w="100%"
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
me="20px"
|
|
||||||
h="40px"
|
|
||||||
minH="40px"
|
|
||||||
minW="40px"
|
|
||||||
align="center"
|
|
||||||
justify={'center'}
|
|
||||||
borderRadius="full"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
>
|
|
||||||
<Icon as={MdPerson} color={brandColor} h="20px" w="20px" />
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
zIndex={2}
|
|
||||||
borderRadius="14px"
|
|
||||||
w="100%"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
p="20px"
|
|
||||||
backdropFilter="blur(24px)"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: 'sm', md: '16px' }}
|
|
||||||
fontWeight={'600'}
|
|
||||||
>
|
|
||||||
{inputOnSubmit}
|
|
||||||
</Text>
|
|
||||||
<Icon
|
|
||||||
as={MdEdit}
|
|
||||||
color={'gray.500'}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
ms="auto"
|
|
||||||
cursor={'pointer'}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex w="100%">
|
|
||||||
<Flex
|
|
||||||
me="20px"
|
|
||||||
h="40px"
|
|
||||||
minW="40px"
|
|
||||||
align="center"
|
|
||||||
justify={'center'}
|
|
||||||
borderRadius="full"
|
|
||||||
background={gradientBg2}
|
|
||||||
>
|
|
||||||
<Icon as={MdAutoAwesome} color={'white'} h="20px" w="20px" />
|
|
||||||
</Flex>
|
|
||||||
<MessageBoxChat output={outputCode} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{/* Chat Input */}
|
|
||||||
<Flex mt="20px" justifyContent={'flex-center'} mx="auto">
|
|
||||||
<Input
|
|
||||||
color={textColor}
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'45px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
w={{ md: '100%', xl: '45vw' }}
|
|
||||||
h="60px"
|
|
||||||
id="email"
|
|
||||||
fontSize={'sm'}
|
|
||||||
fontWeight="500"
|
|
||||||
placeholder="Type your message here..."
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb={{ base: '14px', md: '16px' }}
|
|
||||||
me="20px"
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
h="54px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg: 'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
minW="150px"
|
|
||||||
onClick={handleTranslate}
|
|
||||||
isLoading={loading}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
mt="10px"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
>
|
|
||||||
<Text color={gray} textAlign="center" fontSize="xs">
|
|
||||||
Free Research Preview. ChatGPT may produce inaccurate information
|
|
||||||
about people, places, or facts. Consider checking important
|
|
||||||
information.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,912 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import MessageBox from '@/components/MessageBox';
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import modalImage from '@/public/Modal.png';
|
|
||||||
import { EssayBody, OpenAIModel } from '@/types/types';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { getErrorRedirect } from '@/utils/helpers';
|
|
||||||
import { getStripe } from '@/utils/stripe/client';
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
FormLabel,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Link,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalOverlay,
|
|
||||||
Select,
|
|
||||||
Text,
|
|
||||||
Textarea,
|
|
||||||
useColorModeValue,
|
|
||||||
useDisclosure,
|
|
||||||
useToast
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { IoIosStar } from 'react-icons/io';
|
|
||||||
import {
|
|
||||||
MdCheckCircle,
|
|
||||||
MdChevronRight,
|
|
||||||
MdOutlineWorkspacePremium
|
|
||||||
} from 'react-icons/md';
|
|
||||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AiGenerator(props: Props) {
|
|
||||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
|
||||||
const [plan, setPlan] = useState({
|
|
||||||
product: 'prod_PtTCPDFZbburMa',
|
|
||||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
});
|
|
||||||
const router = useRouter();
|
|
||||||
const currentPath = usePathname();
|
|
||||||
const handleCheckout = async (price: Price) => {
|
|
||||||
setPriceIdLoading(price.id);
|
|
||||||
|
|
||||||
if (!props.user) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push('/signin/signup');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
|
||||||
price,
|
|
||||||
currentPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errorRedirect) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(errorRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(
|
|
||||||
getErrorRedirect(
|
|
||||||
currentPath,
|
|
||||||
'An unknown error occurred.',
|
|
||||||
'Please try again later or contact a system administrator.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stripe = await getStripe();
|
|
||||||
stripe?.redirectToCheckout({ sessionId });
|
|
||||||
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
};
|
|
||||||
// Input States
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const [words, setWords] = useState<'300' | '200'>('200');
|
|
||||||
const [essayType, setEssayType] = useState<
|
|
||||||
'' | 'Argumentative' | 'Classic' | 'Persuasive' | 'Critique'
|
|
||||||
>('');
|
|
||||||
const [topic, setTopic] = useState<string>('');
|
|
||||||
// Response message
|
|
||||||
const [outputCode, setOutputCode] = useState<string>('');
|
|
||||||
// ChatGPT model
|
|
||||||
const [model, setModel] = useState<OpenAIModel>('gpt-3.5-turbo');
|
|
||||||
// Loading state
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
// API Key
|
|
||||||
// const [apiKey, setApiKey] = useState<string>();
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const placeholderColor = useColorModeValue(
|
|
||||||
{ color: 'gray.500' },
|
|
||||||
{ color: 'whiteAlpha.600' }
|
|
||||||
);
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
// -------------- Main API Handler --------------
|
|
||||||
const handleTranslate = async () => {
|
|
||||||
const maxCodeLength = model === 'gpt-3.5-turbo' ? 700 : 700;
|
|
||||||
|
|
||||||
// Chat post conditions(maximum number of characters, valid message etc.)
|
|
||||||
|
|
||||||
// if (!apiKey?.includes('sk-') && !apiKey?.includes('sk-')) {
|
|
||||||
// alert('Please enter an API key.');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (!topic) {
|
|
||||||
alert('Please enter your subject.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!words) {
|
|
||||||
alert('Please choose number of words.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!essayType) {
|
|
||||||
alert('Please choose a type of essay.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (topic.length > maxCodeLength) {
|
|
||||||
alert(
|
|
||||||
`Please enter code less than ${maxCodeLength} characters. You are currently at ${topic.length} characters.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
setOutputCode('');
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
const body: EssayBody = {
|
|
||||||
topic,
|
|
||||||
words,
|
|
||||||
essayType,
|
|
||||||
model
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Fetch --------------
|
|
||||||
const response = await fetch('/api/essayAPI', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
body: JSON.stringify(body)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
setLoading(false);
|
|
||||||
if (response) {
|
|
||||||
alert(
|
|
||||||
'Something went wrong went fetching from the API. Make sure to use a valid API key.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = response.body;
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
setLoading(false);
|
|
||||||
alert('Something went wrong');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = data.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
let done = false;
|
|
||||||
let code = '';
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
const { value, done: doneReading } = await reader.read();
|
|
||||||
done = doneReading;
|
|
||||||
const chunkValue = decoder.decode(value);
|
|
||||||
|
|
||||||
code += chunkValue;
|
|
||||||
|
|
||||||
setOutputCode((prevCode) => prevCode + chunkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
copyToClipboard(code);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Copy Response --------------
|
|
||||||
const copyToClipboard = (text: string) => {
|
|
||||||
const el = document.createElement('textarea');
|
|
||||||
el.value = text;
|
|
||||||
document.body.appendChild(el);
|
|
||||||
el.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(el);
|
|
||||||
};
|
|
||||||
|
|
||||||
// *** Initializing apiKey with .env.local value
|
|
||||||
// useEffect(() => {
|
|
||||||
// ENV file verison
|
|
||||||
// const apiKeyENV = process.env.NEXT_PUBLIC_OPENAI_API_KEY;
|
|
||||||
// if (apiKey === undefined || null) {
|
|
||||||
// setApiKey(apiKeyENV);
|
|
||||||
// }
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// -------------- Input Value Handler --------------
|
|
||||||
const handleChange = (Event: any) => {
|
|
||||||
setTopic(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeParagraphs = (Event: any) => {
|
|
||||||
setWords(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeEssayType = (Event: any) => {
|
|
||||||
setEssayType(Event.target.value);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Essay Generator"
|
|
||||||
description="Essay Generator"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
position="relative"
|
|
||||||
mt={{ base: '70px', md: '0px', xl: '0px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w={{ base: '100%', md: '100%', xl: '100%' }}
|
|
||||||
maxW="100%"
|
|
||||||
justify="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
minW={{ base: '100%', md: '40%', xl: '476px' }}
|
|
||||||
maxW={{ base: '100%', md: '40%', xl: '476px' }}
|
|
||||||
h="min-content"
|
|
||||||
me={{ base: '0px', md: '20px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize={'30px'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
Essay Topic
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="md" color="gray.500" fontWeight="500" mb="30px">
|
|
||||||
What your essay will be about?
|
|
||||||
</Text>
|
|
||||||
<Textarea
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
p="15px 20px"
|
|
||||||
mb="28px"
|
|
||||||
minH="224px"
|
|
||||||
fontWeight="500"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
color={textColor}
|
|
||||||
placeholder="Type here your topic..."
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'parag'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Number of words
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="type"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeParagraphs}
|
|
||||||
>
|
|
||||||
<option value={'200'}>200</option>
|
|
||||||
<option value={'300'}>300</option>
|
|
||||||
</Select>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'type'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Select your Essay type
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="type"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeEssayType}
|
|
||||||
>
|
|
||||||
<option value="Argumentative">Argumentative</option>
|
|
||||||
<option value="Classic">Classic</option>
|
|
||||||
<option value="Persuasive">Persuasive</option>
|
|
||||||
<option value="Critique">Critique</option>
|
|
||||||
</Select>{' '}
|
|
||||||
{props.subscription ? (
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
ms="10px"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Looking for all features?
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Link href="/dashboard/premium-essays">
|
|
||||||
<Flex
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'14px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
py="14px"
|
|
||||||
mb="28px"
|
|
||||||
align="center"
|
|
||||||
px="16px"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius="99px"
|
|
||||||
borderColor="secondaryGray.200"
|
|
||||||
p="10px"
|
|
||||||
h="max-content"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
color="brand.500"
|
|
||||||
as={MdOutlineWorkspacePremium}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Text
|
|
||||||
ms="10px"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
>
|
|
||||||
Try our Premium Essay Generator
|
|
||||||
</Text>
|
|
||||||
<Icon
|
|
||||||
color={textColor}
|
|
||||||
as={MdChevronRight}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
ms="auto"
|
|
||||||
mb="-2px"
|
|
||||||
me="4px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
ms="10px"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Looking for more features?
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => {
|
|
||||||
onOpen();
|
|
||||||
}}
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'14px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
py="14px"
|
|
||||||
mb="28px"
|
|
||||||
align="center"
|
|
||||||
px="16px"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius="99px"
|
|
||||||
borderColor="secondaryGray.200"
|
|
||||||
p="10px"
|
|
||||||
h="max-content"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
color="brand.500"
|
|
||||||
as={MdOutlineWorkspacePremium}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Text
|
|
||||||
ms="10px"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
>
|
|
||||||
Try our Premium Essay Generator
|
|
||||||
</Text>
|
|
||||||
<Icon
|
|
||||||
color={textColor}
|
|
||||||
as={MdChevronRight}
|
|
||||||
h="20px"
|
|
||||||
w="20px"
|
|
||||||
ms="auto"
|
|
||||||
mb="-2px"
|
|
||||||
me="4px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay bg="rgba(0, 0, 0, 0.85)" />
|
|
||||||
<ModalContent
|
|
||||||
mx="8px"
|
|
||||||
bg="transparent"
|
|
||||||
boxShadow="unset"
|
|
||||||
maxW="unset"
|
|
||||||
w="unset"
|
|
||||||
>
|
|
||||||
<ModalBody p="0px" position={'relative'}>
|
|
||||||
<Flex>
|
|
||||||
<Image
|
|
||||||
display={{ base: 'none', md: 'block' }}
|
|
||||||
zIndex="98"
|
|
||||||
borderLeftRadius="16px"
|
|
||||||
src={modalImage.src}
|
|
||||||
w="340px"
|
|
||||||
alt=" "
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
bg="white"
|
|
||||||
borderLeftRadius={{ base: '16px', md: '0px' }}
|
|
||||||
borderRightRadius="16px"
|
|
||||||
direction={'column'}
|
|
||||||
px={{ base: '30px', md: '42px' }}
|
|
||||||
py="34px"
|
|
||||||
w={{ md: '412px', lg: '456px' }}
|
|
||||||
minW={{ md: '412px', lg: '456px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="26px"
|
|
||||||
fontWeight={'800'}
|
|
||||||
color={textColor}
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Upgrade to Unlimited
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize="md"
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
Get access to all features and generate premium and
|
|
||||||
exclusive essays with our unlimited plan!
|
|
||||||
</Text>
|
|
||||||
{/* Features */}
|
|
||||||
<Flex w={{ base: '100%', xl: '80%' }} direction="column">
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Access to 12+ Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 1500 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{/* YEARLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTJ6R3RnzmIPX',
|
|
||||||
price: 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTJ6R3RnzmIPX'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="14px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="20px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="8px"
|
|
||||||
>
|
|
||||||
Yearly
|
|
||||||
</Text>
|
|
||||||
<Badge
|
|
||||||
display={{
|
|
||||||
base: 'flex',
|
|
||||||
lg: 'none',
|
|
||||||
xl: 'flex'
|
|
||||||
}}
|
|
||||||
colorScheme="green"
|
|
||||||
borderRadius="4px"
|
|
||||||
color="green.500"
|
|
||||||
textTransform={'none'}
|
|
||||||
letterSpacing="0px"
|
|
||||||
px="0px"
|
|
||||||
w="max-content"
|
|
||||||
>
|
|
||||||
Save 35%
|
|
||||||
</Badge>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$5.75
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END YEARLY */}
|
|
||||||
{/* MONTHLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTCPDFZbburMa',
|
|
||||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTCPDFZbburMa'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="16px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="28px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="4px"
|
|
||||||
>
|
|
||||||
Monthly
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$9
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END MONTHLY */}
|
|
||||||
{props.products.map((product: any) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price: any) => price.id === plan.price
|
|
||||||
);
|
|
||||||
if (product.id === plan.product) {
|
|
||||||
if (!price) return null;
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={product.id}
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
mb="28px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={() => handleCheckout(price)}
|
|
||||||
>
|
|
||||||
Upgrade now
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
mt="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
<Text
|
|
||||||
fontSize="xs"
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight={'500'}
|
|
||||||
mx="auto"
|
|
||||||
mb="5px"
|
|
||||||
>
|
|
||||||
Used by 80,000+ users monthly
|
|
||||||
</Text>
|
|
||||||
<Flex direction="row" alignItems="center" mx="auto">
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="6px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="800"
|
|
||||||
h="100%"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
4.9
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalCloseButton
|
|
||||||
borderRadius="full"
|
|
||||||
color="#120F43"
|
|
||||||
bg="#F4F6FB !important"
|
|
||||||
_hover={{ bg: '#E9EDF6 !important' }}
|
|
||||||
_focus={{ bg: '#F4F6FB !important' }}
|
|
||||||
_active={{ bg: '#F4F6FB !important' }}
|
|
||||||
zIndex="99"
|
|
||||||
/>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="md"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
onClick={handleTranslate}
|
|
||||||
isLoading={loading ? true : false}
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg: 'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Generate your Essay
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
<Card maxW="100%" h="100%">
|
|
||||||
<Text
|
|
||||||
fontSize={'30px'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
AI Output
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="md" color="gray.500" fontWeight="500" mb="30px">
|
|
||||||
Enjoy your outstanding essay!
|
|
||||||
</Text>
|
|
||||||
<MessageBox output={outputCode} />
|
|
||||||
<Button
|
|
||||||
variant="transparent"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="full"
|
|
||||||
maxW="160px"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
w={{ base: '300px', md: '420px' }}
|
|
||||||
h="54px"
|
|
||||||
onClick={() => {
|
|
||||||
if (outputCode) navigator.clipboard.writeText(outputCode);
|
|
||||||
toast({
|
|
||||||
title: outputCode
|
|
||||||
? `Essay succesfully copied!`
|
|
||||||
: `Generate an essay first!`,
|
|
||||||
position: 'top',
|
|
||||||
status: outputCode ? 'success' : `error`,
|
|
||||||
isClosable: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy text
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import LineChart from '@/components/charts/LineChart';
|
|
||||||
import { lineChartDataMain } from '@/variables/charts';
|
|
||||||
import { lineChartOptionsMain } from '@/variables/charts';
|
|
||||||
import { Flex, useColorModeValue, Text, Box, Icon } from '@chakra-ui/react';
|
|
||||||
import { MdInsights } from 'react-icons/md';
|
|
||||||
|
|
||||||
function OverallRevenue() {
|
|
||||||
const newOptions = {
|
|
||||||
...lineChartOptionsMain,
|
|
||||||
// colors: ['var(--color-500)' ],
|
|
||||||
};
|
|
||||||
|
|
||||||
const bg = useColorModeValue('secondaryGray.300', 'secondaryGray.700');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
return (
|
|
||||||
<Card h="381px" p="24px">
|
|
||||||
<Flex align="center" gap="12px">
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdInsights} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
<Box>
|
|
||||||
<Text as="h5" fontSize={'sm'} fontWeight="500" color="gray.700">
|
|
||||||
Credits usage in the last year
|
|
||||||
</Text>
|
|
||||||
<Text color={textColor} mt="4px" fontSize="24px" fontWeight={'700'}>
|
|
||||||
149,758
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* Charts */}
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
h="100%"
|
|
||||||
flex={{ sm: 'wrap', lg: 'nowrap' }}
|
|
||||||
overflow={{ '2xl': 'hidden' }}
|
|
||||||
>
|
|
||||||
<Box w="100%" h="100%">
|
|
||||||
<LineChart chartData={lineChartDataMain} chartOptions={newOptions} />
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default OverallRevenue;
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
import Card from '@/components/card/Card';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Flex,
|
|
||||||
Table,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
PaginationState,
|
|
||||||
createColumnHelper,
|
|
||||||
useReactTable,
|
|
||||||
ColumnFiltersState,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getFacetedRowModel,
|
|
||||||
getFacetedUniqueValues,
|
|
||||||
getFacetedMinMaxValues,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
flexRender,
|
|
||||||
} from '@tanstack/react-table';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight, MdChevronLeft } from 'react-icons/md';
|
|
||||||
|
|
||||||
type RowObj = {
|
|
||||||
checked?: string;
|
|
||||||
email: string;
|
|
||||||
provider: string;
|
|
||||||
created: string;
|
|
||||||
lastsigned: string;
|
|
||||||
uuid: string;
|
|
||||||
menu?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function CheckTable(props: { tableData: any }) {
|
|
||||||
const { tableData } = props;
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = useColorModeValue('gray.700', 'white');
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'gray.200');
|
|
||||||
const grayLight = useColorModeValue('gray.200', 'white');
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
let defaultData = tableData;
|
|
||||||
const [globalFilter, setGlobalFilter] = React.useState('');
|
|
||||||
// const createPages = (count: number) => {
|
|
||||||
// let arrPageCount = [];
|
|
||||||
|
|
||||||
// for (let i = 1; i <= count; i++) {
|
|
||||||
// arrPageCount.push(i);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return arrPageCount;
|
|
||||||
// };
|
|
||||||
const columns = [
|
|
||||||
columnHelper.accessor('checked', {
|
|
||||||
id: 'checked',
|
|
||||||
header: () => (
|
|
||||||
<Box w="max-content">
|
|
||||||
<Checkbox me="10px" />
|
|
||||||
</Box>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Flex w="max-content" alignItems="center">
|
|
||||||
<Checkbox
|
|
||||||
defaultChecked={info.getValue()[1]}
|
|
||||||
colorScheme="brand"
|
|
||||||
me="10px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('email', {
|
|
||||||
id: 'email',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
EMAIL ADDRESS
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('created', {
|
|
||||||
id: 'created',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
CREATED
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('provider', {
|
|
||||||
id: 'provider',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
PROVIDER
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('lastsigned', {
|
|
||||||
id: 'lastsigned',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
LAST SIGN IN
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('uuid', {
|
|
||||||
id: 'uuid',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
USER UID
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
]; // eslint-disable-next-line
|
|
||||||
const [data, setData] = React.useState(() => [...defaultData]);
|
|
||||||
const [{ pageIndex, pageSize }, setPagination] =
|
|
||||||
React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 11,
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagination = React.useMemo(
|
|
||||||
() => ({
|
|
||||||
pageIndex,
|
|
||||||
pageSize,
|
|
||||||
}),
|
|
||||||
[pageIndex, pageSize],
|
|
||||||
);
|
|
||||||
const table = useReactTable({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
state: {
|
|
||||||
columnFilters,
|
|
||||||
globalFilter,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
||||||
getFacetedMinMaxValues: getFacetedMinMaxValues(),
|
|
||||||
debugTable: true,
|
|
||||||
debugHeaders: true,
|
|
||||||
debugColumns: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card w="100%" h="100%" overflow={'auto'} p="0px">
|
|
||||||
<Box mt="20px" overflowX={{ base: 'scroll', xl: 'hidden' }}>
|
|
||||||
<Table w="100%">
|
|
||||||
<Thead>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<Tr
|
|
||||||
key={headerGroup.id}
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor="gray.200"
|
|
||||||
p="20px"
|
|
||||||
>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<Th
|
|
||||||
key={header.id}
|
|
||||||
colSpan={header.colSpan}
|
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
|
||||||
cursor="pointer"
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
pb="14px"
|
|
||||||
ps="24px"
|
|
||||||
pe="16px"
|
|
||||||
textAlign={'start'}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="space-between"
|
|
||||||
fontSize="12px"
|
|
||||||
color={textColorSecondary}
|
|
||||||
|
|
||||||
// gray 1
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext(),
|
|
||||||
)}
|
|
||||||
{{
|
|
||||||
asc: '',
|
|
||||||
desc: '',
|
|
||||||
}[header.column.getIsSorted() as string] ?? null}
|
|
||||||
</Flex>
|
|
||||||
</Th>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{table
|
|
||||||
.getRowModel()
|
|
||||||
.rows.slice(0, 7)
|
|
||||||
.map((row) => {
|
|
||||||
return (
|
|
||||||
<Tr key={row.id} px="24px">
|
|
||||||
{row.getVisibleCells().map((cell) => {
|
|
||||||
return (
|
|
||||||
<Td
|
|
||||||
key={cell.id}
|
|
||||||
w="max-content"
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext(),
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
{/* pagination */}
|
|
||||||
<Flex
|
|
||||||
mt="8px"
|
|
||||||
w="100%"
|
|
||||||
h="80px"
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="space-between"
|
|
||||||
px="24px"
|
|
||||||
>
|
|
||||||
{/* left side */}
|
|
||||||
<Flex alignItems={'center'} gap="12px">
|
|
||||||
<Text color={textColorSecondary} fontWeight="500" fontSize="sm">
|
|
||||||
Showing 6 rows per page
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* right side */}
|
|
||||||
<Flex alignItems="center" gap="8px">
|
|
||||||
<Button
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
variant="transparent"
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg="transparent"
|
|
||||||
p="8px"
|
|
||||||
fontSize={'18px'}
|
|
||||||
color="gray.700"
|
|
||||||
>
|
|
||||||
<MdChevronLeft />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
variant="transparent"
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg="transparent"
|
|
||||||
p="8px"
|
|
||||||
fontSize={'18px'}
|
|
||||||
color="gray.700"
|
|
||||||
>
|
|
||||||
<MdChevronRight />
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CheckTable;
|
|
||||||
const columnHelper = createColumnHelper<RowObj>();
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import Card from '@/components/card/Card';
|
|
||||||
import { Box, Flex, Text, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const Statistics = (props: {
|
|
||||||
icon?: JSX.Element;
|
|
||||||
title: string;
|
|
||||||
value: number | string;
|
|
||||||
endContent?: JSX.Element;
|
|
||||||
}) => {
|
|
||||||
const { icon, title, value, endContent } = props;
|
|
||||||
const textColorSecondary = useColorModeValue('gray.700', 'white');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
w="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
borderRadius="14px"
|
|
||||||
bg="white"
|
|
||||||
py="30px"
|
|
||||||
>
|
|
||||||
<Flex gap="12px" alignItems={'center'}>
|
|
||||||
{icon}
|
|
||||||
<Box>
|
|
||||||
<Text
|
|
||||||
as="h5"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'500'}
|
|
||||||
color={textColorSecondary}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="700"
|
|
||||||
mt="4px"
|
|
||||||
fontSize="24px"
|
|
||||||
lineHeight={'24px'}
|
|
||||||
>
|
|
||||||
{value}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{endContent}
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Statistics;
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import MainChart from '@/components/dashboard/main/cards/MainChart';
|
|
||||||
import MainDashboardTable from '@/components/dashboard/main/cards/MainDashboardTable';
|
|
||||||
import Statistics from '@/components/dashboard/main/cards/Statistics';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import tableDataUserReports from '@/variables/tableDataUserReports';
|
|
||||||
import { Box, Flex, Grid, Icon, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { HiOutlineChip } from 'react-icons/hi';
|
|
||||||
import { MdOutlineGroup, MdOutlineGroupAdd, MdKey } from 'react-icons/md';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null | any;
|
|
||||||
userDetails: { [x: string]: any } | null | any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Main(props: Props) {
|
|
||||||
const bg = useColorModeValue('secondaryGray.300', 'whiteAlpha.200');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
console.log(props.user);
|
|
||||||
return (
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Main Dashboard"
|
|
||||||
description="Manage your dashboard"
|
|
||||||
>
|
|
||||||
<Box h="100%" w="100%">
|
|
||||||
<Grid
|
|
||||||
mb="20px"
|
|
||||||
w="100%"
|
|
||||||
gridTemplateColumns={{
|
|
||||||
base: 'repeat(1, minmax(0, 1fr))',
|
|
||||||
md: 'repeat(2, minmax(0, 1fr))',
|
|
||||||
xl: 'repeat(4, minmax(0, 1fr))'
|
|
||||||
}}
|
|
||||||
borderRadius="14px"
|
|
||||||
gap="20px"
|
|
||||||
>
|
|
||||||
{/* statistics */}
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdOutlineGroup} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Credits used in the last month"
|
|
||||||
value="46,042"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdOutlineGroupAdd} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Total Credits"
|
|
||||||
value="149,758"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={HiOutlineChip} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Plan Credits"
|
|
||||||
value="100,000"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdKey} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Current Plan"
|
|
||||||
value="Expert+"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Box mb="20px">
|
|
||||||
<MainChart />
|
|
||||||
</Box>
|
|
||||||
{/* Conversion and talbes*/}
|
|
||||||
<Box w="full" h="full" borderRadius="14px">
|
|
||||||
<MainDashboardTable tableData={tableDataUserReports} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,534 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import MessageBox from '@/components/MessageBox';
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import { OpenAIModel, PremiumEssayBody } from '@/types/types';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
FormLabel,
|
|
||||||
Select,
|
|
||||||
Switch,
|
|
||||||
Text,
|
|
||||||
Textarea,
|
|
||||||
useColorModeValue,
|
|
||||||
useToast
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PremiumEssayGenerator(props: Props) {
|
|
||||||
// Input States
|
|
||||||
const [words, setWords] = useState<string>('200-300');
|
|
||||||
const [essayType, setEssayType] = useState<
|
|
||||||
| ''
|
|
||||||
| 'Argumentative'
|
|
||||||
| 'Classic'
|
|
||||||
| 'Persuasive'
|
|
||||||
| 'Memoir'
|
|
||||||
| 'Critique'
|
|
||||||
| 'Compare/Contrast'
|
|
||||||
| 'Narrative'
|
|
||||||
| 'Descriptive'
|
|
||||||
| 'Expository'
|
|
||||||
| 'Cause and Effect'
|
|
||||||
| 'Reflective'
|
|
||||||
| 'Informative'
|
|
||||||
>('');
|
|
||||||
const [topic, setTopic] = useState<string>('');
|
|
||||||
const [tone, setTone] = useState<string>('');
|
|
||||||
const [citation, setCitation] = useState<string>('');
|
|
||||||
const [citations, setCitations] = useState(false);
|
|
||||||
const [level, setLevel] = useState<string>('');
|
|
||||||
// Response message
|
|
||||||
const [outputCode, setOutputCode] = useState<string>('');
|
|
||||||
// ChatGPT model
|
|
||||||
const [model, setModel] = useState<OpenAIModel>('gpt-3.5-turbo');
|
|
||||||
// Loading state
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
// API Key
|
|
||||||
// const [apiKey, setApiKey] = useState<any>();
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const placeholderColor = useColorModeValue(
|
|
||||||
{ color: 'gray.500' },
|
|
||||||
{ color: 'whiteAlpha.600' }
|
|
||||||
);
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
// -------------- Main API Handler --------------
|
|
||||||
const handleTranslate = async () => {
|
|
||||||
const maxCodeLength = model === 'gpt-3.5-turbo' ? 700 : 700;
|
|
||||||
|
|
||||||
// Chat post conditions(maximum number of characters, valid message etc.)
|
|
||||||
|
|
||||||
if (!topic) {
|
|
||||||
alert('Please enter your subject.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!essayType) {
|
|
||||||
alert('Please choose a type of essay');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!tone) {
|
|
||||||
alert('Please choose a essay tone');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!citation) {
|
|
||||||
alert('Please choose a citation format');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!level) {
|
|
||||||
alert('Please choose an level');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (topic.length > maxCodeLength) {
|
|
||||||
alert(
|
|
||||||
`Please enter a topic less than ${maxCodeLength} characters. You are currently at ${topic.length} characters.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
setOutputCode('');
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
const body: PremiumEssayBody = {
|
|
||||||
words,
|
|
||||||
topic,
|
|
||||||
essayType,
|
|
||||||
tone,
|
|
||||||
citation,
|
|
||||||
level,
|
|
||||||
citations,
|
|
||||||
model
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Fetch --------------
|
|
||||||
const response = await fetch('/api/premiumEssayAPI', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
body: JSON.stringify(body)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
setLoading(false);
|
|
||||||
if (response) {
|
|
||||||
alert(
|
|
||||||
'Something went wrong went fetching from the API. Make sure to use a valid API key.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = response.body;
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
setLoading(false);
|
|
||||||
alert('Something went wrong');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = data.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
let done = false;
|
|
||||||
let code = '';
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
const { value, done: doneReading } = await reader.read();
|
|
||||||
done = doneReading;
|
|
||||||
const chunkValue = decoder.decode(value);
|
|
||||||
|
|
||||||
code += chunkValue;
|
|
||||||
|
|
||||||
setOutputCode((prevCode) => prevCode + chunkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
copyToClipboard(code);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------- Copy Response --------------
|
|
||||||
const copyToClipboard = (text: string) => {
|
|
||||||
const el = document.createElement('textarea');
|
|
||||||
el.value = text;
|
|
||||||
document.body.appendChild(el);
|
|
||||||
el.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(el);
|
|
||||||
};
|
|
||||||
|
|
||||||
// *** Initializing apiKey with .env.local value
|
|
||||||
// useEffect(() => {
|
|
||||||
// ENV file verison
|
|
||||||
// const apiKeyENV = process.env.NEXT_PUBLIC_OPENAI_API_KEY;
|
|
||||||
// if (apiKey === undefined || null) {
|
|
||||||
// setApiKey(apiKeyENV);
|
|
||||||
// }
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// -------------- Input Value Handler --------------
|
|
||||||
const handleChangeWords = (Event: any) => {
|
|
||||||
setWords(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChange = (Event: any) => {
|
|
||||||
setTopic(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeEssayType = (Event: any) => {
|
|
||||||
setEssayType(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeEssayTone = (Event: any) => {
|
|
||||||
setTone(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeCitation = (Event: any) => {
|
|
||||||
setCitation(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleChangeLevel = (Event: any) => {
|
|
||||||
setLevel(Event.target.value);
|
|
||||||
};
|
|
||||||
const handleCitations = (Event: any) => {
|
|
||||||
setCitations(!citations);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Premium Generator"
|
|
||||||
description="Premium Generator"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
position="relative"
|
|
||||||
mt={{ base: '70px', md: '0px', xl: '0px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w={{ base: '100%', md: '100%', xl: '100%' }}
|
|
||||||
maxW="100%"
|
|
||||||
justify="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
minW={{ base: '100%', md: '40%', xl: '476px' }}
|
|
||||||
maxW={{ base: '100%', md: '40%', xl: '476px' }}
|
|
||||||
h="min-content"
|
|
||||||
me={{ base: '0px', md: '20px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize={'30px'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
Essay Topic
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="md" color="gray.500" fontWeight="500" mb="30px">
|
|
||||||
What your premium essay will be about?
|
|
||||||
</Text>
|
|
||||||
<Textarea
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
p="15px 20px"
|
|
||||||
mb="28px"
|
|
||||||
minH="124px"
|
|
||||||
fontWeight="500"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
color={textColor}
|
|
||||||
placeholder="Type here your topic..."
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'words'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Number of Words
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="words"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeWords}
|
|
||||||
>
|
|
||||||
<option value="200-300">200-300</option>
|
|
||||||
<option value="300-400">300-400</option>
|
|
||||||
<option value="400-500">400-500</option>
|
|
||||||
<option value="500-600">500-600</option>
|
|
||||||
<option value="700-900">700-900</option>
|
|
||||||
<option value="1000-1500">1000-1500</option>
|
|
||||||
</Select>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'type'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Select your Essay type
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="type"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeEssayType}
|
|
||||||
>
|
|
||||||
<option value="Argumentative">Argumentative</option>
|
|
||||||
<option value="Classic">Classic</option>
|
|
||||||
<option value="Compare/Contrast">Compare/Contrast</option>
|
|
||||||
<option value="Persuasive">Persuasive</option>
|
|
||||||
<option value="Critique">Critique</option>
|
|
||||||
<option value="Memoir">Memoir</option>
|
|
||||||
<option value="Narrative">Narrative</option>
|
|
||||||
<option value="Descriptive">Descriptive</option>
|
|
||||||
<option value="Expository">Expository</option>
|
|
||||||
<option value="Cause and Effect">Cause and Effect</option>
|
|
||||||
<option value="Reflective">Reflective</option>
|
|
||||||
<option value="Informative">Informative</option>
|
|
||||||
</Select>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'tone'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Select your Essay Tone
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="tone"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeEssayTone}
|
|
||||||
>
|
|
||||||
<option value="Academic">Academic</option>
|
|
||||||
<option value="Sarcastic">Sarcastic</option>
|
|
||||||
<option value="Informal">Informal</option>
|
|
||||||
<option value="Assertive">Assertive</option>
|
|
||||||
<option value="Friendly">Friendly</option>
|
|
||||||
<option value="Humorous">Humorous</option>
|
|
||||||
<option value="Formal">Formal</option>
|
|
||||||
</Select>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'citation'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Select your Citation Format
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="citation"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeCitation}
|
|
||||||
>
|
|
||||||
<option value="Cambridge Style">Cambridge Style</option>
|
|
||||||
<option value="Harvard Style">Harvard Style</option>
|
|
||||||
<option value="MLA">MLA</option>
|
|
||||||
<option value="Chicago Style">Chicago Style</option>
|
|
||||||
<option value="APA">APA</option>
|
|
||||||
<option value="AMA">AMA</option>
|
|
||||||
<option value="Oxford Style">Oxford Style</option>
|
|
||||||
<option value="IEEE">IEEE</option>
|
|
||||||
<option value="CSE">CSE</option>
|
|
||||||
<option value="Bluebook">Bluebook</option>
|
|
||||||
<option value="Turabian">Turabian</option>
|
|
||||||
<option value="Vancouver">Vancouver</option>
|
|
||||||
<option value="ACS">ACS</option>
|
|
||||||
<option value="NLM">NLM</option>
|
|
||||||
<option value="AAA">AAA</option>
|
|
||||||
<option value="ASA">ASA</option>
|
|
||||||
<option value="APSA">APSA</option>
|
|
||||||
</Select>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'level'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Academic Level
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'10px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
id="level"
|
|
||||||
placeholder="Select option"
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb="28px"
|
|
||||||
onChange={handleChangeLevel}
|
|
||||||
>
|
|
||||||
<option value="High-School">High-School</option>
|
|
||||||
<option value="Pre Final">Pre Final</option>
|
|
||||||
<option value="Master">Master</option>
|
|
||||||
<option value="Doctoral">Doctoral</option>
|
|
||||||
<option value="Final Year">Final Year</option>
|
|
||||||
</Select>
|
|
||||||
<Flex
|
|
||||||
mb="28px"
|
|
||||||
align={{ base: 'unset', md: 'center' }}
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<FormLabel
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
me={{ base: '0px', md: '20px' }}
|
|
||||||
mb={{ base: '10px', md: '0px' }}
|
|
||||||
htmlFor={'citations'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
Citations and refrences
|
|
||||||
</FormLabel>
|
|
||||||
<Switch
|
|
||||||
isChecked={citations}
|
|
||||||
onChange={handleCitations}
|
|
||||||
colorScheme="brandScheme"
|
|
||||||
id="citations"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="md"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
onClick={handleTranslate}
|
|
||||||
isLoading={loading ? true : false}
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Generate your Essay
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
<Card maxW="100%" h="100%">
|
|
||||||
<Text
|
|
||||||
fontSize={'30px'}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
AI Output
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="md" color="gray.500" fontWeight="500" mb="30px">
|
|
||||||
Enjoy your outstanding essay!
|
|
||||||
</Text>
|
|
||||||
<MessageBox output={outputCode} />
|
|
||||||
<Button
|
|
||||||
variant="transparent"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="full"
|
|
||||||
maxW="160px"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
w={{ base: '300px', md: '420px' }}
|
|
||||||
h="54px"
|
|
||||||
onClick={() => {
|
|
||||||
if (outputCode) navigator.clipboard.writeText(outputCode);
|
|
||||||
toast({
|
|
||||||
title: outputCode
|
|
||||||
? `Essay succesfully copied!`
|
|
||||||
: `Generate an essay first!`,
|
|
||||||
position: 'top',
|
|
||||||
status: outputCode ? 'success' : `error`,
|
|
||||||
isClosable: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy text
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
// import ManageSubscriptionButton from './ManageSubscriptionButton';
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import { HSeparator } from '@/components/separator/Separator';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { handleRequest } from '@/utils/auth-helpers/client';
|
|
||||||
import { updateEmail, updateName } from '@/utils/auth-helpers/server';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
FormLabel,
|
|
||||||
Input,
|
|
||||||
Text,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { redirect, useRouter } from 'next/navigation';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Settings(props: Props) {
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
const router = useRouter();
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const placeholderColor = useColorModeValue(
|
|
||||||
{ color: 'gray.500' },
|
|
||||||
{ color: 'whiteAlpha.600' }
|
|
||||||
);
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
|
|
||||||
|
|
||||||
// -------------- Input Value Handler --------------
|
|
||||||
|
|
||||||
const handleSubmitEmail = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true);
|
|
||||||
// Check if the new email is the same as the old email
|
|
||||||
if (e.currentTarget.newEmail.value === props.user.email) {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsSubmitting(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleRequest(e, updateEmail, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmitName = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
setIsSubmitting(true);
|
|
||||||
// Check if the new name is the same as the old name
|
|
||||||
if (e.currentTarget.fullName.value === props.user.user_metadata.full_name) {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsSubmitting(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleRequest(e, updateName, router);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!props.user) {
|
|
||||||
return redirect('/dashboard/signin');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Account Settings"
|
|
||||||
description="Profile settings."
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
position="relative"
|
|
||||||
mt={{ base: '70px', md: '0px', xl: '0px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w={{ base: '100%', md: '100%', xl: '100%' }}
|
|
||||||
maxW="100%"
|
|
||||||
justify="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
// minW={{ base: '100%' }}
|
|
||||||
maxW={{ base: '100%' }}
|
|
||||||
px={{ base: '10px', md: '20px', lg: '20px' }}
|
|
||||||
py={{ base: '28px', md: '20px', lg: '30px' }}
|
|
||||||
w="820px"
|
|
||||||
h="min-content"
|
|
||||||
me={{ base: '0px', md: '20px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
ps={{ base: '10px', md: '32px' }}
|
|
||||||
fontSize={{ base: 'lg', md: '30px' }}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
>
|
|
||||||
Account Settings
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
ps={{ base: '10px', md: '32px' }}
|
|
||||||
fontSize={{ base: 'sm', md: 'md' }}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
mb="30px"
|
|
||||||
>
|
|
||||||
Here you can change your account information
|
|
||||||
</Text>
|
|
||||||
<FormLabel
|
|
||||||
px={{ base: '10px', md: '32px' }}
|
|
||||||
display="flex"
|
|
||||||
ms="10px"
|
|
||||||
htmlFor={'fullName'}
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Your Name
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
(30 characters maximum)
|
|
||||||
</Text>
|
|
||||||
</FormLabel>
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
px={{ base: '10px', md: '32px' }}
|
|
||||||
mb={{ base: '30px', md: '0px' }}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
id="nameForm"
|
|
||||||
onSubmit={(e) => handleSubmitName(e)}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
color={textColor}
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'45px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
type="text"
|
|
||||||
name="fullName"
|
|
||||||
id="fullName"
|
|
||||||
defaultValue={props.user?.user_metadata.full_name ?? ''}
|
|
||||||
fontWeight="500"
|
|
||||||
placeholder="Please enter your full name"
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
mb={{ base: '14px', md: '26px' }}
|
|
||||||
me="20px"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
ms="10px"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
h="54px"
|
|
||||||
form="nameForm"
|
|
||||||
type="submit"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
minW="150px"
|
|
||||||
>
|
|
||||||
Update name
|
|
||||||
</Button>
|
|
||||||
<HSeparator
|
|
||||||
bg="gray.200"
|
|
||||||
mt={{ base: '30px', md: '0px' }}
|
|
||||||
display={{ md: 'none' }}
|
|
||||||
alignSelf="center"
|
|
||||||
maxW="90%"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
{/* <Text
|
|
||||||
px={{ base: '10px', md: '36px' }}
|
|
||||||
color="red"
|
|
||||||
mb="20px"
|
|
||||||
display={nameError?.status ? 'block' : 'none'}
|
|
||||||
>
|
|
||||||
{nameError?.message}
|
|
||||||
</Text> */}
|
|
||||||
<FormLabel
|
|
||||||
px={{ base: '10px', md: '32px' }}
|
|
||||||
display="flex"
|
|
||||||
htmlFor={'email'}
|
|
||||||
fontSize="md"
|
|
||||||
flexDirection={{ base: 'column', md: 'row' }}
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="bold"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Your Email
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms={{ base: '0px', md: '4px' }}
|
|
||||||
mt={{ base: '6px', md: '0px' }}
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
(We will email you to verify the change)
|
|
||||||
</Text>
|
|
||||||
</FormLabel>
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
px={{ base: '10px', md: '32px' }}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
id="emailForm"
|
|
||||||
onSubmit={(e) => handleSubmitEmail(e)}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="newEmail"
|
|
||||||
id="email"
|
|
||||||
color={textColor}
|
|
||||||
border="1px solid"
|
|
||||||
borderRadius={'45px'}
|
|
||||||
borderColor={borderColor}
|
|
||||||
h="60px"
|
|
||||||
fontWeight="500"
|
|
||||||
placeholder="Please enter your email"
|
|
||||||
_placeholder={placeholderColor}
|
|
||||||
_focus={{ borderColor: 'none' }}
|
|
||||||
defaultValue={props.user.email ?? ''}
|
|
||||||
mb={{ base: '14px', md: '26px' }}
|
|
||||||
me="20px"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
type="submit"
|
|
||||||
form="emailForm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
ms="10px"
|
|
||||||
h="54px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
minW="150px"
|
|
||||||
>
|
|
||||||
Update email
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { createStripePortal } from '@/utils/stripe/server';
|
|
||||||
import { Badge, Button, Flex, Icon, Link, Text } from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { redirect, usePathname, useRouter } from 'next/navigation';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null | any;
|
|
||||||
userDetails: { [x: string]: any } | null | any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Subscription(props: Props) {
|
|
||||||
const router = useRouter();
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
const currentPath = usePathname();
|
|
||||||
|
|
||||||
const handleStripePortalRequest = async () => {
|
|
||||||
setIsSubmitting(true);
|
|
||||||
const redirectUrl = await createStripePortal(currentPath);
|
|
||||||
setIsSubmitting(false);
|
|
||||||
return router.push(redirectUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!props.user) {
|
|
||||||
return redirect('/dashboard/signin');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Subscription Page"
|
|
||||||
description="Manage your subscriptions"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
position="relative"
|
|
||||||
mt={{ base: '70px', md: '0px', xl: '0px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
w={{ base: '100%', md: '100%', xl: '100%' }}
|
|
||||||
maxW="100%"
|
|
||||||
justify="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Card w="830px" maxW={{ base: '100%' }} h="min-content">
|
|
||||||
<Card
|
|
||||||
bg="linear-gradient(15deg, #4A25E1 26.3%, #7B5AFF 86.4%)"
|
|
||||||
maxW={{ base: '100%' }}
|
|
||||||
px={{ base: '20px', md: '40px', lg: '50px' }}
|
|
||||||
py={{ base: '28px', md: '40px', lg: '50px' }}
|
|
||||||
me={{ base: '0px', md: '20px' }}
|
|
||||||
mb="16px"
|
|
||||||
>
|
|
||||||
<Badge
|
|
||||||
w="max-content"
|
|
||||||
mb="10px"
|
|
||||||
fontSize="sm"
|
|
||||||
bg="rgba(255,255,255,0.12)"
|
|
||||||
color="white"
|
|
||||||
fontWeight="bold"
|
|
||||||
borderRadius="8px"
|
|
||||||
>
|
|
||||||
CURRENT PLAN
|
|
||||||
</Badge>
|
|
||||||
{props.subscription ? (
|
|
||||||
props.products?.map((product: any) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price: any) => price.id === props.subscription?.price_id
|
|
||||||
);
|
|
||||||
// {props.subscription?.map((subscription:any) => {
|
|
||||||
// const price = subscription?.prices?.find(
|
|
||||||
// (user:any) => user.id === props?.userDetails.id,
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (price?.id === props.subscription.price_id)
|
|
||||||
return (
|
|
||||||
// IN CASE USER HAS PLAN
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: '30px', md: '44px' }}
|
|
||||||
color="white"
|
|
||||||
fontWeight="800"
|
|
||||||
>
|
|
||||||
{product?.name
|
|
||||||
? product.name?.toString()
|
|
||||||
: 'No plan active'}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: 'sm', md: '20px' }}
|
|
||||||
color="white"
|
|
||||||
fontWeight="500"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
{product?.name
|
|
||||||
? `You are currently on ${product.name?.toString()}`
|
|
||||||
: "You don't have an active subscription."}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: 'lg', md: '24px' }}
|
|
||||||
color="white"
|
|
||||||
textAlign={{ base: 'left', md: 'right' }}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
$
|
|
||||||
{price?.unit_amount !== null
|
|
||||||
? price?.unit_amount / 100
|
|
||||||
: '0'}
|
|
||||||
{price?.interval === 'year'
|
|
||||||
? '/year'
|
|
||||||
: price?.interval === 'month'
|
|
||||||
? '/month'
|
|
||||||
: 'error'}
|
|
||||||
</Text>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="outline"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%', md: '266px' }}
|
|
||||||
h="54px"
|
|
||||||
onClick={handleStripePortalRequest}
|
|
||||||
>
|
|
||||||
{props?.subscription
|
|
||||||
? 'Manage your subscription'
|
|
||||||
: 'See pricing Plans'}
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
display={props?.subscription ? 'none' : 'unset'}
|
|
||||||
mt="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
// IN CASE OF NOW PLAN
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: '30px', md: '44px' }}
|
|
||||||
color="white"
|
|
||||||
fontWeight="800"
|
|
||||||
>
|
|
||||||
No plan active
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: 'sm', md: '20px' }}
|
|
||||||
color="white"
|
|
||||||
fontWeight="500"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
You don't have an active subscription.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: 'lg', md: '24px' }}
|
|
||||||
color="white"
|
|
||||||
textAlign={{ base: 'left', md: 'right' }}
|
|
||||||
fontWeight="800"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
$0/month
|
|
||||||
</Text>
|
|
||||||
<Link href="/pricing">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="outline"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%', md: '190px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
See pricing Plans
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
mt="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
textAlign={'center'}
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="sm"
|
|
||||||
alignSelf="center"
|
|
||||||
color="gray.500"
|
|
||||||
w={{ base: '70%', md: 'unset' }}
|
|
||||||
me="2px"
|
|
||||||
>
|
|
||||||
Got a question regarding your subscription? Chat with us via{' '}
|
|
||||||
</Text>
|
|
||||||
<Link href="mailto:hello@horizon-ui.com">
|
|
||||||
<Text
|
|
||||||
textAlign={'center'}
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="sm"
|
|
||||||
textDecoration="underline"
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
hello@horizon-ui.com.
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</DashboardLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import Card from '@/components/card/Card';
|
|
||||||
import { Box, Flex, Text, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const Statistics = (props: {
|
|
||||||
icon?: JSX.Element;
|
|
||||||
title: string;
|
|
||||||
value: number | string;
|
|
||||||
endContent?: JSX.Element;
|
|
||||||
}) => {
|
|
||||||
const { icon, title, value, endContent } = props;
|
|
||||||
const textColorSecondary = useColorModeValue('gray.700', 'white');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
w="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
borderRadius="14px"
|
|
||||||
bg="white"
|
|
||||||
py="30px"
|
|
||||||
>
|
|
||||||
<Flex gap="12px" alignItems={'center'}>
|
|
||||||
{icon}
|
|
||||||
<Box>
|
|
||||||
<Text
|
|
||||||
as="h5"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'500'}
|
|
||||||
color={textColorSecondary}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="700"
|
|
||||||
mt="4px"
|
|
||||||
fontSize="24px"
|
|
||||||
lineHeight={'24px'}
|
|
||||||
>
|
|
||||||
{value}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{endContent}
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Statistics;
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
import Card from '@/components/card/Card';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Flex,
|
|
||||||
Table,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
PaginationState,
|
|
||||||
createColumnHelper,
|
|
||||||
useReactTable,
|
|
||||||
ColumnFiltersState,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getFacetedRowModel,
|
|
||||||
getFacetedUniqueValues,
|
|
||||||
getFacetedMinMaxValues,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
flexRender,
|
|
||||||
} from '@tanstack/react-table';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight, MdChevronLeft } from 'react-icons/md';
|
|
||||||
|
|
||||||
type RowObj = {
|
|
||||||
checked?: string;
|
|
||||||
email: string;
|
|
||||||
provider: string;
|
|
||||||
created: string;
|
|
||||||
lastsigned: string;
|
|
||||||
uuid: string;
|
|
||||||
menu?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function CheckTable(props: { tableData: any }) {
|
|
||||||
const { tableData } = props;
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = useColorModeValue('gray.700', 'white');
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'gray.200');
|
|
||||||
const grayLight = useColorModeValue('gray.200', 'white');
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
let defaultData = tableData;
|
|
||||||
const [globalFilter, setGlobalFilter] = React.useState('');
|
|
||||||
// const createPages = (count: number) => {
|
|
||||||
// let arrPageCount = [];
|
|
||||||
|
|
||||||
// for (let i = 1; i <= count; i++) {
|
|
||||||
// arrPageCount.push(i);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return arrPageCount;
|
|
||||||
// };
|
|
||||||
const columns = [
|
|
||||||
columnHelper.accessor('checked', {
|
|
||||||
id: 'checked',
|
|
||||||
header: () => (
|
|
||||||
<Box w="max-content">
|
|
||||||
<Checkbox me="10px" />
|
|
||||||
</Box>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Flex w="max-content" alignItems="center">
|
|
||||||
<Checkbox
|
|
||||||
defaultChecked={info.getValue()[1]}
|
|
||||||
colorScheme="brand"
|
|
||||||
me="10px"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('email', {
|
|
||||||
id: 'email',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
EMAIL ADDRESS
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('created', {
|
|
||||||
id: 'created',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
CREATED
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('provider', {
|
|
||||||
id: 'provider',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
PROVIDER
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info: any) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('lastsigned', {
|
|
||||||
id: 'lastsigned',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
LAST SIGN IN
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('uuid', {
|
|
||||||
id: 'uuid',
|
|
||||||
header: () => (
|
|
||||||
<Text
|
|
||||||
justifyContent="space-between"
|
|
||||||
align="center"
|
|
||||||
fontSize={{ sm: '10px', lg: '12px' }}
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
USER UID
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
cell: (info) => (
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="600">
|
|
||||||
{info.getValue()}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
]; // eslint-disable-next-line
|
|
||||||
const [data, setData] = React.useState(() => [...defaultData]);
|
|
||||||
const [{ pageIndex, pageSize }, setPagination] =
|
|
||||||
React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 11,
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagination = React.useMemo(
|
|
||||||
() => ({
|
|
||||||
pageIndex,
|
|
||||||
pageSize,
|
|
||||||
}),
|
|
||||||
[pageIndex, pageSize],
|
|
||||||
);
|
|
||||||
const table = useReactTable({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
state: {
|
|
||||||
columnFilters,
|
|
||||||
globalFilter,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
||||||
getFacetedMinMaxValues: getFacetedMinMaxValues(),
|
|
||||||
debugTable: true,
|
|
||||||
debugHeaders: true,
|
|
||||||
debugColumns: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card w="100%" h="100%" overflow={'auto'} p="0px">
|
|
||||||
<Box mt="20px" overflowX={{ base: 'scroll', xl: 'hidden' }}>
|
|
||||||
<Table w="100%">
|
|
||||||
<Thead>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<Tr
|
|
||||||
key={headerGroup.id}
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor="gray.200"
|
|
||||||
p="20px"
|
|
||||||
>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<Th
|
|
||||||
key={header.id}
|
|
||||||
colSpan={header.colSpan}
|
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
|
||||||
cursor="pointer"
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
pb="14px"
|
|
||||||
ps="24px"
|
|
||||||
pe="16px"
|
|
||||||
textAlign={'start'}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="space-between"
|
|
||||||
fontSize="12px"
|
|
||||||
color={textColorSecondary}
|
|
||||||
|
|
||||||
// gray 1
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext(),
|
|
||||||
)}
|
|
||||||
{{
|
|
||||||
asc: '',
|
|
||||||
desc: '',
|
|
||||||
}[header.column.getIsSorted() as string] ?? null}
|
|
||||||
</Flex>
|
|
||||||
</Th>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{table
|
|
||||||
.getRowModel()
|
|
||||||
.rows.slice(0, 7)
|
|
||||||
.map((row) => {
|
|
||||||
return (
|
|
||||||
<Tr key={row.id} px="24px">
|
|
||||||
{row.getVisibleCells().map((cell) => {
|
|
||||||
return (
|
|
||||||
<Td
|
|
||||||
key={cell.id}
|
|
||||||
w="max-content"
|
|
||||||
borderBottom="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext(),
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
{/* pagination */}
|
|
||||||
<Flex
|
|
||||||
mt="8px"
|
|
||||||
w="100%"
|
|
||||||
h="80px"
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent="space-between"
|
|
||||||
px="24px"
|
|
||||||
>
|
|
||||||
{/* left side */}
|
|
||||||
<Flex alignItems={'center'} gap="12px">
|
|
||||||
<Text color={textColorSecondary} fontWeight="500" fontSize="sm">
|
|
||||||
Showing 6 rows per page
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* right side */}
|
|
||||||
<Flex alignItems="center" gap="8px">
|
|
||||||
<Button
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
variant="transparent"
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg="transparent"
|
|
||||||
p="8px"
|
|
||||||
fontSize={'18px'}
|
|
||||||
color="gray.700"
|
|
||||||
>
|
|
||||||
<MdChevronLeft />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
variant="transparent"
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="full"
|
|
||||||
bg="transparent"
|
|
||||||
p="8px"
|
|
||||||
fontSize={'18px'}
|
|
||||||
color="gray.700"
|
|
||||||
>
|
|
||||||
<MdChevronRight />
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CheckTable;
|
|
||||||
const columnHelper = createColumnHelper<RowObj>();
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import Statistics from '@/components/dashboard/users-list/cards/Statistics';
|
|
||||||
import UserListTable from '@/components/dashboard/users-list/cards/UserListTable';
|
|
||||||
import DashboardLayout from '@/components/layout';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import tableDataUserReports from '@/variables/tableDataUserReports';
|
|
||||||
import { Box, Flex, Grid, Icon, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { redirect } from 'next/navigation';
|
|
||||||
import { MdOutlineGroup, MdOutlineGroupAdd, MdKey } from 'react-icons/md';
|
|
||||||
import { TbDatabase } from 'react-icons/tb';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null | any;
|
|
||||||
userDetails: { [x: string]: any } | null | any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Settings(props: Props) {
|
|
||||||
if (!props.user) {
|
|
||||||
return redirect('/dashboard/signin');
|
|
||||||
}
|
|
||||||
|
|
||||||
const bg = useColorModeValue('secondaryGray.300', 'whiteAlpha.200');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardLayout
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
user={props?.user}
|
|
||||||
products={props.products}
|
|
||||||
subscription={props.subscription}
|
|
||||||
title="Subscription Page"
|
|
||||||
description="Manage your subscriptions"
|
|
||||||
>
|
|
||||||
<Box mt="12px" h="100%" w="100%">
|
|
||||||
<Grid
|
|
||||||
mb="20px"
|
|
||||||
gap="20px"
|
|
||||||
gridTemplateColumns={{
|
|
||||||
base: 'repeat(1, minmax(0, 1fr))',
|
|
||||||
md: 'repeat(2, minmax(0, 1fr))',
|
|
||||||
xl: 'repeat(4, minmax(0, 1fr))'
|
|
||||||
}}
|
|
||||||
borderRadius="14px"
|
|
||||||
>
|
|
||||||
{/* statistics */}
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdOutlineGroup} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Total Users"
|
|
||||||
value="9,794"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdOutlineGroupAdd} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Users Today"
|
|
||||||
value="379"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={TbDatabase} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="REST Requests"
|
|
||||||
value="270,307"
|
|
||||||
/>
|
|
||||||
<Statistics
|
|
||||||
icon={
|
|
||||||
<Flex
|
|
||||||
h={'56px'}
|
|
||||||
w={'56px'}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems={'center'}
|
|
||||||
rounded="full"
|
|
||||||
fontSize="36px"
|
|
||||||
color={brandColor}
|
|
||||||
bg={bg}
|
|
||||||
>
|
|
||||||
<Icon as={MdKey} w="24px" h="24px" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
title="Auth Requests"
|
|
||||||
value="23,484"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
{/* Conversion and talbes*/}
|
|
||||||
<Flex h="100%" w="100%" borderRadius="14px">
|
|
||||||
<UserListTable tableData={tableDataUserReports} />
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/*eslint-disable*/
|
|
||||||
import Link from '@/components/link/Link';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export default function Footer() {
|
|
||||||
const textColor = useColorModeValue('gray.500', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
zIndex="3"
|
|
||||||
flexDirection={{
|
|
||||||
base: 'column',
|
|
||||||
xl: 'row',
|
|
||||||
}}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
px={{ base: '30px', md: '50px' }}
|
|
||||||
pb="30px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
textAlign={{
|
|
||||||
base: 'center',
|
|
||||||
xl: 'start',
|
|
||||||
}}
|
|
||||||
fontWeight="500"
|
|
||||||
mb={{ base: '10px', xl: '0px' }}
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
© {new Date().getFullYear()}
|
|
||||||
<Text as="span" fontWeight="500" ms="4px">
|
|
||||||
Horizon UI Boilerplate. All Rights Reserved.
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
<List display="flex">
|
|
||||||
<ListItem
|
|
||||||
me={{
|
|
||||||
base: '10px',
|
|
||||||
md: '44px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
href="https://horizon-ui.com"
|
|
||||||
>
|
|
||||||
Homepage
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
me={{
|
|
||||||
base: '10px',
|
|
||||||
md: '44px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
href="https://horizon-ui.notion.site/Terms-Conditions-6e79229d25ed48f48a481962bc6de3ee"
|
|
||||||
>
|
|
||||||
Terms of Use
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
href="https://horizon-ui.notion.site/Privacy-Policy-8addde50aa8e408ca5c5f5811c38f971"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/*eslint-disable*/
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Link,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export default function Footer() {
|
|
||||||
let textColor = useColorModeValue('gray.500', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
mt="auto"
|
|
||||||
zIndex="3"
|
|
||||||
flexDirection={{
|
|
||||||
base: 'column',
|
|
||||||
lg: 'row'
|
|
||||||
}}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
px={{ base: '30px', md: '0px' }}
|
|
||||||
pb="30px"
|
|
||||||
>
|
|
||||||
<List
|
|
||||||
display="flex"
|
|
||||||
flexDirection={{
|
|
||||||
base: 'row',
|
|
||||||
md: 'row'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItem
|
|
||||||
me={{
|
|
||||||
base: '16px',
|
|
||||||
md: '44px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: '10px', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
isExternal
|
|
||||||
href="https://horizon-ui.notion.site/Terms-Conditions-6e79229d25ed48f48a481962bc6de3ee"
|
|
||||||
>
|
|
||||||
Terms & Conditions
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
me={{
|
|
||||||
base: '16px',
|
|
||||||
md: '44px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: '10px', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
isExternal
|
|
||||||
href="https://horizon-ui.notion.site/Privacy-Policy-8addde50aa8e408ca5c5f5811c38f971"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<Link
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize={{ base: '10px', md: 'sm' }}
|
|
||||||
color={textColor}
|
|
||||||
isExternal
|
|
||||||
href="https://horizon-ui.notion.site/Refund-Policy-5d5fa25f7fac4978a0be6fcf3036c6ee"
|
|
||||||
>
|
|
||||||
Refund Policy
|
|
||||||
</Link>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import logo from '/public/logo-horizon-boilerplate.png';
|
|
||||||
import { HSeparator } from '@/components/separator/Separator';
|
|
||||||
import { Flex, Link, Text, Image, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export function FooterWebsite() {
|
|
||||||
const textColorSecondary = useColorModeValue('gray.600', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
zIndex="3"
|
|
||||||
flexDirection={{
|
|
||||||
base: 'column',
|
|
||||||
}}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
position="relative"
|
|
||||||
px={{ base: '20px', xl: '0px' }}
|
|
||||||
pb="50px"
|
|
||||||
bg="white"
|
|
||||||
>
|
|
||||||
<HSeparator
|
|
||||||
mb="0px"
|
|
||||||
mt={{ base: '0px', md: '100px', lg: '0px' }}
|
|
||||||
maxW="1170px"
|
|
||||||
mx="auto"
|
|
||||||
bg="gray.200"
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
mt="50px"
|
|
||||||
w={{ base: '100%', xl: '1170px' }}
|
|
||||||
flexDirection={{
|
|
||||||
base: 'column',
|
|
||||||
lg: 'row',
|
|
||||||
}}
|
|
||||||
maxW={{ base: '100%', xl: '1170px' }}
|
|
||||||
maxH="max-content"
|
|
||||||
mx="auto"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent={'center'}
|
|
||||||
href="/"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
w="36px"
|
|
||||||
src={logo.src}
|
|
||||||
/>
|
|
||||||
<Text ms="8px" me="10px" fontWeight="800" color="#120F43">
|
|
||||||
Horizon UI Boilerplate
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
justifyItems="center"
|
|
||||||
alignItems="center"
|
|
||||||
textAlign={'center'}
|
|
||||||
w={{ base: '100%', md: '100%', lg: '100%', xl: 'unset' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
justify={'center'}
|
|
||||||
mx="auto"
|
|
||||||
align="center"
|
|
||||||
mb={{ base: '20px', lg: '0px' }}
|
|
||||||
>
|
|
||||||
<Flex my="auto" direction={{ base: 'column', md: 'row' }}>
|
|
||||||
<Link
|
|
||||||
isExternal={true}
|
|
||||||
href="/pricing"
|
|
||||||
fontSize="sm"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me={{ base: '0px', md: '40px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
Pricing
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
isExternal={true}
|
|
||||||
href="/dashboard/settings"
|
|
||||||
fontSize="sm"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me={{ base: '0px', md: '40px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
Account
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
isExternal={true}
|
|
||||||
href="https://horizon-ui.notion.site/Refund-Policy-5d5fa25f7fac4978a0be6fcf3036c6ee"
|
|
||||||
fontSize="sm"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me={{ base: '0px', md: '40px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
Refund Policy
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
isExternal={true}
|
|
||||||
href="https://horizon-ui.notion.site/Privacy-Policy-8addde50aa8e408ca5c5f5811c38f971"
|
|
||||||
fontSize="sm"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me={{ base: '0px', md: '40px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
isExternal={true}
|
|
||||||
href="https://horizon-ui.notion.site/Terms-Conditions-6e79229d25ed48f48a481962bc6de3ee"
|
|
||||||
fontSize="sm"
|
|
||||||
color={textColorSecondary}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me={{ base: '0px', md: '40px' }}
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
>
|
|
||||||
Terms of service
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex w="100%" maxW="1170px" px={{ base: '10px', md: '100px' }}>
|
|
||||||
<Text
|
|
||||||
lineHeight="180%"
|
|
||||||
fontSize="sm"
|
|
||||||
textAlign="center"
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
mt="40px"
|
|
||||||
mb="40px"
|
|
||||||
>
|
|
||||||
<Text as="span" fontWeight={'700'}>
|
|
||||||
Use it with caution:
|
|
||||||
</Text>{' '}
|
|
||||||
This tool can be helpful, but it is not a substitute for your own
|
|
||||||
knowledge and understanding. Make sure to use it as a supplement to
|
|
||||||
your own research and writing, rather than relying on it exclusively.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { createIcon } from '@chakra-ui/icons';
|
|
||||||
|
|
||||||
export const HeroBg = createIcon({
|
|
||||||
displayName: 'HeroBg',
|
|
||||||
viewBox: '0 0 1618 900',
|
|
||||||
path: (
|
|
||||||
<g
|
|
||||||
width="1618"
|
|
||||||
height="900"
|
|
||||||
viewBox="0 0 1618 900"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M101.583 301.413C94.4517 259.209 122.883 219.215 165.087 212.083L1382.96 6.28832C1472.87 -8.9049 1512.05 115.615 1429.63 154.64L642.704 527.238L1520.96 365.296C1607.06 349.42 1650.61 465.12 1575.37 509.938L942.158 887.084C905.385 908.987 857.818 896.932 835.916 860.158C814.013 823.385 826.068 775.818 862.842 753.916L1129.5 595.09L95.0792 785.827C4.69665 802.492 -35.766 676.987 47.2513 637.679L863.283 251.3L190.913 364.917C148.709 372.048 108.715 343.617 101.583 301.413Z"
|
|
||||||
fill="#E9E3FF"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
)
|
|
||||||
});
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { Image } from './Image';
|
|
||||||
import { chakra, useColorMode } from '@chakra-ui/system';
|
|
||||||
import { ComponentProps } from 'react';
|
|
||||||
|
|
||||||
type AvatarImageProps = Partial<
|
|
||||||
ComponentProps<typeof Image> & {
|
|
||||||
showBorder?: boolean;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
export function NextAvatar({
|
|
||||||
src,
|
|
||||||
showBorder,
|
|
||||||
alt = '',
|
|
||||||
style,
|
|
||||||
...props
|
|
||||||
}: AvatarImageProps) {
|
|
||||||
const { colorMode } = useColorMode();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Image
|
|
||||||
{...props}
|
|
||||||
{...(showBorder
|
|
||||||
? {
|
|
||||||
border: '2px',
|
|
||||||
borderColor: colorMode === 'dark' ? '#120F43' : 'white',
|
|
||||||
}
|
|
||||||
: {})}
|
|
||||||
alt={alt}
|
|
||||||
objectFit={'fill'}
|
|
||||||
src={src}
|
|
||||||
style={{ ...style, borderRadius: '50%' }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChakraNextAvatar = chakra(NextAvatar, {
|
|
||||||
shouldForwardProp: (prop) =>
|
|
||||||
['width', 'height', 'src', 'alt', 'layout'].includes(prop),
|
|
||||||
});
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import { Box, BoxProps } from '@chakra-ui/react';
|
|
||||||
import NextImage, { ImageProps } from 'next/legacy/image';
|
|
||||||
import { ComponentProps } from 'react';
|
|
||||||
|
|
||||||
type ChakraNextImageProps = Partial<ImageProps> &
|
|
||||||
Partial<BoxProps> & {
|
|
||||||
nextProps?: Partial<ComponentProps<typeof NextImage>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseAssetPrefix(image: string) {
|
|
||||||
const alreadyHasHttp = image.match('http');
|
|
||||||
if (alreadyHasHttp) return image;
|
|
||||||
|
|
||||||
const prefix = process.env.NEXT_PUBLIC_BASE_PATH || '';
|
|
||||||
const alreadyHasPrefix = image.match(prefix);
|
|
||||||
|
|
||||||
const finalUrl = alreadyHasPrefix ? image : `${prefix}${image}`;
|
|
||||||
return finalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Image(props: ChakraNextImageProps) {
|
|
||||||
const { src, alt, nextProps = {}, ...rest } = props;
|
|
||||||
|
|
||||||
const imageUrl =
|
|
||||||
typeof src === 'string' ? src : ((src as any)?.src as string);
|
|
||||||
return (
|
|
||||||
<Box overflow={'hidden'} position="relative" {...rest}>
|
|
||||||
<NextImage
|
|
||||||
layout="fill"
|
|
||||||
objectFit="fill"
|
|
||||||
src={parseAssetPrefix(imageUrl)}
|
|
||||||
alt={alt}
|
|
||||||
{...nextProps}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,371 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionButton,
|
|
||||||
AccordionIcon,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionPanel,
|
|
||||||
Flex,
|
|
||||||
Text,
|
|
||||||
Box,
|
|
||||||
SimpleGrid,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
w="100%"
|
|
||||||
pt={{ base: '90', md: '100px', xl: '140px' }}
|
|
||||||
bg="linear-gradient(180deg, #F8FAFC 0%, rgba(255, 255, 255, 0) 47.33%)"
|
|
||||||
id="faqs"
|
|
||||||
bgSize="cover"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
mx="auto"
|
|
||||||
mb="40px"
|
|
||||||
maxW={{ base: '90%', lg: '62%' }}
|
|
||||||
justify="center"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
textAlign={{ base: 'center', lg: 'center' }}
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
color="brand.500"
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
FREQUENTLY ASKED QUESTIONS
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
mx="auto"
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
fontSize={{ base: '30px', md: '38px', lg: '38px', xl: '38px' }}
|
|
||||||
lineHeight={{ base: '38px', md: '50px', lg: '50px', xl: '50px' }}
|
|
||||||
mb={{ base: '10px', md: '20px' }}
|
|
||||||
>
|
|
||||||
Frequently asked questions
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
mx="auto"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize={{ base: 'md', md: 'md', xl: 'lg' }}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
mb="30px"
|
|
||||||
maxW={{ base: '100%', md: '80%', lg: '60%' }}
|
|
||||||
>
|
|
||||||
Looking for something else? Chat with us via{' '}
|
|
||||||
<Link href="mailto:hello@horizon-ui.com ">hello@horizon-ui.com</Link>{' '}
|
|
||||||
and we will try our best to help you with your questions!
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Box
|
|
||||||
w="100%"
|
|
||||||
maxW={{ base: '100%', md: '80%', lg: '860px' }}
|
|
||||||
mx="auto"
|
|
||||||
mb="120px"
|
|
||||||
>
|
|
||||||
<Accordion allowMultiple>
|
|
||||||
<AccordionItem borderTop="0px solid">
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
What is Horizon UI Boilerplate?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
Is Horizon UI Boilerplate Free?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
You can learn more on our{' '}
|
|
||||||
<Link href="/pricing">
|
|
||||||
<Text as="span" fontWeight={'bold'} color="brand.500">
|
|
||||||
Pricing Page.
|
|
||||||
</Text>
|
|
||||||
</Link>{' '}
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
How does Horizon UI Boilerplate work?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
How can I use Horizon UI Boilerplate?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
Is Horizon UI Boilerplate suitable for all academic levels?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
Can I trust the quality of the essays generated by Essay
|
|
||||||
Builder AI?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<Text>
|
|
||||||
<AccordionButton
|
|
||||||
py="25px"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color={textColor}
|
|
||||||
_active={{ boxShadow: 'none' }}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
>
|
|
||||||
<Flex as="h3" flex={1} textAlign="left">
|
|
||||||
Is the content generated by Horizon UI Boilerplate plagiarism-free?
|
|
||||||
</Flex>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Text>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<SimpleGrid gap="40px" columns={1}>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
This is an awesome example of how you can use our accordion
|
|
||||||
component for your FAQs section to provide clear, concise
|
|
||||||
answers while maintaining a clean and engaging user interface.
|
|
||||||
The intuitive design allows users to easily navigate through
|
|
||||||
common questions, expanding each section to find detailed
|
|
||||||
information without overwhelming them with text. It's an ideal
|
|
||||||
way to streamline your website's content and enhance user
|
|
||||||
experience, ensuring that visitors have quick access to the
|
|
||||||
answers they need.
|
|
||||||
</Text>
|
|
||||||
</SimpleGrid>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
import InnerContent from '@/components/layout/innerContent';
|
|
||||||
import image from '@/public/img/features/feature-one.png';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Link,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function FeatureOne() {
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = useColorModeValue('gray.600', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
pt={{ base: '100px', md: '140px', lg: '140px' }}
|
|
||||||
pb={{ base: '100px', md: '140px', lg: '140px' }}
|
|
||||||
overflow="hidden"
|
|
||||||
bgSize="cover"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<InnerContent px={{ base: '20px', md: '40px', xl: '0px' }}>
|
|
||||||
<Flex
|
|
||||||
align={'center'}
|
|
||||||
direction={{ base: 'column', lg: 'row' }}
|
|
||||||
maxW="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
columnGap="50px"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
my="auto"
|
|
||||||
maxW="100%"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
bg="brand.500"
|
|
||||||
bgClip="text"
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
YOUR STARTUP FEATURES
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
fontWeight="800"
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: '30px', md: '40px', xl: '42px' }}
|
|
||||||
lineHeight={{ base: '40px', md: '50px', lg: '52px' }}
|
|
||||||
mb="20px"
|
|
||||||
w={{ base: '100%', md: '80%', lg: '100%' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
maxW={{ base: '100%', md: 'unset' }}
|
|
||||||
>
|
|
||||||
Ready to use Web App
|
|
||||||
<br />
|
|
||||||
for your Startup project
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColorSecondary}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
fontSize={{ base: 'sm', md: 'md', xl: 'md' }}
|
|
||||||
w={{ base: '94%', md: '94%', lg: '97%' }}
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
mb="30px"
|
|
||||||
>
|
|
||||||
It’s so easy to beat your endless procrastination when you have
|
|
||||||
all the necessary resources to get that project done and start to
|
|
||||||
generate your startup’s first dollar in just a few days.
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
mb={{ md: '0px', lg: '30px' }}
|
|
||||||
justifyContent={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Link href="/dashboard/signin" me={{ base: '0px', md: '14px' }}>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '230px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
Get started now
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link href="/dashboard/signin">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="setup"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '160px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
Try it for Free
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
src={image.src}
|
|
||||||
w={{ base: '100%', lg: '415px', xl: '575px' }}
|
|
||||||
mt={{ base: '20px', md: '50px', lg: '0px' }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</InnerContent>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import InnerContent from '@/components/layout/innerContent';
|
|
||||||
import image from '@/public/img/features/feature-three.png';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Link,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function FeatureOne() {
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = useColorModeValue('gray.600', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
pb={{ base: '100px', md: '140px', lg: '140px' }}
|
|
||||||
overflow="hidden"
|
|
||||||
bgSize="cover"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<InnerContent px={{ base: '20px', md: '40px', xl: '0px' }}>
|
|
||||||
<Flex
|
|
||||||
align={'center'}
|
|
||||||
direction={{ base: 'column', lg: 'row' }}
|
|
||||||
maxW="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
columnGap="50px"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
my="auto"
|
|
||||||
maxW="100%"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
bg="linear-gradient(89deg, #3D1DFF 9.03%, #6147FF 10.23%, #D451FF 20.91%, #EC458D 30.02%, #FFCA8B 44.68%)"
|
|
||||||
bgClip="text"
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
DISCOVER MORE WITH PRO
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
fontWeight="800"
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: '26px', md: '40px', xl: '42px' }}
|
|
||||||
lineHeight={{ base: '36px', md: '50px', lg: '52px' }}
|
|
||||||
mb="20px"
|
|
||||||
w={{ base: '100%', md: '80%', lg: '100%' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
maxW={{ base: '100%', md: 'unset' }}
|
|
||||||
>
|
|
||||||
Generate an Outstanding SaaS without limitations
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColorSecondary}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
fontSize={{ base: 'sm', md: 'md', xl: 'md' }}
|
|
||||||
w={{ base: '100%', md: '80%', lg: '97%' }}
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
mb="30px"
|
|
||||||
>
|
|
||||||
Give life to your startup project by choosing from a premium pack
|
|
||||||
of top-notch landing sections like Hero, Features, Call to
|
|
||||||
Actions, Pricing, Navigations, Auth pages, Dashboard, and so on.
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
mb={{ md: '0px', lg: '30px' }}
|
|
||||||
justifyContent={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Link href="/pricing" me={{ base: '0px', md: '14px' }}>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '210px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
Get started with PRO
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link href="/pricing">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="setup"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '190px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
See pricing Plans
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
src={image.src}
|
|
||||||
w={{ base: '100%', lg: '415px', xl: '575px' }}
|
|
||||||
mt={{ base: '20px', md: '50px', lg: '0px' }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</InnerContent>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
import InnerContent from '@/components/layout/innerContent';
|
|
||||||
import image from '@/public/img/features/feature-two.png';
|
|
||||||
import {
|
|
||||||
Flex,
|
|
||||||
Link,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function FeatureOne() {
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const textColorSecondary = useColorModeValue('gray.600', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
pb={{ base: '100px', md: '140px', lg: '140px' }}
|
|
||||||
overflow="hidden"
|
|
||||||
bgSize="cover"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<InnerContent px={{ base: '20px', md: '40px', xl: '0px' }}>
|
|
||||||
<Flex
|
|
||||||
align={'center'}
|
|
||||||
direction={{ base: 'column-reverse', lg: 'row' }}
|
|
||||||
maxW="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
columnGap="90px"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
src={image.src}
|
|
||||||
w={{ base: '100%', lg: '415px', xl: '575px' }}
|
|
||||||
mt={{ base: '20px', md: '50px', lg: '0px' }}
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
my="auto"
|
|
||||||
maxW="100%"
|
|
||||||
alignItems={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
bg="linear-gradient(89deg, #3D1DFF 9.03%, #6147FF 10.23%, #D451FF 20.91%, #EC458D 30.02%, #FFCA8B 44.68%)"
|
|
||||||
bgClip="text"
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
EXCLUSIVE PRO FEATURE
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
fontWeight="800"
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: '30px', md: '40px', xl: '42px' }}
|
|
||||||
lineHeight={{ base: '40px', md: '50px', lg: '52px' }}
|
|
||||||
mb="20px"
|
|
||||||
w={{ base: '100%', md: '70%', lg: '100%' }}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
maxW={{ base: '100%', md: 'unset' }}
|
|
||||||
>
|
|
||||||
Advanced Platform for your Startup Web App
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColorSecondary}
|
|
||||||
textAlign={{ base: 'center', lg: 'left' }}
|
|
||||||
fontSize={{ base: 'sm', md: 'md', xl: 'md' }}
|
|
||||||
w={{ base: '100%', md: '80%', lg: '100%' }}
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
mb="30px"
|
|
||||||
>
|
|
||||||
Horizon UI Boilerplate comes with a premium pack that includes all
|
|
||||||
the necessary resources to launch your startup, like the fully
|
|
||||||
coded web app pages, landing, database, payments and so on.
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
mb={{ md: '0px', lg: '30px' }}
|
|
||||||
justifyContent={{ base: 'center', lg: 'unset' }}
|
|
||||||
>
|
|
||||||
<Link href="/pricing" me={{ base: '0px', md: '14px' }}>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '210px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
Get started now
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link href="/pricing">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="setup"
|
|
||||||
borderRadius="45px"
|
|
||||||
mb={{ base: '20px', md: '0px' }}
|
|
||||||
w={{ base: '335px', md: '190px' }}
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
Try it for Free
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</InnerContent>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
import { HeroBg } from '@/components/icons/HeroBg';
|
|
||||||
import dashboard from '@/public/img/first-section/dashboard-world-greatest-section.png';
|
|
||||||
import left from '@/public/img/first-section/left-image-worlds-greatest.png';
|
|
||||||
import right from '@/public/img/first-section/right-image-worlds-greatest.png';
|
|
||||||
import nextjs from '@/public/img/hero/nextjs.png';
|
|
||||||
import openai from '@/public/img/hero/openai.png';
|
|
||||||
import stripe from '@/public/img/hero/stripe.png';
|
|
||||||
import supabase from '@/public/img/hero/supabase.png';
|
|
||||||
import chakra from '@/public/img/hero/chakra.png';
|
|
||||||
import userauth from '@/public/img/hero/user-auth.png';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Icon,
|
|
||||||
Flex,
|
|
||||||
Image,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
SimpleGrid,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function FirstSection() {
|
|
||||||
const brandColorPrice = useColorModeValue('brand.500', 'white');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
zIndex="2"
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
bgSize="cover"
|
|
||||||
position="relative"
|
|
||||||
pt={{ base: '90px', md: '100px', xl: '100px' }}
|
|
||||||
>
|
|
||||||
<SimpleGrid
|
|
||||||
columns={{ base: 1, md: 3, xl: 6 }}
|
|
||||||
w="100%"
|
|
||||||
gap="16px"
|
|
||||||
maxW="1170px"
|
|
||||||
mx="auto"
|
|
||||||
mb="86px"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={nextjs.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="nextjs logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
NextJS 14
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={stripe.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="stripe logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
Stripe
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={supabase.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="supabase logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
Supabase
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={chakra.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="chakra ui logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
Chakra UI
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={openai.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="openai logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
AI Integration
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
justify="center"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="secondaryGray.400"
|
|
||||||
borderRadius="14px"
|
|
||||||
py="39px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
borderRadius="12px"
|
|
||||||
mb="20px"
|
|
||||||
src={userauth.src}
|
|
||||||
w="60px"
|
|
||||||
h="60px"
|
|
||||||
alt="auth0 logo"
|
|
||||||
/>
|
|
||||||
<Text fontWeight="bold" color={textColor}>
|
|
||||||
User Auth
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</SimpleGrid>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
px={{ base: '20px', md: '40px', xl: '0px' }}
|
|
||||||
maxW="unset"
|
|
||||||
>
|
|
||||||
<Flex direction="column" width="stretch">
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
mx="auto"
|
|
||||||
alignItems="center"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
textAlign={{ base: 'center', lg: 'center' }}
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
color={brandColorPrice}
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
BEST AI NEXTJS BOILERPLATE
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
textAlign={{ base: 'center' }}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
fontSize={{ base: '30px', md: '48px', lg: '48px', xl: '58px' }}
|
|
||||||
lineHeight={{ base: '38px', md: '60px', lg: '60px', xl: '70px' }}
|
|
||||||
mb={{ base: '20px', md: '30px' }}
|
|
||||||
w={{ base: '100%', md: '80%', lg: '60%', xl: '50%' }}
|
|
||||||
mx="auto"
|
|
||||||
>
|
|
||||||
Your All-in-One <br />
|
|
||||||
Startup Boilerplate
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontSize={{ base: 'sm', md: 'md', xl: 'lg' }}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
w={{
|
|
||||||
base: '100%',
|
|
||||||
md: '80%',
|
|
||||||
lg: '80%',
|
|
||||||
xl: '56%',
|
|
||||||
'2xl': '54%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Tap into the power of Artificial Intelligence for your startup
|
|
||||||
needs with Horizon UI Boilerplate, the most complex NextJS
|
|
||||||
boilerplate to launch your web app project in just a few moments.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
position={'relative'}
|
|
||||||
justifyContent="center"
|
|
||||||
mt={{ base: '16px', md: '10px', lg: '40px' }}
|
|
||||||
maxW={{ base: '335px', md: '1170px' }}
|
|
||||||
mx="auto"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
as={HeroBg}
|
|
||||||
position="absolute"
|
|
||||||
mx="auto"
|
|
||||||
w={{
|
|
||||||
base: '500px',
|
|
||||||
md: '750px',
|
|
||||||
lg: '1000px',
|
|
||||||
xl: '1800px',
|
|
||||||
}}
|
|
||||||
h={{
|
|
||||||
base: '500px',
|
|
||||||
md: '750px',
|
|
||||||
lg: '1000px',
|
|
||||||
xl: '820px',
|
|
||||||
}}
|
|
||||||
left="50%"
|
|
||||||
transform="translate(-50%,0px)"
|
|
||||||
top={{
|
|
||||||
base: '-130px',
|
|
||||||
md: '-90px',
|
|
||||||
lg: '-100px',
|
|
||||||
xl: '-80px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src={dashboard.src}
|
|
||||||
zIndex={'1'}
|
|
||||||
alt=""
|
|
||||||
maxH="max-content"
|
|
||||||
w="100%"
|
|
||||||
maxW={{ base: '335px', md: '1170px' }}
|
|
||||||
borderRadius="8px"
|
|
||||||
boxShadow="0px 26.83487px 155.64224px -46.15597px #CBD5E0"
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
zIndex={'1'}
|
|
||||||
src={left.src}
|
|
||||||
alt=""
|
|
||||||
position={'absolute'}
|
|
||||||
w={{ base: '62px', md: '122px', xl: '224px' }}
|
|
||||||
left={{ base: '1px', md: '5px', lg: '10px', xl: '-20px' }}
|
|
||||||
top={{ base: '33px', md: '75px', lg: '36px', xl: '45px' }}
|
|
||||||
boxShadow="0px 10.1683px 61.0098px rgba(0, 0, 0, 0.05)"
|
|
||||||
borderRadius="8px"
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src={right.src}
|
|
||||||
alt=""
|
|
||||||
position="absolute"
|
|
||||||
right={{ base: '-16px', md: '-30px', lg: '-80px', xl: '-53px' }}
|
|
||||||
top={{ base: '48px', md: '105px', lg: '44px', xl: '98px' }}
|
|
||||||
w={{ base: '286px', md: '592px', lg: '806px', xl: '1026px' }}
|
|
||||||
borderRadius="8px"
|
|
||||||
zIndex={'1'}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
import imageLeft from '@/public/img/hero/left-image.png';
|
|
||||||
import imageRight from '@/public/img/hero/right-image.png';
|
|
||||||
import { Button, Image, Icon, Flex, Link, Text } from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { IoIosStar } from 'react-icons/io';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function hero() {
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
mx="auto"
|
|
||||||
mt={{ base: '90px', lg: '114px' }}
|
|
||||||
w="96vw"
|
|
||||||
h={{
|
|
||||||
base: '560px',
|
|
||||||
md: '580px',
|
|
||||||
lg: '580px',
|
|
||||||
xl: '620px',
|
|
||||||
'2xl': '84vh',
|
|
||||||
}}
|
|
||||||
overflow="hidden"
|
|
||||||
alignItems="center"
|
|
||||||
alignContent="center"
|
|
||||||
position={'relative'}
|
|
||||||
maxW="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
bg="var(--linear-3, radial-gradient(98.96% 75.83% at 50% 0%, #948FE8 0%, #363285 22.92%, #110D5B 42.71%, #050327 88.54%))"
|
|
||||||
borderRadius={{ base: '20px', md: '30px' }}
|
|
||||||
>
|
|
||||||
<Flex h="100%" w="100%">
|
|
||||||
<Flex
|
|
||||||
maxW="100%"
|
|
||||||
h="100%"
|
|
||||||
direction="row"
|
|
||||||
width="stretch"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
alignContent="center"
|
|
||||||
mb={{ base: '0px', md: '30px' }}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
position="absolute"
|
|
||||||
left="0"
|
|
||||||
display={{ base: 'none', xl: 'unset' }}
|
|
||||||
src={imageLeft.src}
|
|
||||||
alt=" "
|
|
||||||
w={{ base: '90%', md: '100%', lg: '30%', xl: '30%' }}
|
|
||||||
/>
|
|
||||||
<Flex direction="column" mx="auto" textAlign="center">
|
|
||||||
<Text
|
|
||||||
as="h1"
|
|
||||||
color="white"
|
|
||||||
zIndex="100"
|
|
||||||
fontSize={{
|
|
||||||
base: '32px',
|
|
||||||
md: '44px',
|
|
||||||
lg: '44px',
|
|
||||||
xl: '44px',
|
|
||||||
'2xl': '58px',
|
|
||||||
}}
|
|
||||||
lineHeight={{
|
|
||||||
base: '40px',
|
|
||||||
md: '54px',
|
|
||||||
lg: '54px',
|
|
||||||
xl: '54px',
|
|
||||||
'2xl': '68px',
|
|
||||||
}}
|
|
||||||
w={{
|
|
||||||
base: '90%',
|
|
||||||
md: '80%',
|
|
||||||
lg: '60%',
|
|
||||||
xl: '50%',
|
|
||||||
'2xl': '50%',
|
|
||||||
'3xl': '50%',
|
|
||||||
}}
|
|
||||||
alignSelf="center"
|
|
||||||
mb={{ base: '16px', md: '20px' }}
|
|
||||||
fontWeight="700"
|
|
||||||
>
|
|
||||||
Launch your startup project 10X faster in a few moments
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="p"
|
|
||||||
mb={{ base: '30px', md: '30px' }}
|
|
||||||
color={'white'}
|
|
||||||
alignSelf="center"
|
|
||||||
fontSize={{ base: 'sm', md: 'md', '2xl': 'md' }}
|
|
||||||
lineHeight={{ base: '24px', md: '30px' }}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="500"
|
|
||||||
w={{
|
|
||||||
base: '91%',
|
|
||||||
md: '82%',
|
|
||||||
lg: '62%',
|
|
||||||
xl: '44%',
|
|
||||||
'2xl': '41%',
|
|
||||||
'3xl': '41%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create a professional website for your startup in no time with
|
|
||||||
Horizon UI Boilerplate. Our comprehensive template will help you
|
|
||||||
launch your project 10X faster, leaving you more time to focus on
|
|
||||||
your business.
|
|
||||||
</Text>
|
|
||||||
<Link
|
|
||||||
w={{ base: '300px', md: '340px' }}
|
|
||||||
alignSelf="center"
|
|
||||||
href="/dashboard/signin"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
py="24px"
|
|
||||||
bg="linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important"
|
|
||||||
px="24px"
|
|
||||||
fontSize="md"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '300px', md: '340px' }}
|
|
||||||
h="70px"
|
|
||||||
>
|
|
||||||
Go to dashboard
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
color="white"
|
|
||||||
mt="4px"
|
|
||||||
ms="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
direction="column"
|
|
||||||
justifyContent={{ base: 'center', lg: 'center' }}
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
mt={{ base: '20px', md: '20px' }}
|
|
||||||
mb="10px"
|
|
||||||
color="white"
|
|
||||||
alignSelf="center"
|
|
||||||
fontSize="md"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight={'500'}
|
|
||||||
>
|
|
||||||
Used by 80,000+ users monthly
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
justify={{ base: 'center', md: 'start' }}
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Icon as={IoIosStar} w="22px" h="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} w="22px" h="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} w="22px" h="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} w="22px" h="22px" color="#F6AD55" />
|
|
||||||
<Icon
|
|
||||||
as={IoIosStar}
|
|
||||||
w="22px"
|
|
||||||
h="22px"
|
|
||||||
color="orange.300"
|
|
||||||
me="8px"
|
|
||||||
/>
|
|
||||||
<Text color="white" mt="2px" fontWeight="bold" fontSize="lg">
|
|
||||||
4.9
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Image
|
|
||||||
position="absolute"
|
|
||||||
right="0"
|
|
||||||
display={{ base: 'none', xl: 'unset' }}
|
|
||||||
src={imageRight.src}
|
|
||||||
alt=" "
|
|
||||||
w={{ base: '90%', md: '100%', lg: '30%', xl: '30%' }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
/*eslint-disable*/
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { FooterWebsite } from '@/components/footer/FooterWebsite';
|
|
||||||
import Faq from '@/components/landing/faq';
|
|
||||||
import FeatureOne from '@/components/landing/feature-one';
|
|
||||||
import FeatureThree from '@/components/landing/feature-three';
|
|
||||||
import FeatureTwo from '@/components/landing/feature-two';
|
|
||||||
import FirstSection from '@/components/landing/first-section';
|
|
||||||
import Hero from '@/components/landing/hero';
|
|
||||||
import SecondSection from '@/components/landing/second-section';
|
|
||||||
import NavbarFixed from '@/components/navbar/NavbarFixed';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { Button, Flex, Input, Text } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NavbarFixed />
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
h="100%"
|
|
||||||
minH="100vh"
|
|
||||||
overflow="hidden"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
direction="column"
|
|
||||||
pb={{ base: '0px', md: '80px', lg: '80px', xl: '80px' }}
|
|
||||||
position="relative"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Hero />
|
|
||||||
<FirstSection />
|
|
||||||
<SecondSection />
|
|
||||||
<FeatureOne />
|
|
||||||
<FeatureTwo />
|
|
||||||
<FeatureThree />
|
|
||||||
<Faq />
|
|
||||||
<Flex
|
|
||||||
direction={'column'}
|
|
||||||
maxW="100%"
|
|
||||||
mx="auto"
|
|
||||||
mb={{ base: '60px', md: '60px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color="navy.700"
|
|
||||||
fontWeight={'800'}
|
|
||||||
fontSize="30px"
|
|
||||||
mb="20px"
|
|
||||||
textAlign={'center'}
|
|
||||||
>
|
|
||||||
Join our newsletter
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize={{ base: '15px', md: 'lg' }}
|
|
||||||
color="gray.600"
|
|
||||||
px={{ base: '10px', md: '0px' }}
|
|
||||||
mb="30px"
|
|
||||||
textAlign={'center'}
|
|
||||||
>
|
|
||||||
By subscribing, you'll be the first to know about the latest news
|
|
||||||
and updates.
|
|
||||||
</Text>
|
|
||||||
<form
|
|
||||||
id="form-fbcaaaec-e795-4419-b112-06934fd0051d"
|
|
||||||
action="https://api.encharge.io/v1/forms/fbcaaaec-e795-4419-b112-06934fd0051d/submission/plain"
|
|
||||||
method="POST"
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexWrap: 'wrap'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="sc-jzJRlG kfekry">
|
|
||||||
<div className="encharge-form-group sc-jTzLTM frRvjZ form-group">
|
|
||||||
<Input
|
|
||||||
variant="main"
|
|
||||||
placeholder="Enter your email*"
|
|
||||||
me="14px"
|
|
||||||
h="100%"
|
|
||||||
isRequired={true}
|
|
||||||
w={{ base: '96%', md: '420px' }}
|
|
||||||
maxW="100%"
|
|
||||||
fontWeight="500"
|
|
||||||
_placeholder={{
|
|
||||||
color: 'gray.600',
|
|
||||||
fontWeight: '500'
|
|
||||||
}}
|
|
||||||
borderRadius="70px"
|
|
||||||
mb="0px !important"
|
|
||||||
type="email"
|
|
||||||
id="31b6ea2a-d9c7-4b42-9a01-7677838f07e9"
|
|
||||||
name="email"
|
|
||||||
className="encharge-form-input sc-kAzzGY kTMZCx form-control"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="sc-cSHVUG ebRkVm">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="40px"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'600'}
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
bg="linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important"
|
|
||||||
w={{ base: '100%', md: '150px' }}
|
|
||||||
h="58px"
|
|
||||||
type="submit"
|
|
||||||
className="encharge-form-submit-button sc-gZMcBi ejYzxT btn btn-primary"
|
|
||||||
>
|
|
||||||
Subscribe
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<FooterWebsite />
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// eslint-disabled
|
|
||||||
|
|
||||||
import { Icon, Flex, Text, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function SecondSection() {
|
|
||||||
const brandColorPrice = useColorModeValue('brand.500', 'white');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
id="features"
|
|
||||||
zIndex="2"
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
bgSize="cover"
|
|
||||||
alignItems="center"
|
|
||||||
position="relative"
|
|
||||||
pt={{ base: '90px', md: '100px', xl: '140px' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
px={{ base: '0px', md: '40px', xl: '0px' }}
|
|
||||||
maxW="1170px"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Flex direction="column" width="stretch">
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
mx="auto"
|
|
||||||
alignItems="center"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
textAlign={{ base: 'center', lg: 'center' }}
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
color={brandColorPrice}
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
HOW IT WORKS
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
textAlign={{ base: 'center' }}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
fontSize={{ base: '30px', md: '48px', lg: '48px', xl: '58px' }}
|
|
||||||
lineHeight={{ base: '38px', md: '60px', lg: '60px', xl: '70px' }}
|
|
||||||
mb={{ base: '20px', md: '30px' }}
|
|
||||||
mx="auto"
|
|
||||||
>
|
|
||||||
How it works?
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
w={{ base: '86%', md: '76%', lg: '100%' }}
|
|
||||||
pt={{ base: '40px', md: '60px', xl: '60px' }}
|
|
||||||
maxW="100%"
|
|
||||||
flexDirection={{ base: 'column', lg: 'row' }}
|
|
||||||
align={'center'}
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
me={{ base: '0px', lg: '32px' }}
|
|
||||||
mb={{ base: '50px', lg: '0px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
color="navy.700"
|
|
||||||
fontWeight={'800'}
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
fontSize="20px"
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Step 1: This is an example
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize={{ base: '15px', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
lineHeight={{ base: '28px', md: '30px' }}
|
|
||||||
w="98%"
|
|
||||||
>
|
|
||||||
This is where your first step paragraph goes. For the moment, this
|
|
||||||
is just an example to see what it will look like.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
justify="center"
|
|
||||||
align="center"
|
|
||||||
bg="white"
|
|
||||||
boxShadow={'0px 10.83px 28px -2px rgba(203, 213, 224, 0.79)'}
|
|
||||||
borderRadius="50px"
|
|
||||||
display={{ base: 'none', lg: 'flex' }}
|
|
||||||
me={{ base: '0px', lg: '32px' }}
|
|
||||||
minW="38px"
|
|
||||||
minH="38px"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
w="23px"
|
|
||||||
h="23px"
|
|
||||||
color="#7B5AFF"
|
|
||||||
bg="transparent"
|
|
||||||
as={MdChevronRight}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
me={{ base: '0px', lg: '20px' }}
|
|
||||||
mb={{ base: '50px', lg: '0px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
color="navy.700"
|
|
||||||
fontWeight={'800'}
|
|
||||||
fontSize="20px"
|
|
||||||
mb="12px"
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
>
|
|
||||||
Step 2: This is another example
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize={{ base: '15px', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
lineHeight={{ base: '28px', md: '30px' }}
|
|
||||||
w="98%"
|
|
||||||
>
|
|
||||||
This is where your second step paragraph goes. For the moment,
|
|
||||||
this is just an example to see what it will look like.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
justify="center"
|
|
||||||
align="center"
|
|
||||||
bg="white"
|
|
||||||
boxShadow={'0px 10.83px 28px -2px rgba(203, 213, 224, 0.79)'}
|
|
||||||
borderRadius="50px"
|
|
||||||
display={{ base: 'none', lg: 'flex' }}
|
|
||||||
me={{ base: '0px', lg: '32px' }}
|
|
||||||
minW="38px"
|
|
||||||
minH="38px"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
w="23px"
|
|
||||||
h="23px"
|
|
||||||
color="#7B5AFF"
|
|
||||||
bg="transparent"
|
|
||||||
as={MdChevronRight}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
color="navy.700"
|
|
||||||
fontWeight={'800'}
|
|
||||||
fontSize="20px"
|
|
||||||
mb="12px"
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
>
|
|
||||||
Step 3: This is an example too
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color="gray.600"
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize={{ base: '15px', md: 'md' }}
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
lineHeight={{ base: '28px', md: '30px' }}
|
|
||||||
>
|
|
||||||
This is where your third step paragraph goes. For the moment, this
|
|
||||||
is just an example to see what it will look like.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
import Footer from '@/components/footer/FooterAdmin';
|
|
||||||
import Navbar from '@/components/navbar/NavbarAdmin';
|
|
||||||
import { routes } from '@/components/routes';
|
|
||||||
import Sidebar from '@/components/sidebar/Sidebar';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { getActiveRoute } from '@/utils/navigation';
|
|
||||||
import { Box, useDisclosure } from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import {
|
|
||||||
PlanContext,
|
|
||||||
OpenContext,
|
|
||||||
ProductsContext,
|
|
||||||
SubscriptionContext,
|
|
||||||
UserContext,
|
|
||||||
UserDetailsContext,
|
|
||||||
ApiKeyContext
|
|
||||||
} from '@/contexts/layout';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
interface Props {
|
|
||||||
children: React.ReactNode;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DashboardLayout: React.FC<Props> = (props: Props) => {
|
|
||||||
const pathname = usePathname();
|
|
||||||
const [open, setOpen] = React.useState(false);
|
|
||||||
const [plan, setPlan] = React.useState({
|
|
||||||
product: 'prod_QfhYC6AAtI5IKW',
|
|
||||||
price: 'price_1PoM9GDWNoHJSR0zmwpicH8y'
|
|
||||||
});
|
|
||||||
const [apiKey, setApiKey] = React.useState('default');
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UserContext.Provider value={props.user}>
|
|
||||||
<UserDetailsContext.Provider value={props.user}>
|
|
||||||
<OpenContext.Provider value={{ open, setOpen }}>
|
|
||||||
<PlanContext.Provider value={{ plan, setPlan }}>
|
|
||||||
<ProductsContext.Provider value={props.products}>
|
|
||||||
<SubscriptionContext.Provider value={props.subscription}>
|
|
||||||
<ApiKeyContext.Provider value={{ apiKey, setApiKey }}>
|
|
||||||
<Box>
|
|
||||||
<Sidebar routes={routes} />
|
|
||||||
<Box
|
|
||||||
pt={{ base: '70px', md: '80px' }}
|
|
||||||
float="right"
|
|
||||||
minHeight="100vh"
|
|
||||||
height="100%"
|
|
||||||
overflow="auto"
|
|
||||||
position="relative"
|
|
||||||
maxHeight="100%"
|
|
||||||
w={{ base: '100%', xl: 'calc( 100% - 290px )' }}
|
|
||||||
maxWidth={{ base: '100%', xl: 'calc( 100% - 290px )' }}
|
|
||||||
transition="all 0.33s cubic-bezier(0.685, 0.0473, 0.346, 1)"
|
|
||||||
transitionDuration=".2s, .2s, .35s"
|
|
||||||
transitionProperty="top, bottom, width"
|
|
||||||
transitionTimingFunction="linear, linear, ease"
|
|
||||||
>
|
|
||||||
<Navbar
|
|
||||||
onOpen={onOpen}
|
|
||||||
logoText={'Horizon UI Boilerplate'}
|
|
||||||
userDetails={props.userDetails}
|
|
||||||
brandText={getActiveRoute(routes, pathname)}
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
mx="auto"
|
|
||||||
p={{ base: '20px', md: '30px' }}
|
|
||||||
pe="20px"
|
|
||||||
minH="100vh"
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</Box>
|
|
||||||
<Footer />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</ApiKeyContext.Provider>
|
|
||||||
</SubscriptionContext.Provider>
|
|
||||||
</ProductsContext.Provider>
|
|
||||||
</PlanContext.Provider>
|
|
||||||
</OpenContext.Provider>
|
|
||||||
</UserDetailsContext.Provider>
|
|
||||||
</UserContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DashboardLayout;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function InnerContent(props) {
|
|
||||||
const { children, ...rest } = props;
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
maxW={{ xl: '1170px' }}
|
|
||||||
align="center"
|
|
||||||
mx="auto"
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import { ButtonProps } from '@chakra-ui/react';
|
|
||||||
import NextLink, { LinkProps as NextLinkProps } from 'next/link';
|
|
||||||
|
|
||||||
import { Button } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
type LinkProps = ButtonProps & NextLinkProps;
|
|
||||||
|
|
||||||
function Link({ href, children, ...props }: LinkProps) {
|
|
||||||
return (
|
|
||||||
<NextLink href={href} passHref legacyBehavior>
|
|
||||||
<Button as="a" variant="a" {...props}>
|
|
||||||
{children}
|
|
||||||
</Button>
|
|
||||||
</NextLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Link;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import Link, { LinkProps } from 'next/link';
|
|
||||||
import { Button, ButtonProps } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
type ChakraAndNextProps = ButtonProps & LinkProps;
|
|
||||||
|
|
||||||
export default function LinkButton({
|
|
||||||
href,
|
|
||||||
children,
|
|
||||||
prefetch = true,
|
|
||||||
...props
|
|
||||||
}: ChakraAndNextProps) {
|
|
||||||
return (
|
|
||||||
<Link href={href} passHref prefetch={prefetch}>
|
|
||||||
<Button as="a" variant="a" {...props}>
|
|
||||||
{children}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import NextLink, { LinkProps as NextLinkProps } from 'next/link';
|
|
||||||
import {
|
|
||||||
CSSProperties,
|
|
||||||
ComponentProps,
|
|
||||||
PropsWithChildren,
|
|
||||||
useMemo,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
export type NavLinkProps = NextLinkProps &
|
|
||||||
PropsWithChildren & {
|
|
||||||
styles?: CSSProperties;
|
|
||||||
borderRadius?: ComponentProps<typeof NextLink>['style'];
|
|
||||||
};
|
|
||||||
|
|
||||||
function NavLink({ children, styles, ...props }: NavLinkProps) {
|
|
||||||
const memoizedStyles = useMemo(
|
|
||||||
() => ({
|
|
||||||
...styles,
|
|
||||||
}),
|
|
||||||
[styles],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NextLink style={memoizedStyles} {...props}>
|
|
||||||
{children}
|
|
||||||
</NextLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NavLink;
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// chakra imports
|
|
||||||
import { Icon, Flex, Text, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
import { MdUpgrade } from 'react-icons/md';
|
|
||||||
|
|
||||||
export function ItemContent(props: { info: string }) {
|
|
||||||
const textColor = useColorModeValue('navy.700', 'white');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex
|
|
||||||
justify="center"
|
|
||||||
align="center"
|
|
||||||
borderRadius="16px"
|
|
||||||
minH={{ base: '60px', md: '70px' }}
|
|
||||||
h={{ base: '60px', md: '70px' }}
|
|
||||||
minW={{ base: '60px', md: '70px' }}
|
|
||||||
w={{ base: '60px', md: '70px' }}
|
|
||||||
me="14px"
|
|
||||||
bgGradient="linear(to-b, brand.400, brand.600)"
|
|
||||||
>
|
|
||||||
<Icon as={MdUpgrade} color="white" w={8} h={14} />
|
|
||||||
</Flex>
|
|
||||||
<Flex flexDirection="column">
|
|
||||||
<Text
|
|
||||||
mb="5px"
|
|
||||||
fontWeight="bold"
|
|
||||||
color={textColor}
|
|
||||||
fontSize={{ base: 'md', md: 'md' }}
|
|
||||||
>
|
|
||||||
New Update: {props.info}
|
|
||||||
</Text>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: 'sm', md: 'sm' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
A new update for your downloaded item is available!
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
// Chakra imports
|
|
||||||
import {
|
|
||||||
Icon,
|
|
||||||
Flex,
|
|
||||||
Text,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuList,
|
|
||||||
useDisclosure,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
// Assets
|
|
||||||
import {
|
|
||||||
MdOutlineMoreHoriz,
|
|
||||||
MdOutlinePerson,
|
|
||||||
MdOutlineCardTravel,
|
|
||||||
MdOutlineLightbulb,
|
|
||||||
MdOutlineSettings
|
|
||||||
} from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function Banner(props: { [x: string]: any }) {
|
|
||||||
const { ...rest } = props;
|
|
||||||
|
|
||||||
const textColor = useColorModeValue('secondaryGray.500', 'white');
|
|
||||||
const textHover = useColorModeValue(
|
|
||||||
{ color: '#120F43', bg: 'unset' },
|
|
||||||
{ color: 'secondaryGray.500', bg: 'unset' }
|
|
||||||
);
|
|
||||||
const iconColor = useColorModeValue('brand.500', 'white');
|
|
||||||
const bgList = useColorModeValue('white', 'whiteAlpha.100');
|
|
||||||
const bgShadow = useColorModeValue('14px 17px 40px 4px rgba(112, 144, 176, 0.08)', 'unset');
|
|
||||||
const bgButton = useColorModeValue('secondaryGray.300', 'whiteAlpha.100');
|
|
||||||
const bgHover = useColorModeValue({ bg: 'secondaryGray.400' }, { bg: 'whiteAlpha.50' });
|
|
||||||
const bgFocus = useColorModeValue({ bg: 'secondaryGray.300' }, { bg: 'whiteAlpha.100' });
|
|
||||||
|
|
||||||
// Ellipsis modals
|
|
||||||
const { isOpen: isOpen1, onOpen: onOpen1, onClose: onClose1 } = useDisclosure();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu isOpen={isOpen1} onClose={onClose1}>
|
|
||||||
<MenuButton
|
|
||||||
alignItems='center'
|
|
||||||
justifyContent='center'
|
|
||||||
bg={bgButton}
|
|
||||||
_hover={bgHover}
|
|
||||||
_focus={bgFocus}
|
|
||||||
_active={bgFocus}
|
|
||||||
w='37px'
|
|
||||||
h='37px'
|
|
||||||
lineHeight='100%'
|
|
||||||
onClick={onOpen1}
|
|
||||||
borderRadius='10px'
|
|
||||||
{...rest}>
|
|
||||||
<Icon as={MdOutlineMoreHoriz} color={iconColor} w='24px' h='24px' mt='4px' />
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList
|
|
||||||
w='150px'
|
|
||||||
minW='unset'
|
|
||||||
maxW='150px !important'
|
|
||||||
border='transparent'
|
|
||||||
backdropFilter='blur(63px)'
|
|
||||||
bg={bgList}
|
|
||||||
boxShadow={bgShadow}
|
|
||||||
borderRadius='20px'
|
|
||||||
p='15px'>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlinePerson} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 1
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineCardTravel} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 2
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineLightbulb} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 3
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineSettings} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 4
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
// Chakra imports
|
|
||||||
import {
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuList,
|
|
||||||
useDisclosure,
|
|
||||||
useColorModeValue,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Text
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
// Assets
|
|
||||||
import { MdOutlinePerson, MdOutlineCardTravel, MdOutlineLightbulb, MdOutlineSettings } from 'react-icons/md';
|
|
||||||
export default function Banner(props: { icon: JSX.Element | string; [x: string]: any }) {
|
|
||||||
const { icon, ...rest } = props;
|
|
||||||
|
|
||||||
// Ellipsis modals
|
|
||||||
const { isOpen: isOpen1, onOpen: onOpen1, onClose: onClose1 } = useDisclosure();
|
|
||||||
|
|
||||||
// Chakra color mode
|
|
||||||
|
|
||||||
const textColor = useColorModeValue('secondaryGray.500', 'white');
|
|
||||||
const textHover = useColorModeValue(
|
|
||||||
{ color: '#120F43', bg: 'unset' },
|
|
||||||
{ color: 'secondaryGray.500', bg: 'unset' }
|
|
||||||
);
|
|
||||||
const bgList = useColorModeValue('white', 'whiteAlpha.100');
|
|
||||||
const bgShadow = useColorModeValue('14px 17px 40px 4px rgba(112, 144, 176, 0.08)', 'unset');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu isOpen={isOpen1} onClose={onClose1}>
|
|
||||||
<MenuButton {...rest} onClick={onOpen1}>
|
|
||||||
{icon}
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList
|
|
||||||
w='150px'
|
|
||||||
minW='unset'
|
|
||||||
maxW='150px !important'
|
|
||||||
border='transparent'
|
|
||||||
backdropFilter='blur(63px)'
|
|
||||||
bg={bgList}
|
|
||||||
boxShadow={bgShadow}
|
|
||||||
borderRadius='20px'
|
|
||||||
p='15px'>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlinePerson} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 1
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineCardTravel} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 2
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
mb='10px'>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineLightbulb} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 3
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
transition='0.2s linear'
|
|
||||||
color={textColor}
|
|
||||||
_hover={textHover}
|
|
||||||
p='0px'
|
|
||||||
borderRadius='8px'
|
|
||||||
_active={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
bg: 'transparent'
|
|
||||||
}}>
|
|
||||||
<Flex align='center'>
|
|
||||||
<Icon as={MdOutlineSettings} h='16px' w='16px' me='8px' />
|
|
||||||
<Text fontSize='sm' fontWeight='400'>
|
|
||||||
Panel 4
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/* eslint-disable */
|
|
||||||
import AdminNavbarLinks from './NavbarLinksAdmin';
|
|
||||||
import { isWindowAvailable } from '@/utils/navigation';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLink,
|
|
||||||
Flex,
|
|
||||||
Link,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
|
|
||||||
export default function AdminNavbar(props: {
|
|
||||||
logoText: string;
|
|
||||||
brandText: string;
|
|
||||||
userDetails: { [x: string]: any } | null;
|
|
||||||
onOpen: (...args: any[]) => any;
|
|
||||||
[x: string]: any;
|
|
||||||
}) {
|
|
||||||
const [scrolled, setScrolled] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
isWindowAvailable() && window.addEventListener('scroll', changeNavbar);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isWindowAvailable() && window.removeEventListener('scroll', changeNavbar);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const { brandText, userDetails } = props;
|
|
||||||
|
|
||||||
// Here are all the props that may change depending on navbar's type or state.(secondary, variant, scrolled)
|
|
||||||
let mainText = useColorModeValue('#120F43', 'white');
|
|
||||||
let secondaryText = useColorModeValue('gray.700', 'white');
|
|
||||||
let navbarPosition = 'fixed' as const;
|
|
||||||
let navbarFilter = 'none';
|
|
||||||
let navbarBackdrop = 'blur(20px)';
|
|
||||||
let navbarShadow = 'none';
|
|
||||||
let navbarBg = useColorModeValue(
|
|
||||||
'rgba(244, 247, 254, 0.2)',
|
|
||||||
'rgba(11,20,55,0.5)'
|
|
||||||
);
|
|
||||||
let navbarBorder = 'transparent';
|
|
||||||
let secondaryMargin = '0px';
|
|
||||||
let gap = '0px';
|
|
||||||
const changeNavbar = () => {
|
|
||||||
if (isWindowAvailable() && window.scrollY > 1) {
|
|
||||||
setScrolled(true);
|
|
||||||
} else {
|
|
||||||
setScrolled(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
zIndex="100"
|
|
||||||
position={navbarPosition}
|
|
||||||
boxShadow={navbarShadow}
|
|
||||||
bg={navbarBg}
|
|
||||||
borderColor={navbarBorder}
|
|
||||||
filter={navbarFilter}
|
|
||||||
backdropFilter={navbarBackdrop}
|
|
||||||
backgroundPosition="center"
|
|
||||||
backgroundSize="cover"
|
|
||||||
borderRadius="16px"
|
|
||||||
borderWidth="1.5px"
|
|
||||||
borderStyle="solid"
|
|
||||||
transitionDelay="0s, 0s, 0s, 0s"
|
|
||||||
transitionDuration=" 0.25s, 0.25s, 0.25s, 0s"
|
|
||||||
transition-property="box-shadow, background-color, filter, border"
|
|
||||||
transitionTimingFunction="linear, linear, linear, linear"
|
|
||||||
alignItems={{ xl: 'center' }}
|
|
||||||
display={'flex'}
|
|
||||||
minH="75px"
|
|
||||||
justifyContent={{ xl: 'center' }}
|
|
||||||
lineHeight="25.6px"
|
|
||||||
mx="auto"
|
|
||||||
mt={secondaryMargin}
|
|
||||||
pb="8px"
|
|
||||||
right={{ base: '12px', md: '30px', lg: '30px', xl: '30px' }}
|
|
||||||
ps={{
|
|
||||||
base: '8px',
|
|
||||||
md: '12px'
|
|
||||||
}}
|
|
||||||
pt="8px"
|
|
||||||
top={{ base: '12px', md: '16px', xl: '18px' }}
|
|
||||||
w={{
|
|
||||||
base: 'calc(100vw - 8%)',
|
|
||||||
md: 'calc(100vw - 8%)',
|
|
||||||
lg: 'calc(100vw - 6%)',
|
|
||||||
xl: 'calc(100vw - 350px)',
|
|
||||||
'2xl': 'calc(100vw - 365px)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
flexDirection={{
|
|
||||||
base: 'row',
|
|
||||||
md: 'row'
|
|
||||||
}}
|
|
||||||
alignItems={{ xl: 'center' }}
|
|
||||||
mb={gap}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Breadcrumb>
|
|
||||||
<BreadcrumbItem
|
|
||||||
color={secondaryText}
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
mb={{ base: '0px', md: '5px' }}
|
|
||||||
>
|
|
||||||
<BreadcrumbLink href="#" color={secondaryText}>
|
|
||||||
Pages
|
|
||||||
</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
|
|
||||||
<BreadcrumbItem
|
|
||||||
color={secondaryText}
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
>
|
|
||||||
<BreadcrumbLink href="#" color={secondaryText}>
|
|
||||||
{brandText}
|
|
||||||
</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</Breadcrumb>
|
|
||||||
{/* Here we create navbar brand, based on route name */}
|
|
||||||
<Link
|
|
||||||
color={mainText}
|
|
||||||
href="#"
|
|
||||||
bg="inherit"
|
|
||||||
borderRadius="inherit"
|
|
||||||
fontWeight="bold"
|
|
||||||
fontSize={{ base: 'lg', md: '34px' }}
|
|
||||||
p="0px"
|
|
||||||
_hover={{ color: { mainText } }}
|
|
||||||
_active={{
|
|
||||||
bg: 'inherit',
|
|
||||||
transform: 'none',
|
|
||||||
borderColor: 'transparent'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
boxShadow: 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{brandText}
|
|
||||||
</Link>
|
|
||||||
</Box>
|
|
||||||
<Box ms="auto" w={{ sm: '154px', md: 'unset' }}>
|
|
||||||
<AdminNavbarLinks />
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,403 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import logo from '/public/logo-horizon-boilerplate.png';
|
|
||||||
import {
|
|
||||||
Image,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Link,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuList,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { IoIosStar, IoMdTrophy } from 'react-icons/io';
|
|
||||||
import { IoMenuOutline } from 'react-icons/io5';
|
|
||||||
import { MdChevronRight, MdPrivacyTip } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function AdminNavbar(props) {
|
|
||||||
const [scrolled, setScrolled] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('scroll', changeNavbar);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('scroll', changeNavbar);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const { secondary, message } = props;
|
|
||||||
|
|
||||||
// Here are all the props that may change depending on navbar's type or state.(secondary, variant, scrolled)
|
|
||||||
let textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
let borderColor = useColorModeValue('gray.300', 'white');
|
|
||||||
let navbarPosition = 'fixed';
|
|
||||||
let navbarFilter = 'none';
|
|
||||||
let navbarShadow = '45px 76px 113px 7px rgba(112, 144, 176, 0.08)';
|
|
||||||
let navbarBorder = 'transparent';
|
|
||||||
let paddingX = '15px';
|
|
||||||
let gap = '0px';
|
|
||||||
let navbarTop = '0px';
|
|
||||||
let menuBg = useColorModeValue('white', 'navy.800');
|
|
||||||
const changeNavbar = () => {
|
|
||||||
if (window.scrollY > 1) {
|
|
||||||
setScrolled(true);
|
|
||||||
} else {
|
|
||||||
setScrolled(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
position={navbarPosition}
|
|
||||||
boxShadow={navbarShadow}
|
|
||||||
bg="white"
|
|
||||||
direction="column"
|
|
||||||
borderColor={navbarBorder}
|
|
||||||
filter={navbarFilter}
|
|
||||||
backgroundPosition="center"
|
|
||||||
backgroundSize="cover"
|
|
||||||
zIndex="200"
|
|
||||||
borderStyle="solid"
|
|
||||||
alignItems={{ xl: 'center' }}
|
|
||||||
display={secondary ? 'block' : 'flex'}
|
|
||||||
justifyContent={{ xl: 'center' }}
|
|
||||||
lineHeight="25.6px"
|
|
||||||
mx="auto"
|
|
||||||
left="50%"
|
|
||||||
transform="translate(-50%,0px)"
|
|
||||||
w="100%"
|
|
||||||
top={navbarTop}
|
|
||||||
>
|
|
||||||
{/* Misc */}
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
display={{ base: 'none', lg: 'flex' }}
|
|
||||||
bg="#F3F5FA"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w={{
|
|
||||||
base: 'calc(100vw - 4%)',
|
|
||||||
md: 'calc(100vw - 4%)',
|
|
||||||
lg: '100vw',
|
|
||||||
xl: 'calc(100vw - 250px)',
|
|
||||||
'2xl': '1200px',
|
|
||||||
}}
|
|
||||||
px={{
|
|
||||||
sm: paddingX,
|
|
||||||
md: '10px',
|
|
||||||
lg: '12px',
|
|
||||||
}}
|
|
||||||
py="4px"
|
|
||||||
gap="40px"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Flex direction="row" alignItems="center">
|
|
||||||
<Icon
|
|
||||||
me="6px"
|
|
||||||
w="12px"
|
|
||||||
h="12px"
|
|
||||||
color="brand.500"
|
|
||||||
as={MdPrivacyTip}
|
|
||||||
/>
|
|
||||||
<Text fontSize="xs" fontWeight="500" h="100%" color="gray.500">
|
|
||||||
Founded in EU. We respect your privacy.
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction="row" alignItems="center">
|
|
||||||
<Icon me="1px" w="12px" h="12px" color="brand.500" as={IoIosStar} />
|
|
||||||
<Icon me="1px" w="12px" h="12px" color="brand.500" as={IoIosStar} />
|
|
||||||
<Icon me="1px" w="12px" h="12px" color="brand.500" as={IoIosStar} />
|
|
||||||
<Icon me="1px" w="12px" h="12px" color="brand.500" as={IoIosStar} />
|
|
||||||
<Icon me="6px" w="12px" h="12px" color="brand.500" as={IoIosStar} />
|
|
||||||
<Text fontSize="xs" fontWeight="500" h="100%" color="gray.500">
|
|
||||||
Loved by 80,000+ users worldwide
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex direction="row" alignItems="center">
|
|
||||||
<Icon
|
|
||||||
me="6px"
|
|
||||||
w="12px"
|
|
||||||
h="12px"
|
|
||||||
color="brand.500"
|
|
||||||
as={IoMdTrophy}
|
|
||||||
/>
|
|
||||||
<Text fontSize="xs" fontWeight="500" h="100%" color="gray.500">
|
|
||||||
#1 NextJS boilerplate & admin template
|
|
||||||
|
|
||||||
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* Misc */}
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
w={{
|
|
||||||
base: 'calc(100vw - 4%)',
|
|
||||||
md: 'calc(100vw - 4%)',
|
|
||||||
lg: '100vw',
|
|
||||||
xl: 'calc(100vw - 250px)',
|
|
||||||
'2xl': '1200px',
|
|
||||||
}}
|
|
||||||
px={{
|
|
||||||
sm: paddingX,
|
|
||||||
md: '10px',
|
|
||||||
lg: '12px',
|
|
||||||
}}
|
|
||||||
py="20px"
|
|
||||||
ps={{
|
|
||||||
xl: '12px',
|
|
||||||
}}
|
|
||||||
flexDirection={{
|
|
||||||
sm: 'row',
|
|
||||||
md: 'row',
|
|
||||||
}}
|
|
||||||
alignItems="center"
|
|
||||||
justify="space-between"
|
|
||||||
mb={gap}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent={'center'}
|
|
||||||
href="/"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
w="36px"
|
|
||||||
src={logo.src}
|
|
||||||
/>
|
|
||||||
<Text ms="8px" me="10px" fontWeight="800" color="#120F43">
|
|
||||||
Horizon UI Boilerplate
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
<Flex>
|
|
||||||
<Link
|
|
||||||
display={{ base: 'none', lg: 'block' }}
|
|
||||||
href="/dashboard/signin"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Generator
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
display={{ base: 'none', lg: 'block' }}
|
|
||||||
href="/#features"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Features
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
display={{ base: 'none', lg: 'block' }}
|
|
||||||
href="/pricing"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Pricing
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
display={{ base: 'none', lg: 'block' }}
|
|
||||||
href="#faqs"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
FAQs
|
|
||||||
</Link>
|
|
||||||
<Menu>
|
|
||||||
<MenuButton
|
|
||||||
display={{ base: 'block', xl: 'none' }}
|
|
||||||
p="0px !important"
|
|
||||||
maxH="20px"
|
|
||||||
maxW="20px"
|
|
||||||
alignContent="end"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
display={{ base: 'block', lg: 'none' }}
|
|
||||||
as={IoMenuOutline}
|
|
||||||
color={textColor}
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
/>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList
|
|
||||||
p="0px"
|
|
||||||
mt="10px"
|
|
||||||
borderRadius="10px"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="#CBD5E0"
|
|
||||||
bg={menuBg}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" p="10px">
|
|
||||||
<MenuItem
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
borderRadius="8px"
|
|
||||||
px="14px"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="500"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Generator
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
borderRadius="8px"
|
|
||||||
px="14px"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="/#features"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="500"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Features
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
color="red.400"
|
|
||||||
borderRadius="8px"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="/pricing"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="500"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Pricing
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
color="red.400"
|
|
||||||
borderRadius="8px"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="#faqs"
|
|
||||||
color="gray.600"
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="500"
|
|
||||||
me="30px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
FAQs
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
color="red.400"
|
|
||||||
borderRadius="8px"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin"
|
|
||||||
color={textColor}
|
|
||||||
fontSize="md"
|
|
||||||
fontWeight="600"
|
|
||||||
me="18px"
|
|
||||||
my="auto"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Login
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
<Button
|
|
||||||
variant="transparent"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
color={textColor}
|
|
||||||
fontSize="md"
|
|
||||||
borderRadius="45px"
|
|
||||||
bg="transparent"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
<Link href="/dashboard/signin">
|
|
||||||
Get started for Free
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
mt="3px"
|
|
||||||
ms="5px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Flex>
|
|
||||||
<Flex display={{ base: 'none', lg: 'flex' }}>
|
|
||||||
<Link
|
|
||||||
href="/dashboard/signin"
|
|
||||||
color={textColor}
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
me="18px"
|
|
||||||
letterSpacing="0px"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
Login
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href="/dashboard/signin">
|
|
||||||
<Button
|
|
||||||
variant="transparent"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
color={textColor}
|
|
||||||
fontSize="sm"
|
|
||||||
borderRadius="45px"
|
|
||||||
px="18px"
|
|
||||||
py="12px"
|
|
||||||
bg="transparent"
|
|
||||||
my="auto"
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
Get started for Free
|
|
||||||
<Icon as={MdChevronRight} mt="3px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{secondary ? <Text color="white">{message}</Text> : null}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useSupabase } from '@/app/supabase-provider';
|
|
||||||
import { routes } from '@/components/routes';
|
|
||||||
import { SidebarResponsive } from '@/components/sidebar/Sidebar';
|
|
||||||
import { OpenContext, UserContext } from '@/contexts/layout';
|
|
||||||
import { handleRequest } from '@/utils/auth-helpers/client';
|
|
||||||
import { SignOut } from '@/utils/auth-helpers/server';
|
|
||||||
import { getRedirectMethod } from '@/utils/auth-helpers/settings';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Link,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
|
||||||
import { useContext } from 'react';
|
|
||||||
import { MdOutlineLogout, MdHelpOutline } from 'react-icons/md';
|
|
||||||
|
|
||||||
export default function HeaderLinks(props?: { [x: string]: any }) {
|
|
||||||
const { open, setOpen } = useContext(OpenContext);
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
const router = getRedirectMethod() === 'client' ? useRouter() : null;
|
|
||||||
// Chakra Color Mode
|
|
||||||
const navbarIcon = useColorModeValue('gray.500', 'white');
|
|
||||||
let menuBg = useColorModeValue('white', 'navy.800');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const shadow = useColorModeValue(
|
|
||||||
'14px 17px 40px 4px rgba(112, 144, 176, 0.18)',
|
|
||||||
'0px 41px 75px #081132'
|
|
||||||
);
|
|
||||||
const buttonBg = useColorModeValue('transparent', 'navy.800');
|
|
||||||
const hoverButton = useColorModeValue(
|
|
||||||
{ bg: 'gray.100' },
|
|
||||||
{ bg: 'whiteAlpha.100' }
|
|
||||||
);
|
|
||||||
const activeButton = useColorModeValue(
|
|
||||||
{ bg: 'gray.200' },
|
|
||||||
{ bg: 'whiteAlpha.200' }
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
zIndex="100"
|
|
||||||
w={{ sm: '100%', md: 'auto' }}
|
|
||||||
alignItems="center"
|
|
||||||
flexDirection="row"
|
|
||||||
bg={menuBg}
|
|
||||||
flexWrap={'unset'}
|
|
||||||
p="10px"
|
|
||||||
pl="16px"
|
|
||||||
borderRadius="30px"
|
|
||||||
boxShadow={shadow}
|
|
||||||
>
|
|
||||||
<SidebarResponsive routes={routes} />
|
|
||||||
|
|
||||||
<Menu>
|
|
||||||
<MenuButton p="0px" me="10px">
|
|
||||||
<Icon
|
|
||||||
mt="-5px"
|
|
||||||
as={MdHelpOutline}
|
|
||||||
color={navbarIcon}
|
|
||||||
w="18px"
|
|
||||||
h="18px"
|
|
||||||
/>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList
|
|
||||||
boxShadow={shadow}
|
|
||||||
p="20px"
|
|
||||||
borderRadius="20px"
|
|
||||||
bg={menuBg}
|
|
||||||
border="none"
|
|
||||||
mt="22px"
|
|
||||||
minW={{ base: 'unset' }}
|
|
||||||
maxW={{ base: '360px', md: 'unset' }}
|
|
||||||
>
|
|
||||||
{/* <Flex bgImage={navImage} borderRadius="16px" mb="28px" alt="" /> */}
|
|
||||||
<Flex flexDirection="column" rowGap="10px">
|
|
||||||
<Link w="100%" href="/pricing">
|
|
||||||
<Button
|
|
||||||
bg={buttonBg}
|
|
||||||
border="1px solid"
|
|
||||||
color={textColor}
|
|
||||||
borderColor={useColorModeValue('gray.200', 'whiteAlpha.100')}
|
|
||||||
fontSize="sm"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
minW="44px"
|
|
||||||
h="44px"
|
|
||||||
_placeholder={{ color: 'gray.500' }}
|
|
||||||
_hover={hoverButton}
|
|
||||||
_active={activeButton}
|
|
||||||
_focus={activeButton}
|
|
||||||
>
|
|
||||||
Pricing Plans
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link w="100%" href="mailto:hello@horizon-ui.com">
|
|
||||||
<Button
|
|
||||||
bg={buttonBg}
|
|
||||||
border="1px solid"
|
|
||||||
color={textColor}
|
|
||||||
borderColor={useColorModeValue('gray.200', 'whiteAlpha.100')}
|
|
||||||
fontSize="sm"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
minW="44px"
|
|
||||||
h="44px"
|
|
||||||
_placeholder={{ color: 'gray.500' }}
|
|
||||||
_hover={hoverButton}
|
|
||||||
_active={activeButton}
|
|
||||||
_focus={activeButton}
|
|
||||||
>
|
|
||||||
Help & Support
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link w="100%" href="/#faqs">
|
|
||||||
<Button
|
|
||||||
bg={buttonBg}
|
|
||||||
border="1px solid"
|
|
||||||
color={textColor}
|
|
||||||
borderColor={useColorModeValue('gray.200', 'whiteAlpha.100')}
|
|
||||||
fontSize="sm"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
minW="44px"
|
|
||||||
h="44px"
|
|
||||||
_placeholder={{ color: 'gray.500' }}
|
|
||||||
_hover={hoverButton}
|
|
||||||
_active={activeButton}
|
|
||||||
_focus={activeButton}
|
|
||||||
>
|
|
||||||
FAQs & More
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
<form onSubmit={(e) => handleRequest(e, SignOut, router)}>
|
|
||||||
<input type="hidden" name="pathName" value={usePathname()} />
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
bg="transparent"
|
|
||||||
p="0px"
|
|
||||||
pt="3px"
|
|
||||||
_hover={{ background: 'transparent' }}
|
|
||||||
_active={{ background: 'transparent' }}
|
|
||||||
_focus={{ background: 'transparent' }}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
cursor="pointer"
|
|
||||||
_hover={{ bg: 'none' }}
|
|
||||||
_focus={{ bg: 'none' }}
|
|
||||||
color="red.400"
|
|
||||||
borderRadius="8px"
|
|
||||||
me="10px"
|
|
||||||
>
|
|
||||||
{/* <Text> {`${props.userDetails?.avatar_url}`}</Text> */}
|
|
||||||
<Icon
|
|
||||||
mt="-5px"
|
|
||||||
as={MdOutlineLogout}
|
|
||||||
color={navbarIcon}
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Link w="100%" href="/dashboard/settings">
|
|
||||||
<Avatar h="40px" w="40px" src={user.user_metadata.avatar_url} />
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,978 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
// @ts-nocheck
|
|
||||||
import { FooterWebsite } from '../footer/FooterWebsite';
|
|
||||||
import Card from '@/components/card/Card';
|
|
||||||
import Faq from '@/components/landing/faq';
|
|
||||||
import InnerContent from '@/components/layout/innerContent';
|
|
||||||
import NavbarFixed from '@/components/navbar/NavbarFixed';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { getStripe } from '@/utils/stripe/client';
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Link,
|
|
||||||
SimpleGrid,
|
|
||||||
Text,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
FaCcVisa,
|
|
||||||
FaCcMastercard,
|
|
||||||
FaCcPaypal,
|
|
||||||
FaCcAmex,
|
|
||||||
FaCcApplePay
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import {
|
|
||||||
MdChevronRight,
|
|
||||||
MdCheckCircle,
|
|
||||||
MdAttachMoney,
|
|
||||||
MdLock,
|
|
||||||
MdOutlineDoDisturbOn
|
|
||||||
} from 'react-icons/md';
|
|
||||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
|
||||||
import { getErrorRedirect } from '@/utils/helpers';
|
|
||||||
|
|
||||||
type Subscription = Database['public']['Tables']['subscriptions']['Row'];
|
|
||||||
type Product = Database['public']['Tables']['products']['Row'];
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
user: User | null | undefined;
|
|
||||||
products: ProductWithPrices[];
|
|
||||||
subscription: SubscriptionWithProduct | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Pricing({ user, products, subscription }: Props) {
|
|
||||||
const router = useRouter();
|
|
||||||
const currentPath = usePathname();
|
|
||||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
|
||||||
const handleCheckout = async (price: Price) => {
|
|
||||||
setPriceIdLoading(price.id);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push('/signin/signup');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
|
||||||
price,
|
|
||||||
currentPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errorRedirect) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(errorRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(
|
|
||||||
getErrorRedirect(
|
|
||||||
currentPath,
|
|
||||||
'An unknown error occurred.',
|
|
||||||
'Please try again later or contact a system administrator.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stripe = await getStripe();
|
|
||||||
stripe?.redirectToCheckout({ sessionId });
|
|
||||||
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
};
|
|
||||||
const [version, setVersion] = useState('monthly');
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const brandColor = useColorModeValue('brand.500', 'white');
|
|
||||||
const brandColorPrice = useColorModeValue('brand.500', 'white');
|
|
||||||
const card = useColorModeValue('#fff', 'rgba(255, 255, 255, 0.05)');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
id="pricing"
|
|
||||||
bgSize="cover"
|
|
||||||
w="100%"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
pt={{ base: '120px', md: '180px' }}
|
|
||||||
overflow="hidden"
|
|
||||||
position="relative"
|
|
||||||
>
|
|
||||||
<NavbarFixed />
|
|
||||||
<InnerContent
|
|
||||||
zIndex="1"
|
|
||||||
maxW={{ base: '100%', md: '100%', xl: '1170px' }}
|
|
||||||
pb={{ base: '60px', md: '100px' }}
|
|
||||||
>
|
|
||||||
{/* Title */}
|
|
||||||
<Flex
|
|
||||||
px={{ base: '20px', md: '0px' }}
|
|
||||||
w="100%"
|
|
||||||
mb="40px"
|
|
||||||
direction={{ base: 'column' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
textAlign="start"
|
|
||||||
px={{ base: '20px', md: '40px', xl: '0px' }}
|
|
||||||
mb={{ base: '0px', lg: '30px' }}
|
|
||||||
justify="center"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
as="h3"
|
|
||||||
textAlign={{ base: 'center', lg: 'center' }}
|
|
||||||
fontWeight="700"
|
|
||||||
letterSpacing="2px"
|
|
||||||
color={brandColorPrice}
|
|
||||||
fontSize={{ base: 'xs', md: 'md' }}
|
|
||||||
w="100%"
|
|
||||||
mb="10px"
|
|
||||||
>
|
|
||||||
PRICING PLANS
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
as="h2"
|
|
||||||
textAlign={{ base: 'center', lg: 'center' }}
|
|
||||||
color={textColor}
|
|
||||||
fontWeight="800"
|
|
||||||
fontSize={{ base: '28px', md: '40px', lg: '40px', xl: '48px' }}
|
|
||||||
lineHeight={{ base: '38px', md: '50px', lg: '50px', xl: '60px' }}
|
|
||||||
w={{ base: '100%', md: '90%', lg: '80%', xl: '74%' }}
|
|
||||||
>
|
|
||||||
Create outstanding Essays for less than a Netflix subscription per
|
|
||||||
month
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<SimpleGrid
|
|
||||||
px={{ base: '20px', md: '0px' }}
|
|
||||||
w="100%"
|
|
||||||
columns={{ base: 1, lg: 3 }}
|
|
||||||
gap="20px"
|
|
||||||
mb="20px"
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
borderRadius="16px"
|
|
||||||
bg={card}
|
|
||||||
maxW="377px"
|
|
||||||
mx="auto"
|
|
||||||
alignItems={{ base: 'start', lg: 'center' }}
|
|
||||||
p={{ base: '16px', md: '26px' }}
|
|
||||||
pt={{ base: '26px', md: '36px' }}
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
<Flex mb="20px" mx="auto">
|
|
||||||
<Text
|
|
||||||
fontSize="22px"
|
|
||||||
letterSpacing="0px"
|
|
||||||
textAlign="center"
|
|
||||||
fontWeight="700"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
Demo Plan
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
zIndex="1"
|
|
||||||
px={{ base: '18px', md: '34px' }}
|
|
||||||
pt="40px"
|
|
||||||
pb="40px"
|
|
||||||
borderRadius="16px"
|
|
||||||
bg="#F3F5FA"
|
|
||||||
mb="40px"
|
|
||||||
direction="column"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Flex mb="30px" direction="row" justify="center">
|
|
||||||
<Text
|
|
||||||
color="#120F43"
|
|
||||||
fontSize={{ base: '48px', md: '54px' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="extrabold"
|
|
||||||
>
|
|
||||||
Free
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Link href="/dashboard/signin">
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="checkout"
|
|
||||||
mx="auto"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
h="54px"
|
|
||||||
>
|
|
||||||
{subscription ? 'Manage' : 'Get started now'}
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex w={{ base: '100%', xl: '100%' }} direction="column">
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Standard Essays
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 4 Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 300 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="gray.400"
|
|
||||||
as={MdOutlineDoDisturbOn}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="gray.400"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="gray.400"
|
|
||||||
as={MdOutlineDoDisturbOn}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="gray.400"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="gray.400"
|
|
||||||
as={MdOutlineDoDisturbOn}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="gray.400"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="gray.400"
|
|
||||||
as={MdOutlineDoDisturbOn}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="gray.400"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Premium features
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="gray.400"
|
|
||||||
as={MdOutlineDoDisturbOn}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="gray.400"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Priority Support
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
{products.map((product) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price) => price.id === 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
);
|
|
||||||
if (product.id === 'prod_PtTCPDFZbburMa') {
|
|
||||||
if (!price) return null;
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
key={product.id}
|
|
||||||
borderRadius="16px"
|
|
||||||
bg="#120F43"
|
|
||||||
maxW="377px"
|
|
||||||
mx="auto"
|
|
||||||
alignItems={{ base: 'start', lg: 'center' }}
|
|
||||||
p={{ base: '16px', md: '26px' }}
|
|
||||||
pt={{ base: '26px', md: '36px' }}
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
{/* @ts-ignore-nextline */}
|
|
||||||
<Flex mb="20px" mx="auto">
|
|
||||||
<Text
|
|
||||||
fontSize="22px"
|
|
||||||
letterSpacing="0px"
|
|
||||||
textAlign="center"
|
|
||||||
fontWeight="700"
|
|
||||||
color="white"
|
|
||||||
>
|
|
||||||
{product.name?.toString()}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
zIndex="1"
|
|
||||||
px={{ base: '18px', md: '34px' }}
|
|
||||||
pt="40px"
|
|
||||||
pb="40px"
|
|
||||||
borderRadius="16px"
|
|
||||||
bg="#120F43"
|
|
||||||
boxShadow="0px 8px 25px -4px rgba(255, 255, 255, 0.30) inset"
|
|
||||||
mb="40px"
|
|
||||||
direction="column"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Flex mb="30px" direction="row" justify="center">
|
|
||||||
<Text
|
|
||||||
color="white"
|
|
||||||
fontSize={{ base: '48px', md: '54px' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="extrabold"
|
|
||||||
>
|
|
||||||
$
|
|
||||||
{price.unit_amount !== null
|
|
||||||
? price.unit_amount / 100
|
|
||||||
: price.unit_amount}
|
|
||||||
</Text>
|
|
||||||
<Flex direction="column">
|
|
||||||
<Text
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="700"
|
|
||||||
fontSize="sm"
|
|
||||||
mt="auto"
|
|
||||||
>
|
|
||||||
{version !== 'yearly' ? '/month' : '/month'}
|
|
||||||
</Text>
|
|
||||||
{/* <Text
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize="sm"
|
|
||||||
textDecoration="line-through"
|
|
||||||
>
|
|
||||||
{version !== 'yearly' ? 'reg. $24' : 'reg. $24'}
|
|
||||||
</Text> */}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="checkoutDark"
|
|
||||||
mx="auto"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
h="54px"
|
|
||||||
onClick={() => handleCheckout(price)}
|
|
||||||
>
|
|
||||||
{subscription ? 'Manage' : 'Get started now'}
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* Features */}
|
|
||||||
<Flex w={{ base: '100%', xl: '100%' }} direction="column">
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Unlimited Premium Essays / month
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Access to 12+ Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 1500 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Exceptional Essays in seconds
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Easy-to-use Essay Generator
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color="white"
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Priority Support
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{products.map((product) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price) => price.id === 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
|
||||||
);
|
|
||||||
if (product.id === 'prod_PtTJ6R3RnzmIPX') {
|
|
||||||
if (!price) return null;
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
key={product.id}
|
|
||||||
borderRadius="16px"
|
|
||||||
bg={card}
|
|
||||||
maxW="377px"
|
|
||||||
mx="auto"
|
|
||||||
alignItems={{ base: 'start', lg: 'center' }}
|
|
||||||
p={{ base: '16px', md: '26px' }}
|
|
||||||
pt={{ base: '26px', md: '36px' }}
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
<Flex mb="20px" mx="auto">
|
|
||||||
<Text
|
|
||||||
fontSize="22px"
|
|
||||||
letterSpacing="0px"
|
|
||||||
textAlign="center"
|
|
||||||
fontWeight="700"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
{product.name?.toString()}
|
|
||||||
</Text>
|
|
||||||
<Badge
|
|
||||||
display="flex"
|
|
||||||
bg="#F2EFFF"
|
|
||||||
alignItems="center"
|
|
||||||
borderRadius="99px"
|
|
||||||
px="6px"
|
|
||||||
pb="8px"
|
|
||||||
ms="10px"
|
|
||||||
textColor={brandColor}
|
|
||||||
>
|
|
||||||
Save 35%
|
|
||||||
</Badge>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
zIndex="1"
|
|
||||||
px={{ base: '18px', md: '34px' }}
|
|
||||||
pt="40px"
|
|
||||||
pb="40px"
|
|
||||||
borderRadius="16px"
|
|
||||||
bg="#F3F5FA"
|
|
||||||
mb="40px"
|
|
||||||
direction="column"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Flex mb="30px" direction="row" justify="center">
|
|
||||||
<Text
|
|
||||||
color={'transparent'}
|
|
||||||
fontSize={{ base: '48px', md: '54px' }}
|
|
||||||
lineHeight="100%"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="extrabold"
|
|
||||||
bgGradient="linear-gradient(91deg, #3D1DFF 0%, #6147FF 22.40%, #D451FF 46.35%, #EC458D 75.00%, #FFCA8B 100%)"
|
|
||||||
bgClip="text"
|
|
||||||
>
|
|
||||||
$
|
|
||||||
{price.unit_amount !== null
|
|
||||||
? price.unit_amount / 100
|
|
||||||
: price.unit_amount}
|
|
||||||
</Text>
|
|
||||||
<Flex direction="column" ms="8px">
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="700"
|
|
||||||
fontSize="sm"
|
|
||||||
mt="auto"
|
|
||||||
>
|
|
||||||
{version !== 'yearly' ? '/year' : '/year'}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="500"
|
|
||||||
textDecoration="line-through"
|
|
||||||
fontSize="sm"
|
|
||||||
mt="2px"
|
|
||||||
>
|
|
||||||
{version !== 'yearly' ? 'reg.$108' : 'reg.$108'}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="checkout"
|
|
||||||
mx="auto"
|
|
||||||
borderRadius="45px"
|
|
||||||
w="100%"
|
|
||||||
h="54px"
|
|
||||||
onClick={() => handleCheckout(price)}
|
|
||||||
>
|
|
||||||
{subscription ? 'Manage' : 'Get started now'}
|
|
||||||
<Icon as={MdChevronRight} mt="2px" h="16px" w="16px" />
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* Features */}
|
|
||||||
<Flex w={{ base: '100%', xl: '100%' }} direction="column">
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Unlimited Premium Essays / year
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Access to 12+ Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 1500 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Exceptional Essays in seconds
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Easy-to-use Essay Generator
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={brandColor}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize={{ base: 'sm', lg: 'sm', xl: 'md' }}
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Priority Support
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</SimpleGrid>
|
|
||||||
<Flex
|
|
||||||
px={{ base: '20px', md: '0px' }}
|
|
||||||
direction="column"
|
|
||||||
alignItems="center"
|
|
||||||
mt="30px"
|
|
||||||
>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="4px"
|
|
||||||
w="24px"
|
|
||||||
h="24px"
|
|
||||||
color={textColor}
|
|
||||||
as={MdAttachMoney}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
7-Days money back
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon me="4px" w="24px" h="24px" color={textColor} as={MdLock} />
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="500"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Secured AES-256 Encrypted payments powered by Stripe:
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon me="10px" w="30px" h="30px" color={textColor} as={FaCcVisa} />
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="30px"
|
|
||||||
h="30px"
|
|
||||||
color={textColor}
|
|
||||||
as={FaCcMastercard}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="30px"
|
|
||||||
h="30px"
|
|
||||||
color={textColor}
|
|
||||||
as={FaCcPaypal}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="30px"
|
|
||||||
h="30px"
|
|
||||||
color={textColor}
|
|
||||||
as={FaCcApplePay}
|
|
||||||
/>
|
|
||||||
<Icon w="30px" h="30px" color={textColor} as={FaCcAmex} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</InnerContent>
|
|
||||||
<Faq />
|
|
||||||
<FooterWebsite />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import theme from '@/theme/theme';
|
|
||||||
import { CacheProvider } from '@chakra-ui/next-js';
|
|
||||||
import { ChakraProvider } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export function Providers({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<CacheProvider>
|
|
||||||
<ChakraProvider theme={theme}>{children}</ChakraProvider>
|
|
||||||
</CacheProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import { IoIosStar } from 'react-icons/io';
|
|
||||||
import { Flex, Icon, Text, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export default function Ratings(props: {
|
|
||||||
dark?: boolean;
|
|
||||||
stats: string;
|
|
||||||
stars?: 1 | 2 | 3 | 4 | 5;
|
|
||||||
rating: string | number;
|
|
||||||
}) {
|
|
||||||
const { rating, stats, stars } = props;
|
|
||||||
const gray = useColorModeValue('whiteAlpha.300', 'gray.200');
|
|
||||||
const textColor = useColorModeValue('navy.900', 'white');
|
|
||||||
return (
|
|
||||||
<Flex direction="column" align="center" justify="center">
|
|
||||||
<Text color={gray} mt={{base:"12px", lg:"40px"}} mb="10px" justifySelf={'center'} fontWeight='500'
|
|
||||||
>
|
|
||||||
{stats}
|
|
||||||
</Text>
|
|
||||||
{stars === 1 ? (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color={gray} />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : stars === 2 ? (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color={gray} />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : stars === 3 ? (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color={gray} />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color={gray} />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : stars === 4 ? (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color={gray} />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : stars === 5 ? (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color="#F6AD55" />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Flex align="center" justify={{ base: 'center', md: 'flex-start' }}>
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" color="#F6AD55" />
|
|
||||||
<Icon as={IoIosStar} h="22px" w="22px" me="8px" color="#F6AD55" />
|
|
||||||
<Text fontWeight="bold" my="2px" fontSize="lg" color={textColor}>
|
|
||||||
{rating}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
// Auth Imports
|
|
||||||
import { IRoute } from '@/types/types';
|
|
||||||
import { Icon } from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
MdCreditCard,
|
|
||||||
MdHome,
|
|
||||||
MdOutlineManageAccounts,
|
|
||||||
MdWorkspacePremium,
|
|
||||||
} from 'react-icons/md';
|
|
||||||
|
|
||||||
export const routes: IRoute[] = [
|
|
||||||
{
|
|
||||||
name: 'Main Dashboard',
|
|
||||||
path: '/dashboard/main',
|
|
||||||
icon: (
|
|
||||||
<Icon as={MdHome} mt="-7px" width="20px" height="20px" color="inherit" />
|
|
||||||
),
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AI Pages',
|
|
||||||
path: '/ai-pages',
|
|
||||||
icon: (
|
|
||||||
<Icon
|
|
||||||
as={MdWorkspacePremium}
|
|
||||||
mt="-7px"
|
|
||||||
width="20px"
|
|
||||||
height="20px"
|
|
||||||
color="inherit"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
collapse: true,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'AI Generator',
|
|
||||||
path: '/dashboard/ai-generator',
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AI Assistant',
|
|
||||||
path: '/dashboard/ai-assistant',
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AI Chat',
|
|
||||||
path: '/dashboard/ai-chat',
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Users List',
|
|
||||||
path: '/dashboard/users-list',
|
|
||||||
icon: (
|
|
||||||
<Icon height="24px" viewBox="0 -960 960 960" width="24px" mt="-7px">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M640-400q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35ZM400-160v-76q0-21 10-40t28-30q45-27 95.5-40.5T640-360q56 0 106.5 13.5T842-306q18 11 28 30t10 40v76H400Zm86-80h308q-35-20-74-30t-80-10q-41 0-80 10t-74 30Zm154-240q17 0 28.5-11.5T680-520q0-17-11.5-28.5T640-560q-17 0-28.5 11.5T600-520q0 17 11.5 28.5T640-480Zm0-40Zm0 280ZM120-400v-80h320v80H120Zm0-320v-80h480v80H120Zm324 160H120v-80h360q-14 17-22.5 37T444-560Z"
|
|
||||||
></path>
|
|
||||||
</Icon>
|
|
||||||
),
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Profile Settings',
|
|
||||||
path: '/dashboard/settings',
|
|
||||||
icon: (
|
|
||||||
<Icon
|
|
||||||
mt="-7px"
|
|
||||||
as={MdOutlineManageAccounts}
|
|
||||||
width="20px"
|
|
||||||
height="20px"
|
|
||||||
color="inherit"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Subscription',
|
|
||||||
path: '/dashboard/subscription',
|
|
||||||
icon: (
|
|
||||||
<Icon
|
|
||||||
mt="-7px"
|
|
||||||
as={MdCreditCard}
|
|
||||||
width="20px"
|
|
||||||
height="20px"
|
|
||||||
color="inherit"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
collapse: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { Box } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export const renderTrack = ({ style, ...props }: any) => {
|
|
||||||
const trackStyle = {
|
|
||||||
position: 'absolute',
|
|
||||||
maxWidth: '100%',
|
|
||||||
transition: 'opacity 200ms ease 0s',
|
|
||||||
opacity: 0,
|
|
||||||
background: 'transparent',
|
|
||||||
bottom: 2,
|
|
||||||
top: 2,
|
|
||||||
borderRadius: 3,
|
|
||||||
right: 0,
|
|
||||||
};
|
|
||||||
return <div style={{ ...style, ...trackStyle }} {...props} />;
|
|
||||||
};
|
|
||||||
export const renderThumb = ({ style, ...props }: any) => {
|
|
||||||
const thumbStyle = {
|
|
||||||
borderRadius: 15,
|
|
||||||
background: 'rgba(222, 222, 222, .1)',
|
|
||||||
};
|
|
||||||
return <div style={{ ...style, ...thumbStyle }} {...props} />;
|
|
||||||
};
|
|
||||||
export const renderView = ({ style, ...props }: any) => {
|
|
||||||
const viewStyle = {
|
|
||||||
width: '100%',
|
|
||||||
marginBottom: -22,
|
|
||||||
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
me={{ base: '0px !important', '2xl': '-16px !important' }}
|
|
||||||
transform="translate(0px, 0px)"
|
|
||||||
boxSizing={'unset'}
|
|
||||||
pe="15px"
|
|
||||||
style={{ ...style, ...viewStyle }}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import { Flex, useColorModeValue } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const HSeparator = (props: { variant?: string; [x: string]: any }) => {
|
|
||||||
const textColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
|
||||||
const { variant, ...rest } = props;
|
|
||||||
return <Flex h="1px" w="100%" bg={textColor} {...rest} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const VSeparator = (props: { variant?: string; [x: string]: any }) => {
|
|
||||||
const textColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
|
||||||
const { variant, ...rest } = props;
|
|
||||||
return <Flex w="1px" bg={textColor} {...rest} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { HSeparator, VSeparator };
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import {
|
|
||||||
renderThumb,
|
|
||||||
renderTrack,
|
|
||||||
renderView
|
|
||||||
} from '@/components/scrollbar/Scrollbar';
|
|
||||||
import Content from '@/components/sidebar/components/Content';
|
|
||||||
import { ApiKeyContext } from '@/contexts/layout';
|
|
||||||
import { IRoute } from '@/types/types';
|
|
||||||
import { isWindowAvailable } from '@/utils/navigation';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
Drawer,
|
|
||||||
DrawerBody,
|
|
||||||
Icon,
|
|
||||||
useColorModeValue,
|
|
||||||
DrawerOverlay,
|
|
||||||
useDisclosure,
|
|
||||||
DrawerContent,
|
|
||||||
DrawerCloseButton
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import React, { PropsWithChildren, useContext } from 'react';
|
|
||||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
|
||||||
import { IoMenuOutline } from 'react-icons/io5';
|
|
||||||
|
|
||||||
export interface SidebarProps extends PropsWithChildren {
|
|
||||||
routes: IRoute[];
|
|
||||||
[x: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sidebar(props: SidebarProps) {
|
|
||||||
const { routes } = props;
|
|
||||||
const { apiKey, setApiKey } = useContext(ApiKeyContext);
|
|
||||||
let variantChange = '0.2s linear';
|
|
||||||
let shadow = useColorModeValue(
|
|
||||||
'14px 17px 40px 4px rgba(112, 144, 176, 0.08)',
|
|
||||||
'unset'
|
|
||||||
);
|
|
||||||
let sidebarBg = useColorModeValue('white', 'navy.800');
|
|
||||||
let sidebarRadius = '14px';
|
|
||||||
let sidebarMargins = '0px';
|
|
||||||
return (
|
|
||||||
<Box display={{ base: 'none', xl: 'block' }} position="fixed" minH="100%">
|
|
||||||
<Box
|
|
||||||
bg={sidebarBg}
|
|
||||||
transition={variantChange}
|
|
||||||
w="285px"
|
|
||||||
ms={{
|
|
||||||
sm: '16px'
|
|
||||||
}}
|
|
||||||
my={{
|
|
||||||
sm: '16px'
|
|
||||||
}}
|
|
||||||
h="calc(100vh - 32px)"
|
|
||||||
m={sidebarMargins}
|
|
||||||
borderRadius={sidebarRadius}
|
|
||||||
minH="100%"
|
|
||||||
overflowX="hidden"
|
|
||||||
boxShadow={shadow}
|
|
||||||
>
|
|
||||||
<Scrollbars
|
|
||||||
autoHide
|
|
||||||
renderTrackVertical={renderTrack}
|
|
||||||
renderThumbVertical={renderThumb}
|
|
||||||
renderView={renderView}
|
|
||||||
universal={true}
|
|
||||||
>
|
|
||||||
<Content setApiKey={setApiKey} routes={routes} />
|
|
||||||
</Scrollbars>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------- Sidebar Function for Navbar burger --------------
|
|
||||||
export function SidebarResponsive(props: SidebarProps) {
|
|
||||||
let sidebarBackgroundColor = useColorModeValue('white', 'navy.800');
|
|
||||||
let menuColor = useColorModeValue('gray.400', 'white');
|
|
||||||
const { apiKey, setApiKey } = useContext(ApiKeyContext);
|
|
||||||
// SIDEBAR
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
|
|
||||||
const { routes } = props;
|
|
||||||
return (
|
|
||||||
<Flex display={{ sm: 'flex', xl: 'none' }} alignItems="center">
|
|
||||||
<Flex w="max-content" h="max-content" onClick={onOpen}>
|
|
||||||
<Icon
|
|
||||||
as={IoMenuOutline}
|
|
||||||
color={menuColor}
|
|
||||||
my="auto"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
me="10px"
|
|
||||||
_hover={{ cursor: 'pointer' }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Drawer
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={onClose}
|
|
||||||
placement={
|
|
||||||
isWindowAvailable() && document.documentElement.dir === 'rtl'
|
|
||||||
? 'right'
|
|
||||||
: 'left'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DrawerOverlay />
|
|
||||||
<DrawerContent
|
|
||||||
w="285px"
|
|
||||||
maxW="285px"
|
|
||||||
ms={{
|
|
||||||
sm: '16px'
|
|
||||||
}}
|
|
||||||
my={{
|
|
||||||
sm: '16px'
|
|
||||||
}}
|
|
||||||
borderRadius="16px"
|
|
||||||
bg={sidebarBackgroundColor}
|
|
||||||
>
|
|
||||||
<DrawerCloseButton
|
|
||||||
zIndex="3"
|
|
||||||
onClick={onClose}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
_hover={{ boxShadow: 'none' }}
|
|
||||||
/>
|
|
||||||
<DrawerBody maxW="285px" px="0rem" pb="0">
|
|
||||||
<Scrollbars
|
|
||||||
autoHide
|
|
||||||
renderTrackVertical={renderTrack}
|
|
||||||
renderThumbVertical={renderThumb}
|
|
||||||
renderView={renderView}
|
|
||||||
universal={true}
|
|
||||||
>
|
|
||||||
<Content setApiKey={setApiKey} routes={routes} />
|
|
||||||
</Scrollbars>
|
|
||||||
</DrawerBody>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Sidebar;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import logo from '/public/logo-horizon-boilerplate.png';
|
|
||||||
import { HSeparator } from '@/components/separator/Separator';
|
|
||||||
import { Flex, Image, Link, Text } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export function SidebarBrand() {
|
|
||||||
return (
|
|
||||||
<Flex alignItems="center" flexDirection="column" ps="24px">
|
|
||||||
<Link
|
|
||||||
display={'flex'}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent={'center'}
|
|
||||||
href="/"
|
|
||||||
mb="34px"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
alt=" "
|
|
||||||
w="36px"
|
|
||||||
src={logo.src}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
ms="8px"
|
|
||||||
me="10px"
|
|
||||||
fontWeight="800"
|
|
||||||
fontSize={'17px'}
|
|
||||||
letterSpacing="-0.2px"
|
|
||||||
color="#120F43"
|
|
||||||
>
|
|
||||||
Horizon UI Boilerplate
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
<HSeparator mb="20px" w="310px" />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SidebarBrand;
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
// Custom components
|
|
||||||
import { useSupabase } from '@/app/supabase-provider';
|
|
||||||
import Brand from '@/components/sidebar/components/Brand';
|
|
||||||
import Links from '@/components/sidebar/components/Links';
|
|
||||||
import SidebarCard from '@/components/sidebar/components/SidebarCard';
|
|
||||||
import { UserContext } from '@/contexts/layout';
|
|
||||||
import { IRoute } from '@/types/types';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { PropsWithChildren, useContext } from 'react';
|
|
||||||
import { FiLogOut } from 'react-icons/fi';
|
|
||||||
|
|
||||||
// FUNCTIONS
|
|
||||||
|
|
||||||
interface SidebarContent extends PropsWithChildren {
|
|
||||||
routes: IRoute[];
|
|
||||||
[x: string]: any;
|
|
||||||
}
|
|
||||||
function SidebarContent(props: SidebarContent) {
|
|
||||||
const router = useRouter();
|
|
||||||
const { supabase } = useSupabase();
|
|
||||||
const { routes, setApiKey } = props;
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
console.log(user.user_metadata.avatar_url);
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
|
||||||
const shadowPillBar = useColorModeValue(
|
|
||||||
'4px 17px 40px 4px rgba(112, 144, 176, 0.08)',
|
|
||||||
'none'
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
height="100%"
|
|
||||||
pt="36px"
|
|
||||||
pb="26px"
|
|
||||||
borderRadius="30px"
|
|
||||||
maxW="285px"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Brand />
|
|
||||||
<Stack direction="column" mb="auto" mt="8px" ps="20px" pe="16px">
|
|
||||||
<Box ps="0px" pe={{ md: '0px', '2xl': '0px' }}>
|
|
||||||
<Links routes={routes} />
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
mt="60px"
|
|
||||||
width={'100%'}
|
|
||||||
display={'flex'}
|
|
||||||
justifyContent={'center'}
|
|
||||||
ps="20px"
|
|
||||||
pe="20px"
|
|
||||||
>
|
|
||||||
<SidebarCard />
|
|
||||||
</Box>
|
|
||||||
<Flex
|
|
||||||
mt="20px"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
boxShadow={shadowPillBar}
|
|
||||||
borderRadius="30px"
|
|
||||||
p="14px"
|
|
||||||
px="34px"
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
h="34px"
|
|
||||||
w="34px"
|
|
||||||
me="10px"
|
|
||||||
src={user.user_metadata.avatar_url}
|
|
||||||
/>
|
|
||||||
<Text color={textColor} fontSize="sm" fontWeight="700" me="10px">
|
|
||||||
{user.user_metadata.full_name ? user.user_metadata.full_name : 'User'}
|
|
||||||
</Text>
|
|
||||||
<Button
|
|
||||||
ms="auto"
|
|
||||||
variant="transparent"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="full"
|
|
||||||
w="34px"
|
|
||||||
h="34px"
|
|
||||||
px="0px"
|
|
||||||
minW="34px"
|
|
||||||
justifyContent={'center'}
|
|
||||||
alignItems="center"
|
|
||||||
onClick={(e) => {
|
|
||||||
supabase.auth.signOut();
|
|
||||||
router.push('/');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon as={FiLogOut} width="16px" height="16px" color="inherit" />
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SidebarContent;
|
|
||||||
@@ -1,823 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/* eslint-disable */
|
|
||||||
import NavLink from '@/components/link/NavLink';
|
|
||||||
import {
|
|
||||||
PlanContext,
|
|
||||||
ProductsContext,
|
|
||||||
UserContext,
|
|
||||||
UserDetailsContext
|
|
||||||
} from '@/contexts/layout';
|
|
||||||
import modalImage from '@/public/Modal.png';
|
|
||||||
import { IRoute } from '@/types/types';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { getRedirectMethod } from '@/utils/auth-helpers/settings';
|
|
||||||
import { getErrorRedirect } from '@/utils/helpers';
|
|
||||||
import { getStripe } from '@/utils/stripe/client';
|
|
||||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionButton,
|
|
||||||
AccordionIcon,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionPanel,
|
|
||||||
Badge,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
HStack,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalOverlay,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
useDisclosure
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
|
||||||
import { PropsWithChildren, useCallback, useContext, useState } from 'react';
|
|
||||||
import { FaCircle } from 'react-icons/fa';
|
|
||||||
import { IoIosStar, IoMdAdd } from 'react-icons/io';
|
|
||||||
import { MdCheckCircle, MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
interface SidebarLinksProps extends PropsWithChildren {
|
|
||||||
routes: IRoute[];
|
|
||||||
[x: string]: any;
|
|
||||||
}
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
|
|
||||||
export function SidebarLinks(props: SidebarLinksProps) {
|
|
||||||
const pathname = usePathname();
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
let activeColor = useColorModeValue('#120F43', 'white');
|
|
||||||
let inactiveColor = useColorModeValue('gray.500', 'gray.500');
|
|
||||||
let borderColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
|
||||||
let activeIcon = useColorModeValue('brand.500', 'white');
|
|
||||||
let iconColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
// verifies if routeName is the one active (in browser input)
|
|
||||||
const activeRoute = useCallback(
|
|
||||||
(route: string | { [x: string]: any }) => {
|
|
||||||
let foundActive = 0;
|
|
||||||
if (typeof route === 'string') {
|
|
||||||
return pathname?.includes(route);
|
|
||||||
} else if (route?.items) {
|
|
||||||
route.items.map((item: { [x: string]: any }) => {
|
|
||||||
if (pathname?.includes(item.path)) foundActive = foundActive + 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (foundActive > 0) return true;
|
|
||||||
else return false;
|
|
||||||
},
|
|
||||||
[pathname]
|
|
||||||
);
|
|
||||||
|
|
||||||
const router = getRedirectMethod() === 'client' ? useRouter() : null;
|
|
||||||
const { routes } = props;
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
|
||||||
const { plan, setPlan } = useContext(PlanContext);
|
|
||||||
const products = useContext(ProductsContext);
|
|
||||||
const currentPath = usePathname();
|
|
||||||
|
|
||||||
// this function creates the links and collapses that appear in the sidebar (left menu)
|
|
||||||
const createLinks = (routes: IRoute[]) => {
|
|
||||||
const handleCheckout = async (price: Price) => {
|
|
||||||
setPriceIdLoading(price.id);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push('/dashboard/signin/signup');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
|
||||||
price,
|
|
||||||
currentPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errorRedirect) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(errorRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(
|
|
||||||
getErrorRedirect(
|
|
||||||
currentPath,
|
|
||||||
'An unknown error occurred.',
|
|
||||||
'Please try again later or contact a system administrator.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stripe = await getStripe();
|
|
||||||
stripe?.redirectToCheckout({ sessionId });
|
|
||||||
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
};
|
|
||||||
return routes.map((route, key) => {
|
|
||||||
if (route.collapse && !route.invisible) {
|
|
||||||
return (
|
|
||||||
<Accordion
|
|
||||||
allowToggle
|
|
||||||
defaultIndex={activeRoute(route) ? 0 : 1}
|
|
||||||
key={key}
|
|
||||||
>
|
|
||||||
<AccordionItem border="none" mb="14px" key={key}>
|
|
||||||
<AccordionButton
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
mb="4px"
|
|
||||||
justifyContent="center"
|
|
||||||
_hover={{
|
|
||||||
bg: 'unset'
|
|
||||||
}}
|
|
||||||
_focus={{
|
|
||||||
boxShadow: 'none'
|
|
||||||
}}
|
|
||||||
borderRadius="8px"
|
|
||||||
w="100%"
|
|
||||||
py="0px"
|
|
||||||
ms={0}
|
|
||||||
>
|
|
||||||
{route.icon ? (
|
|
||||||
<Flex align="center" justifyContent="space-between" w="100%">
|
|
||||||
<HStack spacing={activeRoute(route) ? '22px' : '26px'}>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
color={
|
|
||||||
activeRoute(route) ? activeIcon : inactiveColor
|
|
||||||
}
|
|
||||||
me="12px"
|
|
||||||
mt="6px"
|
|
||||||
>
|
|
||||||
{route.icon}
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
me="auto"
|
|
||||||
color={activeRoute(route) ? activeColor : 'gray.500'}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</HStack>
|
|
||||||
<AccordionIcon ms="auto" color={'gray.500'} />
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Flex pt="0px" pb="10px" alignItems="center" w="100%">
|
|
||||||
<HStack
|
|
||||||
spacing={
|
|
||||||
activeRoute(route.path.toLowerCase()) ? '22px' : '26px'
|
|
||||||
}
|
|
||||||
ps="32px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
me="auto"
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeColor
|
|
||||||
: inactiveColor
|
|
||||||
}
|
|
||||||
fontWeight="500"
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
<AccordionIcon ms="auto" color={'gray.500'} />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py="0px" ps={'8px'}>
|
|
||||||
<List>
|
|
||||||
{
|
|
||||||
route.icon && route.items
|
|
||||||
? createLinks(route.items) // for bullet accordion links
|
|
||||||
: route.items
|
|
||||||
? createAccordionLinks(route.items)
|
|
||||||
: '' // for non-bullet accordion links
|
|
||||||
}
|
|
||||||
</List>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
);
|
|
||||||
} else if (!route.invisible) {
|
|
||||||
return (
|
|
||||||
<Box key={key}>
|
|
||||||
{route.icon ? (
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
w="100%"
|
|
||||||
maxW="100%"
|
|
||||||
ps="17px"
|
|
||||||
mb="0px"
|
|
||||||
>
|
|
||||||
<HStack
|
|
||||||
w="100%"
|
|
||||||
mb="14px"
|
|
||||||
spacing={
|
|
||||||
activeRoute(route.path.toLowerCase()) ? '22px' : '26px'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{route.path.includes('premium') && !props.subscription ? (
|
|
||||||
<Flex w="100%">
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay bg="rgba(0, 0, 0, 0.85)" />
|
|
||||||
<ModalContent
|
|
||||||
mx="8px"
|
|
||||||
bg="transparent"
|
|
||||||
boxShadow="unset"
|
|
||||||
maxW="unset"
|
|
||||||
w="unset"
|
|
||||||
>
|
|
||||||
<ModalBody p="0px" position={'relative'}>
|
|
||||||
<Flex>
|
|
||||||
<Image
|
|
||||||
display={{ base: 'none', md: 'block' }}
|
|
||||||
zIndex="98"
|
|
||||||
borderLeftRadius="16px"
|
|
||||||
src={modalImage.src}
|
|
||||||
w="340px"
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
bg="white"
|
|
||||||
borderLeftRadius={{ base: '16px', md: '0px' }}
|
|
||||||
borderRightRadius="16px"
|
|
||||||
direction={'column'}
|
|
||||||
px={{ base: '30px', md: '42px' }}
|
|
||||||
py="34px"
|
|
||||||
w={{ md: '412px', lg: '456px' }}
|
|
||||||
minW={{ md: '412px', lg: '456px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="26px"
|
|
||||||
fontWeight={'800'}
|
|
||||||
color={textColor}
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Upgrade to Unlimited
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize="md"
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
Get access to all features and generate
|
|
||||||
premium and exclusive essays with our
|
|
||||||
unlimited plan!
|
|
||||||
</Text>
|
|
||||||
{/* Features */}
|
|
||||||
<Flex
|
|
||||||
w={{ base: '100%', xl: '80%' }}
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Access to 12+ Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 1500 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{/* YEARLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTJ6R3RnzmIPX',
|
|
||||||
price: 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTJ6R3RnzmIPX'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="14px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="20px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="8px"
|
|
||||||
>
|
|
||||||
Yearly
|
|
||||||
</Text>
|
|
||||||
<Badge
|
|
||||||
display={{
|
|
||||||
base: 'flex',
|
|
||||||
lg: 'none',
|
|
||||||
xl: 'flex'
|
|
||||||
}}
|
|
||||||
colorScheme="green"
|
|
||||||
borderRadius="4px"
|
|
||||||
color="green.500"
|
|
||||||
textTransform={'none'}
|
|
||||||
letterSpacing="0px"
|
|
||||||
px="0px"
|
|
||||||
w="max-content"
|
|
||||||
>
|
|
||||||
Save 35%
|
|
||||||
</Badge>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$5.75
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END YEARLY */}
|
|
||||||
{/* MONTHLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTCPDFZbburMa',
|
|
||||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTCPDFZbburMa'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="16px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="28px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="4px"
|
|
||||||
>
|
|
||||||
Monthly
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$9
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END MONTHLY */}
|
|
||||||
{products.map((product: any) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price: any) => price.id === plan.price
|
|
||||||
);
|
|
||||||
if (product.id === plan.product) {
|
|
||||||
if (!price) return null;
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
mb="28px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={() => handleCheckout(price)}
|
|
||||||
>
|
|
||||||
Upgrade now
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
mt="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
<Text
|
|
||||||
fontSize="xs"
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight={'500'}
|
|
||||||
mx="auto"
|
|
||||||
mb="5px"
|
|
||||||
>
|
|
||||||
Used by 80,000+ users monthly
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
direction="row"
|
|
||||||
alignItems="center"
|
|
||||||
mx="auto"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="6px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="800"
|
|
||||||
h="100%"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
4.9
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalCloseButton
|
|
||||||
borderRadius="full"
|
|
||||||
color="#120F43"
|
|
||||||
bg="#F4F6FB !important"
|
|
||||||
_hover={{ bg: '#E9EDF6 !important' }}
|
|
||||||
_focus={{ bg: '#F4F6FB !important' }}
|
|
||||||
_active={{ bg: '#F4F6FB !important' }}
|
|
||||||
zIndex="99"
|
|
||||||
/>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => onOpen()}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeIcon
|
|
||||||
: inactiveColor
|
|
||||||
}
|
|
||||||
me="12px"
|
|
||||||
mt="6px"
|
|
||||||
>
|
|
||||||
{route.icon}
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
me="auto"
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeColor
|
|
||||||
: 'gray.500'
|
|
||||||
}
|
|
||||||
fontWeight={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? '700'
|
|
||||||
: '500'
|
|
||||||
}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
{route.rightElement ? (
|
|
||||||
<Flex
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="full"
|
|
||||||
w="34px"
|
|
||||||
h="34px"
|
|
||||||
justify={'center'}
|
|
||||||
align="center"
|
|
||||||
color={iconColor}
|
|
||||||
ms="auto"
|
|
||||||
me="10px"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
as={IoMdAdd}
|
|
||||||
width="20px"
|
|
||||||
height="20px"
|
|
||||||
color="inherit"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
) : null}
|
|
||||||
<Badge
|
|
||||||
display={{ base: 'flex', lg: 'none', xl: 'flex' }}
|
|
||||||
colorScheme="brand"
|
|
||||||
borderRadius="25px"
|
|
||||||
color="brand.500"
|
|
||||||
textTransform={'none'}
|
|
||||||
letterSpacing="0px"
|
|
||||||
px="8px"
|
|
||||||
>
|
|
||||||
PRO
|
|
||||||
</Badge>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<NavLink
|
|
||||||
href={
|
|
||||||
route.layout ? route.layout + route.path : route.path
|
|
||||||
}
|
|
||||||
key={key}
|
|
||||||
styles={{ width: '100%' }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
w="100%"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeIcon
|
|
||||||
: inactiveColor
|
|
||||||
}
|
|
||||||
me="12px"
|
|
||||||
mt="6px"
|
|
||||||
>
|
|
||||||
{route.icon}
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
me="auto"
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeColor
|
|
||||||
: 'gray.500'
|
|
||||||
}
|
|
||||||
fontWeight={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? '700'
|
|
||||||
: '500'
|
|
||||||
}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
{route.rightElement ? (
|
|
||||||
<Flex
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="full"
|
|
||||||
w="34px"
|
|
||||||
h="34px"
|
|
||||||
justify={'center'}
|
|
||||||
align="center"
|
|
||||||
color={iconColor}
|
|
||||||
ms="auto"
|
|
||||||
me="10px"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
as={IoMdAdd}
|
|
||||||
width="20px"
|
|
||||||
height="20px"
|
|
||||||
color="inherit"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<ListItem ms={0}>
|
|
||||||
<Flex ps="32px" alignItems="center" mb="8px">
|
|
||||||
<NavLink
|
|
||||||
href={route.layout ? route.layout + route.path : route.path}
|
|
||||||
key={key}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeColor
|
|
||||||
: inactiveColor
|
|
||||||
}
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize="xs"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
</NavLink>
|
|
||||||
</Flex>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// this function creates the links from the secondary accordions (for example auth -> sign-in -> default)
|
|
||||||
const createAccordionLinks = (routes: IRoute[]) => {
|
|
||||||
return routes.map((route: IRoute, key: number) => {
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
ms="28px"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
mb="10px"
|
|
||||||
key={key}
|
|
||||||
>
|
|
||||||
<NavLink href={route.layout + route.path} key={key}>
|
|
||||||
<Icon w="6px" h="6px" me="8px" as={FaCircle} color={activeIcon} />
|
|
||||||
<Text
|
|
||||||
color={
|
|
||||||
activeRoute(route.path.toLowerCase())
|
|
||||||
? activeColor
|
|
||||||
: inactiveColor
|
|
||||||
}
|
|
||||||
fontWeight={
|
|
||||||
activeRoute(route.path.toLowerCase()) ? 'bold' : 'normal'
|
|
||||||
}
|
|
||||||
fontSize="sm"
|
|
||||||
>
|
|
||||||
{route.name}
|
|
||||||
</Text>
|
|
||||||
</NavLink>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// BRAND
|
|
||||||
return <>{createLinks(routes)}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SidebarLinks;
|
|
||||||
@@ -1,556 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ProductsContext,
|
|
||||||
SubscriptionContext,
|
|
||||||
UserContext
|
|
||||||
} from '@/contexts/layout';
|
|
||||||
import modalImage from '@/public/Modal.png';
|
|
||||||
import SidebarImage from '@/public/SidebarBadge.png';
|
|
||||||
import { Database } from '@/types_db';
|
|
||||||
import { getErrorRedirect } from '@/utils/helpers';
|
|
||||||
import { getStripe } from '@/utils/stripe/client';
|
|
||||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
|
||||||
Image,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalOverlay,
|
|
||||||
Text,
|
|
||||||
useColorModeValue,
|
|
||||||
useDisclosure
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
|
||||||
import { useContext, useState } from 'react';
|
|
||||||
import { IoIosStar } from 'react-icons/io';
|
|
||||||
import { MdCheckCircle, MdChevronRight } from 'react-icons/md';
|
|
||||||
|
|
||||||
type Price = Database['public']['Tables']['prices']['Row'];
|
|
||||||
interface SidebarCard {
|
|
||||||
[x: string]: any;
|
|
||||||
}
|
|
||||||
export default function SidebarCard(props: SidebarCard) {
|
|
||||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
|
||||||
const textColor = useColorModeValue('#120F43', 'white');
|
|
||||||
const currentPath = usePathname();
|
|
||||||
const products = useContext(ProductsContext);
|
|
||||||
const subscription = useContext(SubscriptionContext);
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const [plan, setPlan] = useState({
|
|
||||||
product: 'prod_PtTCPDFZbburMa',
|
|
||||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
});
|
|
||||||
const borderColor = 'secondaryGray.200';
|
|
||||||
const router = useRouter();
|
|
||||||
const handleCheckout = async (price: Price) => {
|
|
||||||
setPriceIdLoading(price.id);
|
|
||||||
const user = useContext(UserContext);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push('/dashboard/signin/signup');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
|
||||||
price,
|
|
||||||
currentPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errorRedirect) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(errorRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId) {
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
return router.push(
|
|
||||||
getErrorRedirect(
|
|
||||||
currentPath,
|
|
||||||
'An unknown error occurred.',
|
|
||||||
'Please try again later or contact a system administrator.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stripe = await getStripe();
|
|
||||||
stripe?.redirectToCheckout({ sessionId });
|
|
||||||
|
|
||||||
setPriceIdLoading(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subscription) {
|
|
||||||
// -------------- PRO User Card --------------
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="16px"
|
|
||||||
w="100%"
|
|
||||||
py="16px"
|
|
||||||
px="14px"
|
|
||||||
>
|
|
||||||
<Image alt=" " src={SidebarImage.src} maxW="27px" me="10px" />
|
|
||||||
<Flex direction="column" justify="center" w="100%">
|
|
||||||
<Text fontSize="sm" fontWeight={'700'} color="#120F43" mb="2x">
|
|
||||||
PRO Member
|
|
||||||
</Text>
|
|
||||||
<Text fontWeight={'500'} fontSize="sm" color="gray.500">
|
|
||||||
Unlimited plan active
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// -------------- Free User Card --------------
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
justify="center"
|
|
||||||
direction="column"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={borderColor}
|
|
||||||
borderRadius="16px"
|
|
||||||
w="100%"
|
|
||||||
py="20px"
|
|
||||||
px="18px"
|
|
||||||
>
|
|
||||||
<Image alt=" " src={SidebarImage.src} maxW="54px" />
|
|
||||||
<Flex direction="column" mb="12px" w="100%" pt="16px">
|
|
||||||
<Text
|
|
||||||
fontSize="lg"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="10px"
|
|
||||||
textAlign={'center'}
|
|
||||||
>
|
|
||||||
Upgrade to Unlimited
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
textAlign={'center'}
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="sm"
|
|
||||||
color="gray.500"
|
|
||||||
mb="14px"
|
|
||||||
>
|
|
||||||
Generate premium Essays by upgrading to an unlimited plan!
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
onOpen();
|
|
||||||
}}
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
mb="14px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow: '0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg: 'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Go unlimited for just $9
|
|
||||||
</Button>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalOverlay bg="rgba(0, 0, 0, 0.85)" />
|
|
||||||
<ModalContent
|
|
||||||
mx="8px"
|
|
||||||
bg="transparent"
|
|
||||||
boxShadow="unset"
|
|
||||||
maxW="unset"
|
|
||||||
w="unset"
|
|
||||||
>
|
|
||||||
<ModalBody p="0px" position={'relative'}>
|
|
||||||
<Flex>
|
|
||||||
<Image
|
|
||||||
display={{ base: 'none', md: 'block' }}
|
|
||||||
zIndex="98"
|
|
||||||
borderLeftRadius="16px"
|
|
||||||
src={modalImage.src}
|
|
||||||
w="340px"
|
|
||||||
alt=" "
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
bg="white"
|
|
||||||
borderLeftRadius={{ base: '16px', md: '0px' }}
|
|
||||||
borderRightRadius="16px"
|
|
||||||
direction={'column'}
|
|
||||||
px={{ base: '30px', md: '42px' }}
|
|
||||||
py="34px"
|
|
||||||
w={{ md: '412px', lg: '456px' }}
|
|
||||||
minW={{ md: '412px', lg: '456px' }}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="26px"
|
|
||||||
fontWeight={'800'}
|
|
||||||
color={textColor}
|
|
||||||
mb="12px"
|
|
||||||
>
|
|
||||||
Upgrade to Unlimited
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
mb="24px"
|
|
||||||
fontWeight="500"
|
|
||||||
fontSize="md"
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
Get access to all features and generate premium and
|
|
||||||
exclusive essays with our unlimited plan!
|
|
||||||
</Text>
|
|
||||||
{/* Features */}
|
|
||||||
<Flex w={{ base: '100%', xl: '80%' }} direction="column">
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Access to 12+ Essay types
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Up to 1500 words per Essay
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Citation formats (APA, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="20px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Academic Levels (Master, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" mb="30px">
|
|
||||||
<Icon
|
|
||||||
me="10px"
|
|
||||||
w="20px"
|
|
||||||
h="20px"
|
|
||||||
color={'green.500'}
|
|
||||||
as={MdCheckCircle}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
as="span"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="600"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
>
|
|
||||||
Essay Tones (Academic, etc.)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{/* YEARLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTJ6R3RnzmIPX',
|
|
||||||
price: 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTJ6R3RnzmIPX'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="14px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="20px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="8px"
|
|
||||||
>
|
|
||||||
Yearly
|
|
||||||
</Text>
|
|
||||||
<Badge
|
|
||||||
display={{
|
|
||||||
base: 'flex',
|
|
||||||
lg: 'none',
|
|
||||||
xl: 'flex'
|
|
||||||
}}
|
|
||||||
colorScheme="green"
|
|
||||||
borderRadius="4px"
|
|
||||||
color="green.500"
|
|
||||||
textTransform={'none'}
|
|
||||||
letterSpacing="0px"
|
|
||||||
px="0px"
|
|
||||||
w="max-content"
|
|
||||||
>
|
|
||||||
Save 35%
|
|
||||||
</Badge>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$5.75
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END YEARLY */}
|
|
||||||
{/* MONTHLY */}
|
|
||||||
<Flex
|
|
||||||
onClick={() =>
|
|
||||||
setPlan({
|
|
||||||
product: 'prod_PtTCPDFZbburMa',
|
|
||||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition="0.15s linear"
|
|
||||||
align="center"
|
|
||||||
position="relative"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={
|
|
||||||
plan.product === 'prod_PtTCPDFZbburMa'
|
|
||||||
? 'brand.500'
|
|
||||||
: borderColor
|
|
||||||
}
|
|
||||||
borderRadius="10px"
|
|
||||||
w="100%"
|
|
||||||
py="16px"
|
|
||||||
px="14px"
|
|
||||||
cursor="pointer"
|
|
||||||
mb="28px"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight={'700'}
|
|
||||||
color="#120F43"
|
|
||||||
mb="2x"
|
|
||||||
ms="8px"
|
|
||||||
me="4px"
|
|
||||||
>
|
|
||||||
Monthly
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
display="flex"
|
|
||||||
ms="auto"
|
|
||||||
fontSize="md"
|
|
||||||
color={textColor}
|
|
||||||
letterSpacing="0px"
|
|
||||||
fontWeight="600"
|
|
||||||
lineHeight="100%"
|
|
||||||
>
|
|
||||||
$9
|
|
||||||
<Text
|
|
||||||
fontSize={'14px'}
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight="500"
|
|
||||||
ms="4px"
|
|
||||||
as="span"
|
|
||||||
>
|
|
||||||
/month
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
{/* END MONTHLY */}
|
|
||||||
{products.map((product: any) => {
|
|
||||||
const price = product?.prices?.find(
|
|
||||||
(price: any) => price.id === plan.price
|
|
||||||
);
|
|
||||||
if (product.id === plan.product) {
|
|
||||||
if (!price) return null;
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={product.id}
|
|
||||||
py="20px"
|
|
||||||
px="16px"
|
|
||||||
fontSize="sm"
|
|
||||||
variant="primary"
|
|
||||||
borderRadius="45px"
|
|
||||||
w={{ base: '100%' }}
|
|
||||||
h="54px"
|
|
||||||
mb="28px"
|
|
||||||
_hover={{
|
|
||||||
boxShadow:
|
|
||||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
|
||||||
_disabled: {
|
|
||||||
bg:
|
|
||||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={() => handleCheckout(price)}
|
|
||||||
>
|
|
||||||
Upgrade now
|
|
||||||
<Icon
|
|
||||||
as={MdChevronRight}
|
|
||||||
mt="2px"
|
|
||||||
h="16px"
|
|
||||||
w="16px"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
<Text
|
|
||||||
fontSize="xs"
|
|
||||||
color="gray.500"
|
|
||||||
fontWeight={'500'}
|
|
||||||
mx="auto"
|
|
||||||
mb="5px"
|
|
||||||
>
|
|
||||||
Used by 80,000+ users monthly
|
|
||||||
</Text>
|
|
||||||
<Flex direction="row" alignItems="center" mx="auto">
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="1px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
me="6px"
|
|
||||||
w="16px"
|
|
||||||
h="16px"
|
|
||||||
color="orange.500"
|
|
||||||
as={IoIosStar}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="800"
|
|
||||||
h="100%"
|
|
||||||
color={textColor}
|
|
||||||
>
|
|
||||||
4.9
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalCloseButton
|
|
||||||
borderRadius="full"
|
|
||||||
color="#120F43"
|
|
||||||
bg="#F4F6FB !important"
|
|
||||||
_hover={{ bg: '#E9EDF6 !important' }}
|
|
||||||
_focus={{ bg: '#F4F6FB !important' }}
|
|
||||||
_active={{ bg: '#F4F6FB !important' }}
|
|
||||||
zIndex="99"
|
|
||||||
/>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Text
|
|
||||||
textAlign={'center'}
|
|
||||||
fontWeight={'500'}
|
|
||||||
fontSize="xs"
|
|
||||||
color="gray.500"
|
|
||||||
>
|
|
||||||
Join 80,000+ users now
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
import type { Tables } from '@/types/types_db';
|
|
||||||
import { User } from '@supabase/supabase-js';
|
|
||||||
|
|
||||||
interface PlanContextType {
|
|
||||||
plan: {
|
|
||||||
product: string;
|
|
||||||
price: string;
|
|
||||||
};
|
|
||||||
setPlan: React.Dispatch<
|
|
||||||
React.SetStateAction<{
|
|
||||||
product: string;
|
|
||||||
price: string;
|
|
||||||
}>
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
interface ApiKeyContextType {
|
|
||||||
apiKey: string;
|
|
||||||
setApiKey: React.Dispatch<React.SetStateAction<string>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OpenContextType {
|
|
||||||
open: boolean;
|
|
||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
type Subscription = Tables<'subscriptions'>;
|
|
||||||
type Product = Tables<'products'>;
|
|
||||||
type Price = Tables<'prices'>;
|
|
||||||
|
|
||||||
interface ProductWithPrices extends Product {
|
|
||||||
prices: Price[];
|
|
||||||
}
|
|
||||||
interface PriceWithProduct extends Price {
|
|
||||||
products: Product | null;
|
|
||||||
}
|
|
||||||
interface SubscriptionWithProduct extends Subscription {
|
|
||||||
prices: PriceWithProduct | null;
|
|
||||||
}
|
|
||||||
type UserDetails = { [x: string]: any } | null;
|
|
||||||
|
|
||||||
export const PlanContext = createContext<PlanContextType>(undefined);
|
|
||||||
export const ApiKeyContext = createContext<ApiKeyContextType>(undefined);
|
|
||||||
export const OpenContext = createContext<OpenContextType>(undefined);
|
|
||||||
export const ProductsContext = createContext<ProductWithPrices[] | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
export const SubscriptionContext = createContext<
|
|
||||||
SubscriptionWithProduct | any | null
|
|
||||||
>(undefined);
|
|
||||||
export const UserContext = createContext<User | undefined | null>(undefined);
|
|
||||||
export const UserDetailsContext = createContext<UserDetails | undefined | null>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"template_version": 0
|
|
||||||
},
|
|
||||||
"fixtures": [
|
|
||||||
{
|
|
||||||
"name": "prod_hobby",
|
|
||||||
"path": "/v1/products",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"name": "Hobby",
|
|
||||||
"description": "Hobby product description"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "price_hobby_month",
|
|
||||||
"path": "/v1/prices",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"product": "${prod_hobby:id}",
|
|
||||||
"currency": "usd",
|
|
||||||
"billing_scheme": "per_unit",
|
|
||||||
"unit_amount": 1000,
|
|
||||||
"recurring": {
|
|
||||||
"interval": "month",
|
|
||||||
"interval_count": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "price_hobby_year",
|
|
||||||
"path": "/v1/prices",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"product": "${prod_hobby:id}",
|
|
||||||
"currency": "usd",
|
|
||||||
"billing_scheme": "per_unit",
|
|
||||||
"unit_amount": 10000,
|
|
||||||
"recurring": {
|
|
||||||
"interval": "year",
|
|
||||||
"interval_count": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "prod_freelancer",
|
|
||||||
"path": "/v1/products",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"name": "Freelancer",
|
|
||||||
"description": "Freelancer product description"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "price_freelancer_month",
|
|
||||||
"path": "/v1/prices",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"product": "${prod_freelancer:id}",
|
|
||||||
"currency": "usd",
|
|
||||||
"billing_scheme": "per_unit",
|
|
||||||
"unit_amount": 2000,
|
|
||||||
"recurring": {
|
|
||||||
"interval": "month",
|
|
||||||
"interval_count": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "price_freelancer_year",
|
|
||||||
"path": "/v1/prices",
|
|
||||||
"method": "post",
|
|
||||||
"params": {
|
|
||||||
"product": "${prod_freelancer:id}",
|
|
||||||
"currency": "usd",
|
|
||||||
"billing_scheme": "per_unit",
|
|
||||||
"unit_amount": 20000,
|
|
||||||
"recurring": {
|
|
||||||
"interval": "year",
|
|
||||||
"interval_count": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"jsx": "react",
|
|
||||||
"baseUrl": "src",
|
|
||||||
"paths": {
|
|
||||||
"*": ["src/*"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { type NextRequest } from 'next/server';
|
|
||||||
import { updateSession } from '@/utils/supabase/middleware';
|
|
||||||
|
|
||||||
export async function middleware(request: NextRequest) {
|
|
||||||
return await updateSession(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: [
|
|
||||||
/*
|
|
||||||
* Match all request paths except:
|
|
||||||
* - _next/static (static files)
|
|
||||||
* - _next/image (image optimization files)
|
|
||||||
* - favicon.ico (favicon file)
|
|
||||||
* - images - .svg, .png, .jpg, .jpeg, .gif, .webp
|
|
||||||
* Feel free to modify this pattern to include more paths.
|
|
||||||
*/
|
|
||||||
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
const nextConfig = {
|
|
||||||
reactStrictMode: false, // changed this to false
|
|
||||||
images: {
|
|
||||||
remotePatterns: [
|
|
||||||
{
|
|
||||||
protocol: 'https',
|
|
||||||
hostname: 'lh3.googleusercontent.com',
|
|
||||||
port: '',
|
|
||||||
pathname: '/a/**'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocol: 'https',
|
|
||||||
hostname: '*.googleusercontent.com',
|
|
||||||
port: '',
|
|
||||||
pathname: '**'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = nextConfig;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user