contact page implemented

This commit is contained in:
Cypro Freelance 2024-07-03 22:59:49 +05:30
parent 8dfb3325e8
commit 73fb83b904
13 changed files with 723 additions and 14 deletions

View file

@ -7,3 +7,6 @@ ADMIN_USERNAME=
ADMIN_PASSWORD= ADMIN_PASSWORD=
NEXTAUTH_SECRET= NEXTAUTH_SECRET=
EMAIL=
EMAIL_PASS=

148
app/(root)/contact/page.tsx Normal file
View file

@ -0,0 +1,148 @@
"use client";
import Iconss from "@/components/ui/icons";
import { MapPin, Phone, Mail, Send } from "lucide-react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { contactFormSchema } from "@/lib/validations/validation";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { sendContactForm } from "@/lib/api/contact";
const ContactUs = () => {
const form = useForm<z.infer<typeof contactFormSchema>>({
resolver: zodResolver(contactFormSchema),
defaultValues: {
name: "",
email: "",
message: "",
},
});
async function onSubmit(values: z.infer<typeof contactFormSchema>) {
await sendContactForm(values);
console.log(values);
}
return (
<>
<div className="flex items-center justify-center py-16 md:py-24 w-full transition-all duration-300">
<h1 className="text-4xl md:text-6xl tracking-tight font-bold uppercase">
Contact Us
</h1>
</div>
<section id="contact" className="w-full">
<div className="flex flex-col md:flex-row justify-between mx-auto p-5 max-w-3xl">
{/* Left contact page */}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4 pb-8 mb-4 max-w-sm w-full"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Your Name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="Your Email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<FormItem>
<FormLabel>Message</FormLabel>
<FormControl>
<Textarea placeholder="Your Message" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" variant={"default"} className="w-full">
<div className="flex items-center justify-center">
<span className="tracking-tight font-[600px]">SEND</span>
<Send className="ml-2 w-5 h-5" />
</div>
</Button>
</form>
</Form>
{/* Right contact page */}
<div className="max-w-xl mt-3 md:ml-5">
<ul className="ml-2 max-md:flex-center flex-col pr-5">
<li className="mb-2 text-[#aaa] flex items-center">
<MapPin className="mr-2" size={24} />
<span>webmaster@svrjs.org</span>
</li>
<li className="mb-2 text-[#aaa] flex items-center">
<Phone className="mr-2" size={24} />
<span>
<a
href="tel:1-212-555-5555"
title="Give me a call"
className="text-[#bbb] hover:text-white transition duration-200"
>
bugreports@svrjs.org
</a>
</span>
</li>
<li className="mb-2 text-[#aaa] flex items-center">
<Mail className="mr-2" size={24} />
<span>
<a
href="mailto:#"
title="Send me an email"
className="text-[#bbb] hover:text-white transition duration-200"
>
support@svrjs.org
</a>
</span>
</li>
</ul>
<hr className="border-t border-white opacity-60 my-4" />
<ul className="flex justify-center space-x-3 mb-6">
<Iconss />
</ul>
<hr className="border-t border-white opacity-60 my-4" />
<div className="text-center text-[#555] text-sm font-light">
© ALL RIGHTS RESERVED
</div>
</div>
</div>
</section>
</>
);
};
export default ContactUs;

113
app/api/contact/route.ts Normal file
View file

