1st push
43
.dockerignore
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# 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/
|
||||||
|
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
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
|
||||||
|
/.npm-only-allow
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
|
README.md
|
||||||
|
.next
|
||||||
|
.git
|
||||||
14
.eslintrc.js
|
|
@ -29,8 +29,11 @@ module.exports = {
|
||||||
"sort-imports": [
|
"sort-imports": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
ignoreCase: true,
|
ignoreCase: false,
|
||||||
ignoreDeclarationSort: true,
|
ignoreDeclarationSort: true, // don"t want to sort import lines, use eslint-plugin-import instead
|
||||||
|
ignoreMemberSort: false,
|
||||||
|
memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
|
||||||
|
allowSeparatedGroups: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"tailwindcss/classnames-order": "off",
|
"tailwindcss/classnames-order": "off",
|
||||||
|
|
@ -65,6 +68,13 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
settings: {
|
||||||
|
"import/resolver": {
|
||||||
|
typescript: {
|
||||||
|
project: "./tsconfig.json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDirectoriesToSort() {
|
function getDirectoriesToSort() {
|
||||||
|
|
|
||||||
9
.vscode/settings.json
vendored
|
|
@ -1,5 +1,12 @@
|
||||||
{
|
{
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
"eslint.validate": [
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit"
|
||||||
|
},
|
||||||
"tailwindCSS.experimental.classRegex": [
|
"tailwindCSS.experimental.classRegex": [
|
||||||
["cva(?:<[^>]*>)?(([^)]*))", "[\"'`]([^\"'`]*).*?[\"'`]", "(?:twMerge|twJoin)\\(([^\\);]*)[\\);]"]
|
["cva(?:<[^>]*>)?(([^)]*))", "[\"'`]([^\"'`]*).*?[\"'`]", "(?:twMerge|twJoin)\\(([^\\);]*)[\\);]"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
67
Dockerfile
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
FROM node:21-alpine as base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||||
|
elif [ -f package-lock.json ]; then npm ci; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
||||||
|
else echo "Lockfile not found." && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Next.js collects completely anonymous telemetry data about general usage.
|
||||||
|
# Learn more here: https://nextjs.org/telemetry
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
# If using npm comment out above and use below instead
|
||||||
|
# RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Set the correct permission for prerender cache
|
||||||
|
RUN mkdir .next
|
||||||
|
RUN chown nextjs:nodejs .next
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
# set hostname to localhost
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
# server.js is created by next build from the standalone output
|
||||||
|
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||||
|
CMD ["node", "server.js"]
|
||||||
|
|
@ -301,3 +301,10 @@ This project is licensed under the MIT License. For more information, see the [L
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
|
||||||
|
Techniques used
|
||||||
|
CSR - Client side rendering for data
|
||||||
|
useSWR hook for data fetching
|
||||||
|
SSR - from authorization
|
||||||
|
|
||||||
16
api/route.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// export const dynamic = 'force-dynamic' // defaults to auto
|
||||||
|
// export async function GET(request) {}
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
const session = await getSession(req)
|
||||||
|
|
||||||
|
// Check if the user is authenticated
|
||||||
|
if (!session) {
|
||||||
|
res.status(401).json({
|
||||||
|
error: "User is not authenticated",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("_____________________________________________")
|
||||||
|
// Proceed with the route for authorized users
|
||||||
|
// ... implementation of the API Route
|
||||||
|
}
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export async function GET() {
|
|
||||||
return Response.json({ status: "ok" })
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import "styles/tailwind.css"
|
|
||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
65
app/page.tsx
|
|
@ -1,65 +0,0 @@
|
||||||
import { Metadata } from "next"
|
|
||||||
import { Button } from "components/Button/Button"
|
|
||||||
import { LP_GRID_ITEMS } from "lp-items"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Next.js Enterprise Boilerplate",
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
},
|
|
||||||
openGraph: {
|
|
||||||
url: "https://next-enterprise.vercel.app/",
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
width: 1200,
|
|
||||||
height: 630,
|
|
||||||
url: "https://raw.githubusercontent.com/Blazity/next-enterprise/main/project-logo.png",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Web() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<section className="bg-white dark:bg-gray-900">
|
|
||||||
<div className="mx-auto grid max-w-screen-xl px-4 py-8 text-center lg:py-16">
|
|
||||||
<div className="mx-auto place-self-center">
|
|
||||||
<h1 className="mb-4 max-w-2xl text-4xl font-extrabold leading-none tracking-tight dark:text-white md:text-5xl xl:text-6xl">
|
|
||||||
Next.js Enterprise Boilerplate
|
|
||||||
</h1>
|
|
||||||
<p className="mb-6 max-w-2xl font-light text-gray-500 dark:text-gray-400 md:text-lg lg:mb-8 lg:text-xl">
|
|
||||||
Jumpstart your enterprise project with our feature-packed, high-performance Next.js boilerplate!
|
|
||||||
Experience rapid UI development, AI-powered code reviews, and an extensive suite of tools for a smooth and
|
|
||||||
enjoyable development process.
|
|
||||||
</p>
|
|
||||||
<Button href="https://github.com/Blazity/next-enterprise" className="mr-3">
|
|
||||||
Get started
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
href="https://vercel.com/new/git/external?repository-url=https://github.com/Blazity/next-enterprise"
|
|
||||||
intent="secondary"
|
|
||||||
>
|
|
||||||
Deploy Now
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section className="bg-white dark:bg-gray-900">
|
|
||||||
<div className="mx-auto max-w-screen-xl px-4 py-8 sm:py-16 lg:px-6">
|
|
||||||
<div className="justify-center space-y-8 md:grid md:grid-cols-2 md:gap-12 md:space-y-0 lg:grid-cols-3">
|
|
||||||
{LP_GRID_ITEMS.map((singleItem) => (
|
|
||||||
<div key={singleItem.title} className="flex flex-col items-center justify-center text-center">
|
|
||||||
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-full bg-primary-100 p-1.5 text-blue-700 dark:bg-primary-900 lg:h-12 lg:w-12">
|
|
||||||
{singleItem.icon}
|
|
||||||
</div>
|
|
||||||
<h3 className="mb-2 text-xl font-bold dark:text-white">{singleItem.title}</h3>
|
|
||||||
<p className="text-gray-500 dark:text-gray-400">{singleItem.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import type { Meta, StoryObj } from "@storybook/react"
|
|
||||||
import { Button } from "./Button"
|
|
||||||
|
|
||||||
const meta: Meta<typeof Button> = {
|
|
||||||
title: "Button",
|
|
||||||
component: Button,
|
|
||||||
args: {
|
|
||||||
intent: "primary",
|
|
||||||
underline: false,
|
|
||||||
children: "Button",
|
|
||||||
size: "lg",
|
|
||||||
},
|
|
||||||
argTypes: {
|
|
||||||
intent: {
|
|
||||||
options: ["primary", "secondary"],
|
|
||||||
control: { type: "select" },
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
options: ["sm", "lg"],
|
|
||||||
control: { type: "select" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type Story = StoryObj<typeof Button>
|
|
||||||
|
|
||||||
export const Default: Story = {
|
|
||||||
render: (args) => <Button {...args} />,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default meta
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
|
||||||
|
|
||||||
import { twMerge } from "tailwind-merge"
|
|
||||||
|
|
||||||
const button = cva(
|
|
||||||
[
|
|
||||||
"justify-center",
|
|
||||||
"inline-flex",
|
|
||||||
"items-center",
|
|
||||||
"rounded-xl",
|
|
||||||
"text-center",
|
|
||||||
"border",
|
|
||||||
"border-blue-400",
|
|
||||||
"transition-colors",
|
|
||||||
"delay-50",
|
|
||||||
],
|
|
||||||
{
|
|
||||||
variants: {
|
|
||||||
intent: {
|
|
||||||
primary: ["bg-blue-400", "text-white", "hover:enabled:bg-blue-700"],
|
|
||||||
secondary: ["bg-transparent", "text-blue-400", "hover:enabled:bg-blue-400", "hover:enabled:text-white"],
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
sm: ["min-w-20", "h-full", "min-h-10", "text-sm", "py-1.5", "px-4"],
|
|
||||||
lg: ["min-w-32", "h-full", "min-h-12", "text-lg", "py-2.5", "px-6"],
|
|
||||||
},
|
|
||||||
underline: { true: ["underline"], false: [] },
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
intent: "primary",
|
|
||||||
size: "lg",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLAnchorElement>, VariantProps<typeof button> {
|
|
||||||
underline?: boolean
|
|
||||||
href: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Button({ className, intent, size, underline, ...props }: ButtonProps) {
|
|
||||||
return (
|
|
||||||
<a className={twMerge(button({ intent, size, className, underline }))} {...props}>
|
|
||||||
{props.children}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import * as RadixTooltip from "@radix-ui/react-tooltip"
|
|
||||||
import { cva, VariantProps } from "class-variance-authority"
|
|
||||||
import React from "react"
|
|
||||||
import { twMerge } from "tailwind-merge"
|
|
||||||
|
|
||||||
const tooltipContent = cva([], {
|
|
||||||
variants: {
|
|
||||||
intent: {
|
|
||||||
primary: ["rounded-0.5md", "bg-zinc-700", "font-open-sans", "text-white"],
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
md: ["px-4", "py-2.5", "text-2xs"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
intent: "primary",
|
|
||||||
size: "md",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const tooltipArrow = cva([], {
|
|
||||||
variants: {
|
|
||||||
intent: {
|
|
||||||
primary: ["fill-zinc-700"],
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
md: ["w-4", "h-2"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
intent: "primary",
|
|
||||||
size: "md",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export interface TooltipProps extends VariantProps<typeof tooltipContent>, RadixTooltip.TooltipProps {
|
|
||||||
explainer: React.ReactElement | string
|
|
||||||
children: React.ReactElement
|
|
||||||
className?: string
|
|
||||||
withArrow?: boolean
|
|
||||||
side?: "top" | "right" | "bottom" | "left"
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Tooltip({
|
|
||||||
children,
|
|
||||||
explainer,
|
|
||||||
open,
|
|
||||||
defaultOpen,
|
|
||||||
onOpenChange,
|
|
||||||
intent,
|
|
||||||
size,
|
|
||||||
side = "top",
|
|
||||||
className,
|
|
||||||
withArrow,
|
|
||||||
}: TooltipProps) {
|
|
||||||
return (
|
|
||||||
<RadixTooltip.Provider>
|
|
||||||
<RadixTooltip.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange} delayDuration={200}>
|
|
||||||
<RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>
|
|
||||||
<RadixTooltip.Portal>
|
|
||||||
<RadixTooltip.Content
|
|
||||||
side={side}
|
|
||||||
sideOffset={5}
|
|
||||||
className={twMerge(tooltipContent({ intent, size, className }))}
|
|
||||||
>
|
|
||||||
{explainer}
|
|
||||||
{withArrow ? <RadixTooltip.Arrow className={twMerge(tooltipArrow({ intent, size, className }))} /> : null}
|
|
||||||
</RadixTooltip.Content>
|
|
||||||
</RadixTooltip.Portal>
|
|
||||||
</RadixTooltip.Root>
|
|
||||||
</RadixTooltip.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
9
components/button.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Button } from "@chakra-ui/react"
|
||||||
|
|
||||||
|
export default function MyButton(props) {
|
||||||
|
return (
|
||||||
|
<Button href="/about" color="green.500" _hover={{ color: "blue.500" }} align="center">
|
||||||
|
About
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
44
components/formlabelinput.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Box, Flex, FormLabel, Input, Spacer } from "@chakra-ui/react"
|
||||||
|
import { Hide, Show } from "@chakra-ui/react"
|
||||||
|
import React, { useEffect, useState } from "react"
|
||||||
|
/**
|
||||||
|
* Component used to generate a form input
|
||||||
|
* props -
|
||||||
|
* label
|
||||||
|
* inputtype
|
||||||
|
* placeholder
|
||||||
|
* onChange = the handler function to get the changed data
|
||||||
|
*/
|
||||||
|
export default function FormLabelInput(props) {
|
||||||
|
const [data, setData] = React.useState("")
|
||||||
|
|
||||||
|
const handlepassChange = (data) => {
|
||||||
|
props.onChange(data)
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
handlepassChange(data)
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<Flex direction={["column", "column", "row", "row"]} alignItems="center" marginY={5}>
|
||||||
|
<Box flexBasis={0} flexGrow={1}>
|
||||||
|
<Hide below="md">
|
||||||
|
<FormLabel margin={"auto"} whiteSpace={"nowrap"} textAlign={"left"}>
|
||||||
|
{props.label}
|
||||||
|
</FormLabel>
|
||||||
|
</Hide>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box flexBasis={0} flexGrow={1}>
|
||||||
|
<Input
|
||||||
|
type={props.inputtype}
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
bgColor={"white"}
|
||||||
|
value={data}
|
||||||
|
onChange={(e) => {
|
||||||
|
setData(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
157
components/headbar.js
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
import { Box, Button, Flex, Link, Stack, Text } from "@chakra-ui/react"
|
||||||
|
import { useDisclosure } from "@chakra-ui/react"
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerBody,
|
||||||
|
DrawerCloseButton,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerOverlay,
|
||||||
|
Input,
|
||||||
|
} from "@chakra-ui/react"
|
||||||
|
import { Icon } from "@radix-ui/react-select"
|
||||||
|
import React from "react"
|
||||||
|
import Logo from "./logo"
|
||||||
|
|
||||||
|
function DrawerExample() {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
|
const btnRef = React.useRef()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button ref={btnRef} colorScheme="teal" onClick={onOpen}>
|
||||||
|
<Icon />
|
||||||
|
</Button>
|
||||||
|
<Drawer isOpen={isOpen} placement="left" onClose={onClose} finalFocusRef={btnRef}>
|
||||||
|
<DrawerOverlay />
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerCloseButton />
|
||||||
|
<DrawerHeader>Create your account</DrawerHeader>
|
||||||
|
|
||||||
|
<DrawerBody>
|
||||||
|
<Input placeholder="Type here..." />
|
||||||
|
</DrawerBody>
|
||||||
|
|
||||||
|
<DrawerFooter>
|
||||||
|
<Button variant="outline" mr={3} onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button colorScheme="blue">Save</Button>
|
||||||
|
</DrawerFooter>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NavBar = (props) => {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(false)
|
||||||
|
|
||||||
|
const toggle = () => setIsOpen(!isOpen)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavBarContainer {...props} h={["10%", "100%", "10%", "100%"]}>
|
||||||
|
<DrawerExample />
|
||||||
|
<LogoItem />
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
rounded="md"
|
||||||
|
color={["primary.500", "primary.500", "white", "white"]}
|
||||||
|
bg={["white", "white", "primary.500", "primary.500"]}
|
||||||
|
_hover={{
|
||||||
|
bg: ["primary.100", "primary.100", "primary.600", "primary.600"],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Account
|
||||||
|
</Button>
|
||||||
|
<MenuToggle toggle={toggle} isOpen={isOpen} />
|
||||||
|
<MenuLinks isOpen={isOpen} />
|
||||||
|
</NavBarContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CloseIcon = () => (
|
||||||
|
<svg width="24" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Close</title>
|
||||||
|
<path
|
||||||
|
fill="white"
|
||||||
|
d="M9.00023 7.58599L13.9502 2.63599L15.3642 4.04999L10.4142 8.99999L15.3642 13.95L13.9502 15.364L9.00023 10.414L4.05023 15.364L2.63623 13.95L7.58623 8.99999L2.63623 4.04999L4.05023 2.63599L9.00023 7.58599Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const MenuIcon = () => (
|
||||||
|
<svg width="24px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" fill="white">
|
||||||
|
<title>Menu</title>
|
||||||
|
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const MenuToggle = ({ toggle, isOpen }) => {
|
||||||
|
return (
|
||||||
|
<Box display={{ base: "block", md: "none" }} onClick={toggle}>
|
||||||
|
{isOpen ? <CloseIcon /> : <MenuIcon />}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LogoItem = () => {
|
||||||
|
return (
|
||||||
|
<Link href={"/home"}>
|
||||||
|
<Logo w="100px" color={["white", "white", "primary.500", "primary.500"]}></Logo>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MenuItem = ({ children, isLast, to = "/", ...rest }) => {
|
||||||
|
return (
|
||||||
|
<Link href={to}>
|
||||||
|
<Text display="block" {...rest} align={"center"} paddingTop={["15px", "15px", "10px", "10px"]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MenuLinks = ({ isOpen }) => {
|
||||||
|
return (
|
||||||
|
<Box display={{ base: isOpen ? "block" : "none", md: "block" }} flexBasis={{ base: "100%", md: "auto" }}>
|
||||||
|
<Stack
|
||||||
|
spacing={[6, 8, 10]}
|
||||||
|
align="center"
|
||||||
|
justify={["flex-end", "flex-end", "center", "center"]}
|
||||||
|
direction={["column", "column", "row", "row"]}
|
||||||
|
pt={[0, 0, 0, 0]}
|
||||||
|
>
|
||||||
|
<MenuItem to="/home">Home</MenuItem>
|
||||||
|
<MenuItem to="/how">How It works </MenuItem>
|
||||||
|
<MenuItem to="/pricing">Pricing </MenuItem>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NavBarContainer = ({ children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
as="nav"
|
||||||
|
align="center"
|
||||||
|
justify="space-between"
|
||||||
|
wrap="wrap"
|
||||||
|
w="100%"
|
||||||
|
mb={8}
|
||||||
|
p={[4, 6, 6, 6]}
|
||||||
|
top={0}
|
||||||
|
position="sticky"
|
||||||
|
zIndex={10}
|
||||||
|
bgGradient="linear(to-b, green.200, green.100)"
|
||||||
|
color={["white", "white", "primary.700", "primary.700"]}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavBar
|
||||||
14
components/layout.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import Head from "next/head"
|
||||||
|
import Header from "../components/headbar"
|
||||||
|
export default function Layout({ children }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Learning bee</title>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
</Head>
|
||||||
|
<Header />
|
||||||
|
<main>{children}</main>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
252
components/logincomponent.js
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormErrorMessage,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
Input,
|
||||||
|
Link,
|
||||||
|
Stack,
|
||||||
|
} from "@chakra-ui/react"
|
||||||
|
import { getAuth, isSignInWithEmailLink, sendSignInLinkToEmail, signInWithEmailLink } from "firebase/auth"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { Component, useContext, useState } from "react"
|
||||||
|
import FormLabelInput from "./formlabelinput"
|
||||||
|
import { geturlFormdata } from "../constants"
|
||||||
|
import { AuthContext } from "../context"
|
||||||
|
|
||||||
|
// import Login from "../googlelogin";
|
||||||
|
|
||||||
|
export default function Logincomponent(props) {
|
||||||
|
const auth = getAuth()
|
||||||
|
|
||||||
|
//for emaillink auth
|
||||||
|
const [email, setEmail] = useState("")
|
||||||
|
const [linksent, setLinksent] = useState(false)
|
||||||
|
|
||||||
|
//for email password auth
|
||||||
|
const [password, setPassword] = useState("")
|
||||||
|
|
||||||
|
const authContext = useContext(AuthContext)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const [isloaded, setIsloaded] = useState(false)
|
||||||
|
const [emailselect, setEmailselect] = useState(false)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
setIsloaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// function validateForm() {
|
||||||
|
// return email.length > 0 && password.length > 0
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //for email password authentication
|
||||||
|
// async function handleSubmit(event) {
|
||||||
|
// event.preventDefault()
|
||||||
|
|
||||||
|
// var urlForm = geturlFormdata("user", "login")
|
||||||
|
// console.log(email, password)
|
||||||
|
// await getTokens(urlForm.url, email, password).then((value) => {
|
||||||
|
// var idtype = null
|
||||||
|
// if (email.includes("@")) {
|
||||||
|
// idtype = "email"
|
||||||
|
// } else {
|
||||||
|
// idtype = "phone"
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // getuserdata(idtype , email).then((value)=>{
|
||||||
|
// // router.push(`/home`)
|
||||||
|
// // })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
const onSignInSubmit = () => {}
|
||||||
|
|
||||||
|
const onEmailSubmit = () => {
|
||||||
|
setEmailselect(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
//email link auth
|
||||||
|
|
||||||
|
const actionCodeSettings = {
|
||||||
|
// URL you want to redirect back to. The domain (www.example.com) for this
|
||||||
|
// URL must be in the authorized domains list in the Firebase Console.
|
||||||
|
// url: `${siterooturl}login`,
|
||||||
|
|
||||||
|
url: `http://localhost:3000/login`,
|
||||||
|
|
||||||
|
// // This must be true.
|
||||||
|
handleCodeInApp: true,
|
||||||
|
// iOS: {
|
||||||
|
// bundleId: 'com.example.ios'
|
||||||
|
// },
|
||||||
|
// android: {
|
||||||
|
// packageName: 'com.example.android',
|
||||||
|
// installApp: true,
|
||||||
|
// minimumVersion: '12'
|
||||||
|
// },
|
||||||
|
// dynamicLinkDomain: 'example.page.link'
|
||||||
|
}
|
||||||
|
|
||||||
|
const sighinWithCreds = async (email, password) => {
|
||||||
|
const response = await fetch("/api/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ email, password }),
|
||||||
|
})
|
||||||
|
console.log(response.ok)
|
||||||
|
if (response.ok) {
|
||||||
|
router.push("/home")
|
||||||
|
} else {
|
||||||
|
// Handle errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const signinWithLink = async () => {
|
||||||
|
console.log(auth, email, actionCodeSettings)
|
||||||
|
await sendSignInLinkToEmail(auth, email, actionCodeSettings)
|
||||||
|
.then(() => {
|
||||||
|
// The link was successfully sent. Inform the user.
|
||||||
|
// Save the email locally so you don't need to ask the user for it again
|
||||||
|
// if they open the link on the same device.
|
||||||
|
|
||||||
|
setLinksent(true)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
console.log("error sending link")
|
||||||
|
const errorCode = error.code
|
||||||
|
const errorMessage = error.message
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isloaded && isSignInWithEmailLink(auth, window.location.href)) {
|
||||||
|
// Additional state parameters can also be passed via URL.
|
||||||
|
// This can be used to continue the user's intended action before triggering
|
||||||
|
// the sign-in operation.
|
||||||
|
// Get the email if available. This should be available if the user completes
|
||||||
|
// the flow on the same device where they started it.
|
||||||
|
|
||||||
|
console.log(email)
|
||||||
|
if (!email) {
|
||||||
|
// User opened the link on a different device. To prevent session fixation
|
||||||
|
// attacks, ask the user to provide the associated email again. For example:
|
||||||
|
// email = window.prompt('Please provide your email for confirmation');
|
||||||
|
}
|
||||||
|
// The client SDK will parse the code from the link for you.
|
||||||
|
signInWithEmailLink(auth, email, window.location.href)
|
||||||
|
.then((result) => {
|
||||||
|
// Clear email from storage.
|
||||||
|
console.log("logged in")
|
||||||
|
|
||||||
|
// result.user.providerData
|
||||||
|
onSuccess(result)
|
||||||
|
// window.localStorage.removeItem('emailForSignIn');
|
||||||
|
|
||||||
|
//router.push('/home')
|
||||||
|
// You can access the new user via result.user
|
||||||
|
|
||||||
|
// Additional user info profile not available via:
|
||||||
|
// result.additionalUserInfo.profile == null
|
||||||
|
// You can check if the user is new or existing:
|
||||||
|
// result.additionalUserInfo.isNewUser
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
onFailure()
|
||||||
|
// Some error occurred, you can inspect the code: error.code
|
||||||
|
// Common errors could be invalid email and invalid or expired OTPs.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSuccess = (res) => {
|
||||||
|
console.log(res)
|
||||||
|
// verifyonServer(res._tokenResponse.idToken, "email", res.user.email, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFailure = () => {
|
||||||
|
alert(`Something went Wrong. Try again `)
|
||||||
|
}
|
||||||
|
const confirmCode = (code) => {
|
||||||
|
// confirmationResult.confirm(code).then((result) => {
|
||||||
|
// // User signed in successfully.
|
||||||
|
// const user = result.user;
|
||||||
|
// // ...
|
||||||
|
// }).catch((error) => {
|
||||||
|
// // User couldn't sign in (bad verification code?)
|
||||||
|
// // ...
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailChange = (val) => {
|
||||||
|
setEmail(val)
|
||||||
|
}
|
||||||
|
const handlePasswordChange = (val) => {
|
||||||
|
setPassword(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(password)
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
w={["80%", "60%", "40%"]}
|
||||||
|
align="center"
|
||||||
|
bgGradient="linear(to-b, blue.200, blue.500)"
|
||||||
|
padding={10}
|
||||||
|
borderRadius={10}
|
||||||
|
margin="auto"
|
||||||
|
>
|
||||||
|
{!linksent && (
|
||||||
|
<>
|
||||||
|
<FormLabel margin={"auto"} padding={"auto"} textAlign={"center"}>
|
||||||
|
Log in
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl className={"form-group"}>
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="email"
|
||||||
|
label="Email*"
|
||||||
|
placeholder="abc@xyz.uvw"
|
||||||
|
onChange={(e) => handleEmailChange(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="password"
|
||||||
|
label="Password"
|
||||||
|
placeholder="type your password"
|
||||||
|
onChange={(e) => handlePasswordChange(e)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button className="btn" onClick={() => sighinWithCreds(email, password)}>
|
||||||
|
Log in
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<p>--- or ---</p>
|
||||||
|
<Button className="btn" onClick={signinWithLink}>
|
||||||
|
Continue with signin link
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* <p >Sign in with</p> */}
|
||||||
|
{/* <Login /> */}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{linksent && (
|
||||||
|
<>
|
||||||
|
<h4>Signin Link sent</h4>
|
||||||
|
<div className={"form-group"}>
|
||||||
|
<div style={{ marginBlock: "10%" }}>Check your provided email , And Click on the Link to verify </div>
|
||||||
|
<h3>WE are already waiting for you</h3>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
12
components/logo.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Box, Text } from "@chakra-ui/react"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default function Logo(props) {
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<Text fontSize="lg" fontWeight="bold">
|
||||||
|
Logo
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
39
components/menuitem.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Card, CardBody, CardFooter, CardHeader } from "@chakra-ui/react"
|
||||||
|
|
||||||
|
import { Heading, Image, Stack, Text } from "@chakra-ui/react"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export default function MenuItem(props) {
|
||||||
|
//define states here
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
//router
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//function when clicked
|
||||||
|
|
||||||
|
const handleOnClick = (e) => {
|
||||||
|
router.push(`/learningpath/${props.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card onClick={handleOnClick}>
|
||||||
|
<CardBody>
|
||||||
|
<Image src={props.image_url} alt={props.image_desc} borderRadius="lg" />
|
||||||
|
<Stack mt="6" spacing="3">
|
||||||
|
<Heading size="md">{props.title}</Heading>
|
||||||
|
<Text>{props.summary}</Text>
|
||||||
|
</Stack>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
125
components/signupcomponent.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormErrorMessage,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
Input,
|
||||||
|
Link,
|
||||||
|
Stack,
|
||||||
|
} from "@chakra-ui/react"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useState } from "react"
|
||||||
|
import FormLabelInput from "./formlabelinput"
|
||||||
|
import { geturlFormdata, siterooturl } from "../constants"
|
||||||
|
|
||||||
|
export default function Signupcomponent(props) {
|
||||||
|
const [email, setEmail] = React.useState("")
|
||||||
|
const [phone, setPhone] = React.useState("")
|
||||||
|
const [password, setPassword] = React.useState("")
|
||||||
|
const [confirm_password, setConfirmPassword] = React.useState("")
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const [show, setShow] = useState(true)
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
return email.length > 0 && (phone.length >= 10 || phone.length == 0) && password.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (event) => {
|
||||||
|
console.log(email, password, confirm_password, phone)
|
||||||
|
if (phone.length > 0 && phone.length < 10) {
|
||||||
|
//popup
|
||||||
|
}
|
||||||
|
event.preventDefault()
|
||||||
|
var urlForm
|
||||||
|
if (email.length == 0) {
|
||||||
|
urlForm = geturlFormdata("user", "signup", {}, { phone: phone, password: password })
|
||||||
|
} else if (phone.length == 0) {
|
||||||
|
urlForm = geturlFormdata("user", "signup", {}, { mail: email, password: password })
|
||||||
|
} else {
|
||||||
|
urlForm = geturlFormdata("user", "signup", {}, { mail: email, phone: phone, password: password })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password !== confirm_password) {
|
||||||
|
setShow(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// postsignup(urlForm.url, urlForm.formdata).then(() => {
|
||||||
|
// router.push("/info")
|
||||||
|
// })
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailChange = (val) => {
|
||||||
|
setEmail(val)
|
||||||
|
}
|
||||||
|
const handlePhoneChange = (val) => {
|
||||||
|
setPhone(val)
|
||||||
|
}
|
||||||
|
const handlePasswordChange = (val) => {
|
||||||
|
setPassword(val)
|
||||||
|
}
|
||||||
|
const handleConfirmPasswordChange = (val) => {
|
||||||
|
setConfirmPassword(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
w="50%"
|
||||||
|
align="center"
|
||||||
|
bgGradient="linear(to-b, blue.200, blue.500)"
|
||||||
|
padding={10}
|
||||||
|
borderRadius={10}
|
||||||
|
margin="auto"
|
||||||
|
>
|
||||||
|
<FormControl onSubmit={handleSubmit} align="center">
|
||||||
|
<FormLabel margin={"auto"} padding={"auto"} textAlign={"center"}>
|
||||||
|
Register
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="email"
|
||||||
|
label="Email*"
|
||||||
|
placeholder="abc@xyz.uvw"
|
||||||
|
onChange={(e) => handleEmailChange(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="number"
|
||||||
|
label="Phone"
|
||||||
|
placeholder="XXXXXXXXXX"
|
||||||
|
onChange={(e) => handlePhoneChange(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="password"
|
||||||
|
label="Password"
|
||||||
|
placeholder="type your password"
|
||||||
|
onChange={(e) => handlePasswordChange(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormLabelInput
|
||||||
|
inputtype="password"
|
||||||
|
label="Confirm Password"
|
||||||
|
placeholder="type your password again"
|
||||||
|
onChange={(e) => handleConfirmPasswordChange(e)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" margin="auto" marginY="10px" disabled={!validateForm()} onClick={handleSubmit}>
|
||||||
|
Register
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<FormLabel margin={"auto"} align="center" textAlign={"center"}>
|
||||||
|
Already registered?{" "}
|
||||||
|
<Link href={`${siterooturl}login`} color="black.500" _hover={{ color: "blue.800", fontSize: "130%" }}>
|
||||||
|
log in
|
||||||
|
</Link>
|
||||||
|
</FormLabel>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
constants.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
//here we can define all the things that we can keep in the cache database and remove it after some time
|
||||||
|
|
||||||
|
//getting the type of endpoint at one point
|
||||||
|
export const geturlFormdata = (data, reqtype, queryoptions, formdataoptions) => {
|
||||||
|
var rooturl = "https://api.smorentel.com/"
|
||||||
|
var allurl = rooturl + `${data}/${reqtype}?`
|
||||||
|
|
||||||
|
var queryparam = new String()
|
||||||
|
|
||||||
|
for (const property in queryoptions) {
|
||||||
|
queryparam += `${property}=${queryoptions[property]}&`
|
||||||
|
}
|
||||||
|
|
||||||
|
var formdata = new FormData()
|
||||||
|
for (const formproperty in formdataoptions) {
|
||||||
|
formdata.set(formproperty, formdataoptions[formproperty])
|
||||||
|
}
|
||||||
|
|
||||||
|
return { url: allurl + queryparam.slice(0, queryparam.length - 1), formdata: formdata }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const workstatus_dict = { 1: "progress", 2: "created", 3: "done" }
|
||||||
|
|
||||||
|
export const onRefresh = (authContext) => {
|
||||||
|
try {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
if (localStorage.getItem("refresh_token") != undefined) {
|
||||||
|
authContext.login()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
authContext.login()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
context.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import router from "next/router"
|
||||||
|
import { createContext } from "react"
|
||||||
|
import { getlocal, storelocal } from "./localstore"
|
||||||
|
|
||||||
|
export const AuthContext = createContext({
|
||||||
|
isLoggedIn: true,
|
||||||
|
accounttype: true,
|
||||||
|
firebase: null,
|
||||||
|
premium: false,
|
||||||
|
modelopen: false,
|
||||||
|
setModel: () => {},
|
||||||
|
changeaccount: () => {
|
||||||
|
accounttype = !accounttype
|
||||||
|
storelocal("accounttype", accounttype)
|
||||||
|
},
|
||||||
|
checkType: async () => {
|
||||||
|
var at = getlocal("accounttype")
|
||||||
|
accounttype = at
|
||||||
|
},
|
||||||
|
login: () => {
|
||||||
|
isLoggedIn = true
|
||||||
|
},
|
||||||
|
logout: () => {
|
||||||
|
localStorage.removeItem("access_token")
|
||||||
|
localStorage.removeItem("refresh_token")
|
||||||
|
localStorage.removeItem("userdata")
|
||||||
|
isLoggedIn = false
|
||||||
|
},
|
||||||
|
})
|
||||||
33
datafetch/datafetch.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import useSWR from "swr"
|
||||||
|
|
||||||
|
const fetcher = (...args) => fetch(...args).then((res) => res.json())
|
||||||
|
|
||||||
|
export function menuitemfetcher(url) {
|
||||||
|
const { data, error, isLoading } = useSWR("https://jsonplaceholder.typicode.com/posts", fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
isLoading,
|
||||||
|
isError: error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function learningitemfetcher(url) {
|
||||||
|
const { data, error, isLoading } = useSWR("https://jsonplaceholder.typicode.com/posts", fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
isLoading,
|
||||||
|
isError: error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function learningpodfetcher() {
|
||||||
|
const { data, error, isLoading } = useSWR("https://jsonplaceholder.typicode.com/posts", fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
isLoading,
|
||||||
|
isError: error,
|
||||||
|
}
|
||||||
|
}
|
||||||
28
docker-compose.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
nextjs:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.nextjs
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
pm2:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.pm2
|
||||||
|
volumes:
|
||||||
|
- ./pm2.config.js:/pm2/pm2.config.js
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- nextjs
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.nginx
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- pm2
|
||||||
51
localstore.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
export const storeobjlocal = (key, value) => {
|
||||||
|
localStorage.setItem(key, JSON.stringify(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getobjlocal = (key) => {
|
||||||
|
return JSON.parse(localStorage.getItem(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const storelocal = (key, value) => {
|
||||||
|
localStorage.setItem(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getlocal = (key) => {
|
||||||
|
return localStorage.getItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const localkeys = {
|
||||||
|
access_token: "",
|
||||||
|
refresh_token: "",
|
||||||
|
user: { customer: {}, worker: {} },
|
||||||
|
settings: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stateobj = {
|
||||||
|
user: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* what are the filter options -
|
||||||
|
* 1 place ==> filter_place
|
||||||
|
* 2 price range
|
||||||
|
* 3 location
|
||||||
|
* 4 distance
|
||||||
|
* 5 tags
|
||||||
|
* 6 categories
|
||||||
|
* 7 sub categories
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user infos -
|
||||||
|
* 1 access_token
|
||||||
|
* 2 refresh_token
|
||||||
|
* 3 user_data -> json -> (user_key) ->
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* single page data -
|
||||||
|
* items
|
||||||
|
* bookings
|
||||||
|
* bookingreqs
|
||||||
|
*/
|
||||||
73
middleware.js
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server"
|
||||||
|
import { refreshAccessToken } from "./utils/auth"
|
||||||
|
import { jwtVerify } from "jose"
|
||||||
|
const secretKey = new TextEncoder().encode("Swe4g7c?UBm5Nrd96vhsVDtkyJFbqKMTm!TMw5BDRLtaCFAXNvbq?s4rGKQSZnUP")
|
||||||
|
|
||||||
|
const verifyJWT = async (token) => {
|
||||||
|
const { payload, protectedHeader } = await jwtVerify(token, secretKey)
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function middleware(req, res) {
|
||||||
|
// Extract token from request headers or cookies or wherever it's stored
|
||||||
|
const token = req.cookies.get("token")
|
||||||
|
|
||||||
|
if (!token || token.value == "") {
|
||||||
|
if (req.url == "http://localhost:3000/login") {
|
||||||
|
} else {
|
||||||
|
return NextResponse.redirect(new URL("/login", req.url))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (req.url == "http://localhost:3000/login") {
|
||||||
|
return NextResponse.redirect("http://localhost:3000/home")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verify the token
|
||||||
|
console.log("ASdasdasssssssssssss")
|
||||||
|
console.log(token.value)
|
||||||
|
const payload = await verifyJWT(token.value)
|
||||||
|
|
||||||
|
// Attach the decoded user object to the request
|
||||||
|
// req.user = decoded.user;
|
||||||
|
// return NextResponse.redirect(new URL( req.url, '/home'))
|
||||||
|
// Validate if the user is the one that is present in cookie
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === "JWTExpired") {
|
||||||
|
console.log(error)
|
||||||
|
//delete the token from cookie
|
||||||
|
console.log("deleting the token")
|
||||||
|
//renew the token
|
||||||
|
// console.log(req.cookies.get('rtoken'));
|
||||||
|
|
||||||
|
const rtoken = req.cookies.get("rtoken")
|
||||||
|
if (rtoken == undefined) {
|
||||||
|
console.log("undefined")
|
||||||
|
}
|
||||||
|
console.log("defined")
|
||||||
|
|
||||||
|
const newtoken = await refreshAccessToken(rtoken)
|
||||||
|
if (newtoken == undefined) {
|
||||||
|
const res = NextResponse.redirect(new URL("/login", req.url)) // creates an actual instance
|
||||||
|
res.cookies.set("token", newtoken)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
console.log("defined")
|
||||||
|
const res = NextResponse.next() // creates an actual instance
|
||||||
|
res.cookies.set("token", newtoken)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
console.log(error)
|
||||||
|
// Handle other JWT errors
|
||||||
|
// return Response.json({ message: 'Unauthorized' }, { status: 401 });
|
||||||
|
|
||||||
|
// const res = NextResponse.redirect(new URL( '/login',req.url)) // creates an actual instance
|
||||||
|
// res.cookies.delete('token') // can be called on an instance
|
||||||
|
// return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
||||||
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
import type { NextRequest } from "next/server"
|
|
||||||
|
|
||||||
export function middleware(request: NextRequest) {
|
|
||||||
// TODO: Feel free to remove this block
|
|
||||||
if (request.headers?.get("host")?.includes("next-enterprise.vercel.app")) {
|
|
||||||
return NextResponse.redirect("https://blazity.com/open-source/nextjs-enterprise-boilerplate", { status: 301 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: [
|
|
||||||
/*
|
|
||||||
* Match all request paths except for the ones starting with:
|
|
||||||
* - api (API routes)
|
|
||||||
* - _next/static (static files)
|
|
||||||
* - _next/image (image optimization files)
|
|
||||||
* - favicon.ico (favicon file)
|
|
||||||
*/
|
|
||||||
"/((?!api|_next/static|_next/image|favicon.ico).*)",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -2,9 +2,12 @@ import withBundleAnalyzer from "@next/bundle-analyzer"
|
||||||
import withPlugins from "next-compose-plugins"
|
import withPlugins from "next-compose-plugins"
|
||||||
import { env } from "./env.mjs"
|
import { env } from "./env.mjs"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import('next').NextConfig}
|
* @type {import('next').NextConfig}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
const config = withPlugins([[withBundleAnalyzer({ enabled: env.ANALYZE })]], {
|
const config = withPlugins([[withBundleAnalyzer({ enabled: env.ANALYZE })]], {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
experimental: { instrumentationHook: true },
|
experimental: { instrumentationHook: true },
|
||||||
14
next.config.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// next.config.js
|
||||||
|
module.exports = {
|
||||||
|
// ... rest of the configuration.
|
||||||
|
output: "standalone",
|
||||||
|
async redirects() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/",
|
||||||
|
destination: "/home",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
39577
package-lock.json
generated
Normal file
18
package.json
|
|
@ -23,6 +23,12 @@
|
||||||
"coupling-graph": "npx madge --extensions js,jsx,ts,tsx,css,md,mdx ./ --exclude '.next|tailwind.config.js|reset.d.ts|prettier.config.js|postcss.config.js|playwright.config.ts|next.config.js|next-env.d.ts|instrumentation.ts|e2e/|README.md|.storybook/|.eslintrc.js' --image graph.svg"
|
"coupling-graph": "npx madge --extensions js,jsx,ts,tsx,css,md,mdx ./ --exclude '.next|tailwind.config.js|reset.d.ts|prettier.config.js|postcss.config.js|playwright.config.ts|next.config.js|next-env.d.ts|instrumentation.ts|e2e/|README.md|.storybook/|.eslintrc.js' --image graph.svg"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@auth/core": "^0.26.2",
|
||||||
|
"@chakra-ui/next-js": "^2.2.0",
|
||||||
|
"@chakra-ui/react": "^2.8.2",
|
||||||
|
"@emotion/react": "^11.11.3",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@headlessui/react": "^1.7.18",
|
||||||
"@next/bundle-analyzer": "^14.0.3",
|
"@next/bundle-analyzer": "^14.0.3",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
|
|
@ -47,13 +53,25 @@
|
||||||
"@semantic-release/release-notes-generator": "^12.1.0",
|
"@semantic-release/release-notes-generator": "^12.1.0",
|
||||||
"@t3-oss/env-nextjs": "^0.7.1",
|
"@t3-oss/env-nextjs": "^0.7.1",
|
||||||
"@vercel/otel": "^0.3.0",
|
"@vercel/otel": "^0.3.0",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
|
"firebase": "^10.8.0",
|
||||||
|
"framer-motion": "^11.0.3",
|
||||||
|
"jose": "^5.2.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"melonjs": "^16.1.1",
|
||||||
"next": "^14.0.3",
|
"next": "^14.0.3",
|
||||||
|
"next-auth": "^4.24.5",
|
||||||
"next-compose-plugins": "^2.2.1",
|
"next-compose-plugins": "^2.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-bootstrap": "^2.10.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"swr": "^2.2.4",
|
||||||
"tailwind-merge": "^2.0.0",
|
"tailwind-merge": "^2.0.0",
|
||||||
|
"webcrypto": "^0.1.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
102
pages/_app.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { initializeApp } from "firebase/app"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
|
||||||
|
//Auth imports
|
||||||
|
import { AuthContext } from "../context"
|
||||||
|
import "firebase/auth"
|
||||||
|
import { getAuth } from "firebase/auth"
|
||||||
|
|
||||||
|
//server side rendring things
|
||||||
|
import { SSRProvider } from "react-bootstrap"
|
||||||
|
|
||||||
|
import { ChakraProvider } from "@chakra-ui/react"
|
||||||
|
import customTheme from "../utils/themes"
|
||||||
|
|
||||||
|
function MyApp({ Component, pageProps }) {
|
||||||
|
/**Initailize the states
|
||||||
|
* Important for keeping data over pages
|
||||||
|
*/
|
||||||
|
const [modelopen, setModelopen] = React.useState(false)
|
||||||
|
const [loggedIn, setLoggedIn] = React.useState(false)
|
||||||
|
const [acctype, setAcctype] = React.useState(false)
|
||||||
|
const [loaded, setLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
///**This place is for firebase Auth */
|
||||||
|
|
||||||
|
//initialize firebase
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyARcLufTeUZbGue0-k9iZJVxmKlp0l0HQU",
|
||||||
|
authDomain: "freebees-24743.firebaseapp.com",
|
||||||
|
projectId: "freebees-24743",
|
||||||
|
storageBucket: "freebees-24743.appspot.com",
|
||||||
|
messagingSenderId: "1084271040061",
|
||||||
|
appId: "1:1084271040061:web:91df1dc11b58f5a4807c54",
|
||||||
|
measurementId: "G-6Z61GFCN05",
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = initializeApp(firebaseConfig)
|
||||||
|
|
||||||
|
const auth = getAuth()
|
||||||
|
|
||||||
|
const setModel = (data) => {
|
||||||
|
setModelopen(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const login = () => {
|
||||||
|
setLoggedIn(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
setLoggedIn(false)
|
||||||
|
auth.signOut().then(() => {
|
||||||
|
console.log("successfully signed out")
|
||||||
|
})
|
||||||
|
localStorage.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkType = () => {
|
||||||
|
console.log("check")
|
||||||
|
if (typeof window != undefined) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useEffect has been uesd to take actions on the first rendering
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const app = initializeApp(firebaseConfig)
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
console.log("1st check", firebaseConfig)
|
||||||
|
|
||||||
|
setLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
///// Manual test with console
|
||||||
|
const getLayout = Component.getLayout ?? ((page) => page)
|
||||||
|
return (
|
||||||
|
<SSRProvider>
|
||||||
|
<ChakraProvider theme={customTheme}>
|
||||||
|
<AuthContext.Provider
|
||||||
|
value={{
|
||||||
|
isLoggedIn: loggedIn,
|
||||||
|
firebase: app,
|
||||||
|
login: login,
|
||||||
|
logout: logout,
|
||||||
|
accounttype: acctype,
|
||||||
|
modelopen: modelopen,
|
||||||
|
setModel: setModel,
|
||||||
|
checkType: checkType,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getLayout(<Component {...pageProps} />)}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
</ChakraProvider>
|
||||||
|
</SSRProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyApp
|
||||||
38
pages/api/login.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { createTokens, hashPassword, verifyPassword } from "../../utils/auth"
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(405).json({ message: "Method Not Allowed" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const { email, password } = req.body
|
||||||
|
console.log(password)
|
||||||
|
|
||||||
|
// You'd need to fetch user details from your database or any data source
|
||||||
|
//fetch data from postgres database where we store password as hash
|
||||||
|
|
||||||
|
// Here, for demonstration, let's assume a hardcoded user
|
||||||
|
const user = {
|
||||||
|
id: 1,
|
||||||
|
email: "user@example.com",
|
||||||
|
passwordHash: "$2a$10$Zy/TYg6WwOOctCQ6e/KOXebvH3AliSDjdopPxrIUWELVJBf/KE.iq", // hashed password: "password123"
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await verifyPassword(password, user.passwordHash)
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return res.status(401).json({ message: "Invalid credentials" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await createTokens(user.id)
|
||||||
|
|
||||||
|
res.setHeader("Set-Cookie", [
|
||||||
|
`token=${token.accessToken}; Path=/; HttpOnly`,
|
||||||
|
`rtoken=${token.refreshToken}; Path=/; HttpOnly`,
|
||||||
|
])
|
||||||
|
|
||||||
|
return res.status(200).json({ token })
|
||||||
|
}
|
||||||
|
|
||||||
|
//user@example.com
|
||||||
|
//password123
|
||||||
7
pages/api/logout.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
// if (req.method === 'POST') {
|
||||||
|
// cookies().delete('token');
|
||||||
|
// } else {
|
||||||
|
// throw new NotFoundError();
|
||||||
|
// }
|
||||||
|
}
|
||||||
35
pages/api/refreshtoken.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { issueAccessToken, verifyRefreshToken } from "../../utils/auth"
|
||||||
|
|
||||||
|
// Example middleware to attach access token to outgoing requests
|
||||||
|
async function attachAccessToken(req, res, next) {
|
||||||
|
const accessToken = req.cookies.accessToken
|
||||||
|
if (!accessToken) {
|
||||||
|
return res.status(401).json({ message: "Access token missing" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// You might want to check token expiration here and refresh if necessary
|
||||||
|
|
||||||
|
req.headers.Authorization = `Bearer ${accessToken}`
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(405).json({ message: "Method Not Allowed" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const { refreshToken } = req.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = await verifyRefreshToken(refreshToken.value)
|
||||||
|
// Check if refresh token is valid, then issue a new access token
|
||||||
|
const accessToken = await issueAccessToken(decoded.userId)
|
||||||
|
console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||||
|
res.setHeader("Set-Cookie", [`token=${accessToken}; Path=/; HttpOnly`])
|
||||||
|
|
||||||
|
return res.status(200).json({ accessToken })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error refreshing token:", error)
|
||||||
|
return res.status(401).json({ message: "Invalid refresh token" })
|
||||||
|
}
|
||||||
|
}
|
||||||
35
pages/content.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Head from "next/head"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
import MyButton from "../components/button"
|
||||||
|
import Header from "../components/headbar"
|
||||||
|
import Logincomponent from "../components/logincomponent"
|
||||||
|
import Signupcomponent from "../components/signupcomponent"
|
||||||
|
|
||||||
|
export default function Content(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const data = await fetch("https://jsonplaceholder.typicode.com/todos").then((response) => {
|
||||||
|
response.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { props: { title: "rahul" } }
|
||||||
|
}
|
||||||
37
pages/home.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
import Layout from "../components/layout"
|
||||||
|
|
||||||
|
export default function Home(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => router.push("/menu")}>Click here to read menu</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const data = await fetch("https://jsonplaceholder.typicode.com/todos").then((response) => {
|
||||||
|
response.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { props: { title: "rahul" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Home.getLayout = function getLayout(page) {
|
||||||
|
return <Layout>{page}</Layout>
|
||||||
|
}
|
||||||
30
pages/index.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Box, Center, Flex, Heading, Text } from "@chakra-ui/react"
|
||||||
|
import React, { useEffect } from "react"
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Center bg="gray.200" height="100vh">
|
||||||
|
<Box textAlign="center">
|
||||||
|
<Heading size="4xl" color={"blue.500"}>
|
||||||
|
Coming Soon
|
||||||
|
</Heading>
|
||||||
|
<Text fontSize="2xl" mt="4">
|
||||||
|
⚠ We are working on it! ⚠
|
||||||
|
</Text>
|
||||||
|
<Flex justifyContent="center" mt="8"></Flex>
|
||||||
|
</Box>
|
||||||
|
</Center>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
28
pages/learningpage/[page].js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import * as me from "melonjs"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
|
||||||
|
import Layout from "../../components/layout"
|
||||||
|
|
||||||
|
export default function Page(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
|
||||||
|
Page.getLayout = function getLayout(page) {
|
||||||
|
return <Layout>{page}</Layout>
|
||||||
|
}
|
||||||
77
pages/learningpath/[learningpath].js
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Button, Error, Grid, Spinner } from "@chakra-ui/react"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
import Layout from "../../components/layout"
|
||||||
|
|
||||||
|
import { learningitemfetcher } from "../../datafetch/datafetch"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* params -
|
||||||
|
* length of path - plength ( the number of tasks )
|
||||||
|
* structure of the path - DAG
|
||||||
|
* type of path to be formed - pathtype
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Menubox(props) {
|
||||||
|
const { data, isLoading, isError } = learningitemfetcher()
|
||||||
|
const [showedcount, setShowedcount] = React.useState(40)
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
if (isLoading) return <Spinner />
|
||||||
|
if (isError) return <Error />
|
||||||
|
|
||||||
|
const container = (
|
||||||
|
<>
|
||||||
|
<Grid templateColumns="repeat(10, 1fr)" padding="70" gap={10}>
|
||||||
|
{data.slice(0, showedcount).map((item) => {
|
||||||
|
if ([1, 22, 3, 24, 5, 26].includes(item.id)) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
style={{ backgroundColor: "red", height: 100 }}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/learningpage/${item.id}`)
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return <div key={100} style={{ backgroundColor: "white", height: 100 }}></div>
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LearningPath(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Menubox />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const data = await fetch("https://jsonplaceholder.typicode.com/todos").then((response) => {
|
||||||
|
response.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { props: { title: "rahul" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
LearningPath.getLayout = function getLayout(page) {
|
||||||
|
return <Layout>{page}</Layout>
|
||||||
|
}
|
||||||
41
pages/login.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
import Layout from "../components/layout"
|
||||||
|
|
||||||
|
import Logincomponent from "../components/logincomponent"
|
||||||
|
import Signupcomponent from "../components/signupcomponent"
|
||||||
|
|
||||||
|
export default function Login(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Logincomponent />
|
||||||
|
<button onClick={() => router.push("/menu")}>Click here to read menu</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const data = await fetch("https://jsonplaceholder.typicode.com/todos").then((response) => {
|
||||||
|
response.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { props: { title: "rahul" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Login.getLayout = function getLayout(page) {
|
||||||
|
return <Layout>{page}</Layout>
|
||||||
|
}
|
||||||
67
pages/menu.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { Button, Error, Grid, Spinner } from "@chakra-ui/react"
|
||||||
|
import React, { useContext, useEffect, useState } from "react"
|
||||||
|
import Layout from "../components/layout"
|
||||||
|
|
||||||
|
import MenuItem from "../components/menuitem"
|
||||||
|
|
||||||
|
import { menuitemfetcher } from "../datafetch/datafetch"
|
||||||
|
|
||||||
|
function Menubox(props) {
|
||||||
|
const { data, isLoading, isError } = menuitemfetcher()
|
||||||
|
const [showedcount, setShowedcount] = React.useState(40)
|
||||||
|
|
||||||
|
if (isLoading) return <Spinner />
|
||||||
|
if (isError) return <Error />
|
||||||
|
|
||||||
|
const container = (
|
||||||
|
<>
|
||||||
|
<Grid
|
||||||
|
templateColumns={["repeat(1, 1fr)", "repeat(1, 1fr)", "repeat(2, 1fr)", "repeat(3, 1fr)", "repeat(4, 1fr)"]}
|
||||||
|
padding={["4%", "5%", "6%", "7%"]}
|
||||||
|
gap={6}
|
||||||
|
>
|
||||||
|
{data.slice(1, showedcount).map((item) => (
|
||||||
|
<MenuItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
itemid={item.userId}
|
||||||
|
title={item.title}
|
||||||
|
summary={item.body}
|
||||||
|
image_url="https://example.com/image1.jpg"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
<Button onClick={() => setShowedcount(showedcount + 40)}></Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MenuPage(props) {
|
||||||
|
const [isloaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isloaded) {
|
||||||
|
console.log("isloaded called")
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return <Menubox />
|
||||||
|
}
|
||||||
|
|
||||||
|
/**This place should be used to get any information from the server side
|
||||||
|
* example : we need to get the session token
|
||||||
|
* or any thing related to the page */
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const data = await fetch("https://jsonplaceholder.typicode.com/todos").then((response) => {
|
||||||
|
response.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { props: { title: "rahul" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuPage.getLayout = function getLayout(page) {
|
||||||
|
return <Layout>{page}</Layout>
|
||||||
|
}
|
||||||
17
pm2.config.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: "nextjs-app",
|
||||||
|
script: "npm",
|
||||||
|
args: "start",
|
||||||
|
instances: "max", // or specify the number of instances you want
|
||||||
|
autorestart: true,
|
||||||
|
watch: false,
|
||||||
|
max_memory_restart: "1G",
|
||||||
|
env: {
|
||||||
|
NODE_ENV: "production",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// You can add more apps as needed for other services you want to manage with PM2
|
||||||
|
],
|
||||||
|
}
|
||||||
BIN
public/SMOR-192.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
public/SMOR-256.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/SMOR-384.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/SMOR-512.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
62
public/firebase-messaging-sw.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Give the service worker access to Firebase Messaging.
|
||||||
|
// Note that you can only use Firebase Messaging here. Other Firebase libraries
|
||||||
|
// are not available in the service worker.
|
||||||
|
importScripts("https://www.gstatic.com/firebasejs/9.2.0/firebase-app-compat.js")
|
||||||
|
importScripts("https://www.gstatic.com/firebasejs/9.2.0/firebase-messaging-compat.js")
|
||||||
|
|
||||||
|
// Initialize the Firebase app in the service worker by passing in
|
||||||
|
// your app's Firebase config object.
|
||||||
|
// https://firebase.google.com/docs/web/setup#config-object
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyARcLufTeUZbGue0-k9iZJVxmKlp0l0HQU",
|
||||||
|
authDomain: "freebees-24743.firebaseapp.com",
|
||||||
|
projectId: "freebees-24743",
|
||||||
|
storageBucket: "freebees-24743.appspot.com",
|
||||||
|
messagingSenderId: "1084271040061",
|
||||||
|
appId: "1:1084271040061:web:91df1dc11b58f5a4807c54",
|
||||||
|
measurementId: "G-6Z61GFCN05",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve an instance of Firebase Messaging so that it can handle background
|
||||||
|
// messages.
|
||||||
|
|
||||||
|
const app = firebase.initializeApp(firebaseConfig)
|
||||||
|
const messaging = firebase.messaging(app)
|
||||||
|
|
||||||
|
// If you would like to customize notifications that are received in the
|
||||||
|
// background (Web app is closed or not in browser focus) then you should
|
||||||
|
// implement this optional method.
|
||||||
|
// Keep in mind that FCM will still show notification messages automatically
|
||||||
|
// and you should use data messages for custom notifications.
|
||||||
|
// For more info see:
|
||||||
|
// https://firebase.google.com/docs/cloud-messaging/concept-options
|
||||||
|
|
||||||
|
messaging.onBackgroundMessage((payload) => {
|
||||||
|
console.log("[firebase-messaging-sw.js] Received background message ", payload)
|
||||||
|
// Customize notification here
|
||||||
|
const Title = payload.data.title
|
||||||
|
const Options = {
|
||||||
|
body: payload.data.body,
|
||||||
|
icon: "/images/SMOR-192.png",
|
||||||
|
}
|
||||||
|
|
||||||
|
self.registration.showNotification(Title, Options)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener("notificationclick", function (event) {
|
||||||
|
event.notification.close()
|
||||||
|
event.waitUntil(
|
||||||
|
clients.matchAll({ type: "window", includeUncontrolled: true }).then(function (clientList) {
|
||||||
|
if (clientList.length > 0) {
|
||||||
|
let client = clientList[0]
|
||||||
|
for (let i = 0; i < clientList.length; i++) {
|
||||||
|
if (clientList[i].focused) {
|
||||||
|
client = clientList[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client.focus()
|
||||||
|
}
|
||||||
|
return clients.openWindow("/")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
BIN
public/freebessimagelight.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/icon-256x256.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
public/images/SMOR-192.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
public/images/SMOR-256.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/SMOR-384.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/images/SMOR-512.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
public/images/no-image.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
4
public/images/vercel.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
30
public/manifest.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "PWA App",
|
||||||
|
"short_name": "App",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gcm_sender_id": "1084271040061",
|
||||||
|
"theme_color": "#007",
|
||||||
|
"background_color": "#007",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BIN
public/marker-icon.png
Normal file
|
After Width: | Height: | Size: 8 KiB |
267
public/sw.js
Normal file
|
|
@ -0,0 +1,267 @@
|
||||||
|
if (!self.define) {
|
||||||
|
let e,
|
||||||
|
s = {}
|
||||||
|
const t = (t, c) => (
|
||||||
|
(t = new URL(t + ".js", c).href),
|
||||||
|
s[t] ||
|
||||||
|
new Promise((s) => {
|
||||||
|
if ("document" in self) {
|
||||||
|
const e = document.createElement("script")
|
||||||
|
;(e.src = t), (e.onload = s), document.head.appendChild(e)
|
||||||
|
} else (e = t), importScripts(t), s()
|
||||||
|
}).then(() => {
|
||||||
|
let e = s[t]
|
||||||
|
if (!e) throw new Error(`Module ${t} didn’t register its module`)
|
||||||
|
return e
|
||||||
|
})
|
||||||
|
)
|
||||||
|
self.define = (c, n) => {
|
||||||
|
const i = e || ("document" in self ? document.currentScript.src : "") || location.href
|
||||||
|
if (s[i]) return
|
||||||
|
let a = {}
|
||||||
|
const l = (e) => t(e, i),
|
||||||
|
u = { module: { uri: i }, exports: a, require: l }
|
||||||
|
s[i] = Promise.all(c.map((e) => u[e] || l(e))).then((e) => (n(...e), a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
define(["./workbox-1846d813"], function (e) {
|
||||||
|
"use strict"
|
||||||
|
importScripts("worker-WcZyltu4AIEZdlepY3Peo.js"),
|
||||||
|
self.skipWaiting(),
|
||||||
|
e.clientsClaim(),
|
||||||
|
e.precacheAndRoute(
|
||||||
|
[
|
||||||
|
{ url: "/SMOR-192.png", revision: "694a5d68bb01b831f6c9b5c7d43f0c27" },
|
||||||
|
{ url: "/SMOR-256.png", revision: "93baa0d7792e6776ccfbb917ed8fb1e8" },
|
||||||
|
{ url: "/SMOR-384.png", revision: "c0ef024f0903ba73d308e2dee7889bf9" },
|
||||||
|
{ url: "/SMOR-512.png", revision: "243de524262c12c7e00403a4b5dc14c5" },
|
||||||
|
{ url: "/_next/static/WcZyltu4AIEZdlepY3Peo/_buildManifest.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/WcZyltu4AIEZdlepY3Peo/_middlewareManifest.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/WcZyltu4AIEZdlepY3Peo/_ssgManifest.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/0b7b90cd.23a679f790af5ada.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/1120-4b6c01efbb660cb0.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/1443-c9911baf6aa6969d.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/1857-4a754531d0dc3ce7.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/1944-1172cffe798b2d2d.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/1bfc9850-a5e52aab60a2bf09.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/2022-0b1731e5d5e56391.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/252f366e-25667f96c249ca01.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/3516-4d6afd5e3449cb6f.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/3761-100286903250d54e.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/4067.cc92d8e1edb00a0b.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/4321-45e31c436c8cb7a8.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/4355-471e0659b91aaa0f.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/4356-91fe2cc212fb70f3.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/5302-b90e4a54f702b9b8.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/5365-49d12afab2725ba2.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/5575-2326a978162761c3.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/5979-6f2587baf37a9775.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/6046-38951b11790c6f85.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/6220-3632eefa25b908ea.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/6602-15be9cec14ac902e.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7005-d08d38268c11f695.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7019-594327e28c484778.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7164.b91874f80a184dd3.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7238-8610a6af415f8caf.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7259-0a5f5ceb4cbcd248.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/7411-111c6e401cb97781.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/8423-fc8e044710f19317.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/8608-e397dfae632fb839.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/9651.5057209db3881ec8.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/framework-428dc724d9086efb.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/main-04e4151d30f71ab2.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/_app-d8b8016d7bea506b.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/_error-0b1a861ffa2edde1.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/address-b7eca4e9b569fd1c.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/bookingreqpage-e8e1a6f68f8bb6c4.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/dashboard-1bdf95290ba665cd.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/home-6ee91d9bf555a563.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/index-ffda4f32514d6aa0.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/info-5a0660f1bea67378.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/itempage-69d6598aef5ec5da.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{
|
||||||
|
url: "/_next/static/chunks/pages/itempage/%5Bitemkey%5D-c2f6ebad6a59057d.js",
|
||||||
|
revision: "WcZyltu4AIEZdlepY3Peo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: "/_next/static/chunks/pages/items/%5Buser_lender%5D-33ac1a4b581b143a.js",
|
||||||
|
revision: "WcZyltu4AIEZdlepY3Peo",
|
||||||
|
},
|
||||||
|
{ url: "/_next/static/chunks/pages/itemswindow-706741772e4ee171.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/login-df75183f3fdaf6f4.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/mappage-95b6b631665d0b5d.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/newItem-76961819cc2fa4f2.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/orders-5637e34fa4aeb00c.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/privacypolicy-fea0a9e36d408ce0.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/profile-d750e0bec23d4699.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/searchpage-a20e60074cffe8c4.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/settings-fdcc39f57cd60718.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/signup-56863616abb5769b.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/splash-4020a016f3a18b60.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/taskpage-c2cea2257c510db0.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/welcome-b00b5fc8b217a709.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/wishlist-d8d72984251367c7.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/pages/workpage-dd025f82110469c5.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/polyfills-5cd94c89d3acac5f.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/chunks/webpack-bde62137e53dadca.js", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/css/4ac61f15fc19c268.css", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/css/6c1a23dd8a183dbb.css", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/media/layers-2x.9859cd12.png", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/media/layers.ef6db872.png", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/_next/static/media/marker-icon.d577052a.png", revision: "WcZyltu4AIEZdlepY3Peo" },
|
||||||
|
{ url: "/favicon.ico", revision: "666586677e60428aa5e58d13871736c7" },
|
||||||
|
{ url: "/firebase-messaging-sw.js", revision: "e387921962396472d67f290af1777d55" },
|
||||||
|
{ url: "/freebessimagelight.png", revision: "7e742e758c9028e8bd0c4345655e93c4" },
|
||||||
|
{ url: "/icon-192x192.png", revision: "ed26a1bbca33bba35e0e25bccec594f1" },
|
||||||
|
{ url: "/icon-256x256.png", revision: "ed2c6547ed3aeea9df44fe44bc30007f" },
|
||||||
|
{ url: "/icon-384x384.png", revision: "22a4a4214cd9cfa1b401091618ca0ac9" },
|
||||||
|
{ url: "/icon-512x512.png", revision: "562865edb386df86d7c72587c9096528" },
|
||||||
|
{ url: "/images/SMOR-192.png", revision: "3023f02d03ea538d8e3c39bdf0de383d" },
|
||||||
|
{ url: "/images/SMOR-512.png", revision: "c446f4acb68301e0d833488005e82085" },
|
||||||
|
{ url: "/images/no-image.png", revision: "12f9ee78f32722610c775ca7375c0c11" },
|
||||||
|
{ url: "/images/vercel.svg", revision: "26bf2d0adaf1028a4d4c6ee77005e819" },
|
||||||
|
{ url: "/manifest.json", revision: "d42be44d1990847c3d9aab15fbefdc00" },
|
||||||
|
{ url: "/marker-icon.png", revision: "3023f02d03ea538d8e3c39bdf0de383d" },
|
||||||
|
{ url: "/vercel.svg", revision: "26bf2d0adaf1028a4d4c6ee77005e819" },
|
||||||
|
],
|
||||||
|
{ ignoreURLParametersMatching: [] }
|
||||||
|
),
|
||||||
|
e.cleanupOutdatedCaches(),
|
||||||
|
e.registerRoute(
|
||||||
|
"/",
|
||||||
|
new e.NetworkFirst({
|
||||||
|
cacheName: "start-url",
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
cacheWillUpdate: async ({ request: e, response: s, event: t, state: c }) =>
|
||||||
|
s && "opaqueredirect" === s.type
|
||||||
|
? new Response(s.body, { status: 200, statusText: "OK", headers: s.headers })
|
||||||
|
: s,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,
|
||||||
|
new e.CacheFirst({
|
||||||
|
cacheName: "google-fonts-webfonts",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 4, maxAgeSeconds: 31536e3 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "google-fonts-stylesheets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 4, maxAgeSeconds: 604800 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "static-font-assets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 4, maxAgeSeconds: 604800 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "static-image-assets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 64, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\/_next\/image\?url=.+$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "next-image",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 64, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:mp3|wav|ogg)$/i,
|
||||||
|
new e.CacheFirst({
|
||||||
|
cacheName: "static-audio-assets",
|
||||||
|
plugins: [new e.RangeRequestsPlugin(), new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:mp4)$/i,
|
||||||
|
new e.CacheFirst({
|
||||||
|
cacheName: "static-video-assets",
|
||||||
|
plugins: [new e.RangeRequestsPlugin(), new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:js)$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "static-js-assets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:css|less)$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "static-style-assets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\/_next\/data\/.+\/.+\.json$/i,
|
||||||
|
new e.StaleWhileRevalidate({
|
||||||
|
cacheName: "next-data",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
/\.(?:json|xml|csv)$/i,
|
||||||
|
new e.NetworkFirst({
|
||||||
|
cacheName: "static-data-assets",
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
({ url: e }) => {
|
||||||
|
if (!(self.origin === e.origin)) return !1
|
||||||
|
const s = e.pathname
|
||||||
|
return !s.startsWith("/api/auth/") && !!s.startsWith("/api/")
|
||||||
|
},
|
||||||
|
new e.NetworkFirst({
|
||||||
|
cacheName: "apis",
|
||||||
|
networkTimeoutSeconds: 10,
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 16, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
({ url: e }) => {
|
||||||
|
if (!(self.origin === e.origin)) return !1
|
||||||
|
return !e.pathname.startsWith("/api/")
|
||||||
|
},
|
||||||
|
new e.NetworkFirst({
|
||||||
|
cacheName: "others",
|
||||||
|
networkTimeoutSeconds: 10,
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 86400 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
),
|
||||||
|
e.registerRoute(
|
||||||
|
({ url: e }) => !(self.origin === e.origin),
|
||||||
|
new e.NetworkFirst({
|
||||||
|
cacheName: "cross-origin",
|
||||||
|
networkTimeoutSeconds: 10,
|
||||||
|
plugins: [new e.ExpirationPlugin({ maxEntries: 32, maxAgeSeconds: 3600 })],
|
||||||
|
}),
|
||||||
|
"GET"
|
||||||
|
)
|
||||||
|
})
|
||||||
4
public/vercel.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1068
public/workbox-1846d813.js
Normal file
0
styles/Home.module.css
Normal file
143
styles/globals.css
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
/* background-color: #1f2833; */
|
||||||
|
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 680px) {
|
||||||
|
.Login {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.Login form {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
|
||||||
|
/* display: flex; */
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: sans-serif;
|
||||||
|
/*background: #12c2e9; /* fallback for old browsers */
|
||||||
|
/*background: -webkit-linear-gradient(to right, #f64f59, #c471ed, #12c2e9); /* Chrome 10-25, Safari 5.1-6 */
|
||||||
|
/*background: linear-gradient(to right, #f64f59, #c471ed, #12c2e9); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body, html, .App, #root, .outer {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-light {
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0px 14px 80px rgba(34, 35, 58, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
/* justify-content: center; */
|
||||||
|
flex-direction: column;
|
||||||
|
/* margin: auto; */
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
inner {
|
||||||
|
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 14px 80px rgba(34, 35, 58, 0.2);
|
||||||
|
padding: 40px 30px 45px 30px;
|
||||||
|
border-radius: 15px;
|
||||||
|
transition: all .3s;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer .form-control:focus {
|
||||||
|
border-color: #167bff;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer h3 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-control-label {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password,
|
||||||
|
.forgot-password a {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 13px;
|
||||||
|
padding-top: 10px;
|
||||||
|
color: #7f7d7d;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password a {
|
||||||
|
color: #167bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.nav{
|
||||||
|
border: 1 solid #45a29e !important;
|
||||||
|
}
|
||||||
|
.nav-link {
|
||||||
|
color: aqua !important;
|
||||||
|
border-bottom: 1px solid #45a29e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active {
|
||||||
|
color:#1f2833 !important;
|
||||||
|
border-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading-spinner {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 10px solid #66fcf1; /* Light grey */
|
||||||
|
border-top: 10px solid #45a29e; /* Black */
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spinner 1.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,12 @@ module.exports = {
|
||||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
|
screens: {
|
||||||
|
sm: "480px",
|
||||||
|
md: "768px",
|
||||||
|
lg: "976px",
|
||||||
|
xl: "1440px",
|
||||||
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: {
|
primary: {
|
||||||
|
|
|
||||||
252
testdata/items.json
vendored
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 1",
|
||||||
|
"image_url": "https://example.com/image1.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 2",
|
||||||
|
"image_url": "https://example.com/image2.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 3",
|
||||||
|
"image_url": "https://example.com/image3.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 4",
|
||||||
|
"image_url": "https://example.com/image4.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 5",
|
||||||
|
"image_url": "https://example.com/image5.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 6",
|
||||||
|
"image_url": "https://example.com/image6.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 7",
|
||||||
|
"image_url": "https://example.com/image7.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 8",
|
||||||
|
"image_url": "https://example.com/image8.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 9",
|
||||||
|
"image_url": "https://example.com/image9.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 10",
|
||||||
|
"image_url": "https://example.com/image10.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 11",
|
||||||
|
"image_url": "https://example.com/image11.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 12",
|
||||||
|
"image_url": "https://example.com/image12.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 13",
|
||||||
|
"image_url": "https://example.com/image13.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 14",
|
||||||
|
"image_url": "https://example.com/image14.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 15",
|
||||||
|
"image_url": "https://example.com/image15.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 16",
|
||||||
|
"image_url": "https://example.com/image16.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 17",
|
||||||
|
"image_url": "https://example.com/image17.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 18",
|
||||||
|
"image_url": "https://example.com/image18.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 19",
|
||||||
|
"image_url": "https://example.com/image19.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 20",
|
||||||
|
"image_url": "https://example.com/image20.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 21",
|
||||||
|
"image_url": "https://example.com/image21.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 22",
|
||||||
|
"image_url": "https://example.com/image22.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 23",
|
||||||
|
"image_url": "https://example.com/image23.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 24",
|
||||||
|
"image_url": "https://example.com/image24.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 25",
|
||||||
|
"image_url": "https://example.com/image25.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 26",
|
||||||
|
"image_url": "https://example.com/image26.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 27",
|
||||||
|
"image_url": "https://example.com/image27.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 28",
|
||||||
|
"image_url": "https://example.com/image28.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 29",
|
||||||
|
"image_url": "https://example.com/image29.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 30",
|
||||||
|
"image_url": "https://example.com/image30.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 31",
|
||||||
|
"image_url": "https://example.com/image31.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 32",
|
||||||
|
"image_url": "https://example.com/image32.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 33",
|
||||||
|
"image_url": "https://example.com/image33.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 34",
|
||||||
|
"image_url": "https://example.com/image34.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 35",
|
||||||
|
"image_url": "https://example.com/image35.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 36",
|
||||||
|
"image_url": "https://example.com/image36.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 37",
|
||||||
|
"image_url": "https://example.com/image37.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 38",
|
||||||
|
"image_url": "https://example.com/image38.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 39",
|
||||||
|
"image_url": "https://example.com/image39.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 40",
|
||||||
|
"image_url": "https://example.com/image40.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 41",
|
||||||
|
"image_url": "https://example.com/image41.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 42",
|
||||||
|
"image_url": "https://example.com/image42.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 43",
|
||||||
|
"image_url": "https://example.com/image43.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 44",
|
||||||
|
"image_url": "https://example.com/image44.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 45",
|
||||||
|
"image_url": "https://example.com/image45.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 46",
|
||||||
|
"image_url": "https://example.com/image46.jpg",
|
||||||
|
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 47",
|
||||||
|
"image_url": "https://example.com/image47.jpg",
|
||||||
|
"summary": "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 48",
|
||||||
|
"image_url": "https://example.com/image48.jpg",
|
||||||
|
"summary": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 49",
|
||||||
|
"image_url": "https://example.com/image49.jpg",
|
||||||
|
"summary": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lorem Ipsum 50",
|
||||||
|
"image_url": "https://example.com/image50.jpg",
|
||||||
|
"summary": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
}
|
||||||
|
]
|
||||||
39
themes.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// export const CLR_HEAD = '#ffffff' ;
|
||||||
|
|
||||||
|
// export const CLR_RCARD1 = 'orange';
|
||||||
|
|
||||||
|
// export const CLR_RCARD2 = 'orange';
|
||||||
|
|
||||||
|
// export const CLR_RCARD3 = '#ffffff';
|
||||||
|
|
||||||
|
// export const CLR_RCARD4 = '#ffffff';
|
||||||
|
|
||||||
|
// export const CLR_FBAR = '#ffffff';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// export const CLR_HEAD = '#1f2833' ;
|
||||||
|
|
||||||
|
// export const CLR_HEAD1 = '#1f2833' ;// 1f2833
|
||||||
|
|
||||||
|
// export const CLR_RCARD1 = '#45a29e'; //1F2833
|
||||||
|
|
||||||
|
// export const CLR_RCARD2 = '#66fcf1';
|
||||||
|
|
||||||
|
// export const CLR_RCARD3 = '#c5c6c7';
|
||||||
|
|
||||||
|
// export const CLR_FBAR = '#0b0c10';
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
export const CLR_HEAD = "#1f2833"
|
||||||
|
|
||||||
|
export const CLR_HEAD1 = "#1f2833" // 1f2833
|
||||||
|
|
||||||
|
export const CLR_RCARD1 = "#45a29e" //1F2833
|
||||||
|
|
||||||
|
export const CLR_RCARD2 = "#66fcf1"
|
||||||
|
|
||||||
|
export const CLR_RCARD3 = "#c5c6c7"
|
||||||
|
|
||||||
|
export const CLR_FBAR = "#0b0c10"
|
||||||
|
|
@ -25,6 +25,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.mjs", "jest.config.js", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.mjs", "jest.config.js", ".next/types/**/*.ts", "next.config.js", "next.config.js"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
111
utils/auth.js
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// utils/auth.js
|
||||||
|
import bcrypt from "bcryptjs"
|
||||||
|
|
||||||
|
import { jwtVerify, SignJWT } from "jose"
|
||||||
|
|
||||||
|
const secret = new TextEncoder().encode("Swe4g7c?UBm5Nrd96vhsVDtkyJFbqKMTm!TMw5BDRLtaCFAXNvbq?s4rGKQSZnUP")
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
async function generateToken(payload, expiresIn) {
|
||||||
|
const algo = "HS256"
|
||||||
|
const t = await new SignJWT(payload)
|
||||||
|
.setProtectedHeader({ alg: algo })
|
||||||
|
.setIssuedAt()
|
||||||
|
.setExpirationTime(expiresIn)
|
||||||
|
.sign(secret)
|
||||||
|
|
||||||
|
console.log(t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to create both access and refresh tokens
|
||||||
|
export async function createTokens(userId) {
|
||||||
|
const accessTokenPayload = {
|
||||||
|
userId: userId,
|
||||||
|
// exp: Math.floor(Date.now() / 1000) + (15 * 60), // Access token expires in 15 minutes
|
||||||
|
// iss: 'your_issuer_here', // Replace with your actual issuer
|
||||||
|
// aud: 'your_audience_here', // Replace with your actual audience
|
||||||
|
purpose: "access", // Indicate the purpose of this token
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshTokenPayload = {
|
||||||
|
userId: userId,
|
||||||
|
// exp: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60), // Refresh token expires in 7 days
|
||||||
|
// iss: 'your_issuer_here', // Replace with your actual issuer
|
||||||
|
// aud: 'your_audience_here', // Replace with your actual audience
|
||||||
|
purpose: "refresh", // Indicate the purpose of this token
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = await generateToken(accessTokenPayload, "15m") // 15 minutes expiry
|
||||||
|
const refreshToken = await generateToken(refreshTokenPayload, "2d") // 2 days expiry
|
||||||
|
|
||||||
|
return { accessToken, refreshToken }
|
||||||
|
}
|
||||||
|
///
|
||||||
|
|
||||||
|
export const hashPassword = async (password) => {
|
||||||
|
const salt = await bcrypt.genSalt(10)
|
||||||
|
return bcrypt.hash(password, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifyPassword = async (password, hashedPassword) => {
|
||||||
|
return bcrypt.compare(password, hashedPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const issueAccessToken = async (userId) => {
|
||||||
|
const algo = "HS256"
|
||||||
|
const accessTokenPayload = {
|
||||||
|
userId: userId,
|
||||||
|
// exp: Math.floor(Date.now() / 1000) + (15 * 60), // Access token expires in 15 minutes
|
||||||
|
// iss: 'your_issuer_here', // Replace with your actual issuer
|
||||||
|
// aud: 'your_audience_here', // Replace with your actual audience
|
||||||
|
purpose: "access", // Indicate the purpose of this token
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = await generateToken(accessTokenPayload, "1m") // 15 minutes expiry
|
||||||
|
|
||||||
|
return accessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function verifies a refresh token using a shared secret key
|
||||||
|
export async function verifyRefreshToken(refreshToken) {
|
||||||
|
try {
|
||||||
|
// Verify the refresh token using the shared secret key
|
||||||
|
|
||||||
|
const { payload, protectedHeader } = await jwtVerify(refreshToken, secret)
|
||||||
|
console.log(payload)
|
||||||
|
// Ensure the token has necessary fields and is not expired
|
||||||
|
if (!payload.userId) {
|
||||||
|
throw new Error("Invalid token format")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, you can also check if the token is intended for refresh purposes only
|
||||||
|
if (payload.purpose !== "refresh") {
|
||||||
|
throw new Error("Invalid token purpose")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is valid, return the decoded token
|
||||||
|
return payload
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
throw new Error("Invalid refresh token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function refreshAccessToken(refreshToken) {
|
||||||
|
try {
|
||||||
|
const response = await fetch("http://localhost:3000/api/refreshtoken", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ refreshToken }),
|
||||||
|
})
|
||||||
|
const data = await response.json()
|
||||||
|
return data.accessToken
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error refreshing access token:", error)
|
||||||
|
throw new Error("Failed to refresh access token")
|
||||||
|
}
|
||||||
|
}
|
||||||
567
utils/categories.js
Normal file
|
|
@ -0,0 +1,567 @@
|
||||||
|
export const categoriesjson = [
|
||||||
|
{
|
||||||
|
category: "Clothes men",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "suits",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Cookwares",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Kadhai",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Tawa",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Mixer",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Kitchen Equipments",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Fryer",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Oven",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Minifridge",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Holidays & travel",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Camping",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Tents", "Camping Chairs", "Cooking Equipment", "Other Camping Equipments"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Luggage",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Suitcases", "Travellers Backpacks", "Backpacks", "Other Luggage"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Pool/Sea Toys",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Holiday & Travel",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Party & events",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Special Effects",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Disco Lighting, Lasers & Strobe", "Bubble Machines", "Smoke MAchines", "Other Special Effects"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Staging",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Entertainment",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Karaoke Equipment", "Photo Booths", "Adult Party Games", "Other Event Entertainment"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Catering Equipment",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Display Cabinets / Fridges",
|
||||||
|
"Hot Plates",
|
||||||
|
"Walk-in Fridge Trailers",
|
||||||
|
"Cooking Equipment",
|
||||||
|
"Cutlery, Crockery & Glassware",
|
||||||
|
"Other CAtering Equipment",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Special Effects",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Wedding Decorations", "Christmas Decorations", "Centerpieces", "Other Decorations"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Portaloos",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Event Furniture",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Seating",
|
||||||
|
"Bars",
|
||||||
|
"Smoke MAchines",
|
||||||
|
"LED furniture",
|
||||||
|
"Tables",
|
||||||
|
"Furniture Accessories",
|
||||||
|
"Other Furniture",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Tents & Marquees",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Gazebose", "Marquees", "MArquee Accessories"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Event Lighting",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"DJ, Disco & Stage Lights",
|
||||||
|
"Fairy & Festoon Lights",
|
||||||
|
"Light-up Numbers & Letters",
|
||||||
|
"Other Event Lighting",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Party Package Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other PArty",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Film & Photography",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Cameras",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"DSLR Cameras",
|
||||||
|
"Digital Cinema Cameras",
|
||||||
|
"Camcorders",
|
||||||
|
"GoPros & Headcams",
|
||||||
|
"Film Cameras",
|
||||||
|
"Polaroid",
|
||||||
|
"360 Cameras",
|
||||||
|
"Mirrorless Cameras",
|
||||||
|
"Other Cameras",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Lenses",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Cinema Prime Lenses",
|
||||||
|
"DLSR Lenses",
|
||||||
|
"Medium Format Lenses",
|
||||||
|
"Mirrorless Lenses",
|
||||||
|
"Lens Accessories",
|
||||||
|
"Other Lenses",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Accessories",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Tripods & Heads",
|
||||||
|
"Rigs / Stabiliserws / Gimbals",
|
||||||
|
"Bags and Storage",
|
||||||
|
"Dollies / Sliders",
|
||||||
|
"Grips / Stands / Clamps",
|
||||||
|
"Flashes",
|
||||||
|
"Follow Focus & Lens Support",
|
||||||
|
"Other Accessories",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Lighting",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Continuous Lighting",
|
||||||
|
"Backgrounds & Supports",
|
||||||
|
"Stands , Mounts & Accessories",
|
||||||
|
"Otehr Film & Photography Lighting",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Cameras With Operators",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Photography Studios",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Camera Package Deals / Kits",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Film & Photography",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Drones",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "DJI Drones",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Spark Series",
|
||||||
|
"Mavic Series",
|
||||||
|
"Phantom Series",
|
||||||
|
"Inspire Series",
|
||||||
|
"Industrial Drones",
|
||||||
|
"Other DJI Drones",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Drones With Operators",
|
||||||
|
hassub: 0,
|
||||||
|
subs: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Drone Accessories",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Cases & Bags", "Batteries", "Other Drone Accessories"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Drone PAckage Deals",
|
||||||
|
hassub: 0,
|
||||||
|
subs: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Drones",
|
||||||
|
hassub: 0,
|
||||||
|
subs: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Sports",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Racket Sports",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Squash", "Tennis", "Badminton", "Table Tennis", "Other Racket Sport Equipment"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Gym Equipment",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Free Weights", "Gym MAchines", "Other Gym Equipments"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Water Sports",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Swimming Goggles",
|
||||||
|
"Wetsuits",
|
||||||
|
"Snorkelling Equipment",
|
||||||
|
"Kiteboarding Equipment",
|
||||||
|
"Sailing & Boats",
|
||||||
|
"Kayak & Canoes",
|
||||||
|
"Surfboards",
|
||||||
|
"Windsurfing Equipment",
|
||||||
|
"Jet Skis",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Winter Sports",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Skis",
|
||||||
|
"Snowboards",
|
||||||
|
"Ski Boots",
|
||||||
|
"Snowboard Boots",
|
||||||
|
"Ski & Snowboard Clothing",
|
||||||
|
"Ski & Snowboard Accessories",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Outdoor Sports",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Golf", "Rugby", "Cricket", "Football", "Triathalon", "Hiking", "Surfboards", "Other Outdoor Sports"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Gym Equipment",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Free Weights", "Gym MAchines", "Other Gym Equipments"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Country Sports",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Fishing Equipment", "Horse Riding Equipment", "Shooting Equipment"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Sports Package Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Sports",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Kids",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Babies & Toddlers",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Baby Car Seats",
|
||||||
|
"Booster Seats",
|
||||||
|
"Baby Carriers & Backpacks",
|
||||||
|
"Prams & Pushchairs",
|
||||||
|
"Highchairs",
|
||||||
|
"Bouncers / Swings",
|
||||||
|
"Breast Pumps",
|
||||||
|
"Cots",
|
||||||
|
"Baby Monitors",
|
||||||
|
"Play Pens",
|
||||||
|
"Bottle Sterilisers",
|
||||||
|
"Baby Toys",
|
||||||
|
"Baby Toys",
|
||||||
|
"Other Babies & Toddlers",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Kids",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Hoverboards, Scooters & Rollerblades",
|
||||||
|
"Water Toys",
|
||||||
|
"Bikes",
|
||||||
|
"Beanbags",
|
||||||
|
"Other Toys",
|
||||||
|
"Lawn Toys",
|
||||||
|
"Other Kids",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Kids Parties",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Bouncy Castles", "Inflatables", "Soft Play", "Other Kids Parties"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Kids & Baby Package Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Kids & Baby",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Audio Visual Equipment",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "PA Systems",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Small Venues (<100 people)", "Medium Venues (100-300 people)", "Large Venues (300+ people)"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Speakers",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Speaker Systems", "Portable Speakers", "Moniter Systems", "Other Speakers"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Microphones",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Recording Mics",
|
||||||
|
"Wireless Mics / Systems",
|
||||||
|
"Condenser Mics",
|
||||||
|
"Condenser Mics",
|
||||||
|
"USB Mics",
|
||||||
|
"Standard Mics",
|
||||||
|
"Other Mics",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Recording Equipment",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Recorders",
|
||||||
|
"Studio, Samplers & Sequencers",
|
||||||
|
"Interfaces, Consoles & Controllers",
|
||||||
|
"Headphones",
|
||||||
|
"Sound Blimps",
|
||||||
|
"Other Recording Equipment",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Recording Studios",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Audio Visual PAckage Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Audio Visual",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Dj Equipment",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Decks & Turntables",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Mixers",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Controllers",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "DJ Equipment with Operator",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "DJ Equipment Package Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other DJ Equipment",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Electronics",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Computer",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Laptops",
|
||||||
|
"Ipads & Tablets",
|
||||||
|
"Desktops",
|
||||||
|
"PC Moniters",
|
||||||
|
"Printers & Scanners",
|
||||||
|
"Servers",
|
||||||
|
"Computer Accessories",
|
||||||
|
"Other Computer",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Entertainment & Gaming",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"TVs",
|
||||||
|
"TV Accessories",
|
||||||
|
"Kindles",
|
||||||
|
"Xbox Consoles & Games",
|
||||||
|
"Playstation Consoles & Games",
|
||||||
|
"Nintendo Consoles & Games",
|
||||||
|
"Other Entertainment & Gaming",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Virtual Reality",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Headsets", "Games", "Other Virtual Reality"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Personal Electronics",
|
||||||
|
hassub: 1,
|
||||||
|
subs: [
|
||||||
|
"Mobile Phones",
|
||||||
|
"Smartwatches",
|
||||||
|
"Headphones",
|
||||||
|
"Fitbits & Fitness Electronics",
|
||||||
|
"Other Personal Electronics",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Electronics Package Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Electronics",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Musical Instrument",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Guitars",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Acoustic", "Bass", "Electric", "Pedals", "Guitar Accessories", "Other Guitars"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Amps",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Guitar Amps", "Bass Amps", "Piano Amps", "Other Amps"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Keyboards & Synthesisers",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Electric Keyboards", "Synthesisers", "Pianos", "Other Keyboards"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "String Instruments",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Violins", "Double Bass", "Banjos", "Ouds", "Mandolins", "Ukuele", "Other String Instruments"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Brass",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Trumpet", "Trombone", "Cornet", "Other Brass"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Woodwind",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Saxophone", "Fluter", "Carinet", "Recorders", "Other Woodwind"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Percussion",
|
||||||
|
hassub: 1,
|
||||||
|
subs: ["Drums", "Cymbals", "Other Percussion"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Instrument Accessories",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Musical Instruments Deals",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
item: "Other Musical Instrument",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Others",
|
||||||
|
subcategories: [
|
||||||
|
{
|
||||||
|
item: "Other things",
|
||||||
|
hassub: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
28
utils/themes.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { extendTheme } from "@chakra-ui/react"
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
primary: {
|
||||||
|
100: "#B5FCF1",
|
||||||
|
200: "#27EF96",
|
||||||
|
300: "#10DE82",
|
||||||
|
400: "#0EBE6F",
|
||||||
|
500: "#0CA25F",
|
||||||
|
600: "#0A864F",
|
||||||
|
700: "#086F42",
|
||||||
|
800: "#075C37",
|
||||||
|
900: "#064C2E",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const breakpoints = {
|
||||||
|
base: "0em", // 0px
|
||||||
|
sm: "30em", // ~480px. em is a relative unit and is dependant on the font size.
|
||||||
|
md: "48em", // ~768px
|
||||||
|
lg: "62em", // ~992px
|
||||||
|
xl: "80em", // ~1280px
|
||||||
|
"2xl": "96em", // ~1536px
|
||||||
|
}
|
||||||
|
|
||||||
|
const customTheme = extendTheme({ colors, breakpoints })
|
||||||
|
|
||||||
|
export default customTheme
|
||||||