Skip to content

Commit

Permalink
Merge pull request #81 from softeerbootcamp4th/TASK-171
Browse files Browse the repository at this point in the history
[Feature][Task-171] 어드민 페이지 Socket연동 및 에러 핸들링 추기
  • Loading branch information
nim-od authored Aug 19, 2024
2 parents e8db8ef + 3ce4022 commit 20929cc
Show file tree
Hide file tree
Showing 29 changed files with 368 additions and 81 deletions.
2 changes: 1 addition & 1 deletion packages/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<title>ADMIN PAGE</title>
</head>
<body>
<div id="root"></div>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
3 changes: 1 addition & 2 deletions packages/admin/src/components/appLayout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ interface HeaderProps {
}

function Header({ headerTitle, navTitles }: HeaderProps) {
const h2Styled = 'font-bold text-3xl';
const h2Styled = 'font-bold text-4xl';

if (!navTitles) return <h2 className={`${h2Styled}`}>{headerTitle}</h2>;

return (
<div className="flex flex-col">
<h2 className={`${h2Styled}`}>{headerTitle}</h2>

<ul className="flex gap-2 rounded-sm bg-slate-200">
{navTitles.map((navTitle) => (
<li key={navTitle.navTitleId}>{navTitle.navTitle}</li>
Expand Down
2 changes: 1 addition & 1 deletion packages/admin/src/components/appLayout/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function SideBar({ isChecked, handleClose }: SideBarProps) {
};
return (
<div
className={`absolute m-[-100px_0_0_-50px] h-screen w-[300px] origin-[0%_0%] transform list-none bg-slate-200 px-10 pt-[125px] font-sans antialiased transition-all duration-500 ease-[cubic-bezier(0.77,0.2,0.05,1.0)] ${
className={`absolute m-[-100px_0_0_-50px] min-h-screen w-[300px] origin-[0%_0%] transform list-none bg-slate-200 px-10 pt-[125px] font-sans antialiased transition-all duration-500 ease-[cubic-bezier(0.77,0.2,0.05,1.0)] ${
isChecked ? 'translate-x-0 opacity-100' : '-translate-x-full opacity-0'
} bg-khaki-100`}
>
Expand Down
10 changes: 5 additions & 5 deletions packages/admin/src/components/appLayout/SideBarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ function SideBarContainer() {
'block w-8 h-1 mb-1 relative bg-[#cdcdcd] rounded z-10 origin-[4px_0px] transition-transform transition-bg duration-500 ease-custom-bezier';

return (
<nav role="navigation" className="ml-5 mt-10">
<div id="menuToggle" className="relative z-10 select-none">
<nav role="navigation" className="ml-5 mt-10" >
<div id="menuToggle" className="fixed top-10 z-10 select-none" >
<input
type="checkbox"
checked={isChecked}
Expand All @@ -28,17 +28,17 @@ function SideBarContainer() {
<span
className={`${spanStyled} ${
isChecked
? 'translate-x-[-2px] translate-y-[-1px] rotate-45 transform bg-slate-500'
? 'translate-x-[-2px] translate-y-[-1px] rotate-45 transform bg-slate-500 bg-[#000000]'
: ''
}`}
/>
<span
className={` ${spanStyled} ${isChecked ? 'rotate-0 scale-[0.2,0.2] transform opacity-0' : ''}`}
className={` ${spanStyled} ${isChecked ? 'rotate-0 scale-[0.2,0.2] transform opacity-0 bg-[#000000]' : ''}`}
/>
<span
className={`${spanStyled} ${
isChecked
? 'translate-x-[-2px] translate-y-[-1px] rotate-[-45deg] transform bg-slate-500'
? 'translate-x-[-2px] translate-y-[-1px] rotate-[-45deg] transform bg-slate-500 bg-[#000000]'
: ''
}`}
/>
Expand Down
22 changes: 22 additions & 0 deletions packages/admin/src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { BlockedChat, ChatProps, Message, Notice } from '@softeer/common/components';
import { FunctionComponent, useCallback } from 'react';

export default function Chat({ type, team, sender, content }: ChatProps) {
const Render: FunctionComponent = useCallback(() => {
switch (type) {
case 'n':
return <Notice>{content}</Notice>;
case 'b':
return <BlockedChat />;
case 'm':
default:
return (
<Message sender={sender} team={team} isMyMessage={false}>
{content}
</Message>
);
}
}, [type, sender, content]);

return <Render />;
}
24 changes: 24 additions & 0 deletions packages/admin/src/components/chat/RealTimeChatting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ChatList } from '@softeer/common/components';
import { UseSocketReturnType } from 'src/hooks/socket/index.ts';
import Chat from './Chat.tsx';

/** 실시간 기대평 섹션 */

function RealTimeChatting({
chatSocket: { onSendMessage, messages },
}: Pick<UseSocketReturnType, 'chatSocket'>) {
return (
<section className="container flex max-w-[1200px] snap-start flex-col items-center pb-[115px] pt-[50px]">
<h6 className="text-heading-10 mb-[25px] font-medium">기대평을 남겨보세요!</h6>
<div className="h-[1000px] w-full overflow-y-auto rounded-[10px] bg-neutral-800 py-10">
<ChatList>
{messages.map((message) => (
<Chat key={message.id} {...message} />
))}
</ChatList>
</div>
</section>
);
}

export default RealTimeChatting;
22 changes: 12 additions & 10 deletions packages/admin/src/components/common/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ function Tab({ selectedTabName, setSelectedTabName, tabNames }: CommonTabProps)
setSelectedTabName(tabname);
};
return (
<div className="flex flex-row">
{tabNames.map((tabName) => (
<Button
key={tabName}
disabled={selectedTabName === tabName}
onClick={() => handleSelect(tabName)}
>
{tabName}
</Button>
))}
<div className="flex flex-row mb-4" >
<div className="border-cream-900 flex-0 bg-khaki-500 flex flex-row gap-2 border-2 rounded-4 p-3">
{tabNames.map((tabName) => (
<Button
key={tabName}
disabled={selectedTabName === tabName}
onClick={() => handleSelect(tabName)}
>
{tabName}
</Button>
))}
</div>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/admin/src/components/table/ResultTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function ResultTable({ headers, rows }: ResultTableProps) {
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={`row-${row.join()}`}>
{rows.map((row,rowIndex) => (
<TableRow key={rowIndex}>
{row.map((item, colIndex) => (
<TableCell className="font-medium" key={colIndex}>
{item}
Expand Down
8 changes: 4 additions & 4 deletions packages/admin/src/constants/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// export const BASE_URL = 'http://localhost:5173/src/services/api/mock';
export const BASE_URL = 'http://ec2-3-39-67-88.ap-northeast-2.compute.amazonaws.com:8080/admin';
// export const BASE_URL = 'http://api.softeer.kro.kr:8080/admin';
// export const BASE_URL = 'http://dating.batro.org:8080/admin';
import { API_BASE_URL } from './environments.ts';

export const BASE_URL = API_BASE_URL;

export const enum METHOD {
GET = 'GET',
Expand All @@ -14,6 +13,7 @@ export const enum API {
COMMON_EVENT = '/common-event',
QUIZ_LIST = '/quiz-list',
QUIZ = '/quiz',
QUIZ_WINNER = '/quiz-winner',
RACING_WINNERS = '/racing-winners',
PERSONALITY_TEST_LIST = '/personality-test-list',
PERSONALITY_TEST = '/personality-test',
Expand Down
6 changes: 6 additions & 0 deletions packages/admin/src/constants/environments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//* *********************************** */
//* * Environment Variables */
//* *********************************** */

export const { VITE_BASE_URL: API_BASE_URL, VITE_SOCKET_BASE_URL: SOCKET_BASE_URL } = import.meta
.env;
18 changes: 18 additions & 0 deletions packages/admin/src/hooks/socket/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ACCESS_TOKEN_KEY } from '@softeer/common/constants';
import { Cookie } from '@softeer/common/utils';
import { useEffect } from 'react';
import socketManager from 'src/services/socket.ts';
import useChatSocket from './useChatSocket.ts';

export type UseSocketReturnType = ReturnType<typeof useSocket>;

export default function useSocket() {
const accessToken = Cookie.getCookie<string>(ACCESS_TOKEN_KEY) ?? '';
const chatSocket = useChatSocket();
const { onReceiveMessage, ...chatSocketProps } = chatSocket;
useEffect(() => {
socketManager.connectSocketClient({ token: accessToken, onReceiveMessage });
}, [socketManager, accessToken]);

return { chatSocket: chatSocketProps };
}
47 changes: 47 additions & 0 deletions packages/admin/src/hooks/socket/useChatSocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ChatProps } from '@softeer/common/components';
import { CHAT_SOCKET_ENDPOINTS } from '@softeer/common/constants';
import { SocketSubscribeCallbackType } from '@softeer/common/utils';
import { useCallback, useState } from 'react';
// import { useToast } from 'src/hooks/useToast.ts';
import socketManager from 'src/services/socket.ts';

export type UseChatSocketReturnType = ReturnType<typeof useChatSocket>;

export default function useChatSocket() {
// const { toast } = useToast();

const socketClient = socketManager.getSocketClient();
const [chatMessages, setChatMessages] = useState<ChatProps[]>([]);

const handleIncomingMessage: SocketSubscribeCallbackType = useCallback(
(data: unknown, messageId: string) => {
const parsedData = data as Omit<ChatProps, 'id'>;
const parsedMessage = { id: messageId, ...parsedData };
setChatMessages((prevMessages) => [...prevMessages, parsedMessage] as ChatProps[]);
},
[],
);

const handleSendMessage = useCallback(
(content: string) => {
try {
const chatMessage = { content };

socketClient.sendMessages({
destination: CHAT_SOCKET_ENDPOINTS.PUBLISH,
body: chatMessage,
});
} catch (error) {
// const errorMessage = (error as Error).message;
// toast({ description: errorMessage.length > 0 ? errorMessage : '문제가 발생했습니다.' });
}
},
[socketClient],
);

return {
onReceiveMessage: handleIncomingMessage,
onSendMessage: handleSendMessage,
messages: chatMessages,
};
}
45 changes: 32 additions & 13 deletions packages/admin/src/hooks/useEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
Response,
WinnerSetting,
} from 'src/services/api/types/apiType.ts';
import { useAlert } from 'src/store/provider/AlertProvider.tsx';
import fetchData from 'src/utils/fetchData.ts';

const useEvent = () => {
// const { openAlert } = useAlert();
const { openAlert } = useAlert();

const commonEventQuery = useQuery<Response[API.COMMON_EVENT][METHOD.GET]>({
queryFn: async () => {
Expand Down Expand Up @@ -52,6 +53,17 @@ const useEvent = () => {
queryKey: [API.QUIZ_LIST],
});

const quizWinnerQuery = useQuery<Response[API.QUIZ_WINNER][METHOD.GET]>({
queryFn: async () => {
const response = await fetchData({
path: API.QUIZ_WINNER,
method: METHOD.GET,
});
return response;
},
queryKey: [API.QUIZ_WINNER],
});

const quizEventMutation = useMutation({
mutationFn: async (quizEvent: Quiz) => {
const tmp = quizEvent;
Expand All @@ -67,6 +79,9 @@ const useEvent = () => {
onSuccess: () => {
quizEventQuery.refetch();
},
onError: (error) => {
openAlert(error.message, 'alert');
},
});

const updateQuizEvent = (quizEvent: Quiz) => {
Expand All @@ -85,24 +100,20 @@ const useEvent = () => {
});

const racingWinnerMutation = useMutation({
mutationFn: async (winnerSettings: WinnerSetting[]) => {
const response = await fetchData({
mutationFn: async (winnerSettings: WinnerSetting[]) =>
fetchData({
path: API.RACING_WINNERS,
method: METHOD.POST,
payload: winnerSettings,
});
return response;
// response
// openAlert(await response.text(), 'alert');
// if (response.status === 200) {
// openAlert('추첨이 완료되었습니다.', 'alert');
// const result = await response.json();
// return result;
// }
},
}),
onSuccess: () => {
racingWinnerQuery.refetch();
},
onError: async (error) => {
if (error.name !== 'SyntaxError') {
openAlert(error.message, 'alert');
}
},
});

const updateRacingWinner = (winnerSettings: WinnerSetting[]) => {
Expand Down Expand Up @@ -132,6 +143,9 @@ const useEvent = () => {
onSuccess: () => {
personalityTestListQuery.refetch();
},
onError: (error) => {
openAlert(error.message, 'alert');
},
});

const updatePersonalityTest = (personalityTest: PersonalityTest) => {
Expand All @@ -142,9 +156,14 @@ const useEvent = () => {
commonEvent: commonEventQuery.data,
updateCommonEvent,
refechCommonEvent: commonEventQuery.refetch,

quizEvent: quizEventQuery.data,
quizWinner: quizWinnerQuery.data,
refetchQuizWinner: quizWinnerQuery.refetch,

updateQuizEvent,
refechQuizEvent: quizEventQuery.refetch,

racingWinners: racingWinnerQuery.data,
refetchRacingWinners: racingWinnerQuery.refetch,
updateRacingWinner,
Expand Down
5 changes: 3 additions & 2 deletions packages/admin/src/layouts/appLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function AppLayout() {
const location = useLocation();
const navigate = useNavigate();
const accessToken = Cookie.getCookie<string>(ACCESS_TOKEN_KEY);

useEffect(() => {
if (location.pathname !== RoutePaths.ROOT) {
if (!accessToken) {
Expand All @@ -31,9 +32,9 @@ export default function AppLayout() {
}, [location.pathname]);

return (
<div className="mx-auto flex h-screen w-screen min-w-[1200px]">
<div className="mx-auto flex min-h-screen w-screen min-w-[1200px]">
{accessToken && <SideBarContainer />}
<div className="mx-auto flex h-screen w-full max-w-[1200px] gap-10">
<div className="mx-auto flex min-h-screen h-full w-full max-w-[1200px] gap-10">
<SystemContainer />
<div className="flex w-full flex-col p-4 pb-8 pt-8">
<Header headerTitle={headerTitle} />
Expand Down
7 changes: 3 additions & 4 deletions packages/admin/src/pages/events/CommonEventTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import {

function CommonEventItem({ description, element }: { description: string; element: JSX.Element }) {
return (
<div className="w-1/2 p-1">
<div className="w-1/2 p-2">
<div className="flex rounded-sm border-[1px] border-black">
<div className="flex min-h-[40px] w-32 items-center justify-center border-r-[1px] border-black">
{description}
</div>
<div className="flex w-full items-center justify-center">{element}</div>
<div className="flex w-full items-center justify-center gap-4">{element}</div>
</div>
</div>
);
Expand All @@ -42,7 +42,6 @@ function CommonEventBox({
handleUpdateEvent: (newCommonEvent: CommonEvent) => void;
}) {
const { openAlert, addAlertCallback } = useAlert();
// 흠 이거 reduce 쓰면 뭔가 더 복잡해 보임 -> 유사하게 날짜 수정 로직 생기면 공통 util로 빼는 방식으로 변경
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');

Expand Down Expand Up @@ -126,7 +125,7 @@ function CommonEventBox({
};

return (
<div className="flex flex-row flex-wrap rounded-sm border-[1px] border-black p-1">
<div className="flex flex-row flex-wrap rounded-sm border-[1px] border-black- p-4">
<CommonEventItem description="이벤트 명" element={<div>{commonEvent.eventName}</div>} />
<CommonEventItem description="상태" element={<div>{status}</div>} />
<CommonEventItem
Expand Down
Loading

0 comments on commit 20929cc

Please sign in to comment.