Full rewrite in react

This commit is contained in:
Pranav Jerry 2024-03-24 09:13:23 +05:30
parent c4b51f9e51
commit b31891dd6f
Signed by: pranav
GPG Key ID: F1DCDC4FED0A0C5B
61 changed files with 18450 additions and 1859 deletions

View File

@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@ -1,36 +1,70 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# Getting Started with Create React App
## Getting Started
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
First, run the development server:
## Available Scripts
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
In the project directory, you can run:
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
### `npm start`
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
To learn more about Next.js, take a look at the following resources:
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
To learn React, check out the [React documentation](https://reactjs.org/).
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
### Code Splitting
## Deploy on Vercel
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
### Analyzing the Bundle Size
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,21 +0,0 @@
import type { Metadata } from "next";
import { inter } from "@/app/ui/fonts";
//import './globals.css'
import "@/app/ui/global.css";
export const metadata: Metadata = {
title: "Inconnect",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}

View File

@ -1,132 +0,0 @@
import Link from "next/link";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export default function Login() {
async function login(formData: FormData) {
"use server";
const rawFormData = {
email: formData.get("email"),
pass: formData.get("pass"),
};
const cookiesList = cookies();
if (cookiesList.has("usersdata")) {
var usersdata = cookiesList.get("usersdata");
console.log(usersdata);
var users = JSON.parse(usersdata.value);
if (rawFormData.email in users) {
var user = users[rawFormData.email];
if (rawFormData.pass === user.pass1) {
cookies().set("userdata", JSON.stringify(user));
redirect("/");
} else {
redirect("/login");
}
} else {
redirect("/login");
}
} else {
redirect("/login");
}
}
return (
<div className="mx-auto max-w-screen-xl px-4 py-16 sm:px-6 lg:px-8">
<div className="mx-auto max-w-lg text-center">
<h1 className="text-2xl font-bold sm:text-3xl">{"Login"}</h1>
<p className="mt-4 text-gray-500">
Inconnect needs to login to view your chats and translate to sign
language{" "}
</p>
</div>
<form action={login} className="mx-auto mb-0 mt-8 max-w-md space-y-4">
<div>
<label htmlFor="email" className="sr-only">
Email
</label>
<div className="relative">
<input
type="email"
className="w-full rounded-lg border-gray-200 p-4 pe-12 text-sm shadow-sm"
placeholder="Enter email"
name="email"
/>
<span className="absolute inset-y-0 end-0 grid place-content-center px-4">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"
/>
</svg>
</span>
</div>
</div>
<div>
<label htmlFor="password" className="sr-only">
Password
</label>
<div className="relative">
<input
type="password"
className="w-full rounded-lg border-gray-200 p-4 pe-12 text-sm shadow-sm"
placeholder="Enter password"
name="pass"
/>
<span className="absolute inset-y-0 end-0 grid place-content-center px-4">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
</span>
</div>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-gray-500">
No account?{" "}
<Link className="underline" href="/signup">
Sign up
</Link>
</p>
<button
type="submit"
className="inline-block rounded-lg bg-blue-500 px-5 py-3 text-sm font-medium text-white"
>
Sign in
</button>
</div>
</form>
</div>
);
}

View File

@ -1,100 +0,0 @@
import Image from "next/image";
import Link from "next/link";
import { cookies } from "next/headers";
function LoginButton(loggedIn = false, logoutFunction) {
console.log(loggedIn);
if (loggedIn) {
return (
<form action={logoutFunction}>
<button
className="block rounded-lg bg-indigo-600 px-5 py-3 text-sm font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring"
type="submit"
>
Logout
</button>
</form>
);
} else {
return (
<div>
{" "}
<Link
href={"/login"}
className="block rounded-lg bg-indigo-600 px-5 py-3 text-sm font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring"
type="button"
>
Login
</Link>
</div>
);
}
}
export default function Home() {
var loggedIn = false;
async function logout() {
"use server";
cookies().delete("userdata");
}
const cookiesList = cookies();
if (cookiesList.has("userdata")) {
var userdata = cookiesList.get("userdata");
console.log("Login page");
console.log(userdata);
if (userdata.value) {
var user = JSON.parse(userdata.value);
var username = user.name;
loggedIn = true;
} else {
username = "Guest";
loggedIn = false;
}
} else {
username = "Guest";
loggedIn = false;
}
return (
<>
<header>
<div className="mx-auto max-w-screen-xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8">
<div className="sm:flex sm:items-center sm:justify-between">
<div className="text-center sm:text-left">
<h1 className="text-2xl font-bold text-gray-900 sm:text-3xl">
Welcome, {username}!
</h1>
<p className="mt-1.5 text-sm text-gray-500">
{(loggedIn && "") || "Login to access the features"}
</p>
</div>
<div className="mt-4 flex flex-col gap-4 sm:mt-0 sm:flex-row sm:items-center">
{LoginButton(loggedIn, logout)}
</div>
</div>
</div>
</header>
<main>
<div className="place-content-center self-center flex">
<label className="form-control w-full max-w-xs">
<div className="label">
<span className="label-text">Pick a file</span>
</div>
<input
type="file"
capture="user"
accept="audio/*"
className="file-input file-input-bordered w-full max-w-xs"
/>
<div className="label">
<span className="label-text-alt">Audio files</span>
</div>
</label>
</div>
</main>
</>
);
}

View File

@ -1,137 +0,0 @@
import Link from "next/link";
import Image from "next/image";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export default function SignUp() {
async function createUser(formData: FormData) {
"use server";
const rawFormData = {
name: formData.get("name"),
email: formData.get("email"),
pass1: formData.get("pass1"),
pass2: formData.get("pass2"),
};
const cookiesList = cookies();
if (cookiesList.has("usersdata")) {
var usersdata = cookiesList.get("usersdata");
var users = JSON.parse(usersdata.value);
} else {
var users = {
"abc@example.org": {
name: "ABC",
email: "abc@example.org",
pass1: "abc",
},
};
}
users[rawFormData.email] = rawFormData;
cookies().set("usersdata", JSON.stringify(users));
redirect("/login");
}
return (
<section className="bg-white dark:bg-gray-900">
<div className="lg:grid lg:min-h-screen lg:grid-cols-12">
<aside className="relative block h-16 lg:order-last lg:col-span-5 lg:h-full xl:col-span-6">
<div className="hidden">
Photo by{" "}
<Link href="https://unsplash.com/@patrickian4?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
Patrick Fore
</Link>{" "}
on{" "}
<Link href="https://unsplash.com/photos/peace-sign-hfv2GImBsfM?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
Unsplash
</Link>
</div>
<Image
alt="Peace"
src="/patrick-fore-hfv2GImBsfM-unsplash.jpg"
width={4707}
height={3138}
className="absolute inset-0 h-full w-full object-cover"
/>
</aside>
<main className="flex items-center justify-center px-8 py-8 sm:px-12 lg:col-span-7 lg:px-16 lg:py-12 xl:col-span-6">
<div className="max-w-xl lg:max-w-3xl">
<h1 className="mt-6 text-2xl font-bold text-gray-900 dark:text-white sm:text-3xl md:text-4xl">
Sign Up
</h1>
<p className="mt-4 leading-relaxed text-gray-500 dark:text-gray-400">
Create an account to use Inconnect and sign away!
</p>
<form action={createUser} className="mt-8 grid grid-cols-6 gap-6">
<div className="col-span-6">
<input
type="text"
id="Name"
name="name"
placeholder="Name"
required
autoComplete="off"
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
/>
</div>
<div className="col-span-6">
<input
type="email"
id="Email"
name="email"
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
placeholder="Email"
required
autoComplete="off"
/>
</div>
<div className="col-span-6 sm:col-span-3">
<input
type="password"
id="Password"
name="pass1"
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
placeholder="Password"
required
autoComplete="off"
/>
</div>
<div className="col-span-6 sm:col-span-3">
<input
type="password"
id="PasswordConfirmation"
name="pass2"
className="mt-4 pb-3 w-full rounded-md border-b-2 border-gray-300 bg-white text-sm text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 focus:border-blue-500 focus:outline-none"
placeholder="Confirm Password"
required
autoComplete="off"
/>
</div>
<div className="col-span-6 sm:flex sm:items-center sm:gap-4 mt-6">
<button className="inline-block shrink-0 rounded-md border border-blue-600 bg-blue-600 px-12 py-3 text-sm font-medium text-white transition hover:bg-transparent hover:text-blue-600 focus:outline-none focus:ring active:text-blue-500 dark:hover:bg-blue-700 dark:hover:text-white">
Create an account
</button>
<p className="mt-5 text-sm text-gray-500 dark:text-gray-400 sm:mt-0">
Already have an account?{" "}
<Link
href="/login"
className="text-gray-700 underline dark:text-gray-200"
>
Log in
</Link>
.
</p>
</div>
</form>
</div>
</main>
</div>
</section>
);
}

View File

@ -1,3 +0,0 @@
import { Inter } from "next/font/google";
export const inter = Inter({ subsets: ["latin"] });

View File

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,4 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
module.exports = nextConfig

17504
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,53 @@
{
"name": "inconnect",
"name": "inconnect-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@auth/prisma-adapter": "^1.0.10",
"@prisma/client": "^5.7.0",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"firebase": "^10.9.0",
"framer-motion": "^11.0.8",
"gsap": "^3.12.5",
"howler": "^2.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"react-intersection-observer": "^9.8.1",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"splitting": "^1.0.6",
"styled-components": "^6.1.8",
"uuid": "^9.0.1",
"wavesurfer-react": "^3.0.2",
"wavesurfer.js": "^7.7.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.0.3",
"postcss": "^8",
"prisma": "^5.7.0",
"tailwindcss": "^3.3.0",
"typescript": "^5"
"sass": "^1.71.1"
}
}

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -1,11 +0,0 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

