diff --git a/package-lock.json b/package-lock.json
index a659f657..4a910e74 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19994,6 +19994,126 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.13.tgz",
+ "integrity": "sha512-Dv1RBGs2TTjkwEnFMVL5XIfJEavnLqqwYSD6LXgTPdEy/u6FlSrLBSSfe1pcfqhFEXRAgVL3Wpjibe5wXJzWog==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.13.tgz",
+ "integrity": "sha512-yB1tYEFFqo4ZNWkwrJultbsw7NPAAxlPXURXioRl9SdW6aIefOLS+0TEsKrWBtbJ9moTDgU3HRILL6QBQnMevg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.13.tgz",
+ "integrity": "sha512-v5jZ/FV/eHGoWhMKYrsAweQ7CWb8xsWGM/8m1mwwZQ/sutJjoFaXchwK4pX8NqwImILEvQmZWyb8pPTcP7htWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.13.tgz",
+ "integrity": "sha512-aVc7m4YL7ViiRv7SOXK3RplXzOEe/qQzRA5R2vpXboHABs3w8vtFslGTz+5tKiQzWUmTmBNVW0UQdhkKRORmGA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.13.tgz",
+ "integrity": "sha512-4wWY7/OsSaJOOKvMsu1Teylku7vKyTuocvDLTZQq0TYv9OjiYYWt63PiE1nTuZnqQ4RPvME7Xai+9enoiN0Wrg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.13.tgz",
+ "integrity": "sha512-uP1XkqCqV2NVH9+g2sC7qIw+w2tRbcMiXFEbMihkQ8B1+V6m28sshBwAB0SDmOe0u44ne1vFU66+gx/28RsBVQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-ia32-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.13.tgz",
+ "integrity": "sha512-V26ezyjPqQpDBV4lcWIh8B/QICQ4v+M5Bo9ykLN+sqeKKBxJVDpEc6biDVyluTXTC40f5IqCU0ttth7Es2ZuMw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.13.tgz",
+ "integrity": "sha512-WwzOEAFBGhlDHE5Z73mNU8CO8mqMNLqaG+AO9ETmzdCQlJhVtWZnOl2+rqgVQS+YHunjOWptdFmNfbpwcUuEsw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
}
}
}
diff --git a/src/app/my-map/[mapId]/crew-info-editable-item.tsx b/src/app/my-map/[mapId]/crew-info-editable-item.tsx
index 429eae98..9a5c3c77 100644
--- a/src/app/my-map/[mapId]/crew-info-editable-item.tsx
+++ b/src/app/my-map/[mapId]/crew-info-editable-item.tsx
@@ -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,
@@ -128,7 +129,10 @@ const CrewInfoEditableItem = ({
return (
<>
-
+
{
+ className="flex h-full items-center gap-[2px]"
+ onClick={(e) => {
+ e.preventDefault()
setIsOpenRoleModal(true)
}}
>
@@ -164,7 +169,7 @@ const CrewInfoEditableItem = ({
)}
-
+
diff --git a/src/app/my-map/[mapId]/crew-info-read-only-item.tsx b/src/app/my-map/[mapId]/crew-info-read-only-item.tsx
index 3cfe2061..124d3cdd 100644
--- a/src/app/my-map/[mapId]/crew-info-read-only-item.tsx
+++ b/src/app/my-map/[mapId]/crew-info-read-only-item.tsx
@@ -1,20 +1,27 @@
import Avatar from '@/components/common/avatar'
import Chip from '@/components/common/chip'
import Typography from '@/components/common/typography'
-import type { MapMemberData } from '@/models/map'
+import type { MapInfo, MapMemberData } from '@/models/map'
import { korRole } from './constant'
+import Link from 'next/link'
const CrewInfoReadOnlyItem = ({
member,
isMe,
avatarColor,
+ mapId,
}: {
+ mapId: MapInfo['id']
member: MapMemberData
avatarColor: Parameters[0]['colorScheme']
isMe: boolean
}) => {
return (
-
+
{korRole[member.role]}
-
+
)
}
diff --git a/src/app/place/[placeId]/place-liked-users.tsx b/src/app/place/[placeId]/place-liked-users.tsx
index 0fee6ffe..749d04a9 100644
--- a/src/app/place/[placeId]/place-liked-users.tsx
+++ b/src/app/place/[placeId]/place-liked-users.tsx
@@ -3,7 +3,9 @@ import Typography from '@/components/common/typography'
import type { LikeUser } from '@/components/place/types'
import type { ClassName } from '@/models/common'
import type { User } from '@/models/user'
+import { getMapId } from '@/services/map-id'
import cn from '@/utils/cn'
+import { useRouter } from 'next/navigation'
interface PlaceLikedUserProps extends ClassName {
me: User | null
@@ -11,10 +13,22 @@ interface PlaceLikedUserProps extends ClassName {
}
const PlaceLikedUser = ({ likedUser, className, me }: PlaceLikedUserProps) => {
+ const router = useRouter()
+
+ const handleMoveProfile = async (userId: User['id']) => {
+ const mapId = await getMapId()
+ if (!mapId) return
+ router.push(`/profile/${mapId}/${userId}`)
+ }
+
return (
{likedUser.map((user) => (
- -
+
- handleMoveProfile(user.id)}
+ >
{
+ const [places, setPlaces] = useState()
+ const [showMorePlace, setShowMorePlace] = useState(false)
+
+ const renderPlaces = () => {
+ if (typeof places === 'undefined') {
+ return (
+
+ )
+ }
+ if (places.length === 0) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ {places
+ .slice(
+ 0,
+ showMorePlace ? places.length : INITIAL_VISIBLE_PLACE_LENGTH,
+ )
+ .map((place) => (
+
+ ))}
+
+ {!showMorePlace && places.length > INITIAL_VISIBLE_PLACE_LENGTH && (
+
+
+
+ )}
+
+ )
+ }
+
+ useEffect(() => {
+ if (places) {
+ setShowMorePlace(places.length <= INITIAL_VISIBLE_PLACE_LENGTH)
+ }
+ }, [places])
+
+ useEffect(() => {
+ const getLikedPlace = async () => {
+ try {
+ const { data } = await api.place.like.mapId.userId.get({
+ mapId,
+ userId,
+ })
+ setPlaces(data)
+ } catch (error) {
+ if (error instanceof APIError) {
+ notify.error(error.message)
+ } else {
+ notify.error('에러가 발생했습니다.')
+ }
+ }
+ }
+
+ getLikedPlace()
+ }, [userId, mapId])
+
+ return (
+
+ {renderPlaces()}
+
+ )
+}
+
+export default LikedPlacePanel
diff --git a/src/app/profile/[mapId]/[id]/page.tsx b/src/app/profile/[mapId]/[id]/page.tsx
new file mode 100644
index 00000000..d7a627f5
--- /dev/null
+++ b/src/app/profile/[mapId]/[id]/page.tsx
@@ -0,0 +1,92 @@
+'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'
+import type { MapInfo } from '@/models/map'
+
+type PlaceFilter = 'register' | 'liked'
+
+const Profile = ({
+ params: { id, mapId },
+}: {
+ params: { id: User['id']; mapId: MapInfo['id'] }
+}) => {
+ const router = useSafeRouter()
+ const { data: userData } = useFetch(() => api.users.id.get(id))
+ const [type, setType] = useState('register')
+
+ const getIsSelected = (currentType: PlaceFilter) => type === currentType
+
+ return (
+
+
+ router.back()}
+ />
+
+ 그룹원 프로필
+
+
+
+
+
+
+
{userData?.nickname}
+
+
+
+
+
+
+
+
+
+ {type === 'liked' ? (
+
+ ) : (
+
+ )}
+
+
+ )
+}
+
+export default Profile
diff --git a/src/app/profile/[mapId]/[id]/place-item.tsx b/src/app/profile/[mapId]/[id]/place-item.tsx
new file mode 100644
index 00000000..09adff37
--- /dev/null
+++ b/src/app/profile/[mapId]/[id]/place-item.tsx
@@ -0,0 +1,201 @@
+'use client'
+
+import { forwardRef, useEffect, useState } from 'react'
+
+import Link from 'next/link'
+
+import { notify } from '@/components/common/custom-toast'
+import Icon from '@/components/common/icon'
+import ProxyImage from '@/components/common/proxy-image'
+import Typography from '@/components/common/typography'
+import LikeButton from '@/components/like-button'
+import PickChip from '@/components/pick-chip'
+import TagList from '@/components/tag-list'
+import useFetch from '@/hooks/use-fetch'
+import { APIError } from '@/models/api'
+import type { PlaceType } from '@/models/api/place'
+import type { ClassName } from '@/models/common'
+import { api } from '@/utils/api'
+import cn from '@/utils/cn'
+import { roundOnePoint } from '@/utils/number'
+import { getStarByScore } from '@/utils/score'
+
+interface PlaceItemProps extends ClassName {
+ selectedPlace: PlaceType
+ mapId: string
+ onRefreshOldPlace?: VoidFunction
+}
+
+const PlaceItem = forwardRef(
+ ({ selectedPlace, className, mapId, onRefreshOldPlace }, ref) => {
+ const [isLikePlace, setIsLikePlace] = useState(false)
+ const { data: user, revalidate } = useFetch(api.users.me.get, {
+ key: ['user'],
+ })
+
+ const place = selectedPlace.place
+
+ const getNumOfLike = () => {
+ const initialNumOfLike = selectedPlace.likedUser?.length || 0
+
+ if (!user?.id) return initialNumOfLike
+ if (selectedPlace.likedUser.some((liked) => liked.id === user.id)) {
+ if (isLikePlace) return initialNumOfLike
+ return initialNumOfLike - 1
+ }
+ if (isLikePlace) return initialNumOfLike + 1
+ return initialNumOfLike
+ }
+
+ const handleLikePlace = async () => {
+ try {
+ if (!mapId) return
+
+ setIsLikePlace(true)
+ await api.place.mapId.placeId.like.put({
+ placeId: place.id,
+ mapId,
+ })
+ revalidate(['places', mapId])
+ } catch (error) {
+ setIsLikePlace(false)
+ if (error instanceof APIError || error instanceof Error) {
+ notify.error(error.message)
+ }
+ } finally {
+ if (onRefreshOldPlace) {
+ onRefreshOldPlace()
+ }
+ }
+ }
+
+ const handleUnLikePlace = async () => {
+ try {
+ if (!mapId) return
+
+ setIsLikePlace(false)
+ await api.place.mapId.placeId.like.delete({
+ placeId: place.id,
+ mapId,
+ })
+ revalidate(['places', mapId])
+ } catch (error) {
+ setIsLikePlace(true)
+ if (error instanceof APIError || error instanceof Error) {
+ notify.error(error.message)
+ }
+ } finally {
+ if (onRefreshOldPlace) {
+ onRefreshOldPlace()
+ }
+ }
+ }
+
+ const kakaoPlace = place.kakaoPlace
+ const tags = selectedPlace.tags
+ const pick = {
+ isLiked: isLikePlace,
+ isMyPick:
+ typeof selectedPlace.createdBy != 'undefined' &&
+ selectedPlace.createdBy.id === user?.id,
+ numOfLikes: getNumOfLike(),
+ onClickLike: isLikePlace ? handleUnLikePlace : handleLikePlace,
+ }
+
+ useEffect(() => {
+ if (!user) return
+ setIsLikePlace(
+ selectedPlace.likedUser.some((liked) => liked.id === user.id),
+ )
+ }, [user, selectedPlace])
+
+ return (
+
+
+
+
+
+
+ {kakaoPlace.name}
+
+
+ {kakaoPlace.category}
+
+
+
+
+ {!!kakaoPlace.score && (
+
+
+
+ {roundOnePoint(kakaoPlace.score)}
+
+
+ )}
+
+ {kakaoPlace.address}
+
+
+
+
+ {pick && (
+
+
+
{
+ e.stopPropagation()
+ e.preventDefault()
+ pick.onClickLike()
+ }}
+ />
+
+ )}
+
+
+ {kakaoPlace.mainPhotoUrl && (
+
+ )}
+
+
+ {!!tags?.length && (
+
+ )}
+
+ )
+ },
+)
+
+PlaceItem.displayName = 'PlaceItem'
+
+export default PlaceItem
diff --git a/src/app/profile/[mapId]/[id]/registered-place-panel.tsx b/src/app/profile/[mapId]/[id]/registered-place-panel.tsx
new file mode 100644
index 00000000..3df22def
--- /dev/null
+++ b/src/app/profile/[mapId]/[id]/registered-place-panel.tsx
@@ -0,0 +1,113 @@
+import type { PlaceType } from '@/models/api/place'
+import type { MapInfo } from '@/models/map'
+import type { User } from '@/models/user'
+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'
+import { INITIAL_VISIBLE_PLACE_LENGTH } from './liked-place-panel'
+import Icon from '@/components/common/icon'
+import Typography from '@/components/common/typography'
+
+const RegisterededPlacePanel = ({
+ userId,
+ mapId,
+}: {
+ userId: User['id']
+ mapId: MapInfo['id']
+}) => {
+ const [places, setPlaces] = useState()
+ const [showMorePlace, setShowMorePlace] = useState(false)
+
+ const renderPlaces = () => {
+ if (typeof places === 'undefined') {
+ return (
+
+ )
+ }
+ if (places.length === 0) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ {places
+ .slice(
+ 0,
+ showMorePlace ? places.length : INITIAL_VISIBLE_PLACE_LENGTH,
+ )
+ .map((place) => (
+
+ ))}
+
+ {!showMorePlace && places.length > INITIAL_VISIBLE_PLACE_LENGTH && (
+
+
+
+ )}
+
+ )
+ }
+
+ useEffect(() => {
+ if (places) {
+ setShowMorePlace(places.length <= INITIAL_VISIBLE_PLACE_LENGTH)
+ }
+ }, [places])
+
+ useEffect(() => {
+ const getRegisteredPlace = async () => {
+ try {
+ const { data } = await api.place.mapId.userId.get({
+ mapId,
+ userId,
+ })
+ setPlaces(data)
+ } catch (error) {
+ if (error instanceof APIError) {
+ notify.error(error.message)
+ } else {
+ notify.error('에러가 발생했습니다.')
+ }
+ }
+ }
+
+ getRegisteredPlace()
+ }, [userId, mapId])
+
+ return (
+
+ {renderPlaces()}
+
+ )
+}
+
+export default RegisterededPlacePanel
diff --git a/src/app/profile/[mapId]/[id]/taste-rate.tsx b/src/app/profile/[mapId]/[id]/taste-rate.tsx
new file mode 100644
index 00000000..1aaf5195
--- /dev/null
+++ b/src/app/profile/[mapId]/[id]/taste-rate.tsx
@@ -0,0 +1,59 @@
+'use client'
+
+import { notify } from '@/components/common/custom-toast'
+import Icon from '@/components/common/icon'
+import Typography from '@/components/common/typography'
+import { APIError } from '@/models/api'
+import type { User } from '@/models/user'
+import { getMapId } from '@/services/map-id'
+import { api } from '@/utils/api'
+import { useEffect, useState } from 'react'
+
+const TasteRate = ({ userId }: { userId: User['id'] }) => {
+ const [rate, setRate] = useState(0)
+
+ useEffect(() => {
+ const getRate = async () => {
+ try {
+ const mapId = await getMapId()
+ if (!mapId) {
+ throw new Error('잘못된 접근입니다.')
+ }
+ const { data: tasteRate } = await api.place.differ.mapId.userId.get({
+ userId,
+ mapId,
+ })
+ setRate(tasteRate ?? 0)
+ } catch (error) {
+ if (error instanceof APIError) {
+ notify.error(error.message)
+ } else {
+ notify.error('알 수 없는 에러가 발생했습니다. ')
+ }
+ }
+ }
+
+ getRate()
+ }, [userId])
+ return (
+
+
+ {`나와 취향 유사도 ${rate}%`}
+
+ )
+}
+
+export default TasteRate
diff --git a/src/components/common/icon.tsx b/src/components/common/icon.tsx
index 9e45da84..09bab468 100644
--- a/src/components/common/icon.tsx
+++ b/src/components/common/icon.tsx
@@ -49,6 +49,9 @@ const IconVariants = cva<{
'purple-700': '[&_path]:stroke-purple-700',
'purple-800': '[&_path]:stroke-purple-800',
'yellow-100': '[&_path]:stroke-yellow-100',
+ 'blue-100': '[&_path]:stroke-blue-100',
+ 'blue-200': '[&_path]:stroke-blue-200',
+ 'blue-300': '[&_path]:stroke-blue-300',
'profile-coral': '[&_path]:stroke-profile-coral',
'profile-dark-blue': '[&_path]:stroke-profile-dark-blue',
'profile-sky-blue': '[&_path]:stroke-profile-sky-blue',
@@ -85,6 +88,9 @@ const IconVariants = cva<{
'purple-700': '[&_path]:fill-purple-700',
'purple-800': '[&_path]:fill-purple-800',
'yellow-100': '[&_path]:fill-yellow-100',
+ 'blue-100': '[&_path]:fill-blue-100',
+ 'blue-200': '[&_path]:fill-blue-200',
+ 'blue-300': '[&_path]:fill-blue-300',
'profile-coral': '[&_path]:fill-profile-coral',
'profile-dark-blue': '[&_path]:fill-profile-dark-blue',
'profile-sky-blue': '[&_path]:fill-profile-sky-blue',
diff --git a/src/components/common/typography.tsx b/src/components/common/typography.tsx
index 16fb6576..15b47e35 100644
--- a/src/components/common/typography.tsx
+++ b/src/components/common/typography.tsx
@@ -79,6 +79,9 @@ export const TypographyVariants = cva<{
'profile-sky-blue': 'text-profile-sky-blue',
'profile-violet': 'text-profile-violet',
'profile-green': 'text-profile-green',
+ 'blue-100': 'text-blue-100',
+ 'blue-200': 'text-blue-200',
+ 'blue-300': 'text-blue-300',
},
},
defaultVariants: {
diff --git a/src/components/tag-list.tsx b/src/components/tag-list.tsx
index 60f00e72..e0956cc8 100644
--- a/src/components/tag-list.tsx
+++ b/src/components/tag-list.tsx
@@ -10,9 +10,21 @@ import { changeSpaceToHyphen } from '@/utils/tags'
interface TagListProps extends ClassName {
placeId: PlaceType['place']['id']
tags: TagItem[] | string[]
+ tagColorScheme?:
+ | 'neutral-400'
+ | 'neutral-500'
+ | 'neutral-600'
+ | 'neutral-800'
+ | 'orange'
+ | 'purple'
}
-const TagList = ({ placeId, tags, className }: TagListProps) => {
+const TagList = ({
+ placeId,
+ tags,
+ className,
+ tagColorScheme,
+}: TagListProps) => {
const tagNames = tags.map((tag) => (typeof tag === 'string' ? tag : tag.name))
return (
@@ -25,8 +37,12 @@ const TagList = ({ placeId, tags, className }: TagListProps) => {
{tagNames.map((tag) => (
{`#${tag}`}
))}
diff --git a/src/models/color.ts b/src/models/color.ts
index 95fd2bd3..a9538f3e 100644
--- a/src/models/color.ts
+++ b/src/models/color.ts
@@ -32,3 +32,6 @@ export type ColorKey =
| 'profile-sky-blue'
| 'profile-violet'
| 'profile-green'
+ | 'blue-100'
+ | 'blue-200'
+ | 'blue-300'
diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts
index c94eb519..c3af1108 100644
--- a/src/utils/api/index.ts
+++ b/src/utils/api/index.ts
@@ -149,6 +149,16 @@ const place = {
mapId: {
get: (mapId: MapInfo['id']): Promise> =>
client.secure.get(`/place/${mapId}`),
+ userId: {
+ get: ({
+ mapId,
+ userId,
+ }: {
+ mapId: MapInfo['id']
+ userId: User['id']
+ }): Promise> =>
+ client.secure.get(`/place/${mapId}/${userId}`),
+ },
placeId: {
delete: ({ placeId, mapId }: PlaceIdWithMapId) =>
@@ -193,6 +203,34 @@ const place = {
get: (placeId: string): Promise> =>
client.secure.get(`place/${placeId}`),
},
+ differ: {
+ mapId: {
+ userId: {
+ get: ({
+ mapId,
+ userId,
+ }: {
+ mapId: MapInfo['id']
+ userId: User['id']
+ }): Promise> =>
+ client.secure.get(`/place/differ/${mapId}/${userId}`),
+ },
+ },
+ },
+ like: {
+ mapId: {
+ userId: {
+ get: ({
+ userId,
+ mapId,
+ }: {
+ userId: User['id']
+ mapId: MapInfo['id']
+ }): Promise> =>
+ client.secure.get(`/place/like/${mapId}/${userId}`),
+ },
+ },
+ },
}
const proxy = {
diff --git a/tailwind.config.ts b/tailwind.config.ts
index a3a56766..9b807c96 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -171,6 +171,11 @@ const config: Config = {
yellow: {
'100': '#FFDE59',
},
+ blue: {
+ '100': '#C2D8F8',
+ '200': '#4B93FF',
+ '300': '#0066FF',
+ },
profile: {
coral: '#E5684C',
'dark-blue': '#5456D7',