diff --git a/apps/web-client/package.json b/apps/web-client/package.json
index 42494c6f..08f1adf2 100644
--- a/apps/web-client/package.json
+++ b/apps/web-client/package.json
@@ -71,7 +71,7 @@
"sync:type": "yarn dlx typesync",
"check:type": "tsc",
"check:type:watch": "yarn check:type --watch",
- "codegen": "openapi -i https://dev.poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ -c fetch --useUnionTypes --useOptions",
+ "codegen": "openapi -i https://dev.poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ --useUnionTypes --useOptions -c axios",
"postinstall": "yarn codegen",
"lint": "eslint . && prettier . --check --ignore-path .gitignore",
"format": "eslint . --fix && prettier . --write --ignore-path .gitignore"
diff --git a/apps/web-client/src/App.tsx b/apps/web-client/src/App.tsx
index 9cc5c17d..01e8b04d 100644
--- a/apps/web-client/src/App.tsx
+++ b/apps/web-client/src/App.tsx
@@ -103,9 +103,9 @@ function App() {
-
-
-
+
+
+
diff --git a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.styles.ts b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.styles.ts
index 1c622fdf..5eb1fef2 100644
--- a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.styles.ts
+++ b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.styles.ts
@@ -38,6 +38,7 @@ export const TextContainer = styled.div`
export const NameContainer = styled.div`
display: flex;
align-items: center;
+ gap: 8px;
@media (max-width: 576px) {
justify-content: center;
}
@@ -50,7 +51,6 @@ export const Name = styled.p`
export const Status = styled.span`
font-weight: 300;
- margin-left: 5px;
`;
export const DepartmentContainer = styled.div`
diff --git a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx
index a8dfeb9a..ed5890f5 100644
--- a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx
+++ b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx
@@ -1,5 +1,6 @@
-import Icon from '@ant-design/icons';
-import { Avatar } from 'antd';
+import Icon, { MessageOutlined } from '@ant-design/icons';
+import { Avatar, Button, Tooltip } from 'antd';
+import { useHistory } from 'react-router';
import ActivityCard from '~/components/activity/ActivityCard/ActivityCard';
import ProjectCard from '~/components/projects/ProjectCard/ProjectCard';
import getFileUrl from '~/lib/utils/getFileUrl';
@@ -19,16 +20,45 @@ import {
ActivityContainer,
Activities,
} from './MemberDetailContent.styles';
-import { MemberControllerService, MemberResponse, queryKey, useAppSuspenseQuery } from '~/lib/api-v2';
+import { ConversationControllerService, MemberControllerService, MemberResponse, queryKey, useAppMutation, useAppSuspeneseQueries } from '~/lib/api-v2';
+import { MENU } from '~/constants/menus';
export default function MemberDetailContent({ loginId }: { loginId: string }) {
- const { data: _member } = useAppSuspenseQuery({
- queryKey: queryKey.member.id(loginId),
- queryFn: () => MemberControllerService.getMemberWithProjectAndActivityUsingGet({ loginId }),
+ const history = useHistory();
+ const [{ data: _member }, { data: me }] = useAppSuspeneseQueries({
+ queries: [
+ {
+ queryKey: queryKey.member.id(loginId),
+ queryFn: () => MemberControllerService.getMemberWithProjectAndActivityUsingGet({ loginId }),
+ },
+ {
+ queryKey: queryKey.member.me,
+ queryFn: MemberControllerService.getMeUsingGet,
+ },
+ ],
});
const member = _member as unknown as Required;
+ const { mutate } = useAppMutation({
+ mutationFn: () =>
+ ConversationControllerService.createConversationUsingPost({
+ request: {
+ otherLoginID: member.loginID,
+ },
+ }),
+ });
+
+ const onMessageButtonClick = () => {
+ if (confirm(`${member.name}님과의 대화를 시작할까요?`)) {
+ mutate(undefined, {
+ onSuccess: (conversationId) => {
+ history.push(`/${MENU.MESSAGE}/${conversationId}/${MENU.MESSAGE_FORM}`);
+ },
+ });
+ }
+ };
+
return (
<>
@@ -40,6 +70,11 @@ export default function MemberDetailContent({ loginId }: { loginId: string }) {
{member.name}
{member.isAdmin && PoolC임원}
{member.badge && }
+ {member.loginID !== me.loginID && (
+
+ } type="primary" onClick={onMessageButtonClick} />
+
+ )}
{member.department && (
diff --git a/apps/web-client/src/components/message/MessageAllListContent.tsx b/apps/web-client/src/components/message/MessageAllListContent.tsx
new file mode 100644
index 00000000..4bf5bfaf
--- /dev/null
+++ b/apps/web-client/src/components/message/MessageAllListContent.tsx
@@ -0,0 +1,81 @@
+import { Button, List, Space, Typography } from 'antd';
+import { ArrowLeftOutlined } from '@ant-design/icons';
+import { useHistory } from 'react-router-dom';
+import { createStyles } from 'antd-style';
+
+const useStyles = createStyles(({ css }) => ({
+ wrapper: css`
+ width: 100%;
+ max-width: 1200px;
+ box-sizing: border-box;
+ `,
+ fullWidth: css`
+ width: 100%;
+ `,
+ metaInfo: css`
+ justify-content: space-between;
+ width: 100%;
+ `,
+ messageType: css`
+ font-weight: 700;
+ `,
+ topBox: css`
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ `,
+ topBoxName: css`
+ margin-bottom: 0;
+ font-weight: 700;
+ font-size: 20px;
+ `,
+}));
+
+export default function MessageAllListContent() {
+ const { styles } = useStyles();
+ const history = useHistory();
+
+ // TODO: API 호출로 대체
+ const data = [
+ {
+ title: 'Ant Design Title 1',
+ },
+ {
+ title: 'Ant Design Title 2',
+ },
+ {
+ title: 'Ant Design Title 3',
+ },
+ {
+ title: 'Ant Design Title 4',
+ },
+ ];
+
+ return (
+
+
+
+
+ 대화목록
+
+
+ (
+
+
+
+ 받은 쪽지
+ 2023.08.15 19:20:50
+
+ 내용내용내용
+
+
+ )}
+ />
+
+ );
+}
diff --git a/apps/web-client/src/components/message/MessageAllListView.tsx b/apps/web-client/src/components/message/MessageAllListView.tsx
new file mode 100644
index 00000000..0d70bd6f
--- /dev/null
+++ b/apps/web-client/src/components/message/MessageAllListView.tsx
@@ -0,0 +1,25 @@
+import { createStyles } from 'antd-style';
+import { Suspense } from 'react';
+import { Block, WhiteBlock } from '~/styles/common/Block.styles';
+import MessageAllListContent from './MessageAllListContent';
+import Spinner from '../common/Spinner/Spinner';
+
+const useStyles = createStyles(({ css }) => ({
+ whiteBlock: css`
+ padding: 30px 20px;
+ `,
+}));
+
+export default function MessageAllListView() {
+ const { styles } = useStyles();
+
+ return (
+
+
+ }>
+
+
+
+
+ );
+}
diff --git a/apps/web-client/src/components/message/MessageFormView.tsx b/apps/web-client/src/components/message/MessageFormView.tsx
new file mode 100644
index 00000000..763f8a25
--- /dev/null
+++ b/apps/web-client/src/components/message/MessageFormView.tsx
@@ -0,0 +1,86 @@
+import { ArrowLeftOutlined } from '@ant-design/icons';
+import { Button, Input, Space, Typography } from 'antd';
+import { createStyles } from 'antd-style';
+import { FormEventHandler, useState } from 'react';
+import { useHistory, useParams } from 'react-router-dom';
+import { MENU } from '~/constants/menus';
+import { MessageControllerService, useAppMutation } from '~/lib/api-v2';
+import { Block, WhiteBlock } from '~/styles/common/Block.styles';
+
+const useStyles = createStyles(({ css }) => ({
+ whiteBlock: css`
+ padding: 30px 20px;
+ `,
+ wrapper: css`
+ width: 100%;
+ max-width: 1200px;
+ box-sizing: border-box;
+ `,
+ fullWidth: css`
+ width: 100%;
+ `,
+ topBox: css`
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ `,
+ topBoxName: css`
+ margin-bottom: 0;
+ font-weight: 700;
+ font-size: 20px;
+ `,
+ textarea: css`
+ min-height: 300px !important;
+ resize: none;
+ `,
+}));
+
+export default function MessageFormView() {
+ const { styles } = useStyles();
+ // TODO: react-hook-form 기반으로 변경
+ const [content, setContent] = useState('');
+ const history = useHistory();
+ const { conversationId } = useParams<{ conversationId: string }>();
+
+ const { mutate: sendMessage } = useAppMutation({
+ mutationFn: () =>
+ MessageControllerService.sendMessageUsingPost({
+ request: {
+ content,
+ conversationId,
+ },
+ }),
+ });
+
+ const onFormSubmit: FormEventHandler = (e) => {
+ e.preventDefault();
+ sendMessage(undefined, {
+ onSuccess: () => {
+ history.push(`/${MENU.MESSAGE}/${conversationId}`);
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/apps/web-client/src/components/message/MessageListContent.tsx b/apps/web-client/src/components/message/MessageListContent.tsx
new file mode 100644
index 00000000..ea72058e
--- /dev/null
+++ b/apps/web-client/src/components/message/MessageListContent.tsx
@@ -0,0 +1,92 @@
+import { Button, List, Space, Typography } from 'antd';
+import { createStyles } from 'antd-style';
+import { ArrowLeftOutlined } from '@ant-design/icons';
+import { Link, useHistory } from 'react-router-dom';
+import { Block, WhiteBlock } from '~/styles/common/Block.styles';
+import { MENU } from '~/constants/menus';
+
+const useStyles = createStyles(({ css }) => ({
+ whiteBlock: css`
+ padding: 30px 20px;
+ `,
+ wrapper: css`
+ width: 100%;
+ max-width: 1200px;
+ box-sizing: border-box;
+ `,
+ fullWidth: css`
+ width: 100%;
+ `,
+ metaInfo: css`
+ justify-content: space-between;
+ width: 100%;
+ `,
+ messageType: css`
+ font-weight: 700;
+ `,
+ topBox: css`
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ `,
+ topBoxName: css`
+ margin-bottom: 0;
+ font-weight: 700;
+ font-size: 20px;
+ `,
+}));
+
+export default function MessageListContent() {
+ const { styles } = useStyles();
+ const history = useHistory();
+
+ const data = [
+ {
+ title: 'Ant Design Title 1',
+ },
+ {
+ title: 'Ant Design Title 2',
+ },
+ {
+ title: 'Ant Design Title 3',
+ },
+ {
+ title: 'Ant Design Title 4',
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+ 익명
+
+
+
+
+
+ (
+
+
+
+ 받은 쪽지
+ 2023.08.15 19:20:50
+
+ 내용내용내용
+
+
+ )}
+ />
+
+
+
+ );
+}
diff --git a/apps/web-client/src/components/message/MessageListView.tsx b/apps/web-client/src/components/message/MessageListView.tsx
new file mode 100644
index 00000000..3e7f0722
--- /dev/null
+++ b/apps/web-client/src/components/message/MessageListView.tsx
@@ -0,0 +1,25 @@
+import { createStyles } from 'antd-style';
+import { Suspense } from 'react';
+import { Block, WhiteBlock } from '~/styles/common/Block.styles';
+import Spinner from '../common/Spinner/Spinner';
+import MessageListContent from './MessageListContent';
+
+const useStyles = createStyles(({ css }) => ({
+ whiteBlock: css`
+ padding: 30px 20px;
+ `,
+}));
+
+export default function MessageAllListView() {
+ const { styles } = useStyles();
+
+ return (
+
+
+ }>
+
+
+
+
+ );
+}
diff --git a/apps/web-client/src/constants/menus.js b/apps/web-client/src/constants/menus.js
index db43b5c2..43504238 100644
--- a/apps/web-client/src/constants/menus.js
+++ b/apps/web-client/src/constants/menus.js
@@ -24,9 +24,8 @@ const MENU = {
MY_PAGE_BADGE_LIST: 'badge-list',
MY_PAGE_MY_POSTS: 'my-posts',
MY_PAGE_MY_SCRAPS: 'my-scraps',
- MESSAGE_LIST: 'message-list',
- MESSAGE_FORM: 'message-form',
- MESSAGE_ALL_LIST: 'message-all-list',
+ MESSAGE: 'message',
+ MESSAGE_FORM: 'form',
ROOM_RESERVATION: 'room-reservation',
};
diff --git a/apps/web-client/src/lib/api-v2/queryKey.ts b/apps/web-client/src/lib/api-v2/queryKey.ts
index bee78dbd..6f3f7efa 100644
--- a/apps/web-client/src/lib/api-v2/queryKey.ts
+++ b/apps/web-client/src/lib/api-v2/queryKey.ts
@@ -29,4 +29,8 @@ export const queryKey = {
room: {
range: (start: string, end: string) => ['room.range', start, end] as const,
},
+ conversation: {
+ all: ['conversation.all'] as const,
+ conversation: (id: number) => ['conversation.conversation', id] as const,
+ },
};
diff --git a/apps/web-client/src/pages/message/MessageAllListPage.tsx b/apps/web-client/src/pages/message/MessageAllListPage.tsx
index f5456990..0678463a 100644
--- a/apps/web-client/src/pages/message/MessageAllListPage.tsx
+++ b/apps/web-client/src/pages/message/MessageAllListPage.tsx
@@ -1,88 +1,5 @@
-import { Button, List, Space, Typography } from 'antd';
-import { createStyles } from 'antd-style';
-import { ArrowLeftOutlined } from '@ant-design/icons';
-import { useHistory } from 'react-router-dom';
-import { Block, WhiteBlock } from '~/styles/common/Block.styles';
-
-const useStyles = createStyles(({ css }) => ({
- whiteBlock: css`
- padding: 30px 20px;
- `,
- wrapper: css`
- width: 100%;
- max-width: 1200px;
- box-sizing: border-box;
- `,
- fullWidth: css`
- width: 100%;
- `,
- metaInfo: css`
- justify-content: space-between;
- width: 100%;
- `,
- messageType: css`
- font-weight: 700;
- `,
- topBox: css`
- justify-content: space-between;
- align-items: center;
- width: 100%;
- `,
- topBoxName: css`
- margin-bottom: 0;
- font-weight: 700;
- font-size: 20px;
- `,
-}));
+import MessageAllListView from '~/components/message/MessageAllListView';
export default function MessageAllListPage() {
- const { styles } = useStyles();
- const history = useHistory();
-
- const data = [
- {
- title: 'Ant Design Title 1',
- },
- {
- title: 'Ant Design Title 2',
- },
- {
- title: 'Ant Design Title 3',
- },
- {
- title: 'Ant Design Title 4',
- },
- ];
-
- return (
-
-
-
-
-
-
- 쪽지목록
-
-
- (
-
-
-
- 받은 쪽지
- 2023.08.15 19:20:50
-
- 내용내용내용
-
-
- )}
- />
-
-
-
- );
+ return ;
}
diff --git a/apps/web-client/src/pages/message/MessageFormPage.tsx b/apps/web-client/src/pages/message/MessageFormPage.tsx
index 48edb023..abda5b57 100644
--- a/apps/web-client/src/pages/message/MessageFormPage.tsx
+++ b/apps/web-client/src/pages/message/MessageFormPage.tsx
@@ -1,61 +1,5 @@
-import { ArrowLeftOutlined } from '@ant-design/icons';
-import { Button, Input, Space, Typography } from 'antd';
-import { createStyles } from 'antd-style';
-import { useState } from 'react';
-import { useHistory } from 'react-router-dom';
-import { Block, WhiteBlock } from '~/styles/common/Block.styles';
+import MessageFormView from '~/components/message/MessageFormView';
-const useStyles = createStyles(({ css }) => ({
- whiteBlock: css`
- padding: 30px 20px;
- `,
- wrapper: css`
- width: 100%;
- max-width: 1200px;
- box-sizing: border-box;
- `,
- fullWidth: css`
- width: 100%;
- `,
- topBox: css`
- justify-content: space-between;
- align-items: center;
- width: 100%;
- `,
- topBoxName: css`
- margin-bottom: 0;
- font-weight: 700;
- font-size: 20px;
- `,
- textarea: css`
- min-height: 300px !important;
- resize: none;
- `,
-}));
-
-export default function MyPageMessageFormPage() {
- const { styles } = useStyles();
- const [content, setContent] = useState('');
- const history = useHistory();
-
- return (
-
-
-
-
-
-
- 쪽지보내기
-
-
- setContent(e.target.value)} />
-
-
-
-
- );
+export default function MessageFormPage() {
+ return ;
}
diff --git a/apps/web-client/src/pages/message/MessageListPage.tsx b/apps/web-client/src/pages/message/MessageListPage.tsx
index 14cc9343..c2738e74 100644
--- a/apps/web-client/src/pages/message/MessageListPage.tsx
+++ b/apps/web-client/src/pages/message/MessageListPage.tsx
@@ -1,7 +1,7 @@
import { Button, List, Space, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { ArrowLeftOutlined } from '@ant-design/icons';
-import { Link, useHistory } from 'react-router-dom';
+import { Link, useHistory, useParams } from 'react-router-dom';
import { Block, WhiteBlock } from '~/styles/common/Block.styles';
import { MENU } from '~/constants/menus';
@@ -39,6 +39,7 @@ const useStyles = createStyles(({ css }) => ({
export default function MyPageMessageListPage() {
const { styles } = useStyles();
const history = useHistory();
+ const { conversationId } = useParams<{ conversationId: string }>();
const data = [
{
@@ -66,7 +67,7 @@ export default function MyPageMessageListPage() {
익명
-
+