38
src/App.css Normal file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

38
src/App.js Normal file
View File

@ -0,0 +1,38 @@
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import LandingPage from './Components/LandingPage/LandingPage';
import Uploader from './Components/FileUpload/Uploader';
import Footer from './Components/Footer/Footer';
import Login from './Components/reg_and_log/Login';
import Register from './Components/reg_and_log/Register';
import Dashboard from './Components/Chat/Dashboard';
import { useContext } from 'react';
import { AuthContext } from './Components/context/AuthContext';
function App() {
const { currentUser } = useContext(AuthContext)
const ProtectedRoute = ({ children }) => {
if (!currentUser) {
return <Navigate to="/Login" />
}
return children
};
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<>
<LandingPage />
<Uploader />
<Footer />
</>} />
<Route path='/Login' element={<Login />} />
<Route path='/Register' element={<Register />} />
<Route path='/Dashboard' element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
</Routes>
</Router>
</div>
);
}
export default App;

8
src/App.test.js Normal file
View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,4 @@
export const footerTextAnimation = {
hidden: { x: -200, opacity: 0 },
show: { x: 1, opacity: 1 },
};

View File

@ -0,0 +1,20 @@
import React, { useEffect, useState, useContext } from 'react';
import Msg from './messages';
import Input from './input';
import { ChatContext } from '../context/ChatContext';
const Chat = () => {
const { data } = useContext(ChatContext);
return (
<div className='chat'>
<div className="chatInfo">
<span>{data.user.displayName || "Welcome to inconnect"}</span>
</div>
<Msg />
<Input />
</div>
)
}
export default Chat;

View File

@ -0,0 +1,50 @@
import { doc, onSnapshot } from "firebase/firestore";
import React, { useContext, useEffect, useState } from "react";
import { AuthContext } from "../context/AuthContext";
import { ChatContext } from "../context/ChatContext";
import { db } from "../firebase";
const Chats = () => {
const [chats, setChats] = useState([]);
const { currentUser } = useContext(AuthContext);
const { dispatch } = useContext(ChatContext);
useEffect(() => {
const getChats = () => {
const unsub = onSnapshot(doc(db, "userChats", currentUser.uid), (doc) => {
setChats(doc.data());
});
return () => {
unsub();
};
};
currentUser.uid && getChats();
}, [currentUser.uid]);
const handleSelect = (u) => {
dispatch({ type: "CHANGE_USER", payload: u });
};
return (
<div className="chats">
{Object.entries(chats)?.sort((a, b) => b[1].date - a[1].date).map((chat) => (
<div
className="userChat"
key={chat[0]}
onClick={() => handleSelect(chat[1].userInfo)}
>
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
<div className="userChatInfo">
<span>{chat[1].userInfo.displayName}</span>
<p>{chat[1].lastMessage?.text}</p>
</div>
</div>
))}
</div>
);
};
export default Chats;

View File

@ -0,0 +1,18 @@
import React from 'react'
import Sidebar from './Sidebar'
import Chat from './Chat'
import './chat.css'
const Dashboard = () => {
return (
<div className='dashboard'>
<div className="dash-container">
<Sidebar />
<Chat />
</div>
</div>
)
}
export default Dashboard

