-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat(#163): μ μ λ° μΈμ μ§λ Admin μ‘°ν API μμ * feat(#163): μ μ λ° μΈμ μ§λ Admin UI μμ
- Loading branch information
Showing
16 changed files
with
575 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import useSWR from "swr"; | ||
|
||
export interface ILifeMap { | ||
id: number; | ||
isPublic: boolean; | ||
goals: IGoal[]; | ||
goalCount: number; | ||
viewCount: number; | ||
cheeringCount: number; | ||
} | ||
|
||
export interface IGoal { | ||
title: string; | ||
description: string; | ||
deadline: string; | ||
stickerUrl: string | ||
tagInfo: ITag; | ||
tasks: ITask[] | ||
} | ||
|
||
export interface ITag { | ||
tagId: number; | ||
tagContent: string; | ||
} | ||
|
||
|
||
export interface ITask { | ||
taskId: number; | ||
isTaskDone: boolean; | ||
taskDescription: string; | ||
} | ||
export interface ILifeMapResponse { | ||
result: number; | ||
body: ILifeMap | ||
error: { | ||
code: string; | ||
message: string; | ||
data: object; | ||
}; | ||
} | ||
|
||
export const useLifeMap = (id: string | number) => { | ||
return useSWR<ILifeMapResponse>(`life-map?userId=${id}`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import useSWR from "swr"; | ||
|
||
export interface IUser { | ||
id: number; | ||
email: string; | ||
username: string; | ||
nickname: string; | ||
birth: string; | ||
image: string; | ||
provider: string; | ||
authority: string; | ||
} | ||
export interface IUsersResponse { | ||
result: number; | ||
body: IUser[] | ||
error: { | ||
code: string; | ||
message: string; | ||
data: object; | ||
}; | ||
} | ||
|
||
export interface IUserResponse { | ||
result: number; | ||
body: IUser | ||
error: { | ||
code: string; | ||
message: string; | ||
data: object; | ||
}; | ||
} | ||
|
||
export const useUsers = () => { | ||
return useSWR<IUsersResponse>(`user`); | ||
}; | ||
|
||
export const useUser = (id: string | number) => { | ||
return useSWR<IUserResponse>(`user/${id}`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import DefaultTable from "@/components/shared/ui/default-table"; | ||
import {Alert, Button, Modal} from "antd"; | ||
import { ColumnsType } from "antd/es/table"; | ||
import { useRouter } from "next/router"; | ||
import React, {useCallback} from "react"; | ||
import {IUser, useUsers} from "@/client/user"; | ||
|
||
const UserList = () => { | ||
const router = useRouter(); | ||
|
||
const { data, error, isLoading } = useUsers(); | ||
|
||
const handleChangePage = useCallback( | ||
(pageNumber: number) => { | ||
router.push({ | ||
pathname: router.pathname, | ||
query: { ...router.query, page: pageNumber }, | ||
}); | ||
}, | ||
[router] | ||
); | ||
|
||
const convertProvider = (provider: string) => { | ||
if(provider === 'KAKAO') { | ||
return 'μΉ΄μΉ΄μ€' | ||
} else if (provider === 'GOOGLE') { | ||
return 'ꡬκΈ' | ||
} else if (provider === 'NAVER') { | ||
return 'λ€μ΄λ²' | ||
} | ||
|
||
return 'μ μ μλ κ²½λ‘' | ||
} | ||
|
||
const openLifeMapPopup = (userId: number) => { | ||
window.open(`/popup/life-map/${userId}`, 'LifeMap', 'width=1000, height=610'); | ||
}; | ||
|
||
|
||
|
||
const columns: ColumnsType<IUser> = [ | ||
{ | ||
title: "μΈμ μ§λ", | ||
key: "action", | ||
width: 120, | ||
align: "center", | ||
render: (_value: unknown, record: IUser) => { | ||
return ( | ||
<span className="flex justify-center gap-2"> | ||
<Button type="primary" onClick={() => openLifeMapPopup(record.id)}> | ||
보기 | ||
</Button> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μ μ ID", | ||
dataIndex: "id", | ||
width: 100, | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.id}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μ μ μ΄λ¦ (username)", | ||
dataIndex: "username", | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.username}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μ μ λ³λͺ (nickname)", | ||
dataIndex: "nickname", | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.nickname}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μλ μ", | ||
dataIndex: "birth", | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.birth}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μ μ μ΄λ―Έμ§", | ||
dataIndex: "image", | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<img src={record.image} height={100} width={100} className="px-2 py-1 mr-1 bg-gray-100 rounded" /> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "κ°μ κ²½λ‘", | ||
dataIndex: "provider", | ||
render: (value: string, record: IUser) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{convertProvider(record.provider)}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
]; | ||
|
||
if (error) { | ||
return <Alert message="λ°μ΄ν° λ‘λ© μ€ μ€λ₯κ° λ°μνμ΅λλ€." type="warning" />; | ||
} | ||
|
||
return ( | ||
<> | ||
<DefaultTable<IUser> | ||
columns={columns} | ||
dataSource={data?.body || []} | ||
loading={isLoading} | ||
pagination={{ | ||
current: Number(router.query.page || 1), | ||
defaultPageSize: 10, | ||
total: data?.body.length || 0, | ||
showSizeChanger: false, | ||
onChange: handleChangePage, | ||
}} | ||
className="mt-3" | ||
countLabel={data?.body.length} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default React.memo(UserList); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { getDefaultLayout, IDefaultLayoutPage, IPageHeader } from "@/components/layout/default-layout"; | ||
import { useRouter } from "next/router"; | ||
import {IGoal, ITask, useLifeMap} from "@/client/life-map"; | ||
import DefaultTable from "@/components/shared/ui/default-table"; | ||
import React from "react"; | ||
import {ColumnsType} from "antd/es/table"; | ||
|
||
const pageHeader: IPageHeader = { | ||
title: "μΈμ μ§λ", | ||
}; | ||
|
||
const LifeMapPopup: IDefaultLayoutPage = () => { | ||
const router = useRouter(); | ||
const { data, error, isLoading, isValidating } = useLifeMap(router.query.id as string); | ||
|
||
const formattingTask = (task: ITask): string => { | ||
if(task.isTaskDone) { | ||
return 'β ' + task.taskDescription + ' \n \n' | ||
} | ||
|
||
return 'β ' + task.taskDescription + ' \n \n'; | ||
} | ||
|
||
const columns: ColumnsType<IGoal> = [ | ||
{ | ||
title: "λͺ©ν λͺ ", | ||
dataIndex: "title", | ||
width: 100, | ||
render: (value: string, record: IGoal) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.title}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μ€λͺ ", | ||
dataIndex: "description", | ||
width: 100, | ||
render: (value: string, record: IGoal) => { | ||
return ( | ||
<> | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.description}</span> | ||
</span> | ||
</> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "λ§κ°μΌ", | ||
dataIndex: "deadline", | ||
width: 100, | ||
render: (value: string, record: IGoal) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.deadline}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "νκ·Έ λͺ ", | ||
dataIndex: "tagContent", | ||
width: 100, | ||
render: (value: string, record: IGoal) => { | ||
return ( | ||
<span> | ||
<span className="px-2 py-1 mr-1 bg-gray-100 rounded">{record.tagInfo.tagContent}</span> | ||
</span> | ||
); | ||
}, | ||
}, | ||
{ | ||
title: "μΈλΆ λͺ©ν", | ||
dataIndex: "tasks", | ||
width: 100, | ||
render: (value: string, record: IGoal) => { | ||
return ( | ||
<span> | ||
{ record.tasks.map((task, index) => ( | ||
<> | ||
<span className=" rounded border-b-2">{formattingTask(task)}</span> | ||
<br/> | ||
</> | ||
))} | ||
</span> | ||
); | ||
}, | ||
}, | ||
]; | ||
|
||
return ( | ||
<> | ||
<div className="flex items-center text-2xl text-gray-900 mt-5">πͺ κ³΅κ° μ¬λΆ: {data?.body.isPublic === true ? '곡κ°' : 'λΉκ³΅κ°'}</div> | ||
<div className="flex items-center text-2xl text-gray-900 mt-5">π μμ λ°μ μ: {data?.body.cheeringCount}</div> | ||
<div className="flex items-center text-2xl text-gray-900 mt-5">π₯ λ°©λ¬Έμ μ: {data?.body.viewCount}</div> | ||
<div className="flex items-center text-2xl text-gray-900 mt-5">π© λͺ©ν 리μ€νΈ</div> | ||
<DefaultTable<IGoal> | ||
columns={columns} | ||
dataSource={data?.body.goals || []} | ||
loading={isLoading} | ||
className="mt-3" | ||
countLabel={data?.body.goals.length} | ||
/> | ||
</> | ||
) | ||
}; | ||
|
||
LifeMapPopup.getLayout = getDefaultLayout; | ||
LifeMapPopup.pageHeader = pageHeader; | ||
LifeMapPopup.setSidebarClose = true; | ||
|
||
export default LifeMapPopup; |
Oops, something went wrong.