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: group profile #146

Merged
merged 18 commits into from
Oct 3, 2024
Merged
13 changes: 9 additions & 4 deletions src/app/my-map/[mapId]/crew-info-editable-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import BottomModal from '@/components/common/bottom-modal'
import { api } from '@/utils/api'
import { notify } from '@/components/common/custom-toast'
import useFetch from '@/hooks/use-fetch'
import Link from 'next/link'

const RoleButton = ({
role,
Expand Down Expand Up @@ -128,7 +129,10 @@ const CrewInfoEditableItem = ({

return (
<>
<li className="flex h-[52px] items-center justify-between">
<Link
href={`/profile/${member.id}`}
className="flex h-[52px] items-center justify-between"
>
<div className="flex items-center gap-2">
<Avatar
value={member.nickname}
Expand All @@ -153,8 +157,9 @@ const CrewInfoEditableItem = ({
) : (
<button
type="button"
className="flex items-center gap-[2px]"
onClick={() => {
className="flex h-full items-center gap-[2px]"
onClick={(e) => {
e.preventDefault()
setIsOpenRoleModal(true)
}}
>
Expand All @@ -164,7 +169,7 @@ const CrewInfoEditableItem = ({
<Icon type="caretDown" size="md" stroke="neutral-200" />
</button>
)}
</li>
</Link>

<BottomModal
layout="none"
Expand Down
6 changes: 6 additions & 0 deletions src/app/my-map/[mapId]/crew-info-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type { User } from '@/models/user'
import cn from '@/utils/cn'
import CrewInfoReadOnlyItem from './crew-info-read-only-item'
import CrewInfoEditableItem from './crew-info-editable-item'
import { useEffect } from 'react'
import { updateMapIdCookie } from '@/services/map-id'

interface CrewInfoListProps extends ClassName {
user: User
Expand Down Expand Up @@ -45,6 +47,10 @@ const CrewInfoList = ({
const isMyMap = mapInfo.createBy.id === user.id
const members = mapInfo.users

useEffect(() => {
updateMapIdCookie(mapInfo.id)
}, [mapInfo.id])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지도 홈에 진입했을 때만 updateMapIdCookie 해줘도 될 것 같아요! 이게 mount를 두 번 트리거해서 골칫덩이더라구요 그래서 map/mapId의 경우에 좀 귀찮은 작업을 추가해서 updateMapIdCookie를 해줬어요 ㅜ e397684


return (
<section className={cn('', className)}>
<div className="flex gap-1">
Expand Down
9 changes: 7 additions & 2 deletions src/app/my-map/[mapId]/crew-info-read-only-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Chip from '@/components/common/chip'
import Typography from '@/components/common/typography'
import type { MapMemberData } from '@/models/map'
import { korRole } from './constant'
import Link from 'next/link'

const CrewInfoReadOnlyItem = ({
member,
Expand All @@ -14,7 +15,11 @@ const CrewInfoReadOnlyItem = ({
isMe: boolean
}) => {
return (
<li className="flex h-[52px] items-center justify-between">
<Link
role="listitem"
href={`/profile/${member.id}`}
className="flex h-[52px] items-center justify-between"
>
<div className="flex items-center gap-2">
<Avatar
value={member.nickname}
Expand All @@ -35,7 +40,7 @@ const CrewInfoReadOnlyItem = ({
<Typography size="body3" color="neutral-200">
{korRole[member.role]}
</Typography>
</li>
</Link>
)
}

Expand Down
10 changes: 8 additions & 2 deletions src/app/place/[placeId]/place-liked-users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { LikeUser } from '@/components/place/types'
import type { ClassName } from '@/models/common'
import type { User } from '@/models/user'
import cn from '@/utils/cn'
import Link from 'next/link'

interface PlaceLikedUserProps extends ClassName {
me: User | null
Expand All @@ -14,7 +15,12 @@ const PlaceLikedUser = ({ likedUser, className, me }: PlaceLikedUserProps) => {
return (
<ul className={cn('flex flex-col pb-6', className)}>
{likedUser.map((user) => (
<li key={user.id} className="flex items-center gap-2 pt-2">
<Link
role="listitem"
href={`/profile/${user.id}`}
key={user.id}
className="flex items-center gap-2 pt-2"
>
<Avatar
value={user.nickname}
imageUrl={user.profileImage}
Expand All @@ -23,7 +29,7 @@ const PlaceLikedUser = ({ likedUser, className, me }: PlaceLikedUserProps) => {
<Typography size="body1" color="neutral-100">
{user.nickname}
</Typography>
</li>
</Link>
))}
</ul>
)
Expand Down
82 changes: 82 additions & 0 deletions src/app/profile/[id]/liked-place-panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { PlaceType } from '@/models/api/place'
import type { MapInfo } from '@/models/map'
import type { User } from '@/models/user'
import { getMapId } from '@/services/map-id'
import { api } from '@/utils/api'
import { useEffect, useState } from 'react'
import PlaceItem from './place-item'
import { APIError } from '@/models/api'
import { notify } from '@/components/common/custom-toast'
import EmptyPlaceList from '@/components/place/empty-place-list'
import PlacePopupSkeleton from '@/components/place/place-popup-skeleton'

const LikedPlacePanel = ({ userId }: { userId: User['id'] }) => {
const [mapId, setMapId] = useState<MapInfo['id']>('')
const [places, setPlaces] = useState<PlaceType[]>()

const renderPlaces = () => {
if (typeof places === 'undefined') {
return (
<div className="flex flex-col gap-2.5 py-[18px]">
<PlacePopupSkeleton />
<PlacePopupSkeleton />
<PlacePopupSkeleton />
</div>
)
}
if (places.length === 0) {
return (
<EmptyPlaceList
className="pt-[75px]"
message="등록하거나 좋아요한 맛집이 없어요"
/>
)
}

return (
<div className="flex flex-col gap-2.5 py-[18px]">
{places.map((place) => (
<PlaceItem
key={place.place.id}
className="w-full border-[1px] border-neutral-500 bg-neutral-600"
mapId={mapId}
selectedPlace={place}
/>
))}
</div>
)
}

useEffect(() => {
const getLikedPlace = async () => {
try {
const currentMapId = await getMapId()
if (!currentMapId) {
throw new Error('잘못된 접근입니다.')
}
setMapId(currentMapId)
const { data } = await api.place.like.mapId.userId.get({
mapId: currentMapId,
userId,
})
setPlaces(data)
} catch (error) {
if (error instanceof APIError) {
notify.error(error.message)
} else {
notify.error('에러가 발생했습니다.')
}
}
}

getLikedPlace()
}, [userId])

return (
<div role="tabpanel" id="tappanel-liked" aria-labelledby="tap-liked">
{renderPlaces()}
</div>
)
}

export default LikedPlacePanel
87 changes: 87 additions & 0 deletions src/app/profile/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use client'

import AccessibleIconButton from '@/components/common/accessible-icon-button'
import Avatar from '@/components/common/avatar'
import Typography from '@/components/common/typography'
import useFetch from '@/hooks/use-fetch'
import useSafeRouter from '@/hooks/use-safe-router'
import type { User } from '@/models/user'
import { api } from '@/utils/api'
import TasteRate from './taste-rate'
import { useState } from 'react'
import LikedPlacePanel from './liked-place-panel'
import RegisterededPlacePanel from './registered-place-panel'

type PlaceFilter = 'register' | 'liked'

const Profile = ({ params: { id } }: { params: { id: User['id'] } }) => {
const router = useSafeRouter()
const { data: userData } = useFetch(() => api.users.id.get(id))
const [type, setType] = useState<PlaceFilter>('register')

const getIsSelected = (currentType: PlaceFilter) => type === currentType

return (
<div className="flex flex-col gap-6">
<header className="relative flex items-center pt-4">
<AccessibleIconButton
icon={{ type: 'caretLeft', size: 'xl' }}
label="이전 페이지"
className="p-[10px]"
onClick={() => router.back()}
/>
<Typography
className="absolute left-1/2 translate-x-[-50%]"
as="h1"
size="body0"
>
그룹원 프로필
</Typography>
</header>

<div className="flex flex-col items-center gap-[18px]">
<div className="flex flex-col items-center gap-6">
<Avatar
value={userData?.nickname}
imageUrl={userData?.profileImage}
className={`h-20 w-20 text-[40px] ${!userData?.profileImage && 'border-2 border-[#17171A] border-opacity-20'}`}
/>
<Typography size="h3">{userData?.nickname}</Typography>
</div>
<TasteRate userId={id} />
<div className="h-[18px] w-full bg-neutral-600"></div>
</div>
<section className="flex flex-col px-5">
<div role="tablist" className="flex items-center gap-4">
<button
role="tab"
className={`border-b-[1px] pb-1 ${getIsSelected('register') ? 'border-neutral-000' : 'border-transparent'}`}
id="tap-register"
aria-controls="tappanel-register"
aria-selected={getIsSelected('register')}
onClick={() => setType('register')}
>
<Typography size="body1">맛집 등록</Typography>
</button>
<button
role="tab"
className={`border-b-[1px] pb-1 ${getIsSelected('liked') ? 'border-neutral-000' : 'border-transparent'}`}
id="tap-liked"
aria-controls="tappanel-liked"
aria-selected={getIsSelected('liked')}
onClick={() => setType('liked')}
>
<Typography size="body1">좋아요</Typography>
</button>
</div>
{type === 'liked' ? (
<LikedPlacePanel userId={id} />
) : (
<RegisterededPlacePanel userId={id} />
)}
</section>
</div>
)
}

export default Profile
Loading
Loading