View File

@ -0,0 +1,68 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import { AuthContext } from "../context/AuthContext";
import { ChatContext } from "../context/ChatContext";
import { FaPlay, FaPause } from 'react-icons/fa';
const Message = ({ message }) => {
const { currentUser } = useContext(AuthContext);
const { data } = useContext(ChatContext);
const [audioUrl, setAudioUrl] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const audioRef = useRef();
useEffect(() => {
if (message.audio) {
setAudioUrl(message.audio);
}
}, [message]);
const handlePlayPause = () => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.play();
}
setIsPlaying(!isPlaying);
}
};
const handleAudioEnd = () => {
setIsPlaying(false);
};
return (
<div
className={`message ${message.senderId === currentUser.uid && "owner"}`}
>
<div className="messageInfo">
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
<span>just now</span>
</div>
<div className="messageContent">
{audioUrl ? (
<div className="audiowrapper">
{isPlaying ? (
<FaPause
style={{ marginRight: '5px', marginTop: '2px', cursor: 'pointer' }}
onClick={handlePlayPause}
/>
) : (
<FaPlay
style={{ marginRight: '5px', marginTop: '2px', cursor: 'pointer' }}
onClick={handlePlayPause}
/>
)}
Audio
<audio ref={audioRef} style={{ display: 'none' }} onEnded={handleAudioEnd}>
<source src={audioUrl} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</div>
) : (
<p>{message.text}</p>
)}
</div>
</div>
);
};
export default Message;

View File

@ -0,0 +1,46 @@
import React, { useContext, useEffect, useState } from 'react';
import { signOut } from "firebase/auth";
import { auth, db } from "../firebase";
import { AuthContext } from '../context/AuthContext';
import { getDoc, doc } from "firebase/firestore";
const Nav = () => {
const { currentUser } = useContext(AuthContext);
const [displayName, setDisplayName] = useState(null);
useEffect(() => {
const fetchDisplayName = async () => {
if (currentUser) {
try {
const userDoc = await getDoc(doc(db, "users", currentUser.uid));
if (userDoc.exists()) {
const userData = userDoc.data();
setDisplayName(userData.displayName);
} else {
console.log("User document does not exist");
}
} catch (error) {
console.error("Error fetching user document:", error);
}
}
};
fetchDisplayName();
}, [currentUser]);
return (
<div className='navbar'>
<span className='logo'>Inconnect Chat</span>
<div className="user">
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
<span>{displayName || "Guest"}</span>
{currentUser && (
<button onClick={() => signOut(auth)}>Logout</button>
)}
</div>
</div>
);
};
export default Nav;

View File

@ -0,0 +1,16 @@
import React from 'react';
import Nav from './Nav';
import BotPage from './bot_page';
import Chatting from './Chatting';
const Sidebar = () => {
return (
<div className='sidebar'>
<Nav />
<BotPage />
<Chatting />
</div>
);
}
export default Sidebar;

View File

@ -0,0 +1,116 @@
import React, { useContext, useState, useEffect } from "react";
import {
collection,
query,
where,
getDocs,
setDoc,
doc,
updateDoc,
serverTimestamp,
getDoc,
} from "firebase/firestore";
import { db } from "../firebase";
import { AuthContext } from "../context/AuthContext";
const Search = () => {
const [username, setUsername] = useState("");
const [user, setUser] = useState(null);
const [err, setErr] = useState(false);
const [chatExists, setChatExists] = useState(false);
const { currentUser } = useContext(AuthContext);
const predefinedUserId = "el9VwVaAlzbwUtZqqHQtundVp1p1";
useEffect(() => {
const fetchPredefinedUser = async () => {
try {
const userDoc = await getDoc(doc(db, "users", predefinedUserId));
if (userDoc.exists()) {
setUser(userDoc.data());
} else {
setErr(true);
}
} catch (error) {
console.error("Error fetching predefined user:", error);
setErr(true);
}
};
fetchPredefinedUser();
}, []);
useEffect(() => {
if (user) {
const combinedId =
currentUser.uid > user.uid
? currentUser.uid + user.uid
: user.uid + currentUser.uid;
const checkChatExistence = async () => {
try {
const res = await getDoc(doc(db, "chats", combinedId));
setChatExists(res.exists());
} catch (error) {
console.error("Error checking chat existence:", error);
}
};
checkChatExistence();
}
}, [user, currentUser.uid]);
const handleSelect = async () => {
const combinedId =
currentUser.uid > user.uid
? currentUser.uid + user.uid
: user.uid + currentUser.uid;
try {
const res = await getDoc(doc(db, "chats", combinedId));
if (!res.exists()) {
await setDoc(doc(db, "chats", combinedId), { messages: [] });
const currentUserChatUpdate = {
[combinedId + ".userInfo"]: {
uid: user.uid,
displayName: user.displayName,
},
[combinedId + ".date"]: serverTimestamp(),
};
await updateDoc(doc(db, "userChats", currentUser.uid), currentUserChatUpdate);
const otherUserChatUpdate = {
[combinedId + ".userInfo"]: {
uid: currentUser.uid,
displayName: currentUser.displayName,
},
[combinedId + ".date"]: serverTimestamp(),
};
await updateDoc(doc(db, "userChats", user.uid), otherUserChatUpdate);
setUser(null);
setUsername("");
}
} catch (error) {
console.error("Error creating chat:", error);
}
};
return (
<div className="search">
{user && !chatExists && (
<div className="userChat" onClick={handleSelect}>
<img src="https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png" alt="" />
<div className="userChatInfo">
{/* Render the display name only if the chat does not exist */}
<span>{user.displayName}</span>
</div>
</div>
)}
</div>
);
};
export default Search;

View File

