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

feat: Implement favorites feature #22

Draft
wants to merge 62 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
0ed723e
feat: add LeftSideBar component
Sep 29, 2024
db79550
feat: add RightCommentBar
Sep 29, 2024
7d787a8
chore: resolve ci issue
Sep 29, 2024
3f75b35
feat: add BlogCard
Sep 30, 2024
40d331b
refactor: remove useless style
dev-khs Sep 30, 2024
86bb2fc
feat: add Layout container
dev-khs Sep 30, 2024
b0f0f81
feat: add responsive layout style
dev-khs Sep 30, 2024
7048f49
feat: add window event listener
dev-khs Sep 30, 2024
cc3f6cc
refactor: improve responsive layout style
dev-khs Sep 30, 2024
250636d
refactor: improve responsive layout style
dev-khs Sep 30, 2024
764770f
feat: add new styles
dev-khs Oct 1, 2024
67a89b2
feat: add button component
dev-khs Oct 1, 2024
79c5987
refactor: modify blog-card style
dev-khs Oct 1, 2024
19a8935
Merge branch 'feat/blog-card' into feat/layout
dev-khs Oct 1, 2024
df361ff
Merge branch 'feat/layout' into feat/button-component
dev-khs Oct 1, 2024
b26f6ea
feat: add comment box component
dev-khs Oct 1, 2024
efd1f24
feat: add icon component
dev-khs Oct 1, 2024
b348388
feat: add new icons
dev-khs Oct 1, 2024
bd9347d
feat: add BlogPage
dev-khs Oct 1, 2024
ad8fa02
refactor: add home icon
dev-khs Oct 1, 2024
4c8d42d
refactor: modify blog-card style
dev-khs Oct 3, 2024
e724a47
feat: add MainPage and Typography component
dev-khs Oct 3, 2024
3ae2e5e
feat: add introduction
dev-khs Oct 3, 2024
3e38804
feat: add FavoritePage
dev-khs Oct 3, 2024
2ffada3
feat: add BlogList component
dev-khs Oct 3, 2024
738375d
feat: implement BlogDetailPage screen
dev-khs Oct 3, 2024
09783ec
feat: implement BlogDetail api routes
dev-khs Oct 3, 2024
65aa254
chore: modify mock data
dev-khs Oct 3, 2024
ed8877b
refactor: modify blog styles
dev-khs Oct 4, 2024
b55dba0
feat: add create icon
dev-khs Oct 4, 2024
7908b7c
feat: add BlogCreatePage screen
dev-khs Oct 4, 2024
c846cb4
feat: add MarkdownPreview
dev-khs Oct 5, 2024
9807793
feat: improve and rename markdown component
dev-khs Oct 5, 2024
750ee7f
feat: add Input component
dev-khs Oct 5, 2024
55e34ed
feat: add Textarea component
dev-khs Oct 5, 2024
e28bb40
feat: add Link tag
dev-khs Oct 5, 2024
7257dcc
feat: implement LoginForm
dev-khs Oct 5, 2024
438bb48
feat: add supabase settings
dev-khs Oct 5, 2024
9a0af5b
feat: add SessionProvider and add zustand package
dev-khs Oct 5, 2024
24a7e94
feat: implement logout feature
dev-khs Oct 5, 2024
103deda
refactor: apply image precessing based on login status
dev-khs Oct 5, 2024
1a1f1dd
refactor: add user info nickname
dev-khs Oct 5, 2024
91a786e
feat: resolve conflict
dev-khs Oct 9, 2024
040e20b
feat: implement blog features
dev-khs Oct 6, 2024
b81aa53
feat: implement fetch blog detail data
dev-khs Oct 6, 2024
2fa4068
feat: implement fetch imageUrl
dev-khs Oct 6, 2024
68eda6f
feat: resolve conflict
dev-khs Oct 9, 2024
daa79c9
feat: add createComment function
dev-khs Oct 9, 2024
a34d48e
feat: implement createComment
dev-khs Oct 9, 2024
4ec32ba
feat: add get comment features
dev-khs Oct 9, 2024
88657cc
feat: update comment ui style
dev-khs Oct 10, 2024
866a852
feat: implement get all comments data
dev-khs Oct 10, 2024
193f2d9
refactor: modify fetch blogs data and remove useless file
dev-khs Oct 10, 2024
ce7fe3b
refactor: resolve conflict issue
dev-khs Oct 12, 2024
b902160
feat: add favorite api file
dev-khs Oct 12, 2024
0319590
feat: implement fetch favorites data
dev-khs Oct 12, 2024
11c64e4
refactor: modify button component
dev-khs Oct 12, 2024
4902ebb
feat: implement favorite button UI
dev-khs Oct 12, 2024
73efb4d
feat: implement getFavorites feature
dev-khs Oct 12, 2024
9f3a2f7
feat: implement createFavorite feature
dev-khs Oct 12, 2024
8095904
feat implement delete favorite feature
dev-khs Oct 12, 2024
03a5c11
feat: improve favorite page feature
dev-khs Oct 12, 2024
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@supabase/supabase-js": "^2.45.4",
"axios": "^1.7.7",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.35.2",
Expand Down
30 changes: 30 additions & 0 deletions src/app/api/Blog/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {NextResponse} from 'next/server'