@ -0,0 +1,113 @@
import { mailOptions, transporter } from "@/lib/nodemailer/nodemailer";
import { NextRequest, NextResponse } from "next/server";
const CONTACT_MESSAGE_FIELDS: Record<string, string> = {
name: "Name",
email: "Email",
message: "Message",
};
const generateEmailContent = (data: Record<string, string>) => {
const stringData = Object.entries(data).reduce(
(str, [key, val]) =>
str + `${CONTACT_MESSAGE_FIELDS[key] || key}: \n${val} \n\n`,
""
);
const htmlData = Object.entries(data).reduce(
(str, [key, val]) =>
str +
`<h3 class="form-heading">${
CONTACT_MESSAGE_FIELDS[key] || key
}</h3><p class="form-answer">${val}</p>`,
""
);
return {
text: stringData,
html: `<!DOCTYPE html>
<html>
<head>
<title>Contact Email</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<style type="text/css">
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table { border-collapse: collapse !important; }
body { height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important; }
@media screen and (max-width: 525px) {
.wrapper { width: 100% !important; max-width: 100% !important; }
.responsive-table { width: 100% !important; }
.padding { padding: 10px 5% 15px 5% !important; }
.section-padding { padding: 0 15px 50px 15px !important; }
}
.form-container { margin-bottom: 24px; padding: 20px; border: 1px dashed #ccc; }
.form-heading { color: #2a2a2a; font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-weight: 400; text-align: left; line-height: 20px; font-size: 18px; margin: 0 0 8px; padding: 0; }
.form-answer { color: #2a2a2a; font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-weight: 300; text-align: left; line-height: 20px; font-size: 16px; margin: 0 0 24px; padding: 0; }
div[style*="margin: 16px 0;"] { margin: 0 !important; }
</style>
</head>
<body style="margin: 0 !important; padding: 0 !important; background: #fff">
<div style="display: none; font-size: 1px; color: #fefefe; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden;"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td bgcolor="#ffffff" align="center" style="padding: 10px 15px 30px 15px" class="section-padding">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 500px" class="responsive-table">
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="padding: 0 0 0 0; font-size: 16px; line-height: 25px; color: #232323;" class="padding message-content">
<h2>New Contact Message</h2>
<div class="form-container">${htmlData}</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>`,
};
};
export async function POST(req: NextRequest) {
if (req.method !== "POST") {
return NextResponse.json(
{ message: "Method Not Allowed" },
{ status: 405 }
);
}
try {
const data = await req.json();
console.log(data);
await transporter.sendMail({
...mailOptions,
...generateEmailContent(data),
subject: "Contact Email",
});
return NextResponse.json(
{ message: "Email sent successfully" },
{ status: 200 }
);
} catch (error) {
console.error("Error sending email:", error);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}

BIN
bun.lockb

Binary file not shown.

View file

@ -0,0 +1,24 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"
export { Textarea }

129
components/ui/toast.tsx Normal file
View file

@ -0,0 +1,129 @@
"use client"
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const ToastProvider = ToastPrimitives.Provider
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
/>
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
})
Toast.displayName = ToastPrimitives.Root.displayName
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
/>
))
ToastAction.displayName = ToastPrimitives.Action.displayName
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
{...props}
/>
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
{...props}
/>
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement = React.ReactElement<typeof ToastAction>
export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
}

35
components/ui/toaster.tsx Normal file
View file

@ -0,0 +1,35 @@
"use client"
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "@/components/ui/toast"
import { useToast } from "@/components/ui/use-toast"
export function Toaster() {
const { toasts } = useToast()
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</div>
{action}
<ToastClose />
</Toast>
)
})}
<ToastViewport />
</ToastProvider>
)
}

194
components/ui/use-toast.ts Normal file
View file

@ -0,0 +1,194 @@
"use client"
// Inspired by react-hot-toast library
import * as React from "react"
import type {
ToastActionElement,
ToastProps,
} from "@/components/ui/toast"
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
let count = 0
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}
type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
interface State {
toasts: ToasterToast[]
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
toastTimeouts.set(toastId, timeout)
}
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
case "DISMISS_TOAST": {
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
}
const listeners: Array<(state: State) => void> = []
let memoryState: State = { toasts: [] }
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}
type Toast = Omit<ToasterToast, "id">
function toast({ ...props }: Toast) {
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})
return {
id: id,
dismiss,
update,
}
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
}
export { useToast, toast }

9
lib/api/contact.ts Normal file
View file

@ -0,0 +1,9 @@
export const sendContactForm = async (data: any) =>
fetch("/api/contact", {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});

View file