@ -0,0 +1,257 @@
.dashboard {
background-color: #a7bcff;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.dash-container {
border: 1px solid white;
border-radius: 10px;
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
}
.sidebar {
flex: 1;
border-right: 1px solid #3e3c61;
background-color: #3e3c61;
.bot {
border-bottom: 1px solid gray;
}
.userChat {
padding: 10px;
display: flex;
align-items: center;
gap: 10px;
color: white;
cursor: pointer;
&:hover {
background-color: #2f2d52;
}
p {
font-size: 10px;
}
img {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
}
}
}
.navbar {
display: flex;
align-items: center;
background-color: #2f2d52;
height: 50px;
padding: 10px;
justify-content: space-between;
color: #ddddf7;
.logo {
font-weight: bold;
}
.user {
display: flex;
gap: 10px;
img {
background-color: #ddddf7;
height: 24px;
width: 24px;
border-radius: 50%;
object-fit: cover;
}
button {
background-color: #5d5b8d;
color: #ddddf7;
font-size: 15px;
border: none;
cursor: pointer;
}
}
}
.chat {
flex: 2;
.chatInfo {
height: 50px;
background-color: #5d5b8d;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
color: lightgray;
}
}
.preview {
display: flex;
margin-bottom: 30px;
}
.messages {
background-color: #ddddf7;
padding: 10px;
height: calc(100% - 160px);
overflow: hidden;
.message {
display: flex;
gap: 20px;
margin-bottom: 20px;
overflow: hidden;
.messageInfo {
display: flex;
flex-direction: column;
color: gray;
font-weight: 300;
img {
margin-top: 10px;
margin-left: 0px;
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
span {
font-size: 15px;
margin-left: -10px;
margin-top: 5px;
}
}
.messageContent {
max-width: 80%;
display: flex;
flex-direction: column;
gap: 10px;
img {
width: 50%;
}
.audiowrapper {
margin-top: 15px;
background-color: #8da4f1;
padding: 10px 20px;
color: white;
border-radius: 0px 10px 10px 10px;
max-width: max-content;
}
}
&.owner {
flex-direction: row-reverse;
.message {
align-items: flex-end;
.emptyBox {
background-color: #8da4f1;
color: white;
border-radius: 10px 0px 10px 10px;
}
}
}
}
}
.input {
height: 50px;
background-color: white;
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
.left-buttons {
align-items: flex-start;
}
.right-buttons {
align-items: flex-end;
}
.right-buttons button {
margin-left: 120px;
background-color: #8da4f1;
color: white;
border: none;
font-size: 15px;
padding: 5px 20px;
display: inline-flex;
flex-direction: row;
align-items: center;
cursor: pointer;
}
.left-buttons button {
margin-right: 80px;
background-color: #8da4f1;
color: white;
border: none;
font-size: 15px;
padding: 5px 20px;
display: inline-flex;
flex-direction: row;
align-items: center;
cursor: pointer;
}
.recordButton {
font-size: 15px;
padding: 10px;
border-radius: 50%;
margin-right: 30px;
cursor: pointer;
}
.uploadButton {
font-size: 15px;
padding: 10px;
border-radius: 50%;
margin-right: 30px;
cursor: pointer;
}
h5 {
margin-right: 30px;
font-size: 15px;
}
.send {
display: flex;
align-items: center;
gap: 70px;
button {
border: none;
padding: 10px 15px;
background-color: #8da4f1;
color: white;
}
}
}

View File

@ -0,0 +1,190 @@
import { Timestamp, arrayUnion, updateDoc, doc, serverTimestamp } from 'firebase/firestore';
import React, { useRef, useState, useEffect, useContext } from 'react';
import { FaMicrophone, FaUpload, FaPlay, FaPause, FaTimes } from 'react-icons/fa';
import WaveSurfer from 'wavesurfer.js';
import { AuthContext } from "../context/AuthContext";
import { ChatContext } from "../context/ChatContext";
import { db, storage } from "../firebase";
import { v4 as uuid } from "uuid";
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
const Input = () => {
const wavesurferRef = useRef(null);
const [audioSrc, setAudioSrc] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const [isRecording, setIsRecording] = useState(false);
const [mediaRecorder, setMediaRecorder] = useState(null);
const [audio, setAudio] = useState(null);
const { currentUser } = useContext(AuthContext);
const { data } = useContext(ChatContext);
useEffect(() => {
if (audioSrc) {
const wavesurfer = WaveSurfer.create({
container: wavesurferRef.current,
waveColor: 'violet',
progressColor: 'purple',
barWidth: 2,
cursorWidth: 1,
height: 70,
responsive: true,
});
wavesurfer.load(audioSrc);
wavesurfer.on('ready', function () {
wavesurfer.setVolume(0.5);
});
wavesurfer.on('play', function () {
setIsPlaying(true);
});
wavesurfer.on('pause', function () {
setIsPlaying(false);
});
wavesurferRef.current = wavesurfer;
}
}, [audioSrc]);
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
if (file.type.startsWith('audio/')) {
setAudio(file);
setAudioSrc(URL.createObjectURL(file));
} else {
alert("Please select an audio file.");
event.target.value = null;
}
}
};
const startRecording = () => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const recorder = new MediaRecorder(stream);
setMediaRecorder(recorder);
const chunks = [];
recorder.ondataavailable = e => {
chunks.push(e.data);
};
recorder.onstop = () => {
const blob = new Blob(chunks, { type: 'audio/webm' });
const audioFile = new File([blob], `${uuid()}.wav`, { type: 'audio/wav' });
setAudioSrc(URL.createObjectURL(blob));
setAudio(audioFile);
};
recorder.start();
setIsRecording(true);
})
.catch(error => console.error("Error accessing the microphone:", error));
}
};
const stopRecording = () => {
if (mediaRecorder && isRecording) {
mediaRecorder.stop();
setIsRecording(false);
}
};
const handleRecordClick = () => {
if (!isRecording) {
startRecording();
} else {
stopRecording();
}
};
const handlePlayPause = () => {
if (wavesurferRef.current) {
if (isPlaying) {
wavesurferRef.current.pause();
} else {
wavesurferRef.current.play();
}
}
};
const handleCancel = () => {
setAudio(null);
setAudioSrc(null);
};
const handleSend = async () => {
if (audio) {
const storageRef = ref(storage, uuid());
try {
await uploadBytes(storageRef, audio);
const downloadURL = await getDownloadURL(storageRef);
await updateDoc(doc(db, "chats", data.chatId), {
messages: arrayUnion({
id: uuid(),
senderId: currentUser.uid,
date: Timestamp.now(),
audio: downloadURL,
}),
});
setAudio(null);
setAudioSrc(null);
const lastMessage = { text: "audio" };
const userChatsUpdates = {
[data.chatId + ".lastMessage"]: lastMessage,
[data.chatId + ".date"]: serverTimestamp(),
};
await Promise.all([
updateDoc(doc(db, "userChats", currentUser.uid), userChatsUpdates),
updateDoc(doc(db, "userChats", data.user.uid), userChatsUpdates),
]);
} catch (error) {
console.error("Error uploading audio:", error);
}
}
};
return (
<div className='input'>
{audioSrc ? (
<>
<div className='left-buttons'>
<button onClick={handlePlayPause}>
{isPlaying ? <FaPause /> : <FaPlay />}
</button>
<button onClick={handleCancel}>
<FaTimes />
</button>
</div>
<div style={{ width: '30%' }} ref={wavesurferRef}></div>
<div className='right-buttons'>
<button onClick={handleSend}>
Send
</button>
</div>
</>
) : (
<>
<input
type="file"
accept="audio/*"
onChange={handleFileChange}
style={{ display: 'none' }}
id="fileInput"
/>
<label htmlFor="fileInput" className="uploadButton">
<FaUpload />
</label>
<h5>or</h5>
<label htmlFor="recordButton" className="recordButton" onClick={handleRecordClick}>
{isRecording ? <FaMicrophone style={{ color: 'red' }} /> : <FaMicrophone />}
</label>
</>
)}
</div>
);
}
export default Input;

