Skip to content

Commit

Permalink
Merge pull request #11 from devzero-inc/local-supabase
Browse files Browse the repository at this point in the history
completes the app functionality
  • Loading branch information
AdoshSingh authored Feb 9, 2024
2 parents 999ca15 + 59b2bee commit efc9d5b
Show file tree
Hide file tree
Showing 24 changed files with 2,169 additions and 81 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/roadmap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Roadmap Voting App CI

on:
push:
branches: [ main ]
paths:
- 'roadmap-voting-app/**'
pull_request:
branches: [ main ]
paths:
- 'roadmap-voting-app/**'

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '20.x'

- name: Install Dependencies
run: |
cd roadmap-voting-app
npm install
# - name: Lint
# run: |
# cd roadmap-voting-app
# npm run lint

# - name: Run Tests
# run: |
# cd roadmap-voting-app
# npm test
3 changes: 3 additions & 0 deletions roadmap-voting-app/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["denoland.vscode-deno"]
}
10 changes: 10 additions & 0 deletions roadmap-voting-app/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"deno.enablePaths": [
"supabase/functions"
],
"deno.lint": true,
"deno.unstable": true,
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}
}
30 changes: 30 additions & 0 deletions roadmap-voting-app/app/api/post/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '../../../lib/supabaseClient';