import {createBrowserClient} from '@/supabase/client'

export async function GET() {
const supabase = createBrowserClient()

try {
const [
{data: postsData, error: postsError},
{data: commentsData, error: commentsError},
] = await Promise.all([
supabase.from('posts').select('*'),
supabase.from('comments').select('*'),
])

if (postsError || commentsError) {
return NextResponse.json(
{error: postsError?.message || commentsError?.message},
{status: 500},
)
}
return NextResponse.json({posts: postsData, comments: commentsData})
} catch (error) {
return NextResponse.json(
{error: 'Error fetching blog detail and comments'},
{status: 500},
)
}
}
27 changes: 15 additions & 12 deletions src/app/api/BlogDetail/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,33 @@ import {createBrowserClient} from '@/supabase/client'

export async function GET(req: NextRequest) {
const supabase = createBrowserClient()

const {searchParams} = new URL(req.url)

const blogId = searchParams.get('id')

if (!blogId) {
return NextResponse.json({error: 'Blog ID is required'}, {status: 400})
}

try {
const {data, error} = await supabase
.from('posts')
.select('*')
.eq('id', blogId)
.single()

if (error) {
return NextResponse.json({error: error.message}, {status: 500})
const [
{data: postData, error: postError},
{data: commentsData, error: commentsError},
] = await Promise.all([
supabase.from('posts').select('*').eq('id', blogId).single(),
supabase.from('comments').select('*').eq('post_id', blogId), // comments 테이블에서 post_id가 blogId인 것들을 가져옴
])

if (postError || commentsError) {
return NextResponse.json(
{error: postError?.message || commentsError?.message},
{status: 500},
)
}

return NextResponse.json(data)
return NextResponse.json({post: postData, comments: commentsData})
} catch (error) {
return NextResponse.json(
{error: 'Error fetching blog detail'},
{error: 'Error fetching blog detail and comments'},
{status: 500},
)
}
Expand Down
22 changes: 22 additions & 0 deletions src/app/api/Favorite/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {NextResponse} from 'next/server'

import {createBrowserClient} from '@/supabase/client'

export async function GET() {
const supabase = createBrowserClient()

try {
const {data, error} = await supabase.from('favorites').select('*')

if (error) {
return NextResponse.json({error}, {status: 500})
}

return NextResponse.json({data})
} catch (error) {
return NextResponse.json(
{error: 'Error fetching blog detail and comments'},
{status: 500},
)
}
}
29 changes: 29 additions & 0 deletions src/app/api/addFavorite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {createBrowserClient} from '@/supabase/client'

export async function addFavorite({
userId,
blogId,
blogTitle,
}: {
userId: string
blogId: string
blogTitle: string
}) {
const supabase = createBrowserClient()

try {
const {error} = await supabase.from('favorites').insert({
user_id: userId,
post_id: blogId,
post_title: blogTitle,
created_at: new Date().toISOString(),
})

if (error) {
return alert('권한이 없습니다.')
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}
34 changes: 34 additions & 0 deletions src/app/api/createComment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {createBrowserClient} from '@/supabase/client'

export async function createComment({
username,
userId,
blogId,
content,
}: {
username: string
userId: string
blogId: string
content: string
}) {
const supabase = createBrowserClient()

try {
const {data, error} = await supabase.from('comments').insert({
username: username,
user_id: userId,
post_id: blogId,
content,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
})

if (error) {
return alert('권한이 없습니다.')
}
return data
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}
26 changes: 26 additions & 0 deletions src/app/api/deleteFavorite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {createBrowserClient} from '@/supabase/client'

export async function deleteFavorite({
userId,
blogId,
}: {
userId: string
blogId: string
}) {
const supabase = createBrowserClient()

try {
const {error} = await supabase
.from('favorites')
.delete()
.eq('user_id', userId)
.eq('post_id', blogId)

if (error) {
return alert('권한이 없습니다.')
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}
17 changes: 0 additions & 17 deletions src/app/api/getBlog.ts

This file was deleted.

45 changes: 45 additions & 0 deletions src/app/api/getFavorite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {createBrowserClient} from '@/supabase/client'

export async function getBlogsFavorites({userId}: {userId: string}) {
const supabase = createBrowserClient()

try {
const {data, error} = await supabase
.from('favorites')
.select('*')
.eq('user_id', userId)

if (error) {
alert('error')

return []
}

return data || []
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}

export async function getBlogFavorite({blogId}: {blogId: string}) {
const supabase = createBrowserClient()

try {
const {data, error} = await supabase
.from('favorites')
.select('*')
.eq('post_id', blogId)

if (error) {
alert('error')

return []
}

return data || []
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
}
5 changes: 3 additions & 2 deletions src/components/BlogList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import Typography from '@/components/Typography'

interface Props {
url: string
title: string
}

const BlogList = ({url}: Props) => {
const BlogList = ({url, title}: Props) => {
return (
<Link
className="group flex items-center mb-5 p-3.5 border border-n-3 rounded-xl h6 transition-all hover:border-transparent hover:shadow-[0_0_1rem_0.25rem_rgba(0,0,0,0.04),0px_2rem_1.5rem_-1rem_rgba(0,0,0,0.12)] last:mb-0 2xl:p-2.5 lg:p-3.5"
Expand All @@ -20,7 +21,7 @@ const BlogList = ({url}: Props) => {
fill="fill-accent-6"
/>
</div>
<Typography text="즐겨찾기 리스트입니다." />
<Typography text={title} />
<Icon
className="ml-auto fill-n-4 transition-colors"
iconName="arrowNext"
Expand Down
10 changes: 6 additions & 4 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import {type ReactElement} from 'react'

interface Props {
buttonName: string
type?: 'submit' | 'reset' | 'button'
className?: string
onClick?: () => void
onClick?: (event: React.FormEvent) => void | Promise<void>
children: ReactElement
}

const Button = ({buttonName, className, onClick, type = 'button'}: Props) => {
const Button = ({className, onClick, type = 'button', children}: Props) => {
return (
<button
className={`btn-dark btn-small ${className}`}
onClick={onClick}
type={type}>
{buttonName}
{children}
</button>
)
}
Expand Down
29 changes: 22 additions & 7 deletions src/components/CommentInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import {useState} from 'react'
import {useCallback, useState} from 'react'

import Icon from '@/components/Icon'
import Input from '@/components/Input'

const CommentInput = () => {
interface Props {
onClick: (content: string) => Promise<void>
}

const CommentInput = ({onClick}: Props) => {
const [commentValue, setCommentValue] = useState('')

const handleSubmit = useCallback(
async (event: React.FormEvent) => {
event.preventDefault()

await onClick(commentValue)
},
[commentValue, onClick],
)

return (
<div className="relative md:w-full md:mr-0">
<div className="group absolute top-3 left-4 text-0 w-5 h-5">
Expand All @@ -14,11 +27,13 @@ const CommentInput = () => {
iconName="search"
/>
</div>
<Input
value={commentValue}
onChange={e => setCommentValue(e.target.value)}
placeholder="댓글을 입력해주세요."
/>
<form onSubmit={handleSubmit}>
<Input
value={commentValue}
onChange={e => setCommentValue(e.target.value)}
placeholder="댓글을 입력해주세요."
/>
</form>
</div>
)
}
Expand Down
Loading
Loading