View File

@ -0,0 +1,31 @@
import { doc, onSnapshot } from "firebase/firestore";
import React, { useContext, useEffect, useState } from "react";
import { ChatContext } from "../context/ChatContext";
import { db } from "../firebase";
import Message from "./Message";
const Messages = () => {
const [messages, setMessages] = useState([]);
const { data } = useContext(ChatContext);
useEffect(() => {
const unSub = onSnapshot(doc(db, "chats", data.chatId), (doc) => {
doc.exists() && setMessages(doc.data().messages);
});
return () => {
unSub();
};
}, [data.chatId]);
console.log(messages);
return (
<div className='messages'>
{messages.map(
m => <Message key={m.id} message={m} />
)}
</div>
);
};
export default Messages;

View File

@ -0,0 +1,107 @@
import WaveSurfer from 'wavesurfer.js';
import { useEffect, useRef, useState } from 'react';
import './uploader.css';
import upload from './upload.svg';
const Uploader = () => {
const [audioSrc, setAudioSrc] = useState(null);
const waveformRef = useRef(null);
const wavesurferRef = useRef(null);
useEffect(() => {
if (audioSrc) {
const wavesurfer = WaveSurfer.create({
container: waveformRef.current,
waveColor: 'black',
progressColor: 'gray',
cursorWidth: 0,
barWidth: 2,
barRadius: 2,
});
wavesurfer.load(audioSrc);
wavesurferRef.current = wavesurfer;
}
}, [audioSrc]);
const handleFileChange = (e) => {
const file = e.target.files[0];
if (file) {
const fileType = file.type.split('/')[0];
if (fileType === 'audio') {
const reader = new FileReader();
reader.onload = (event) => {
setAudioSrc(event.target.result);
};
reader.readAsDataURL(file);
} else {
alert('Please upload an audio file.');
}
}
};
const handleUpload = () => {
console.log("Uploading audio:", audioSrc);
};
const handlePlayPause = () => {
if (wavesurferRef.current) {
wavesurferRef.current.playPause();
}
};
const handleCancel = (e) => {
e.preventDefault();
setAudioSrc(null);
if (wavesurferRef.current) {
wavesurferRef.current.destroy();
}
};
const handleProceed = () => {
// Proceed with uploaded audio
};
return (
<div className='hero'>
<h1 className='convert-text'>Convert</h1>
<p className='convert-paragraph'>Convert speech to sign language easily</p>
<label htmlFor="input-file" id="drop-area">
<input
type="file"
accept="audio/*"
id="input-file"
hidden
onChange={handleFileChange}
/>
<div id="audio-play">
{audioSrc ? (
<>
<div className="container-1">
<button onClick={handlePlayPause} className='btn'>Play/Pause</button>
<button onClick={(e) => handleCancel(e)} className='btn'>Cancel</button>
</div>
{audioSrc && (
<div id="audio-preview">
<div ref={waveformRef} id="waveform" />
</div>
)}
</>
) : (
<>
<img src={upload} alt="upload" />
<p>Drag and drop or click here<br />to upload audio</p>
<span>Upload Audio</span>
</>
)}
</div>
</label>
<button onClick={handleProceed} className='btn-2'>Convert</button>
</div>
);
};
export default Uploader;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" id="upload"><path d="M398.1 233.2c0-1.2.2-2.4.2-3.6 0-65-51.8-117.6-115.7-117.6-46.1 0-85.7 27.4-104.3 67-8.1-4.1-17.2-6.5-26.8-6.5-29.5 0-54.1 21.9-58.8 50.5C57.3 235.2 32 269.1 32 309c0 50.2 40.1 91 89.5 91H224v-80h-48.2l80.2-83.7 80.2 83.6H288v80h110.3c45.2 0 81.7-37.5 81.7-83.4 0-45.9-36.7-83.2-81.9-83.3z"></path></svg>

After

Width:  |  Height:  |  Size: 412 B

View File

@ -0,0 +1,104 @@
.hero {
width: 100%;
min-height: 100vh;
background: #e4e8ed86;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.convert-text {
font-size: 8rem;
margin-bottom: 5px;
}
.convert-paragraph {
font-size: 2.3rem;
padding: 0 0 5px 0;
}
#drop-area {
margin-top: 70px;
width: 1000px;
height: 300px;
padding: 30px;
background: white;
text-align: center;
}
#audio-play {
width: 100%;
height: 100%;
border: 2px dashed #000;
background: #f7f8ff;
}
#audio-play img {
width: 100px;
margin-top: 25px;
}
#audio-play p {
font-size: 30px;
}
#audio-play span {
display: block;
font-size: 12px;
color: #777;
}
#audio-preview {
margin-top: 50px;
}
.btn {
gap: 13px;
margin-top: 12px;
margin-left: 10px;
font-family: 'Primary', Arial, sans-serif;
font-weight: 100;
font-style: normal;
font-display: swap;
background-color: black;
border: none;
color: white;
padding: 10px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
}
.btn:hover {
background-color: gray;
}
.btn-2 {
gap: 13px;
margin-top: 30px;
margin-left: 10px;
font-size: 1.5rem;
font-family: 'Primary', Arial, sans-serif;
/* Adding fallback fonts */
font-weight: 100;
font-style: normal;
font-display: swap;
background-color: #000;
/* Green */
border: none;
color: white;
padding: 20px 30px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
}
.btn-2:hover {
background-color: gray;
}

View File