@ -0,0 +1,37 @@
import nodemailer from "nodemailer";
export const transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASS,
},
});
export const mailOptions = {
from: process.env.EMAIL,
to: process.env.EMAIL,
html: `
<table bgcolor="#fafafa" style=" width: 100%!important; height: 100%; background-color: #fafafa; padding: 20px; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, 'Lucida Grande', sans-serif; font-size: 100%; line-height: 1.6;">
<tr>
<td></td>
<td bgcolor="#FFFFFF" style="border: 1px solid #eeeeee; background-color: #ffffff; border-radius:5px; display:block!important; max-width:600px!important; margin:0 auto!important; clear:both!important;"><div style="padding:20px; max-width:600px; margin:0 auto; display:block;">
<table style="width: 100%;">
<tr>
<td><p style="text-align: center; display: block; padding-bottom:20px; margin-bottom:20px; border-bottom:1px solid #dddddd;"><img src="URLOFYOURLOGO"/></p>
<h1 style="font-weight: 200; font-size: 36px; margin: 20px 0 30px 0; color: #333333;">Contact Via Website</h1>
<p style="margin-bottom: 10px; font-weight: normal; font-size:16px; color: #333333;">[YOURFIELD]</p>
<h2 style="font-weight: 200; font-size: 16px; margin: 20px 0; color: #333333;"> [YOURFIELD] </h2>
<h2 style="font-weight: 200; font-size: 16px; margin: 20px 0; color: #333333;"> [YOURFIELD] </h2>
<h2 style="font-weight: 200; font-size: 16px; margin: 20px 0; color: #333333;"> [YOURFIELD] </h2>
<h2 style="font-weight: 200; font-size: 16px; margin: 20px 0; color: #333333;"> [YOURFIELD] </h2>
<p style="text-align: center; display: block; padding-top:20px; font-weight: bold; margin-top:30px; color: #666666; border-top:1px solid #dddddd;">YOUR COMPANY NAME</p></td>
</tr>
</table>
</div></td>
<td></td>
</tr>
</table>
`,
};

View file

@ -1,22 +1,36 @@
import { z } from "zod"; import { z } from "zod";
export const downloadSchema = z.object({ export const downloadSchema = z.object({
fileName: z.string().nonempty(), fileName: z.string().nonempty(),
version: z.string().nonempty(), version: z.string().nonempty(),
downloadLink: z.string().url().nonempty(), downloadLink: z.string().url().nonempty(),
fileSize: z.string().nonempty(), fileSize: z.string().nonempty(),
}); });
export const modsSchema = z.object({ export const modsSchema = z.object({
fileName: z.string().nonempty(), fileName: z.string().nonempty(),
version: z.string().nonempty(), version: z.string().nonempty(),
downloadLink: z.string().url().nonempty(), downloadLink: z.string().url().nonempty(),
fileSize: z.string().nonempty(), fileSize: z.string().nonempty(),
}); });
export const logsSchema = z.object({ export const logsSchema = z.object({
fileName: z.string().nonempty(), fileName: z.string().nonempty(),
version: z.string().nonempty(), version: z.string().nonempty(),
downloadLink: z.string().url().nonempty(), downloadLink: z.string().url().nonempty(),
fileSize: z.string().nonempty(), fileSize: z.string().nonempty(),
});
// Contact Page
export const contactFormSchema = z.object({
name: z.string().min(2, {
message: "Name must be at least 2 characters.",
}),
email: z.string().email({
message: "Invalid email address.",
}),
message: z.string().min(10, {
message: "Message must be at least 10 characters.",
}),
}); });

View file

@ -16,11 +16,13 @@
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/themes": "^3.0.5", "@radix-ui/themes": "^3.0.5",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/cookie": "^0.6.0", "@types/cookie": "^0.6.0",
"@types/mdx": "^2.0.13", "@types/mdx": "^2.0.13",
"@types/nodemailer": "^6.4.15",
"@uploadthing/react": "^6.6.0", "@uploadthing/react": "^6.6.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
@ -34,6 +36,7 @@
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"nextra": "^2.13.4", "nextra": "^2.13.4",
"nextra-theme-docs": "^2.13.4", "nextra-theme-docs": "^2.13.4",
"nodemailer": "^6.9.14",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-hook-form": "^7.52.0", "react-hook-form": "^7.52.0",

View file

@ -21,6 +21,6 @@
"@/*": ["./*"] "@/*": ["./*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "lib/Hoc/withAuth.jsx" , "**/*.mdx", "pages/_app.tsx", "hashedpass.s"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "lib/Hoc/withAuth.jsx" , "**/*.mdx", "pages/_app.tsx", "hashedpass.s", "app/(root)/contact"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }