sungjin

Fix some issues for page, Change design to light page

...@@ -4,12 +4,16 @@ import { SERVER_BASE_URL } from '..'; ...@@ -4,12 +4,16 @@ import { SERVER_BASE_URL } from '..';
4 4
5 // login request to the server with axios and next-auth 5 // login request to the server with axios and next-auth
6 export const login = async (email, password) => { 6 export const login = async (email, password) => {
7 - const response = await axios.post(`${SERVER_BASE_URL}/auth/signin` , { 7 + const response = await axios.post(`${SERVER_BASE_URL}/auth/signin`, {
8 email, 8 email,
9 password, 9 password,
10 + }).catch(err => {
11 + if (err.response.status === 400 || err.response.status === 401) {
12 + throw alert('Invalid email or password');
13 + }
10 }); 14 });
11 - if (response.status !== 200 && response.status !== 201) { 15 + if (response == undefined || (response.status !== 200 && response.status !== 201)) {
12 - throw new Error('Login failed!'); 16 + throw alert('Login failed!');
13 } 17 }
14 console.log(response.data.access_token) 18 console.log(response.data.access_token)
15 useSession.accessToken = response.data.access_token; 19 useSession.accessToken = response.data.access_token;
...@@ -22,15 +26,20 @@ export const signup = async (name, email, password) => { ...@@ -22,15 +26,20 @@ export const signup = async (name, email, password) => {
22 name, 26 name,
23 email, 27 email,
24 password, 28 password,
29 + }).catch(err => {
30 + if (err.response.status === 400 || err.response.status === 401) {
31 + throw alert('Signup failed, maybe email is already used');
32 + }
25 }); 33 });
26 if (response.status !== 200 && response.status !== 201) { 34 if (response.status !== 200 && response.status !== 201) {
27 - throw new Error('Signup failed!'); 35 + throw alert('Signup failed!');
28 } 36 }
29 useSession.accessToken = response.data.access_token; 37 useSession.accessToken = response.data.access_token;
30 return response.data; 38 return response.data;
31 } 39 }
32 40
33 -export const logout = async () => {1 41 +export const logout = async () => {
42 + 1
34 useSession.accessToken = null; 43 useSession.accessToken = null;
35 return true; 44 return true;
36 } 45 }
...@@ -60,6 +69,8 @@ export const validateToken = async () => { ...@@ -60,6 +69,8 @@ export const validateToken = async () => {
60 console.log(useSession.accessToken); 69 console.log(useSession.accessToken);
61 const response = await axios.post(`${SERVER_BASE_URL}/auth/validate`, { 70 const response = await axios.post(`${SERVER_BASE_URL}/auth/validate`, {
62 token: useSession.accessToken, 71 token: useSession.accessToken,
72 + }).catch(err => {
73 + throw false;
63 }); 74 });
64 if (response.status !== 200 && response.status !== 201) { 75 if (response.status !== 200 && response.status !== 201) {
65 return false; 76 return false;
......
1 import * as auth from '../auth' 1 import * as auth from '../auth'
2 import axios from 'axios'; 2 import axios from 'axios';
3 import { SERVER_BASE_URL } from '..'; 3 import { SERVER_BASE_URL } from '..';
4 +import { useSession } from 'next-auth/client';
4 5
5 export const newPost = async ( 6 export const newPost = async (
6 title, 7 title,
...@@ -57,3 +58,14 @@ export const getPostsByDifficulty = async (difficulty) => { ...@@ -57,3 +58,14 @@ export const getPostsByDifficulty = async (difficulty) => {
57 console.log(response.data); 58 console.log(response.data);
58 return response.data; 59 return response.data;
59 } 60 }
61 +
62 +export const createComment = async (id, content) => {
63 + const response = await axios.post(`${SERVER_BASE_URL}/post/comment/${id}`, {
64 + token : useSession.accessToken,
65 + content,
66 + });
67 + if (response.status !== 200 && response.status !== 201) {
68 + throw new Error('Failed to create comment!');
69 + }
70 + return response.data;
71 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -14,7 +14,7 @@ import Link from "next/link" ...@@ -14,7 +14,7 @@ import Link from "next/link"
14 function Header() { 14 function Header() {
15 15
16 return ( 16 return (
17 - <header className="flex flex-col sm:flex-row m-5 justify-between items-center"> 17 + <header className="flex flex-col sm:flex-row m-5 mt-0 pt-5 justify-between items-center">
18 <Link href="/" passHref> 18 <Link href="/" passHref>
19 <div className="flex cursor-pointer transform hover:scale-105"> 19 <div className="flex cursor-pointer transform hover:scale-105">
20 <CubeIcon className="h-20" /> 20 <CubeIcon className="h-20" />
......
...@@ -3,7 +3,7 @@ import Link from "next/link" ...@@ -3,7 +3,7 @@ import Link from "next/link"
3 function HeaderItem({ Icon, title, link }) { 3 function HeaderItem({ Icon, title, link }) {
4 return ( 4 return (
5 <Link href={link}> 5 <Link href={link}>
6 - <div className="flex flex-col items-center cursor-pointer group w-12 sm:w-20 hover:text-white"> 6 + <div className="flex flex-col items-center cursor-pointer group w-12 sm:w-20 hover:text-black">
7 <Icon className="h-8 mb-1 group-hover:animate-bounce" /> 7 <Icon className="h-8 mb-1 group-hover:animate-bounce" />
8 <p className="opacity-0 group-hover:opacity-100 tracking-widest">{title}</p> 8 <p className="opacity-0 group-hover:opacity-100 tracking-widest">{title}</p>
9 </div> 9 </div>
......
...@@ -25,20 +25,20 @@ export default function Nav() { ...@@ -25,20 +25,20 @@ export default function Nav() {
25 <div className="flex px-10 sm:px-20 text-2xl whitespace-nowrap 25 <div className="flex px-10 sm:px-20 text-2xl whitespace-nowrap
26 space-x-10 sm:space-x-20"> 26 space-x-10 sm:space-x-20">
27 <Button onClick={moverun} className="cursor-pointer transition 27 <Button onClick={moverun} className="cursor-pointer transition
28 - duration-100 transform hover:scale-125 hover:text-white 28 + duration-100 transform hover:scale-125 hover:text-black
29 - active:text-blue-500">테스트</Button> 29 + active:text-white">테스트</Button>
30 <Button onClick={movelow} className="cursor-pointer transition 30 <Button onClick={movelow} className="cursor-pointer transition
31 - duration-100 transform hover:scale-125 hover:text-white 31 + duration-100 transform hover:scale-125 hover:text-black
32 - active:text-blue-500">난이도 </Button> 32 + active:text-white">난이도 </Button>
33 <Button onClick={movemed} className="cursor-pointer transition 33 <Button onClick={movemed} className="cursor-pointer transition
34 - duration-100 transform hover:scale-125 hover:text-white 34 + duration-100 transform hover:scale-125 hover:text-black
35 - active:text-blue-500">난이도 </Button> 35 + active:text-white">난이도 </Button>
36 <Button onClick={movehigh} className="cursor-pointer transition 36 <Button onClick={movehigh} className="cursor-pointer transition
37 - duration-100 transform hover:scale-125 hover:text-white 37 + duration-100 transform hover:scale-125 hover:text-black
38 - active:text-blue-500">난이도 </Button> 38 + active:text-white">난이도 </Button>
39 <Button onClick={movenew} className="cursor-pointer transition 39 <Button onClick={movenew} className="cursor-pointer transition
40 - duration-100 transform hover:scale-125 hover:text-white 40 + duration-100 transform hover:scale-125 hover:text-black
41 - active:text-blue-500">문제 만들기</Button> 41 + active:text-white">문제 만들기</Button>
42 </div> 42 </div>
43 </nav> 43 </nav>
44 ) 44 )
......
...@@ -6,11 +6,13 @@ import Footer from '../components/Footer' ...@@ -6,11 +6,13 @@ import Footer from '../components/Footer'
6 function MyApp({ Component, pageProps }) { 6 function MyApp({ Component, pageProps }) {
7 return ( 7 return (
8 <div> 8 <div>
9 - <div className="main"> 9 + <div className="min-h-screen">
10 + <div className="main bg-neutral pb-4 border-b-4 border-carbon">
10 <Header /> 11 <Header />
11 <Nav /> 12 <Nav />
12 </div> 13 </div>
13 <Component {...pageProps} /> 14 <Component {...pageProps} />
15 + </div>
14 <Footer /> 16 <Footer />
15 </div> 17 </div>
16 ) 18 )
......
1 import { useRouter } from "next/router"; 1 import { useRouter } from "next/router";
2 +import { Button } from "semantic-ui-react";
2 import * as auth from "../api/auth"; 3 import * as auth from "../api/auth";
3 4
4 export default function Login() { 5 export default function Login() {
5 const router = useRouter(); 6 const router = useRouter();
6 7
7 return ( 8 return (
8 - <div> 9 + <div className="flex h-auto">
9 - <h1>Login</h1> 10 + <div className="w-auto inline-block p-3 bg-black rounded-lg m-auto">
11 + <h1 className="font-bold text-4xl text-center">로그인</h1>
10 <form onSubmit={handleSubmit}> 12 <form onSubmit={handleSubmit}>
11 - <label htmlFor="email">Email</label> 13 + <input type="email" id="email" placeholder="email" className="my-2 rounded-sm"/>
12 - <input type="email" id="email" /> 14 + <br />
13 - <label htmlFor="password">Password</label> 15 + <input type="password" id="password" className="my-2 rounded-sm" placeholder="password" />
14 - <input type="password" id="password" /> 16 + <br />
15 - <button type="submit">Login</button> 17 + <button type="submit" className="mt-2 px-4 py-2 text-base font-semibold text-white transition duration-500 transform bg-blue-300 rounded-lg hover:shadow-lg focus:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-300 focus:ring-offset-2 focus:ring-offset-blue-300 motion-reduce:transform-none hover:scale-105 tramsform">Login</button>
16 </form> 18 </form>
17 </div> 19 </div>
20 + </div>
18 ); 21 );
19 22
20 async function handleSubmit(event) { 23 async function handleSubmit(event) {
...@@ -23,7 +26,7 @@ export default function Login() { ...@@ -23,7 +26,7 @@ export default function Login() {
23 const email = form.email.value; 26 const email = form.email.value;
24 const password = form.password.value; 27 const password = form.password.value;
25 const login = await auth.login(email, password); 28 const login = await auth.login(email, password);
26 - if(!login) { 29 + if (!login) {
27 alert("Login failed"); 30 alert("Login failed");
28 } 31 }
29 else { 32 else {
......
1 import { Button } from "semantic-ui-react"; 1 import { Button } from "semantic-ui-react";
2 import { newPost } from "../api/post"; 2 import { newPost } from "../api/post";
3 +import React from "react";
4 +import dynamic from "next/dynamic";
5 +import "@uiw/react-textarea-code-editor/dist.css";
6 +
7 +const CodeEditor = dynamic(
8 + () => import("@uiw/react-textarea-code-editor").then((mod) => mod.default),
9 + { ssr: false }
10 +);
3 11
4 export default function New() { 12 export default function New() {
13 + const [code, setCode] = React.useState("");
14 +
5 async function makePost(event) { 15 async function makePost(event) {
6 event.preventDefault(); 16 event.preventDefault();
7 const form = event.target; 17 const form = event.target;
8 const title = form.title.value; 18 const title = form.title.value;
9 const explain = form.explain.value; 19 const explain = form.explain.value;
10 - const example = form.example.value; 20 + const example = code;
11 const testinput = form.testinput.value.split("\n"); 21 const testinput = form.testinput.value.split("\n");
12 const testoutput = form.testoutput.value.split("\n"); 22 const testoutput = form.testoutput.value.split("\n");
13 const difficulty = form.level.value; 23 const difficulty = form.level.value;
...@@ -27,8 +37,20 @@ export default function New() { ...@@ -27,8 +37,20 @@ export default function New() {
27 </select> 37 </select>
28 <label>문제 설명</label> 38 <label>문제 설명</label>
29 <textarea id="explain"></textarea><br /> 39 <textarea id="explain"></textarea><br />
30 - <label>예시 코드</label> 40 + <label>예시 코드 (C++언어로 작성해야 합니다.)</label>
31 - <textarea id="example"></textarea><br /> 41 + <CodeEditor
42 + value={code}
43 + language="cpp"
44 + placeholder="Please enter example code."
45 + onChange={(evn) => setCode(evn.target.value)}
46 + padding={15}
47 + style={{
48 + fontSize: 12,
49 + backgroundColor: "#f5f5f5",
50 + fontFamily:
51 + "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace"
52 + }}
53 + />
32 <label>예시 입력(Enter 구분)</label> 54 <label>예시 입력(Enter 구분)</label>
33 <textarea id="testinput"></textarea> 55 <textarea id="testinput"></textarea>
34 <label>예시 출력(Enter 구분)</label> 56 <label>예시 출력(Enter 구분)</label>
......
1 import { useRouter } from "next/dist/client/router" 1 import { useRouter } from "next/dist/client/router"
2 -import { getPostbyId } from "../../api/post" 2 +import { createComment, getPostbyId } from "../../api/post"
3 import "@uiw/react-textarea-code-editor/dist.css"; 3 import "@uiw/react-textarea-code-editor/dist.css";
4 import dynamic from "next/dynamic"; 4 import dynamic from "next/dynamic";
5 import { useEffect, useState } from "react"; 5 import { useEffect, useState } from "react";
6 import { Button } from "semantic-ui-react"; 6 import { Button } from "semantic-ui-react";
7 import { run } from "../../api/runner"; 7 import { run } from "../../api/runner";
8 +import { useSession } from "next-auth/client";
9 +import SyntaxHighlighter from 'react-syntax-highlighter';
8 10
9 const CodeEditor = dynamic( 11 const CodeEditor = dynamic(
10 () => import("@uiw/react-textarea-code-editor").then((mod) => mod.default), 12 () => import("@uiw/react-textarea-code-editor").then((mod) => mod.default),
...@@ -20,6 +22,7 @@ export default function Post() { ...@@ -20,6 +22,7 @@ export default function Post() {
20 if (!router.isReady) return; 22 if (!router.isReady) return;
21 const { id } = router.query 23 const { id } = router.query
22 getPostbyId(id).then(res => { 24 getPostbyId(id).then(res => {
25 + console.log(res);
23 setPost(res) 26 setPost(res)
24 }) 27 })
25 }, [router.isReady]); 28 }, [router.isReady]);
...@@ -28,16 +31,45 @@ export default function Post() { ...@@ -28,16 +31,45 @@ export default function Post() {
28 31
29 const [code, setCode] = useState(""); 32 const [code, setCode] = useState("");
30 33
31 - const [answer, setAnswer] = useState("asdf"); 34 + const [answer, setAnswer] = useState("Answer Test");
35 +
36 + const [comment, setComment] = useState("");
37 +
38 + const addComment = async () => {
39 + const { id } = router.query
40 + const response = await createComment(id, comment)
41 + }
42 +
43 + const displayComment = () => {
44 + console.log(post.comments);
45 + if (post.comments != undefined) {
46 + return post.comments.map(comment => (
47 + <div className="w-full">
48 + <div className="w-full flex justify-between">
49 + <div className="w-1/2">
50 + <span className="text-gray-700">{comment.username}</span>
51 + </div>
52 + </div>
53 + </div>)
54 + )
55 + }
56 + return <div></div>
57 + }
58 +
32 59
33 const runCode = async function () { 60 const runCode = async function () {
61 + if (useSession.accessToken == null) {
62 + alert("You need to login first!")
63 + router.push("/login")
64 + return;
65 + }
34 var result = await run(code, value); 66 var result = await run(code, value);
35 console.log(result); 67 console.log(result);
36 setAnswer(`출력 : ${result}`); 68 setAnswer(`출력 : ${result}`);
37 } 69 }
38 70
39 return ( 71 return (
40 - <div> 72 + <div className="ml-10 mr-10">
41 <h3 className="text-3xl font-bold">{post.title}</h3> 73 <h3 className="text-3xl font-bold">{post.title}</h3>
42 <p>{post.testinput}</p> 74 <p>{post.testinput}</p>
43 <p>{post.testoutput}</p> 75 <p>{post.testoutput}</p>
...@@ -49,7 +81,7 @@ export default function Post() { ...@@ -49,7 +81,7 @@ export default function Post() {
49 <option value="go">golang</option> 81 <option value="go">golang</option>
50 <option value="py">python</option> 82 <option value="py">python</option>
51 </select> 83 </select>
52 - <span className="left-100 fixed text-2xl font-semibold">문제 설명</span> 84 + <span className="absolute left-1/2 text-2xl font-semibold">문제 설명</span>
53 <br></br> 85 <br></br>
54 <div className="w-6/12 inline-block"> 86 <div className="w-6/12 inline-block">
55 <CodeEditor 87 <CodeEditor
...@@ -64,15 +96,24 @@ export default function Post() { ...@@ -64,15 +96,24 @@ export default function Post() {
64 fontFamily: 96 fontFamily:
65 "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace" 97 "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace"
66 }} 98 }}
67 - className="w-8/12 h-96 rounded" 99 + className="w-8/12 min-h-[24rem] rounded"
68 100
69 /> 101 />
70 </div> 102 </div>
71 <div className="w-6/12 inline-block align-top">{post.explain}</div> 103 <div className="w-6/12 inline-block align-top">{post.explain}</div>
72 <Button onClick={runCode} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Run</Button> 104 <Button onClick={runCode} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Run</Button>
73 <div className="">{answer}</div> 105 <div className="">{answer}</div>
74 - 106 + <details>
75 - <p>{post.example}</p> 107 + <summary>예시 코드 보기</summary>
108 + <SyntaxHighlighter language="cpp">
109 + {post.example || "Loading Example..."}
110 + </SyntaxHighlighter>
111 + </details>
112 + <div>
113 + <textarea className="w-full h-24" placeholder="Write your comment..." value={comment} onChange={(e) => setComment(e.target.value)}></textarea>
114 + <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={addComment}>댓글 쓰기</button>
115 + {displayComment()}
116 + </div>
76 </div> 117 </div>
77 ) 118 )
78 } 119 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -8,10 +8,16 @@ export default function Signup() { ...@@ -8,10 +8,16 @@ export default function Signup() {
8 <form onSubmit={handleSubmit}> 8 <form onSubmit={handleSubmit}>
9 <label htmlFor="name">Name</label> 9 <label htmlFor="name">Name</label>
10 <input type="text" id="name" /> 10 <input type="text" id="name" />
11 + <br />
11 <label htmlFor="email">Email</label> 12 <label htmlFor="email">Email</label>
12 <input type="email" id="email" /> 13 <input type="email" id="email" />
14 + <br />
13 <label htmlFor="password">Password</label> 15 <label htmlFor="password">Password</label>
14 <input type="password" id="password" /> 16 <input type="password" id="password" />
17 + <br />
18 + <label htmlFor="password-confirm">Confirm Password</label>
19 + <input type="password" id="password-confirm" />
20 + <br />
15 <button type="submit">Signup</button> 21 <button type="submit">Signup</button>
16 </form> 22 </form>
17 </div> 23 </div>
...@@ -24,7 +30,12 @@ function handleSubmit(event) { ...@@ -24,7 +30,12 @@ function handleSubmit(event) {
24 const name = form.name.value; 30 const name = form.name.value;
25 const email = form.email.value; 31 const email = form.email.value;
26 const password = form.password.value; 32 const password = form.password.value;
27 - console.log(name, email, password); 33 + const passwordConfirm = form.passwordConfirm.value;
34 + console.log(name, email, password, passwordConfirm);
35 + if (password !== passwordConfirm) {
36 + alert("Passwords do not match");
37 + return;
38 + }
28 const signup = auth.signup(name, email, password); 39 const signup = auth.signup(name, email, password);
29 if(!signup) { 40 if(!signup) {
30 alert("Signup failed"); 41 alert("Signup failed");
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
4 4
5 @layer base { 5 @layer base {
6 body { 6 body {
7 - @apply bg-[#06202A] text-gray-300 7 + @apply bg-neutral text-black;
8 } 8 }
9 } 9 }
10 10
...@@ -32,7 +32,7 @@ span.left-100.fixed { ...@@ -32,7 +32,7 @@ span.left-100.fixed {
32 } 32 }
33 33
34 .footer { 34 .footer {
35 - position: absolute; 35 + position: flex;
36 bottom: 0; 36 bottom: 0;
37 left: 0; 37 left: 0;
38 } 38 }
......
...@@ -3,7 +3,14 @@ module.exports = { ...@@ -3,7 +3,14 @@ module.exports = {
3 purge: ['./src/**/*.{js,ts,jsx,tsx}'], 3 purge: ['./src/**/*.{js,ts,jsx,tsx}'],
4 darkMode: false, // or 'media' or 'class' 4 darkMode: false, // or 'media' or 'class'
5 theme: { 5 theme: {
6 - extend: {}, 6 + extend: {
7 + colors: {
8 + carbon: '#A9A9A9',
9 + watermelon: "#ff3b3f",
10 + neutral: "#efefef",
11 + sky: "#caebf2",
12 + },
13 + },
7 }, 14 },
8 variants: { 15 variants: {
9 extend: {}, 16 extend: {},
......