@ -0,0 +1,68 @@
import React from 'react';
import styled from "styled-components";
import { BsFacebook, BsTwitter, BsYoutube, BsBehance } from 'react-icons/bs';
import { motion } from "framer-motion";
import { useScroll } from "../useScroll";
import { footerTextAnimation } from "../Animation";
function Footer() {
const [element, controls] = useScroll();
return (
<Foot ref={element}>
<motion.span
variants={footerTextAnimation}
animate={controls}
transition={{
delay: 0.02,
type: "tween",
duration: 0.8,
}}
>
&copy; Neural Harbour AI
</motion.span>
<motion.div className="footer__social__icons"
variants={footerTextAnimation}
animate={controls}
transition={{
delay: 0.02,
type: "tween",
duration: 0.8,
}}
>
<BsFacebook />
<BsTwitter />
<BsYoutube />
<BsBehance />
</motion.div>
</Foot>
)
}
const Foot = styled(motion.footer)`
background-color: black;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5rem 10rem;
.footer__social__icons {
display: flex;
gap: 2rem;
svg {
font-size: 1.4rem;
cursor: pointer;
transition: 0.5s ease-in-out;
&:hover {
color: var(--secondary-color);
}
}
}
@media screen and (min-width: 280px) and (max-width: 1080px) {
padding: 1rem;
flex-direction: column;
text-align: center;
gap: 1rem;
}
`;
export default Footer

View File

@ -0,0 +1,147 @@
$theme: #454cad;
$dark-text: #5f6982;
.uploader {
display: block;
clear: both;
margin: 0 auto;
width: 100%;
max-width: 600px;
label {
float: left;
clear: both;
width: 100%;
padding: 2rem 1.5rem;
text-align: center;
background: #fff;
border-radius: 7px;
border: 3px solid #eee;
transition: all .2s ease;
user-select: none;
&:hover {
border-color: $theme;
}
&.hover {
border: 3px solid $theme;
box-shadow: inset 0 0 0 6px #eee;
#start i.fa {
transform: scale(0.8);
opacity: 0.3;
}
}
}
#start {
float: left;
clear: both;
width: 100%;
&.hidden {
display: none;
}
i.fa {
font-size: 50px;
margin-bottom: 1rem;
transition: all .2s ease-in-out;
}
}
#response {
float: left;
clear: both;
width: 100%;
&.hidden {
display: none;
}
#messages {
margin-bottom: .5rem;
}
}
#file-image {
display: inline;
margin: 0 auto .5rem auto;
width: auto;
height: auto;
max-width: 180px;
&.hidden {
display: none;
}
}
#notimage {
display: block;
float: left;
clear: both;
width: 100%;
&.hidden {
display: none;
}
}
progress,
.progress {
display: inline;
clear: both;
margin: 0 auto;
width: 100%;
max-width: 180px;
height: 8px;
border: 0;
border-radius: 4px;
background-color: #eee;
overflow: hidden;
&[value]::-webkit-progress-bar {
border-radius: 4px;
background-color: #eee;
}
&[value]::-webkit-progress-value,
&[value]::-moz-progress-bar {
background: linear-gradient(to right, darken($theme, 8%) 0%, $theme 50%);
border-radius: 4px;
}
}
input[type="file"] {
display: none;
}
div {
margin: 0 0 .5rem 0;
color: $dark-text;
}
.btn {
display: inline-block;
margin: .5rem .5rem 1rem .5rem;
clear: both;
font-family: inherit;
font-weight: 700;
font-size: 14px;
text-decoration: none;
text-transform: initial;
border: none;
border-radius: .2rem;
outline: none;
padding: 0 1rem;
height: 36px;
line-height: 36px;
color: #fff;
transition: all 0.2s ease-in-out;
box-sizing: border-box;
background: $theme;
border-color: $theme;
cursor: pointer;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

View File

@ -0,0 +1,46 @@
import Splitting from 'splitting';
import { gsap } from 'gsap';
const nav = document.querySelector('.nav');
const section = {
title: Splitting({
target: '.section_title',
by: 'chars',
}),
paragraphs: Splitting({
target: '.section_col',
by: 'words',
}),
image: document.querySelector('.section_col_image'),
overlay: document.querySelector('.section_col_overlay'),
};
const animateSection = () => {
const titleChars = section.title[0].chars;
const paragraphsWords = section.paragraphs[0].words;
gsap.set(titleChars, { autoAlpha: 0, yPercent: -100, rotate: '-15deg' });
gsap.set(paragraphsWords, { autoAlpha: 0, display: 'inline-flex' });
gsap.set(section.image, { autoAlpha: 0, scale: 1.5 });
const t1 = gsap.timeline({ defaults: { duration: 1.64, ease: 'power4.inOut' } });
t1.addLabel('start')
.from(nav, { yPercent: -100 })
.addLabel('section')
.to(titleChars, {
autoAlpha: 1,
yPercent: 0,
rotate: '0deg',
stagger: 0.024,
}, 'start+=0.6')
.to(paragraphsWords, {
autoAlpha: 1,
stagger: 0.024,
}, 'start+=0.6')
.addLabel('image')
.to(section.overlay, { yPercent: 101 }, 'start+=0.6')
.to(section.image, { autoAlpha: 1, scale: 1 }, 'start+=0.6');
};
export default animateSection;

View File

@ -0,0 +1,179 @@
:root {
--primary: #e4e8ed;
--secondary: #191919;
--alternate: #9db2ea;
}
@font-face {
font-family: "Primary";
src: url("../../fonts/PPMori-Regular.woff2") format("woff2");
font-weight: 100;
font-style: normal;
font-display: swap;
}
html {
font-size: max(100vw / 1920 * 10);
}
body {
font-family: "Primary";
background-color: var(--primary);
color: var(--secondary);
}
h2 {
font-size: 10rem;
}
h3 {
font-size: 8rem;
}
h4 {
font-size: 4rem;
}
li,
span {
font-size: 2.4rem;
line-height: 1.2;
}
.Lander {
position: relative;
}
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 1fr;
gap: 1rem 0;
}
.nav {
position: fixed;
left: 0;
top: 0;
width: 100%;
z-index: 40;
background-color: rgb(236, 244, 255);
}
.nav_wrapper {
grid-template-areas: "a a b b b b . . . c d d";
padding: 2rem;
}
.nav_logo {
grid-area: a;
}
.nav_pages {
grid-area: b;
display: inline-flex;
gap: 2rem;
}
.nav_pages span a {
color: inherit;
text-decoration: none;
}
.nav_menu {
grid-area: c;
}
.nav_menu_button {
cursor: pointer;
}
.nav_menu_line {
display: block;
width: 6.4rem;
height: 0.2rem;
background-color: var(--secondary);
margin: 1rem 0;
cursor: pointer;
}
.section {
position: relative;
width: 100%;
height: 100vh;
z-index: 10;
}
.section_wrapper {
display: flex;
height: inherit;
}
.section_title {
position: absolute;
top: 16rem;
left: 50%;
transform: translateX(-50%);
z-index: 20;
overflow: hidden;
}
.section_title h2,
.section_title span {
display: inline-flex;
font-size: 10rem;
}
.section_col {
position: relative;
flex: 1;
height: inherit;
overflow: hidden;
}
.section_col_overlay {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--primary);
z-index: 10;
}
.section_col_image {
position: relative;
width: 100%;
height: 808px;
object-fit: cover;
}
.section_col:nth-child(2) {
display: flex;
align-items: flex-end;
justify-content: flex-end;
}
.section_col_left {
display: flex;
flex: 1;
flex-direction: column;
gap: 18rem;
padding: 2rem;
}
.section_col_left_text h4,
.section_col_left_text span {
font-size: 4rem;
}
.section_col_left_cta {
display: inline-flex;
gap: 1.6rem;
cursor: pointer;
}
.section_col_right {
flex: 1;
padding: 2rem;
}

