Skip to content

Commit

Permalink
Merge pull request #59 from devHarshShah/v2
Browse files Browse the repository at this point in the history
chore: Initial commit to set up Next-JS project
  • Loading branch information
Chandram-Dutta authored Sep 16, 2024
2 parents ef48398 + a8a606c commit a03ea94
Show file tree
Hide file tree
Showing 23 changed files with 5,561 additions and 0 deletions.
3 changes: 3 additions & 0 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
36 changes: 36 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [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.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

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.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
53 changes: 53 additions & 0 deletions frontend/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import Footer from '../components/Footer';
import Navbar from '../components/Navbar';

const page = () => {
return (
<>
<Navbar/>
<div className="bg-[#2E2E2E] text-foreground p-6 rounded-lg shadow-md font-jetbrains-mono mb-10">
<h1 className="text-2xl font-bold mb-4 text-secondary">Triton - GDSC VIT's pastebin and URL shortener</h1>
<p className="mb-4">Sharing short code samples, logs or links is now easier than ever!</p>
<h2 className="text-xl font-semibold mt-6 text-secondary">Basics</h2>
<p className="mb-4">
The easiest way to use triton, is from inside your browser. Type or paste your content into the text area on the homepage, save it, and copy the link. You can now share this link whenever you want. If you want to create a short url from a link, simply paste the link in the text field. You
can now copy the resulting link and it will automatically redirect everyone.
</p>

<h2 className="text-xl font-semibold mt-6 text-secondary">Editing</h2>
<p className="mb-4">
You can edit your documents by clicking the edit button. When you create your first document we will automatically create an (anonymous) account for you, so you will be able to edit your documents for as long as you keep our cookie. If you want a more permanent way to keep your pastes and
edit them from multiple devices you can create an account. Pastes which you created before signing up will be automatically transferred to your account.
</p>

<h2 className="text-xl font-semibold mt-6 text-secondary">Markdown support</h2>
<p className="mb-4">We support markdown documents formatting in the CommonMark spec. Append ".md" to the end of the url to render any paste as markdown.</p>

<h2 className="text-xl font-semibold mt-6 text-secondary">Analytics</h2>
<p className="mb-4">
We support per-page analytics using a privacy-friendly analytics provider,{' '}
<a href="#" className="text-primary hover:underline">
Simple Analytics
</a>
.
</p>

<h2 className="text-xl font-semibold mt-6 text-secondary">Document lifetime</h2>
<p className="mb-4">There is no explicit expiration time for documents on triton. Documents may however be removed at any time without notice.</p>

<h2 className="text-xl font-semibold mt-6 text-secondary">Open source</h2>
<p className="mb-4">
Triton is fully open source and built on open source technology. All sources can be found on{' '}
<a href="#" className="text-primary hover:underline">
GitHub
</a>
. You can easily deploy it yourself wherever you want.
</p>
</div>
<Footer/>
</>
);
};

export default page;
31 changes: 31 additions & 0 deletions frontend/app/auth/callback/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useSearchParams } from 'next/navigation';

const OAuthCallback = () => {
const router = useRouter();
const searchParams = useSearchParams();

useEffect(() => {
const handleOAuthCallback = () => {
const token = searchParams.get('token');

if (token) {
localStorage.setItem('token', token);
console.log('Token stored in localStorage:', token);
router.push('/');
} else {
console.error('No token found in query parameters');
}
};

handleOAuthCallback();
}, [router, searchParams]);

return (
<div>Loading...</div>
);
};

export default OAuthCallback;
13 changes: 13 additions & 0 deletions frontend/app/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Link from 'next/link';

const Footer = () => {
return (
<footer className="bg-black text-white flex justify-end p-4 fixed bottom-0 w-full font-jetbrains-mono">
<Link href="/about" className="text-white hover:text-secondary">
About
</Link>
</footer>
);
};

export default Footer;
130 changes: 130 additions & 0 deletions frontend/app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import axios from 'axios';

const Navbar = ({ content, titleN }: { content: string, titleN:string }) => {
const [isOpen, setIsOpen] = useState(false);
const [title, setTitle] = useState(titleN);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [oAuthToken, setOAuthToken] = useState('');

const toggleMenu = () => {
setIsOpen(!isOpen);
};

useEffect(() => {
const token = localStorage.getItem('token') ?? '';
setOAuthToken(token);
setIsLoggedIn(!!token);
}, []);

const handleTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.target.value);
};

const handleSave = async () => {
const [fileName, extension] = title.split('.');
const payload = {
title: fileName,
content,
extension: extension || '.txt',
url: fileName.replace(/\s+/g, '-').toLocaleLowerCase(),
};

console.log(payload);

try {
const response = await axios.post('http://localhost:8080/v1/pastebin/create', payload, {
headers: {
Authorization: `Bearer ${oAuthToken}`,
},
});
const generatedUrl = `http://localhost:8080/${fileName.replace(/\s+/g, '-').toLocaleLowerCase()}`;
console.log('Saved successfully:', response.data);
console.log('Generated URL:', generatedUrl);
} catch (error) {
console.error('Error saving content:', error);
}
};

