Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge #17

Merged
merged 8 commits into from
Aug 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "soma",
"version": "0.1.0",
"name": "inforum",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
Binary file removed public/img/profile.png
Binary file not shown.
1 change: 0 additions & 1 deletion public/next.svg

This file was deleted.

1 change: 0 additions & 1 deletion public/vercel.svg

This file was deleted.

4 changes: 3 additions & 1 deletion src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import GoogleLoginBtn from '@/components/GoogleLoginBtn';
import LoginForm from '@/components/LoginForm';
import { Metadata } from 'next';
import Link from 'next/link';
Expand All @@ -18,7 +19,8 @@ export default function LoginPage() {
</Link>
<h1 className="text-3xl font-bold my-5">Inforum</h1>
<LoginForm />
<div className="flex items-center">
<GoogleLoginBtn />
<div className="flex items-center mt-7">
<p className="text-soma-grey-50 mr-3 text-xs sm:text-sm">
계정이 없으신가요?
</p>
Expand Down
121 changes: 90 additions & 31 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,103 @@
'use client';

import Image from 'next/image';
import Link from 'next/link';
import { useState } from 'react';
import { useRef, useState } from 'react';
import TypeIt from 'typeit-react';
import landing from '@/assets/landing.gif';
import {
BsFillArrowDownCircleFill,
BsFillArrowUpCircleFill,
} from 'react-icons/bs';

export default function LandingPage() {
const [state, setState] = useState(false);
const [readyState, setReadyState] = useState(false);
const firstScreenRef = useRef<null | HTMLDivElement>(null);

const scrolltoTop = () => {
if (firstScreenRef.current) {
firstScreenRef.current.scrollIntoView({ behavior: 'smooth' });
}
};

return (
<div className="flex flex-col justify-center items-center h-screen px-3">
<h1 className="text-4xl sm:text-7xl font-bold text-center overflow-hidden">
<TypeIt
options={{
strings: [''],
speed: 70,
waitUntilVisible: true,
afterComplete: () => {
setState(true);
},
}}
>
개발 기록을 쉽고 즐겁게, 인포럼
</TypeIt>
</h1>
<div className="snap-y snap-mandatory overflow-scroll w-screen h-screen">
<div
className={`text-center transition-opacity ${
state ? 'opacity-100' : 'opacity-0 invisible'
} transform ${
state ? 'translate-y-0' : 'translate-y-10'
} duration-1000 ease-in-out`}
className="flex relative flex-col justify-center items-center h-screen px-3 snap-always snap-center"
ref={firstScreenRef}
>
<p className="text-sm sm:text-2xl my-7 text-center">
자동 텍스트 생성 기능을 통해 글을 쉽고 빠르게 작성하세요
<br />
파스텔톤 메모에 개발 기록을 간단하게 작성해보세요
<h1 className="text-4xl sm:text-7xl font-bold text-center overflow-hidden">
<TypeIt
options={{
strings: [''],
speed: 70,
waitUntilVisible: true,
afterComplete: () => {
setReadyState(true);
},
}}
>
개발 기록을 쉽고 즐겁게, 인포럼
</TypeIt>
</h1>
<div
className={`text-center transition-opacity ${
readyState ? 'opacity-100' : 'opacity-0 invisible'
} duration-1000 ease-in-out`}
>
<p className="text-sm sm:text-2xl my-7 text-center">
자동 텍스트 생성 기능을 통해 글을 쉽고 빠르게 작성하세요
<br />
파스텔톤 메모에 개발 기록을 간단하게 작성해보세요
</p>
<Link href="/memos">
<button className="bg-soma-blue-40 text-white px-3.5 py-2 rounded-3xl transition hover:bg-soma-blue-50 sm:text-2xl">
시작하기
</button>
</Link>
</div>
<BsFillArrowDownCircleFill
className={`absolute bottom-10 animate-bounce text-soma-blue-40 w-10 h-10 transition-opacity
${
readyState ? 'opacity-100' : 'opacity-0 invisible'
} duration-1000 ease-in-out duration`}
/>
</div>
<div className="flex flex-col items-center justify-center h-screen font-semibold snap-always snap-center bg-soma-grey-20">
<p className="sm:text-2xl ">
질문을 작성 후 ++를 입력해 text를 자동 생성해보세요!
</p>
<Link href="/memos">
<button className="bg-soma-blue-40 text-white px-3.5 py-2 rounded-3xl transition hover:bg-soma-blue-50 sm:text-2xl">
시작하기
</button>
</Link>
<br />
<Image
src={landing}
alt="landing"
width={1000}
className="rounded-2xl p-2"
/>
</div>
<div className="h-screen relative flex flex-col items-center justify-center snap-always snap-center">
<p className="text-lg sm:text-2xl font-semibold">지원 예정</p>
<br />
<ul className="grid grid-cols-2 gap-20 items-center sm:text-lg">
<li className="flex justify-center items-center w-32 h-32 sm:w-48 sm:h-48 bg-red-100 rounded-full font-semibold">
story 기능
</li>
<li className="flex justify-center items-center w-32 h-32 sm:w-48 sm:h-48 bg-blue-100 rounded-full font-semibold">
질문 서비스
</li>
<li className="flex justify-center items-center w-32 h-32 sm:w-48 sm:h-48 bg-green-100 rounded-full font-semibold">
등급 제도
</li>
<li className="flex justify-center items-center w-32 h-32 sm:w-48 sm:h-48 bg-orange-100 rounded-full font-semibold text-center">
사용자 log 기반 <br />
나만의 포트폴리오
</li>
</ul>
<button onClick={scrolltoTop}>
<BsFillArrowUpCircleFill
className={`absolute bottom-10 right-10 text-soma-blue-40 w-10 h-10`}
/>
</button>
</div>
</div>
);
Expand Down
92 changes: 92 additions & 0 deletions src/app/signup/nickname/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'use client';

import BlueBtn from '@/components/shared/BlueBtn';
import IsValidBtn from '@/components/shared/IsValidBtn';
import PromptgMessage from '@/components/shared/PromptMessage';
import { checkNickname, registerNickname } from '@/service/auth';
import { validateNickname } from '@/service/validation';
import { useRouter } from 'next/navigation';
import { useState } from 'react';

type Props = {
params: {
slug: number;
};
};

export default function SignupNicknameSettingPage({ params: { slug } }: Props) {
const router = useRouter();
const [nickname, setNickname] = useState('');
const [isValidNickname, setIsValidNickname] = useState(false);

const [messageText, setMessageText] = useState('');
const [messageType, setMessageType] = useState(false); // true : 성공 메시지 false : 경고 메시지
const [isMessageVisible, setIsMessageVisible] = useState(false);

const handleIsValidNickname = () => {
if (!validateNickname(nickname)) {
showMessage('닉네임 형식에 맞지 않습니다.', false);
setIsValidNickname(false);
return;
}

checkNickname(nickname).then((data) => {
showMessage(data.message, data.valid);
setIsValidNickname(data.valid ? true : false);
});
};

const handleClick = () => {
if (!isValidNickname) {
showMessage('닉네임 설정에 문제가 있습니다.', false);
return;
}

registerNickname(nickname, slug).then(() => {
router.push(`/signup/setting/${slug}`);
});
};

const showMessage = (text: string, type: boolean) => {
setMessageText(text);
setMessageType(type);
setIsMessageVisible(true);
setTimeout(() => {
setIsMessageVisible(false);
}, 2000);
};

return (
<section className="flex flex-col w-full items-center max-w-screen-sm mx-auto mt-32 py-5 px-10 sm:px-32">
<h1 className="text-3xl font-bold my-5">Inforum</h1>
<PromptgMessage
text={messageText}
type={messageType}
isVisible={isMessageVisible}
/>
<div className="flex flex-col my-1 w-full">
<label htmlFor="nickname" className="text-xs sm:text-sm">
닉네임
</label>
<div className="flex items-center">
<input
className="border-2 my-2 py-2 px-4 rounded-lg bg-soma-grey-20 border-soma-grey-30 grow text-sm sm:text-base"
type="text"
id="nickname"
name="nickname"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
placeholder="4~14자리 (영어, 숫자 : 1자, 한글 : 2자)"
/>
<IsValidBtn
text="중복확인"
onClick={handleIsValidNickname}
isValid={isValidNickname}
/>
</div>
</div>
<BlueBtn text="다음" onClick={handleClick} extraStyle="my-5 w-full" />
</section>
);
}
54 changes: 54 additions & 0 deletions src/assets/icons/google_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/landing.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions src/components/GoogleLoginBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';

import Link from 'next/link';
import googleIcon from '../assets/icons/google_icon.svg';

import Image from 'next/image';

export default function GoogleLoginBtn() {
return (
<Link href="/api/oauth2/authorization/google" className="w-full">
<button
className={`flex w-full justify-between items-center px-3.5 py-2 rounded-3xl transition border-2 border-soma-grey-30 hover:bg-soma-grey-20 text-sm sm:text-base`}
onClick={() => {}}
>
<Image src={googleIcon} alt="googleIcon" width={20} height={20} />
<p>구글 계정 로그인</p>
<div className="w-5 h-5"></div>
</button>
</Link>
);
}
2 changes: 1 addition & 1 deletion src/components/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function LoginForm() {
required
placeholder="비밀번호"
/>
<BlueBtn text="로그인" onClick={() => {}} extraStyle="my-5" />
<BlueBtn text="로그인" onClick={() => {}} extraStyle="my-2" />
</form>
);
}
6 changes: 4 additions & 2 deletions src/components/MyInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import BlueBtn from './shared/BlueBtn';
import BlueBtn2 from './shared/BlueBtn2';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { registerUserInfo, updateUserInfo } from '@/service/auth';
import { checkUser, registerUserInfo, updateUserInfo } from '@/service/auth';
import { UserInfo2 } from '@/types';

type Props = {
Expand Down Expand Up @@ -55,7 +55,9 @@ export default function MyInfoForm({ userId, userInfo, isSignup }: Props) {
tags,
imageUrl === '' && imageFile === null ? 'true' : 'false'
).then(() => {
router.push('/login');
checkUser().then((data) => {
router.push(data.isLogin ? '/memos' : '/login');
});
})
: updateUserInfo(
userId,
Expand Down
Loading