View File

@ -0,0 +1,116 @@
import React, { useEffect, useRef } from 'react';
import img from './IMG-KONVERT.jpg';
import './Landing.css';
import Splitting from 'splitting';
import { gsap } from 'gsap';
import { Link } from "react-router-dom";
const LandingPage = () => {
const navRef = useRef(null);
const sectionTitleRef = useRef(null);
const sectionColRef = useRef(null);
const sectionImageRef = useRef(null);
const sectionOverlayRef = useRef(null);
useEffect(() => {
const nav = navRef.current;
const sectionTitle = sectionTitleRef.current;
const sectionCol = sectionColRef.current;
const sectionImage = sectionImageRef.current;
const sectionOverlay = sectionOverlayRef.current;
const section = {
title: Splitting({
target: sectionTitle,
by: 'chars',
}),
paragraphs: Splitting({
target: sectionCol,
by: 'words',
}),
image: sectionImage,
overlay: sectionOverlay,
};
const titleChars = section.title[0].chars;
const paragraphsWords = section.paragraphs[0].words;
gsap.set(titleChars, { autoAlpha: 0, yPercent: -100, rotate: '-15deg' });
gsap.set(paragraphsWords, { autoAlpha: 0, display: 'inline-flex' });
gsap.set(section.image, { autoAlpha: 0, scale: 1.5 });
const t1 = gsap.timeline({ defaults: { duration: 1.64, ease: 'power4.inOut' } });
t1.addLabel('section')
.to(titleChars, {
autoAlpha: 1,
yPercent: 0,
rotate: '0deg',
stagger: 0.024,
}, 'start+=0.6')
.to(paragraphsWords, {
autoAlpha: 1,
stagger: 0.024,
}, 'start+=0.6')
.to(section.overlay, { yPercent: 101 })
.to(section.image, { autoAlpha: 1, scale: 1 }, '-=2');
}, []);
const handleFileUpload = (event) => {
const file = event.target.files[0];
};
return (
<body>
<main className="Lander">
<div className="nav" ref={navRef}>
<div className="nav_wrapper grid">
<div className="nav_logo">
<span>INCONNECT</span>
</div>
<div className="nav_pages">
<span>Home</span>
<span><Link to="/login">Login</Link></span>
<span><Link to="/Register">SignUp</Link></span>
</div>
<div className='nav_menu'>
<div className="nav_menu_button menu-open">
<span className='nav_menu_line'></span>
<span className='nav_menu_line'></span>
</div>
</div>
</div>
</div>
<section className='section'>
<div className="section_wrapper">
<div className="section_title" ref={sectionTitleRef}>
<h2>Inconnect</h2>
</div>
<div className="section_col" ref={sectionColRef}>
<div className="section_col_left">
<h4 className='section_col_left_text'>
Next-Gen Speech to Sign Language Technology. Introducing our revolutionary Intelligent Sign Language Technology,Powered by cutting-edge AI technology.
</h4>
<div className="setion_col_left_cta">
<span>&rarr;</span>
<span>Powered By Neural Harbour AI</span>
</div>
</div>
<div className="section_col_right">
<p className="section_col_right_text">
Our sophisticated algorithms delve into the speech's intricate details,enabling anyone to perform quick,accurate and comprehensive conversions.
</p>
</div>
</div>
<div className='section_col'>
<div className="section_col_overlay" ref={sectionOverlayRef}></div>
<img src={img} alt="dummy" className='section_col_image' ref={sectionImageRef} />
</div>
</div>
</section>
</main>
</body >
);
};
export default LandingPage;

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,26 @@
import { createContext, useEffect, useState } from "react";
import { auth } from "../firebase";
import { onAuthStateChanged } from "firebase/auth";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState({});
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
console.log(user);
});
return () => {
unsub();
};
}, []);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
};

View File

@ -0,0 +1,40 @@
import {
createContext,
useContext,
useReducer,
} from "react";
import { AuthContext } from "./AuthContext";
export const ChatContext = createContext();
export const ChatContextProvider = ({ children }) => {
const { currentUser } = useContext(AuthContext);
const INITIAL_STATE = {
chatId: "null",
user: {},
};
const chatReducer = (state, action) => {
switch (action.type) {
case "CHANGE_USER":
return {
user: action.payload,
chatId:
currentUser.uid > action.payload.uid
? currentUser.uid + action.payload.uid
: action.payload.uid + currentUser.uid,
};
default:
return state;
}
};
const [state, dispatch] = useReducer(chatReducer, INITIAL_STATE);
return (
<ChatContext.Provider value={{ data: state, dispatch }}>
{children}
</ChatContext.Provider>
);
};

View File

@ -0,0 +1,17 @@
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getStorage } from "firebase/storage";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "AIzaSyDKHA6ZmllEUfjqmQLd0h5ujiPo0bObTc0",
authDomain: "inconnect-fb444.firebaseapp.com",
projectId: "inconnect-fb444",
storageBucket: "inconnect-fb444.appspot.com",
messagingSenderId: "114710194072",
appId: "1:114710194072:web:ee8de5852a8995789f2ded"
};
export const app = initializeApp(firebaseConfig);
export const auth = getAuth();
export const storage = getStorage();
export const db = getFirestore();

View File