export async function GET(req: NextRequest) {
try {

const postId = req.nextUrl.searchParams.get("postId");

if (postId) {
const { data, error } = await supabase
.from('votes')
.select('*')
.eq('postid', postId);

if (error) throw error;
return NextResponse.json({ votes: data, status: 200 });
}else{
const { data, error } = await supabase.from('posts').select('*');

if (error) throw error;
return NextResponse.json({ posts: data, status: 200 });
}
} catch (error) {
if (error) {
return NextResponse.json({ message: error, status: 401 });
} else {
return NextResponse.json({ message: 'An unexpected error occurred', status: 500 });
}
}
}
29 changes: 22 additions & 7 deletions roadmap-voting-app/app/api/user/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ export async function POST(req: NextRequest) {
const email = formdata.get("email") as string;
const password = formdata.get("password") as string;
if (login) {
const { data , error } = await supabase.auth.signInWithPassword({ email, password });
if (error) throw error;
return NextResponse.json({ message: "User logged in successfully", data: data, status: 200 });
const signInresponse = await supabase.auth.signInWithPassword({ email, password });
if (signInresponse.error) throw signInresponse.error;

const userId = signInresponse.data.user.id;

const { data: userData, error: userError } = await supabase
.from('userstable')
.select('*')
.eq('id', userId)
.single();

if (userError) throw userError;
return NextResponse.json({ message: "User logged in successfully", data: signInresponse.data, userData: userData, status: 200 });
} else {
const username = formdata.get("username") as string;
const username = formdata.get("name") as string;

console.log(username, email, password);
if (!username) {
return NextResponse.json({ message: 'Username is required', status: 400 });
}
Expand All @@ -28,10 +37,16 @@ export async function POST(req: NextRequest) {
const signUpResponse = await supabase.auth.signUp({ email, password });
if (signUpResponse.error) throw signUpResponse.error;

return NextResponse.json({ message: "user created successfuly", data:signUpResponse.data , status: 200 });
if (signUpResponse.data.user) {
const { error: insertError } = await supabase
.from('userstable')
.insert([{ id: signUpResponse.data.user.id, name: username, email: email }]);
if (insertError) throw insertError;
}

return NextResponse.json({ message: "user created successfuly", data:signUpResponse.data, status: 200 });
}
} catch (error) {
// console.log(error);
if (error instanceof Error) {
return NextResponse.json({ message: error.message, status: 401 });
} else {
Expand Down
59 changes: 59 additions & 0 deletions roadmap-voting-app/app/api/vote/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '../../../lib/supabaseClient';
import { v4 as uuidv4 } from 'uuid';
import {authenticate} from '../../../lib/authenticate';

export async function POST(req: NextRequest) {
try {
const user = await authenticate(req);

const formdata = await req.formData();
const postId = formdata.get("postId") as string;
const userId = formdata.get("userId") as string;
const type = formdata.get("type") as string;

if (!postId || !userId) {
return NextResponse.json({ message: 'Post ID and User ID are required', status: 400 });
}
if (!type) {
return NextResponse.json({ message: 'Vote type is required', status: 400 });
}

const existingVote = await supabase
.from('votes')
.select('*')
.eq('postid', postId)
.eq('userid', userId)
.single();

if (existingVote.data) {
const response = await supabase
.from('votes')
.update({ type, updatedAt: new Date() })
.eq('postid', postId)
.eq('userid', userId);
if (response.error){
throw response.error;
}

return NextResponse.json({ message: "Vote updated successfully", status: 201 });
}else{
const date = new Date();
const id = uuidv4();
const response = await supabase
.from('votes')
.insert([{ id: id, postid: postId, userid: userId, type: type }]);
if (response.error){
throw response.error;
}
return NextResponse.json({ message: "Vote recorded successfully", data: {id: id, postId: postId, userId: userId, type: type, createdAt: date, updatedAt: date }, status: 200 });
}
} catch (error) {
console.log(error);
if (error) {
return NextResponse.json({ message: error, status: 401 });
} else {
return NextResponse.json({ message: 'An unexpected error occurred', status: 500 });
}
}
}
42 changes: 36 additions & 6 deletions roadmap-voting-app/app/auth/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
"use client";

import Link from "next/link";
import { useState } from "react";
import { useRouter } from "next/navigation";

const SignIn = () => {

const router = useRouter();
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");

const handleSignin = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const newFormData = new FormData();
newFormData.append("email", email);
newFormData.append("password", password);

fetch("/api/user?login=yes", {
method: "POST",
body: newFormData
})
.then(res => res.json())
.then(data => {
localStorage.setItem("session", JSON.stringify(data.data.session));
localStorage.setItem("name", data.userData.name);
window.dispatchEvent(new Event("storageUpdate"));
router.push('/');
})
.catch(err => console.log(err));
}

const Signup = () => {
return (
<div className=" bg-cusSec border border-cusBorder flex-1 w-[50%] mb-5 rounded-lg text-white p-4 flex flex-col items-center justify-around">
<h1 className=" text-6xl font-bold bg-clip-text text-transparent bg-custom-gradient p-2">Sign In</h1>
<form className="flex flex-col gap-4">
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="email" placeholder="Enter your email"/>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="password" placeholder="Enter your password"/>
<form className="flex flex-col gap-4" onSubmit={handleSignin}>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="email" placeholder="Enter your email" onChange={e => setEmail(e.target.value)} />
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="password" placeholder="Enter your password" onChange={e => setPassword(e.target.value)} />
<Link href="/auth/signup" className="outline-none text-center">
Don't have an account? <span className="bg-clip-text text-transparent bg-custom-gradient">Signup</span>
</Link>
<button className=" bg-custom-gradient px-4 py-3 rounded-lg outline-none">
<button type="submit" className=" bg-custom-gradient px-4 py-3 rounded-lg outline-none">
Sign In
</button>
</form>
</div>
)
}

export default Signup
export default SignIn
37 changes: 32 additions & 5 deletions roadmap-voting-app/app/auth/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
"use client";

import Link from "next/link";
import { useState } from "react";

const Signup = () => {

const [name, setName] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");

const handleSignup = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const newFormData = new FormData();
newFormData.append("name", name);
newFormData.append("email", email);
newFormData.append("password", password);

fetch("/api/user", {
method: "POST",
body: newFormData
})
.then(res => res.json())
.then(data => {
window.location.href = '/auth/signin';
})
.catch(err => console.log(err));
}

return (
<div className=" bg-cusSec border border-cusBorder flex-1 w-[50%] mb-5 rounded-lg text-white p-4 flex flex-col items-center justify-around">
<h1 className=" text-6xl font-bold bg-clip-text text-transparent bg-custom-gradient p-2">Sign Up</h1>
<form className="flex flex-col gap-4">
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="text" placeholder="Enter your name"/>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="email" placeholder="Enter your email"/>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="password" placeholder="Enter your password"/>
<form className="flex flex-col gap-4" onSubmit={handleSignup}>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="text" placeholder="Enter your name" onChange={e => setName(e.target.value)}/>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="email" placeholder="Enter your email" onChange={e => setEmail(e.target.value)}/>
<input className=" px-4 py-3 rounded-lg bg-cusInput outline-none" type="password" placeholder="Enter your password" onChange={e => setPassword(e.target.value)}/>
<Link href="/auth/signin" className="outline-none text-center">
Already have an account? <span className="bg-clip-text text-transparent bg-custom-gradient">Signin</span>
</Link>
<button className=" bg-custom-gradient px-4 py-3 rounded-lg outline-none">
<button type="submit" className=" bg-custom-gradient px-4 py-3 rounded-lg outline-none">
Sign Up
</button>
</form>
Expand Down
7 changes: 5 additions & 2 deletions roadmap-voting-app/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb:15 11 41 ;
--background-end-rgb: 15 11 41;
/* --background-start-rgb:15 11 41 ;
--background-end-rgb: 15 11 41; */
--background-start-rgb:243,239,254 ;
--background-end-rgb: 243,239,254;
/* 164,179,182 */
/* background-color: rgb(121, 33, 146); */
}

Expand Down
2 changes: 1 addition & 1 deletion roadmap-voting-app/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Navbar from "@/components/Navbar";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Create Next App",
title: "Roadmap Voting App",
description: "Generated by create next app",
};

Expand Down
44 changes: 43 additions & 1 deletion roadmap-voting-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
"use client";

import Topbar from "@/components/Topbar";
import Post from "@/components/Post";
import { useEffect, useState } from "react";

interface post {
id: string,
title: string,
description: string,
status: string,
target: string,
createdAt: string,
updatedAt: string,
}

export default function Home() {

const [posts, setPosts] = useState<post[]>();

useEffect(() => {

fetch("/api/post")
.then((res) => res.json())
.then((data) => {
setPosts(data.posts);
})
.catch((err) => console.log(err));

}, [])

return (
<div className="">
<div className=" w-[50%] flex flex-col mb-4">
<Topbar />

{posts && posts.map((ele: post, it: number): JSX.Element => (
<Post
key={ele.id}
id={ele.id}
title={ele.title}
description={ele.description}
status={ele.status}
target={ele.target}
isLast={it === posts.length - 1 ? true : false}
/>
))}

</div>
);
Expand Down
Loading

0 comments on commit efc9d5b

Please sign in to comment.