const handleLogout = async () => {
try {
await axios.post('http://localhost:8080/v1/auth/logout', {}, {
headers: {
Authorization: `Bearer ${oAuthToken}`,
},
});
localStorage.removeItem('token');
setIsLoggedIn(false);
setOAuthToken('');
console.log('Logged out successfully');
} catch (error) {
console.error('Error logging out:', error);
}
};

return (
<nav className="bg-background flex items-center justify-between p-4 mx-4 font-jetbrains-mono">
<div className="flex items-center">
<Image alt="logo" src="/logo.svg" width={45} height={30} />
<div className="flex flex-col space-y-1">
<input type="text" className="text-xl text-[#8F8F8F] ml-6 bg-black outline-none" placeholder="Untitled file" value={title} onChange={handleTitleChange} />
<div className="flex space-x-4 ml-6">
<button className="text-muted hover:text-muted-foreground underline decoration-secondary underline-offset-4">Workspace</button>
<button className="text-muted hover:text-muted-foreground underline decoration-secondary underline-offset-4">Preview</button>
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<div className="hidden md:flex items-center space-x-8">
<button className="bg-white text-black px-4 py-1 rounded-2xl hover:bg-secondary">Analytics</button>
<button onClick={handleSave} className="text-muted hover:text-muted-foreground flex items-center space-x-2">
<Image alt="save icon" src="/save.svg" width={25} height={25} />
<span>Save</span>
</button>
{isLoggedIn ? (
<button type="button" className="w-full text-left text-muted hover:text-muted-foreground flex items-center space-x-2" onClick={handleLogout}>
<span>Logout</span>
</button>
) : (
<form action="http://localhost:8080/v1/auth/login/oauth" method="GET">
<button type="submit" className="w-full text-left text-muted hover:text-muted-foreground flex items-center space-x-2">
<span>Login</span>
</button>
</form>
)}
</div>
<div className="md:hidden">
<button onClick={toggleMenu} className="text-muted hover:text-muted-foreground">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16m-7 6h7"></path>
</svg>
</button>
</div>
</div>
{isOpen && (
<div className="md:hidden absolute top-16 right-4 bg-background border border-gray-700 rounded-lg shadow-lg p-4 bg-black">
<button className="block w-full text-left text-white px-4 py-2 rounded-lg hover:bg-secondary mb-2">Analytics</button>
<button onClick={handleSave} className="w-full text-left text-muted hover:text-muted-foreground flex items-center space-x-2">
<Image alt="save icon" src="/save.svg" width={25} height={25} />
<span>Save</span>
</button>
{isLoggedIn ? (
<button type="button" className="w-full text-left text-muted hover:text-muted-foreground flex items-center space-x-2" onClick={handleLogout}>
<span>Logout</span>
</button>
) : (
<form action="http://localhost:8080/v1/auth/login/oauth" method="GET">
<button type="submit" className="w-full text-left text-muted hover:text-muted-foreground flex items-center space-x-2">
<span>Login</span>
</button>
</form>
)}
</div>
)}
</nav>
);
};

export default Navbar;
40 changes: 40 additions & 0 deletions frontend/app/components/Pastebin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';
import { useState } from 'react';
import Navbar from './Navbar';
import Footer from './Footer';

interface PastebinProps {
initialContent?: string;
initialTitle?: string;
}

const Pastebin: React.FC<PastebinProps> = ({ initialContent = '', initialTitle = 'Untitled file' }) => {
const [content, setContent] = useState(initialContent);
const handleContentChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setContent(event.target.value);
};

return (
<>
<Navbar content={content} titleN={initialTitle} />
<div className="flex flex-col md:flex-row h-[85vh] font-jetbrains-mono">
<textarea
className="w-full md:w-1/2 h-full resize-none p-4 bg-[#212121] focus:outline-none"
placeholder="Pastebin and URL shortener. Paste, save and share!"
value={content}
onChange={handleContentChange}
></textarea>
<div className="w-full h-0.5 md:w-0.5 md:h-full bg-black"></div>
<textarea
className="w-full md:w-1/2 h-full resize-none p-4 mb-10 bg-[#212121] focus:outline-none"
placeholder="Pastebin and URL shortener. Paste, save and share!"
readOnly
value={content}
></textarea>
</div>
<Footer />
</>
);
}

export default Pastebin;
Binary file added frontend/app/favicon.ico
Binary file not shown.
39 changes: 39 additions & 0 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@500;&display=swap');

body {
font-family: 'JetBrains Mono', monospace;
}

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}
Loading

0 comments on commit a03ea94

Please sign in to comment.