@ -0,0 +1,159 @@
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
.bod {
display: grid;
height: 100vh;
width: 100%;
place-items: center;
background-color: white;
}
.contain {
max-width: 350px;
width: 100%;
padding: 25px 35px;
}
.contain form .title {
text-align: center;
font-size: 30px;
font-weight: 600;
margin: 20px 0 10px 0;
position: relative;
}
.contain form .title::before {
position: absolute;
height: 4px;
width: 33px;
bottom: -2px;
left: 0;
border-radius: 5px;
background: skyblue;
}
.contain form .input-box {
width: 100%;
height: 45px;
margin-top: 25px;
position: relative;
}
.contain form .input-box input {
height: 100%;
width: 100%;
outline: none;
font-size: 16px;
border: none;
}
.contain form .underline:before {
position: absolute;
content: '';
height: 2px;
width: 100%;
background: #ccc;
left: 0;
bottom: 0;
}
.contain form .underline:after {
position: absolute;
content: '';
background: linear-gradient(to right, #7AE5F5 0%, #C9F6FF 100%);
height: 2px;
width: 100%;
left: 0;
bottom: 0;
transform: scaleX(0);
transform-origin: left;
transition: all 0.3s ease-in;
}
.contain form .input-box input:focus~.underline:after,
.contain form .input-box input:valid~.underline:after {
transform: scaleX(1);
transform-origin: left;
}
.contain form .input-box input[type="submit"] {
font-size: 17px;
color: #fff;
border-radius: 5px;
cursor: pointer;
background: linear-gradient(to right, #7AE5F5 0%, #C9F6FF 100%);
transition: all 0.3s ease;
}
.contain form .input-box input[type="submit"]:hover {
letter-spacing: 1px;
background: linear-gradient(to left, #7AE5F5 0%, #C9F6FF 100%);
}
.contain form .button {
margin: 40px 0 20px 0;
}
.contain .option {
font-size: 14px;
text-align: center;
}
.contain .google a,
.contain .facebook a {
display: block;
width: 100%;
height: 45px;
font-size: 15px;
text-decoration: none;
padding-left: 20px;
line-height: 45px;
border-radius: 5px;
}
.contain .google i,
.contain .facebook i {
padding-right: 12px;
font-size: 20px;
}
.contain .google a {
margin: 20px 0px 15px 0px;
background: white;
border: 1px solid rgba(0, 0, 0, 0.25);
color: black;
}
.contain .facebook a {
margin: 20px 0px 15px 0px;
background: #3b5998;
border: 1px solid rgba(0, 0, 0, 0.25);
color: white;
}
.contain .crac {
font-size: 12px;
text-align: center;
padding-top: 20px;
}
.back-button {
display: none;
}
.crac a {
text-decoration: none;
color: inherit;
}
@media screen and (max-width: 768px) {
.back-button.responsive {
display: block;
position: fixed;
left: 50px;
top: 1.9rem;
}
}

View File

@ -0,0 +1,61 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import './Login.css'
import { auth } from '../firebase'
import { signInWithEmailAndPassword } from 'firebase/auth';
const Login = () => {
const history = useNavigate();
const handleGoBack = () => {
history(-1);
};
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
const email = e.target.email.value;
const password = e.target.password.value;
try {
await signInWithEmailAndPassword(auth, email, password);
history("/Dashboard")
} catch (error) {
setError(error.message);
console.error(error);
}
};
return (
<section className='bod'>
<div onClick={handleGoBack} className="back-button responsive">
<i className='fa-solid fa-arrow-left' />
</div>
<div className='contain'>
<form onSubmit={handleSubmit}>
<div className='title'>
Login
</div>
<div className="input-box">
<input type='Text' placeholder='Email' name="email" required />
<div className='underline' />
</div>
<div className="input-box">
<input type='password' placeholder='Password' name="password" required />
<div className='underline' />
</div>
<div className="input-box button">
<input type='submit' value="Login" />
</div>
{error && <span>{error}</span>}
</form>
<div className="crac">
<Link to="/Register">Don't Have An Account ?</Link>
</div>
</div>
</section>
)
}
export default Login

View File

@ -0,0 +1,76 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import './Login.css';
import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth, db } from '../firebase';
import { doc, setDoc } from "firebase/firestore";
const Register = () => {
const [error, setError] = useState(null);
const history = useNavigate();
const handleGoBack = () => {
history(-1);
};
const handleSubmit = async (e) => {
e.preventDefault();
const displayName = e.target[0].value;
const email = e.target[1].value;
const password = e.target[2].value;
try {
const res = await createUserWithEmailAndPassword(auth, email, password);
await setDoc(doc(db, "users", res.user.uid), {
uid: res.user.uid,
displayName,
email
});
await setDoc(doc(db, "userChats", res.user.uid), {
});
history("/Login");
} catch (error) {
setError(error.message);
console.error(error);
}
};
return (
<section className='bod'>
<div onClick={handleGoBack} className="back-button responsive">
<i className='fa-solid fa-arrow-left' />
</div>
<div className='contain'>
<form onSubmit={handleSubmit}>
<div className='title'>
SignUp
</div>
<div className="input-box">
<input type='text' placeholder='Username' name='displayName' required />
<div className='underline' />
</div>
<div className="input-box">
<input type='email' placeholder='Email-ID' name='email' required />
<div className='underline' />
</div>
<div className="input-box">
<input type='password' placeholder='Password' name='password' required />
<div className='underline' />
</div>
<div className="input-box button">
<input type='submit' value="SignUp" />
</div>
{error && <span>{error}</span>}
</form>
<div className="crac">
<Link to="/Login">Already Have An Account ?</Link>
</div>
</div>
</section>
);
};
export default Register;

View File

@ -0,0 +1,18 @@
import { useInView } from "react-intersection-observer";
import { useAnimation } from "framer-motion";
import { useEffect } from "react";
export const useScroll = (thresh = 0.1) => {
const controls = useAnimation();
const [element, view] = useInView({ threshold: thresh });
useEffect(() => {
if (view) {
controls.start("show");
} else {
controls.start("hidden");
}
}, [controls, view]);
return [element, controls];
};

Binary file not shown.

Binary file not shown.

13
src/index.css Normal file
View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

23
src/index.js Normal file
View File

@ -0,0 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AuthContextProvider } from './Components/context/AuthContext';
import { ChatContextProvider } from './Components/context/ChatContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<AuthContextProvider>
<ChatContextProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</ChatContextProvider>
</AuthContextProvider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

13
src/reportWebVitals.js Normal file
View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@ -1,19 +0,0 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
};
export default config;

View File

@ -1,27 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}