From e12463486d182a14c76c887517c4957ab8387225 Mon Sep 17 00:00:00 2001 From: Denovo Date: Thu, 23 Nov 2023 04:09:45 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EC=9A=94=EC=B2=AD=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20=EC=A0=95=EB=A0=AC=EC=A0=81=EC=9A=A9=20=EB=B0=8F?= =?UTF-8?q?=20=20=EC=86=8C=EC=BC=93=EC=97=B0=EA=B2=B0=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.js | 20 ++- package-lock.json | 150 ++++++++++++++++- package.json | 5 + .../container/RequestContainer/index.tsx | 1 - .../container/RequestInfoContainer/index.tsx | 8 +- src/components/elements/Toggle/index.tsx | 1 + src/components/module/RequestInfo/Header.tsx | 68 +++++--- src/components/module/RequestInfo/Table.tsx | 47 ++---- src/hooks/api/useRequest.ts | 85 +++++++--- src/hooks/socket/useRequestRealtime.tsx | 46 +++++ src/store/request.store.tsx | 112 +++++-------- src/types/api/api.d.ts | 157 +++++++++--------- 12 files changed, 457 insertions(+), 243 deletions(-) create mode 100644 src/hooks/socket/useRequestRealtime.tsx diff --git a/next.config.js b/next.config.js index 829c580..e33f20c 100644 --- a/next.config.js +++ b/next.config.js @@ -27,14 +27,22 @@ const nextConfig = { }, ]; }, - webpack: (config, { webpack }) => { - config.plugins.push( - new webpack.IgnorePlugin({ - resourceRegExp: /import2/, // adjust the module name - }) - ); + webpack: (config) => { + config.externals.push({ + "utf-8-validate": "commonjs utf-8-validate", + bufferutil: "commonjs bufferutil", + "supports-color": "commonjs supports-color", + }); return config; }, + // webpack: (config, { webpack }) => { + // config.plugins.push( + // new webpack.IgnorePlugin({ + // resourceRegExp: /import2/, // adjust the module name + // }) + // ); + // return config; + // }, }; module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json index 7d89484..b7276dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/react": "18.2.16", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.14", + "bufferutil": "^4.0.8", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "date-fns": "^2.30.0", @@ -39,12 +40,15 @@ "react-dom": "18.2.0", "react-grid-layout": "^1.3.4", "react-hook-form": "^7.47.0", + "socket.io-client": "^4.7.2", "swr": "^2.2.4", "tailwind-merge": "^1.14.0", "tailwindcss": "3.3.3", "tailwindcss-animate": "^1.0.7", "typescript": "5.1.6", "ulid": "^2.3.0", + "utf-8-validate": "^5.0.10", + "ws": "^8.14.2", "zod": "^3.22.4", "zustand": "^4.4.6" }, @@ -58,6 +62,7 @@ "@types/react-grid-layout": "^1.3.2", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", + "encoding": "^0.1.13", "eslint": "^8.47.0", "eslint-config-next": "^13.4.19", "eslint-config-prettier": "^9.0.0", @@ -2905,6 +2910,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -4486,6 +4496,18 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -5165,7 +5187,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5509,6 +5530,67 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "devOptional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "devOptional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -9679,8 +9761,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -9821,6 +9902,16 @@ } } }, + "node_modules/node-gyp-build": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz", + "integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11735,6 +11826,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -12795,6 +12912,18 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13047,10 +13176,9 @@ } }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "engines": { "node": ">=10.0.0" }, @@ -13082,6 +13210,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 3ef4e38..8a1997d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@types/react": "18.2.16", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.14", + "bufferutil": "^4.0.8", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "date-fns": "^2.30.0", @@ -54,12 +55,15 @@ "react-dom": "18.2.0", "react-grid-layout": "^1.3.4", "react-hook-form": "^7.47.0", + "socket.io-client": "^4.7.2", "swr": "^2.2.4", "tailwind-merge": "^1.14.0", "tailwindcss": "3.3.3", "tailwindcss-animate": "^1.0.7", "typescript": "5.1.6", "ulid": "^2.3.0", + "utf-8-validate": "^5.0.10", + "ws": "^8.14.2", "zod": "^3.22.4", "zustand": "^4.4.6" }, @@ -73,6 +77,7 @@ "@types/react-grid-layout": "^1.3.2", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", + "encoding": "^0.1.13", "eslint": "^8.47.0", "eslint-config-next": "^13.4.19", "eslint-config-prettier": "^9.0.0", diff --git a/src/components/container/RequestContainer/index.tsx b/src/components/container/RequestContainer/index.tsx index 24c64d1..9345904 100644 --- a/src/components/container/RequestContainer/index.tsx +++ b/src/components/container/RequestContainer/index.tsx @@ -32,7 +32,6 @@ function coordinateOnCircle( export const RequestContainer = () => { const numberOfDots = 12; const radius = 8.2; // Half of 16.4rem - const adjustedRadius = radius; // Adjust for dot size const { toast } = useToast(); const requestOnClick = () => { diff --git a/src/components/container/RequestInfoContainer/index.tsx b/src/components/container/RequestInfoContainer/index.tsx index 38039e7..dcf0032 100644 --- a/src/components/container/RequestInfoContainer/index.tsx +++ b/src/components/container/RequestInfoContainer/index.tsx @@ -1,14 +1,8 @@ "use client"; import { RequestInfoPageHeader } from "@/components/module/RequestInfo/Header"; import { RequestTable } from "@/components/module/RequestInfo/Table"; -import { useRequest } from "@/hooks/api/useRequest"; -import { useEffect } from "react"; export const RequestInfoContainer = () => { - const { request_list } = useRequest(); - useEffect(() => { - console.log(request_list); - }, [request_list]); return (
@@ -16,7 +10,7 @@ export const RequestInfoContainer = () => {
- +
diff --git a/src/components/elements/Toggle/index.tsx b/src/components/elements/Toggle/index.tsx index 1d5b32c..a37af74 100644 --- a/src/components/elements/Toggle/index.tsx +++ b/src/components/elements/Toggle/index.tsx @@ -16,6 +16,7 @@ interface ToggleProps { checked: boolean; onChange?: (e?: React.ChangeEvent) => void; bgColor?: BrandColor; + onClick?: (e?: React.MouseEvent) => void; } export const Toggle = ({ diff --git a/src/components/module/RequestInfo/Header.tsx b/src/components/module/RequestInfo/Header.tsx index a2b1ffa..7c52f74 100644 --- a/src/components/module/RequestInfo/Header.tsx +++ b/src/components/module/RequestInfo/Header.tsx @@ -1,30 +1,38 @@ "use client"; +import { Toggle } from "@/components/elements/Toggle"; import { useWindowSize } from "@/hooks"; +import { useRequest } from "@/hooks/api/useRequest"; import { cn } from "@/lib/utils"; -import { useRequestStore } from "@/store/request.store"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; export const RequestInfoPageHeader = () => { const { width } = useWindowSize(); - const [status, setStatus] = useState<"ALL" | "REQUESTED" | "REJECTED">("ALL"); - const { setQueryStatus } = useRequestStore(); - useEffect(() => {}, [status]); + const { + pageStatus, + setPageStatus, + order, + setOrder, + orderby, + setOrderBy, + sort, + } = useRequest(); const typeHandler = (status: "ALL" | "REQUESTED" | "REJECTED") => () => { - setStatus(status); - if (status === "ALL") { - setQueryStatus([]); - return; - } - if (status === "REQUESTED") { - setQueryStatus(["REQUESTED", "VIEWED"]); - return; - } - if (status === "REJECTED") { - setQueryStatus(["REJECTED"]); - return; - } + setPageStatus(status); }; + const onClickOrder = () => { + setOrder(!order); + }; + const onClickOrderBy = () => { + setOrderBy(orderby === "DISTANCE" ? "TIME" : "DISTANCE"); + }; + const onChageHandler = () => { + console.log(order, orderby); + }; + useEffect(() => { + sort(); + }, [order, orderby, sort]); + return (
{
전체
요청중
요청거절
+
+ 오름차순 + +
+
+ 거리/시간 + +
); }; diff --git a/src/components/module/RequestInfo/Table.tsx b/src/components/module/RequestInfo/Table.tsx index 7f49a5d..ef1d45b 100644 --- a/src/components/module/RequestInfo/Table.tsx +++ b/src/components/module/RequestInfo/Table.tsx @@ -1,48 +1,29 @@ import { Tag } from "@/components/elements/Tag"; -import { useWindowSize } from "@/hooks"; +import { useRequest } from "@/hooks/api/useRequest"; import { useRequestStore } from "@/store/request.store"; import { RequestInfo, reqeustStatueKorMap } from "@/types/model/request"; import Link from "next/link"; -import { useEffect, useRef } from "react"; - -interface RequestTableProps { - request_list: RequestInfo[]; -} - -export const RequestTable = ({ request_list }: RequestTableProps) => { - const { height } = useWindowSize(); +import { useEffect, useRef, useState } from "react"; +export const RequestTable = () => { const ref = useRef(null); - const { pageLimit, query, setQueryPage, setQueryLimit } = useRequestStore(); + const { requests, rejectedRequests, requestedRequests } = useRequest(); + const { pageStatus } = useRequestStore(); + const [request_list, setRequestList] = useState([]); useEffect(() => { - if (height && height > 1340) { - setQueryLimit(30); + if (pageStatus === "ALL") { + setRequestList(requests); } - }, [height, setQueryLimit]); - useEffect(() => { - if (ref.current) { - console.log(ref.current.scrollHeight); + if (pageStatus === "REQUESTED") { + setRequestList(requestedRequests); } - }, [ref, setQueryPage]); - - const onScroll = (e: React.UIEvent) => { - if ( - e.currentTarget?.scrollHeight - - e.currentTarget?.scrollTop - - e.currentTarget?.clientHeight < - 1 - ) { - if (query.page < pageLimit.total_page) { - request_list.length && setQueryPage(query.page + 1); - console.log("fetch"); - } + if (pageStatus === "REJECTED") { + setRequestList(rejectedRequests); } - }; - useEffect(() => { if (ref.current) { ref.current.scrollTo(0, 0); } - }, [query.request_status]); + }, [pageStatus, requests, rejectedRequests, requestedRequests, ref]); return (
@@ -51,7 +32,7 @@ export const RequestTable = ({ request_list }: RequestTableProps) => {
거리
상태
-
+
{request_list.length > 0 && request_list.map((request, i) => { return ( diff --git a/src/hooks/api/useRequest.ts b/src/hooks/api/useRequest.ts index 1de5b22..c5bc7d9 100644 --- a/src/hooks/api/useRequest.ts +++ b/src/hooks/api/useRequest.ts @@ -1,10 +1,27 @@ import { useRequestStore } from "@/store/request.store"; -import { useEffect } from "react"; +import { useCallback, useEffect } from "react"; import { useGetApi } from "."; +/** + * 무한스크롤링으로 페이지네이션 할 경우 + * 소켓으로 실시간으로 데이터를 받아올때 중복데이터가 발생할 수 있음 + * + * 요청 데이터는 이론상 1개~600개의 데이터가 생성되기에 + * 페이지네이션을 하지 않고 한번에 데이터를 받아오는 방법을 사용하는 것이 더 적합한 방법이라고 판단됨 + * + */ export const useRequest = () => { - const { request_list, query, setRequestList, setPageLimit } = - useRequestStore(); + const { + query, + setRequestList, + rejectedRequests, + requestedRequests, + requests, + order, + orderby, + ...rest + } = useRequestStore(); + const { data, isLoading } = useGetApi( "/requests/ems-to-er/ems", { useLoader: true }, @@ -14,27 +31,57 @@ export const useRequest = () => { }, } ); + + const sort = useCallback(() => { + setRequestList((prev) => { + const sortedRequests = [...prev]; + if (orderby === "DISTANCE") { + sortedRequests.sort((a, b) => + order ? a.distance - b.distance : b.distance - a.distance + ); + } + if (orderby === "TIME") { + sortedRequests.sort((a, b) => + order + ? new Date(a.request_date).getTime() - + new Date(b.request_date).getTime() + : new Date(b.request_date).getTime() - + new Date(a.request_date).getTime() + ); + } + return sortedRequests; + }); + }, [orderby, order, setRequestList]); + useEffect(() => { + sort(); + }, [sort]); + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(() => { if (data) { const { result } = data; - const { request_list, count } = result; + const { request_list } = result; setRequestList((prev) => { - if (!request_list.length) return prev; - if ( - prev.find( - (item) => - item.emergency_center_id === request_list[0].emergency_center_id - ) - ) - return prev; - return [...prev, ...request_list]; - }); - setPageLimit({ - total_count: count, - total_page: Math.ceil(count / query.limit), + if (prev.length === 0) { + return request_list; + } + if (prev.length !== request_list.length) { + return request_list; + } + return prev; }); } - }, [data, setRequestList, setPageLimit, query?.limit]); + }, [data, setRequestList]); - return { request_list, isLoading }; + return { + rejectedRequests, + requestedRequests, + requests, + setRequestList, + isLoading, + order, + orderby, + sort, + ...rest, + }; }; diff --git a/src/hooks/socket/useRequestRealtime.tsx b/src/hooks/socket/useRequestRealtime.tsx new file mode 100644 index 0000000..cdbd7c2 --- /dev/null +++ b/src/hooks/socket/useRequestRealtime.tsx @@ -0,0 +1,46 @@ +import { useRequestStore } from "@/store/request.store"; +import { RequestInfo } from "@/types/model/request"; +import { useEffect } from "react"; +import io from "socket.io-client"; +import { usePatient } from "../api/usePatient"; + +export const useRequestSocket = () => { + const socket = io(`/request`); + const { patient } = usePatient(); + const { setRequestList } = useRequestStore(); + useEffect(() => { + socket.on("connect", () => { + console.log("socket connected"); + }); + // 응답 관련 + socket.on("ems-request-er-response", (data) => { + console.log("request", data); + }); + + // 응답 업데이트관련 + socket.on("ems-request-er-update", (data) => { + console.log("request", data); + }); + socket.on("ems-request-er", (data: RequestInfo) => { + console.log("request", data); + // setRequestList((prev) => { + // const index = prev.findIndex( + // (request) => request.emergency_center_id === data.emergency_center_id + // ); + // if (orderBy && index === -1) { + // return [...prev, data]; + // } + // if (!orderBy && index === -1) { + // return [...prev, data]; + // } + + // return [...prev]; + // }); + }); + socket.on("disconnect", () => {}); + return () => { + socket.disconnect(); + }; + }, [socket, patient, setRequestList]); + return {}; +}; diff --git a/src/store/request.store.tsx b/src/store/request.store.tsx index ec17f6d..5afd9f5 100644 --- a/src/store/request.store.tsx +++ b/src/store/request.store.tsx @@ -1,5 +1,4 @@ -import { arr_diff } from "@/lib/utils"; -import { RequestInfo, RequestStatus } from "@/types/model/request"; +import { RequestInfo } from "@/types/model/request"; import { create } from "zustand"; interface Query { @@ -24,91 +23,54 @@ interface Query { } interface RequestStore { - request_list: RequestInfo[]; + requests: RequestInfo[]; + rejectedRequests: RequestInfo[]; + requestedRequests: RequestInfo[]; + pageStatus: "ALL" | "REQUESTED" | "REJECTED"; + setPageStatus: (status: "ALL" | "REQUESTED" | "REJECTED") => void; query: Query; - pageLimit: { - total_count: number; - total_page: number; - }; - setQueryStatus: (request_status?: RequestStatus[]) => void; - setQueryPage: (page: number) => void; - setQueryLimit: (limit: number) => void; setRequestList: ( - request_list: RequestInfo[] | ((prevState: RequestInfo[]) => RequestInfo[]) + requests: RequestInfo[] | ((prevState: RequestInfo[]) => RequestInfo[]) ) => void; - setPageLimit: (pageLimit: { - total_count: number; - total_page: number; - }) => void; + // 정렬은 시간순과 거리순으로나누어짐 그에따른 상태는 + // + order: boolean; + setOrder: (orderBy: boolean) => void; + orderby: "TIME" | "DISTANCE"; + setOrderBy: (orderBy: "TIME" | "DISTANCE") => void; + // sort: () => void; } export const useRequestStore = create((set) => ({ - request_list: [], + requests: [], + rejectedRequests: [], + requestedRequests: [], + pageStatus: "ALL", + setPageStatus: (status) => set((state) => ({ ...state, pageStatus: status })), query: { - limit: 20, + limit: 1000, page: 1, + request_status: ["REQUESTED", "REJECTED", "VIEWED", "ACCEPTED"], }, - pageLimit: { - total_count: 0, - total_page: 0, - }, - setQueryStatus: (request_status?: RequestStatus[]) => - set((state) => { - return { - ...state, - query: { - ...state.query, - request_status, - page: 1, - }, - request_list: arr_diff( - state.query?.request_status || [], - request_status || [] - ) - ? [] - : state.request_list, - }; - }), - - setRequestList: ( - request_list: RequestInfo[] | ((prevState: RequestInfo[]) => RequestInfo[]) - ) => - set((state) => { - return { - ...state, - request_list: - typeof request_list === "function" - ? request_list(state.request_list) - : request_list, - }; - }), - - setQueryPage: (page: number) => - set((state) => { - return { - ...state, - query: { - ...state.query, - page, - }, - }; - }), - setQueryLimit: (limit: number) => - set((state) => { - return { - ...state, - query: { - ...state.query, - limit, - }, - request_list: [], - }; - }), - setPageLimit: (pageLimit: { total_count: number; total_page: number }) => + setRequestList: (requests) => set((state) => { + const newRequests = + typeof requests === "function" ? requests(state.requests) : requests; return { ...state, - pageLimit, + requests: newRequests, + rejectedRequests: newRequests.filter( + (request) => request.request_status === "REJECTED" + ), + requestedRequests: newRequests.filter( + (request) => + request.request_status === "REQUESTED" || + request.request_status === "VIEWED" + ), }; }), + order: true, + setOrder: (orderBy) => set((state) => ({ ...state, order: orderBy })), + orderby: "DISTANCE", + setOrderBy: (orderBy) => set((state) => ({ ...state, orderby: orderBy })), })); diff --git a/src/types/api/api.d.ts b/src/types/api/api.d.ts index 193aa17..4fca42b 100644 --- a/src/types/api/api.d.ts +++ b/src/types/api/api.d.ts @@ -1343,7 +1343,7 @@ export interface paths { patch: { requestBody: { content: { - "application/json": components["schemas"]["__type.o24"][]; + "application/json": components["schemas"]["__type.o25"][]; }; }; responses: { @@ -1432,7 +1432,7 @@ export interface paths { patch: { requestBody: { content: { - "application/json": components["schemas"]["__type.o27"][]; + "application/json": components["schemas"]["__type.o28"][]; }; }; responses: { @@ -2396,6 +2396,7 @@ export interface components { */ patient_emergency_cause: "TRAFFIC_ACCIDENT" | "FIRE" | "CRIMINAL" | "DISASTER" | "DISEASE" | "OTHER"; patient_guardian?: components["schemas"]["EmsPatientRequest.PatientGuardianDTO"]; + rapid_evaluation?: components["schemas"]["__type.o3"]; }; "EmsPatientRequest.PatientGuardianDTO": { /** @@ -2420,6 +2421,14 @@ export interface components { */ guardian_relation: "OTHER" | "PARENT" | "SPOUSE" | "CHILD" | "SIBLING" | "FRIEND"; }; + "__type.o3": { + /** @enum {string} */ + trauma: "TRUE" | "FALSE"; + /** @enum {string} */ + clear: "TRUE" | "FALSE"; + /** @enum {string} */ + conscious: "TRUE" | "FALSE"; + }; "EMS_PATIENT_ERROR.INCHARGED_PATIENT_ALREADY_EXIST": { /** @enum {string} */ message: "INCHARGED_PATIENT_ALREADY_EXIST"; @@ -2516,12 +2525,12 @@ export interface components { message: string; }; "EmsPatientResponse.GetPatientDetail": { - guardian: components["schemas"]["__type.o3.Nullable"]; - abcde: components["schemas"]["__type.o4"][]; - dcap_btls: components["schemas"]["__type.o5"][]; - vs: components["schemas"]["__type.o6"][]; - sample: components["schemas"]["__type.o7"][]; - opqrst: components["schemas"]["__type.o8"][]; + guardian: components["schemas"]["__type.o4.Nullable"]; + abcde: components["schemas"]["__type.o5"][]; + dcap_btls: components["schemas"]["__type.o6"][]; + vs: components["schemas"]["__type.o7"][]; + sample: components["schemas"]["__type.o8"][]; + opqrst: components["schemas"]["__type.o9"][]; patient_id: string; /** @description 익명으로 기본값 */ patient_name: string; @@ -2553,7 +2562,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o3.Nullable": { + "__type.o4.Nullable": { guardian_id: string; guardian_name: string; guardian_phone: string; @@ -2570,7 +2579,7 @@ export interface components { } | null; /** @enum {string} */ ems_GuardianRelation: "OTHER" | "PARENT" | "SPOUSE" | "CHILD" | "SIBLING" | "FRIEND"; - "__type.o4": { + "__type.o5": { patient_id: string; airway_status: components["schemas"]["ems_AirwayStatus"]; /** @description 호흡수 //단위 횟수/분 */ @@ -2599,7 +2608,7 @@ export interface components { ems_BreathingQuality: "NORMAL" | "SHALLOW" | "DEEP" | "LABORED" | "IRREGULAR" | "RAPID" | "SLOW" | "AGONAL"; /** @enum {string} */ ems_DisabilityAVPU: "ALERT" | "VERBAL_STIMULI" | "PAIN_STIMULI" | "UNRESPONSIVE"; - "__type.o5": { + "__type.o6": { patient_id: string; affected_area: components["schemas"]["ems_AffectedArea"]; deformity: string; @@ -2621,7 +2630,7 @@ export interface components { }; /** @enum {string} */ ems_AffectedArea: "UNKNOWN" | "HEAD" | "NECK" | "CHEST" | "ABDOMEN" | "LEFT_ARM" | "RIGHT_ARM" | "LEFT_LEG" | "RIGHT_LEG" | "BACK" | "PELVIS"; - "__type.o6": { + "__type.o7": { patient_id: string; /** @description 심박수 맥박 //단위 횟수/분 */ heart_rate: number; @@ -2642,7 +2651,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o7": { + "__type.o8": { patient_id: string; /** @description 증상 및 증후 */ signs_symptoms: string; @@ -2668,7 +2677,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o8": { + "__type.o9": { patient_id: string; /** @description 발병상황 - 증상이 시작되었을 때 와 무엇을 하고있었고, 통증은 언제부터 시작됬는지 */ onset: string; @@ -2741,7 +2750,7 @@ export interface components { http_status_code: 403; }; ResponseDTO_lt___type_gt_: { - result: components["schemas"]["__type.o4"]; + result: components["schemas"]["__type.o5"]; /** @enum {boolean} */ is_success: true; request_to_response?: number; @@ -2796,7 +2805,7 @@ export interface components { swelling: string; }; "ResponseDTO_lt___type_gt_.o1": { - result: components["schemas"]["__type.o5"]; + result: components["schemas"]["__type.o6"]; /** @enum {boolean} */ is_success: true; request_to_response?: number; @@ -2830,7 +2839,7 @@ export interface components { temperature: number; }; "ResponseDTO_lt___type_gt_.o2": { - result: components["schemas"]["__type.o6"]; + result: components["schemas"]["__type.o7"]; /** @enum {boolean} */ is_success: true; request_to_response?: number; @@ -2870,7 +2879,7 @@ export interface components { events_leading_to_illness: string; }; "ResponseDTO_lt___type_gt_.o3": { - result: components["schemas"]["__type.o7"]; + result: components["schemas"]["__type.o8"]; /** @enum {boolean} */ is_success: true; request_to_response?: number; @@ -2911,7 +2920,7 @@ export interface components { time: string; }; "ResponseDTO_lt___type_gt_.o4": { - result: components["schemas"]["__type.o8"]; + result: components["schemas"]["__type.o9"]; /** @enum {boolean} */ is_success: true; request_to_response?: number; @@ -2939,7 +2948,7 @@ export interface components { message: string; }; "_blt__space_doctor_specializations:_space__blt__space_doctor_specialization_id:_space_string;_space_doctor_specialization_name:_space_string;_space_department_id:_space_number;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt__alt__agt_;_space__bgt__space__and__space__blt__space_department_id:_space_number;_space_department_name:_space_string;_space_parent_department_id:_space_number_space__or__space_null;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt_": { - doctor_specializations: components["schemas"]["__type.o9"][]; + doctor_specializations: components["schemas"]["__type.o10"][]; department_id: number; department_name: string; parent_department_id: number | null; @@ -2952,7 +2961,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o9": { + "__type.o10": { doctor_specialization_id: string; doctor_specialization_name: string; department_id: number; @@ -2995,12 +3004,12 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - parent_department?: components["schemas"]["__type.o10.Nullable"]; - sub_departments?: components["schemas"]["__type.o10"][]; - doctor_specializations?: components["schemas"]["__type.o9"][]; + parent_department?: components["schemas"]["__type.o11.Nullable"]; + sub_departments?: components["schemas"]["__type.o11"][]; + doctor_specializations?: components["schemas"]["__type.o10"][]; hospital_departments?: components["schemas"]["_blt__space_hospital_id:_space_string;_space_department_id:_space_number;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt__space__and__space__blt__space_hospital?:_space__blt__space_hospital_id:_space_string;_space_hospital_name:_space_string;_space_hospital_address:_space_string;_space_hospital_type:_space_er_MedicalFacilityType;_space_hospital_phone:_space_string_space__or__space_null;_space_hospital_city:_space_string;_space_hospital_district:_space_string;_space_latitude:_space_number_space__or__space_null;_space_longitude:_space_number_space__or__space_null;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt__space__or__space_undefined;_space__bgt_"][]; }; - "__type.o10.Nullable": ({ + "__type.o11.Nullable": ({ department_id: number; department_name: string; parent_department_id: number | null; @@ -3013,7 +3022,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }) | null; - "__type.o10": { + "__type.o11": { department_id: number; department_name: string; parent_department_id: number | null; @@ -3037,9 +3046,9 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - hospital?: components["schemas"]["__type.o11"]; + hospital?: components["schemas"]["__type.o12"]; }; - "__type.o11": { + "__type.o12": { hospital_id: string; hospital_name: string; hospital_address: string; @@ -3079,21 +3088,21 @@ export interface components { request_to_response?: number; message: string; }; - "ErDepartment.GetHospitalDepartmentList": components["schemas"]["__type.o12"][]; - "__type.o12": { + "ErDepartment.GetHospitalDepartmentList": components["schemas"]["__type.o13"][]; + "__type.o13": { department_id: number; status: components["schemas"]["Status"]; - department: components["schemas"]["__type.o13"]; + department: components["schemas"]["__type.o14"]; }; - "__type.o13": { + "__type.o14": { department_id: number; department_name: string; parent_department_id: number | null; }; "ErDepartmentRequest.UpdateHospitalDepartmentDto": { - update_department_list: components["schemas"]["__type.o14"][]; + update_department_list: components["schemas"]["__type.o15"][]; }; - "__type.o14": { + "__type.o15": { /** * 진료과 id * @description 변경할 진료과 id @@ -3225,7 +3234,7 @@ export interface components { message: string; }; "ErEmergencyCenter.GetEmergencyRoomByIdReturn": { - emergency_room_beds: components["schemas"]["__type.o15"][]; + emergency_room_beds: components["schemas"]["__type.o16"][]; emergency_room_id: string; emergency_center_id: string; emergency_room_type: components["schemas"]["er_EmergencyRoomType"]; @@ -3236,8 +3245,8 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o15": { - emergency_room_bed_patient: components["schemas"]["__type.o16.Nullable"]; + "__type.o16": { + emergency_room_bed_patient: components["schemas"]["__type.o17.Nullable"]; emergency_room_id: string; emergency_room_bed_num: number; emergency_room_bed_status: components["schemas"]["er_EmergencyRoomBedStatus"]; @@ -3247,8 +3256,8 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o16.Nullable": { - patient: components["schemas"]["__type.o17"]; + "__type.o17.Nullable": { + patient: components["schemas"]["__type.o18"]; emergency_room_id: string; emergency_room_bed_num: number; emergency_room_bed_status: components["schemas"]["er_EmergencyRoomBedStatus"]; @@ -3261,7 +3270,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; } | null; - "__type.o17": { + "__type.o18": { patient_name: string; patient_id: string; patient_birth: string; @@ -3349,7 +3358,7 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - department: components["schemas"]["__type.o10"]; + department: components["schemas"]["__type.o11"]; }; "_blt__space_hospital_id:_space_string;_space_medical_equipment_id:_space_number;_space_medical_equipment_count:_space_number;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt__space__and__space__blt__space_medical_equipment:_space__blt__space_medical_equipment_id:_space_number;_space_medical_equipment_name:_space_string;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt_;_space__bgt_": { hospital_id: string; @@ -3363,9 +3372,9 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - medical_equipment: components["schemas"]["__type.o18"]; + medical_equipment: components["schemas"]["__type.o19"]; }; - "__type.o18": { + "__type.o19": { medical_equipment_id: number; medical_equipment_name: string; /** @@ -3388,9 +3397,9 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - servere_illness: components["schemas"]["__type.o19"]; + servere_illness: components["schemas"]["__type.o20"]; }; - "__type.o19": { + "__type.o20": { servere_illness_id: string; servere_illness_name: string; /** @@ -3415,10 +3424,10 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - emergency_room_beds: components["schemas"]["__type.o20"][]; - _count: components["schemas"]["__type.o21"]; + emergency_room_beds: components["schemas"]["__type.o21"][]; + _count: components["schemas"]["__type.o22"]; }; - "__type.o20": { + "__type.o21": { emergency_room_id: string; emergency_room_bed_num: number; emergency_room_bed_status: components["schemas"]["er_EmergencyRoomBedStatus"]; @@ -3431,7 +3440,7 @@ export interface components { updated_at: string; status: components["schemas"]["Status"]; }; - "__type.o21": { + "__type.o22": { emergency_room_beds: number; }; "ErEmergencyCenterRequest.AssignPatientToBedDto": { @@ -3625,7 +3634,7 @@ export interface components { hospital_id: string; employee_doctor_specializations?: components["schemas"]["_blt__space_employee_id:_space_string;_space_doctor_specialization_id:_space_string;_space_created_at:_space_Date;_space_status:_space_Status;_space__bgt__space__and__space__blt__space_doctor_specialization:_space__blt__space_doctor_specialization_id:_space_string;_space_doctor_specialization_name:_space_string;_space_department_id:_space_number;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt_;_space__bgt_"][]; employee_nurse_specializations?: components["schemas"]["_blt__space_employee_id:_space_string;_space_nurse_specialization_id:_space_string;_space_created_at:_space_Date;_space_status:_space_Status;_space__bgt__space__and__space__blt__space_nurse_specialization:_space__blt__space_nurse_specialization_id:_space_string;_space_nurse_specialization_name:_space_string;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt_;_space__bgt_"][]; - department?: components["schemas"]["__type.o10.Nullable"]; + department?: components["schemas"]["__type.o11.Nullable"]; }; /** @enum {string} */ er_EmployeeRole: "ADMIN" | "SPECIALIST" | "RESIDENT" | "NURSE" | "EMT" | "RECEPTIONIST"; @@ -3638,7 +3647,7 @@ export interface components { */ created_at: string; status: components["schemas"]["Status"]; - doctor_specialization: components["schemas"]["__type.o9"]; + doctor_specialization: components["schemas"]["__type.o10"]; }; "_blt__space_employee_id:_space_string;_space_nurse_specialization_id:_space_string;_space_created_at:_space_Date;_space_status:_space_Status;_space__bgt__space__and__space__blt__space_nurse_specialization:_space__blt__space_nurse_specialization_id:_space_string;_space_nurse_specialization_name:_space_string;_space_created_at:_space_Date;_space_updated_at:_space_Date;_space_status:_space_Status;_space__bgt_;_space__bgt_": { employee_id: string; @@ -3649,9 +3658,9 @@ export interface components { */ created_at: string; status: components["schemas"]["Status"]; - nurse_specialization: components["schemas"]["__type.o22"]; + nurse_specialization: components["schemas"]["__type.o23"]; }; - "__type.o22": { + "__type.o23": { nurse_specialization_id: string; nurse_specialization_name: string; /** @@ -3670,7 +3679,7 @@ export interface components { request_to_response?: number; message: string; }; - "ErEmployeeResponse.GetNurseSpecilizationList": components["schemas"]["__type.o22"][]; + "ErEmployeeResponse.GetNurseSpecilizationList": components["schemas"]["__type.o23"][]; "Try_lt_ErEquipment.GetEquipmentStatusByIdReturn_gt_": { result: components["schemas"]["ErEquipment.GetEquipmentStatusByIdReturn"]; /** @enum {boolean} */ @@ -3678,8 +3687,8 @@ export interface components { request_to_response?: number; message: string; }; - "ErEquipment.GetEquipmentStatusByIdReturn": components["schemas"]["__type.o23"][] | components["schemas"]["ER_EQUIPMENT_ERROR.HOSPITAL_INVALID"]; - "__type.o23": { + "ErEquipment.GetEquipmentStatusByIdReturn": components["schemas"]["__type.o24"][] | components["schemas"]["ER_EQUIPMENT_ERROR.HOSPITAL_INVALID"]; + "__type.o24": { equipment_id: number; equipment_name: string; equipment_count: number; @@ -3693,13 +3702,13 @@ export interface components { http_status_code: 400; }; Try_lt_Array_lt___type_gt__gt_: { - result: components["schemas"]["__type.o23"][]; + result: components["schemas"]["__type.o24"][]; /** @enum {boolean} */ is_success: true; request_to_response?: number; message: string; }; - "__type.o24": { + "__type.o25": { equipment_id: number; equipment_count: number; }; @@ -3718,8 +3727,8 @@ export interface components { request_to_response?: number; message: string; }; - "ErIllness.GetIllnessesReturn": components["schemas"]["__type.o25"][]; - "__type.o25": { + "ErIllness.GetIllnessesReturn": components["schemas"]["__type.o26"][]; + "__type.o26": { illness_id: string; illness_name: string; }; @@ -3734,8 +3743,8 @@ export interface components { request_to_response?: number; message: string; }; - "ErIllness.GetServableIllnessesStatusReturn": components["schemas"]["__type.o26"][] | components["schemas"]["ER_ILLNESS_ERROR.HOSPITAL_INVALID"]; - "__type.o26": { + "ErIllness.GetServableIllnessesStatusReturn": components["schemas"]["__type.o27"][] | components["schemas"]["ER_ILLNESS_ERROR.HOSPITAL_INVALID"]; + "__type.o27": { servable_illness_id: string; servable_illness_name: string; status: components["schemas"]["Status"]; @@ -3749,13 +3758,13 @@ export interface components { http_status_code: 400; }; "Try_lt_Array_lt___type_gt__gt_.o1": { - result: components["schemas"]["__type.o26"][]; + result: components["schemas"]["__type.o27"][]; /** @enum {boolean} */ is_success: true; request_to_response?: number; message: string; }; - "__type.o27": { + "__type.o28": { illness_id: string; /** @enum {string} */ illness_status: "ACTIVE" | "INACTIVE" | "DELETED"; @@ -3767,7 +3776,7 @@ export interface components { request_to_response?: number; message: string; }; - "ErIllness.UpdateServableIllnessesStatusReturn": components["schemas"]["__type.o26"][] | components["schemas"]["ER_ILLNESS_ERROR.HOSPITAL_INVALID"] | components["schemas"]["ER_ILLNESS_ERROR.ILLNESS_NOT_EXIST"]; + "ErIllness.UpdateServableIllnessesStatusReturn": components["schemas"]["__type.o27"][] | components["schemas"]["ER_ILLNESS_ERROR.HOSPITAL_INVALID"] | components["schemas"]["ER_ILLNESS_ERROR.ILLNESS_NOT_EXIST"]; "ER_ILLNESS_ERROR.ILLNESS_NOT_EXIST": { /** @enum {string} */ message: "Illness not exist: "; @@ -3808,7 +3817,7 @@ export interface components { * @description 환자의 주소 */ patient_address: string; - guardian?: components["schemas"]["__type.o28"]; + guardian?: components["schemas"]["__type.o29"]; /** * 환자의 담당 의사 고유 아이디 * @description 환자의 담당 의사 고유 아이디 @@ -3820,7 +3829,7 @@ export interface components { */ nurse_id: string; }; - "__type.o28": { + "__type.o29": { /** * 보호자의 이름 * @description 보호자의 이름 @@ -3874,9 +3883,9 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - guardian: components["schemas"]["__type.o29.Nullable"]; + guardian: components["schemas"]["__type.o30.Nullable"]; }; - "__type.o29.Nullable": { + "__type.o30.Nullable": { guardian_id: string; guardian_name: string; guardian_phone: string; @@ -3936,10 +3945,10 @@ export interface components { message: string; }; "ReqEmsToErResponse.createEmsToErRequest": { - target_emergency_center_list: components["schemas"]["__type.o30"][]; - patient: components["schemas"]["__type.o31"]; + target_emergency_center_list: components["schemas"]["__type.o31"][]; + patient: components["schemas"]["__type.o32"]; }; - "__type.o30": { + "__type.o31": { patient_id: string; emergency_center_id: string; request_status: components["schemas"]["RequestStatus"]; @@ -3964,7 +3973,7 @@ export interface components { }; /** @enum {string} */ RequestStatus: "REQUESTED" | "ACCEPTED" | "CANCELED" | "COMPLETED" | "VIEWED" | "REJECTED"; - "__type.o31": { + "__type.o32": { patient_id: string; /** @description 익명으로 기본값 */ patient_name: string; @@ -4036,7 +4045,7 @@ export interface components { /** Format: date-time */ updated_at: string; status: components["schemas"]["Status"]; - patient: components["schemas"]["__type.o31"]; + patient: components["schemas"]["__type.o32"]; }; "ReqEmsToErRequest.RespondEmsToErRequestDto": { /** @enum {string} */ From 12f87fdb3e3fe810b2aab7d47e68efc91727bbc2 Mon Sep 17 00:00:00 2001 From: Denovo Date: Thu, 23 Nov 2023 22:51:15 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=EC=9D=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=90=9C=20=EB=A1=9C=EC=A7=81=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20useAuth=20accessToken=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.js | 21 +++++---- .../container/RequestContainer/index.tsx | 5 +++ .../container/RequestInfoContainer/index.tsx | 2 + .../OpqrstEvaluation/OpqrstEvaluationForm.tsx | 4 +- .../SampleEvaluation/SampleEvaluationForm.tsx | 4 +- .../VsEvaluation/VsEvaluationForm.tsx | 4 +- .../module/My/ChangePasswordForm.tsx | 4 +- .../module/PatientInfo/GuardianInfoForm.tsx | 3 ++ src/components/module/RequestInfo/Table.tsx | 8 ++++ src/hooks/api/api.ts | 44 +++++++++++++++---- src/hooks/api/useEmergencyCenterList.tsx | 22 +++++++++- src/hooks/api/usePatient.ts | 16 ++++--- src/hooks/socket/useRequestRealtime.tsx | 31 ++++--------- src/lib/api.ts | 16 ++++--- src/lib/utils/IO.ts | 13 ++++++ src/providers/AuthProvider.tsx | 29 +++++++----- src/types/api/auth.ts | 1 + 17 files changed, 158 insertions(+), 69 deletions(-) create mode 100644 src/lib/utils/IO.ts diff --git a/next.config.js b/next.config.js index e33f20c..925a306 100644 --- a/next.config.js +++ b/next.config.js @@ -3,7 +3,9 @@ const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; const protocol = process.env.NEXT_PUBLIC_PROTOCOL; const apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX; +const socketSubdomain = process.env.NEXT_PUBLIC_SOCKET_SUBDOMAIN; console.log(`${protocol}://api.${domain}/${apiPrefix}/:path*`); +console.log(`${protocol}://${socketSubdomain}.${domain}/socket.io/:path*/`); const nextConfig = { reactStrictMode: true, experimental: { instrumentationHook: true }, @@ -22,27 +24,24 @@ const nextConfig = { { source: "/socket.io/:path*", destination: domain - ? `${protocol}://socket.${domain}/socket.io/:path*/` - : "http://localhost:8001/socket.io/:path*", + ? `${protocol}://${socketSubdomain}.${domain}/socket.io/:path*/` + : "http://localhost:8001/socket.io/:path*/", }, ]; }, - webpack: (config) => { + webpack: (config, { webpack }) => { config.externals.push({ "utf-8-validate": "commonjs utf-8-validate", bufferutil: "commonjs bufferutil", "supports-color": "commonjs supports-color", }); + config.plugins.push( + new webpack.IgnorePlugin({ + resourceRegExp: /import2/, // adjust the module name + }) + ); return config; }, - // webpack: (config, { webpack }) => { - // config.plugins.push( - // new webpack.IgnorePlugin({ - // resourceRegExp: /import2/, // adjust the module name - // }) - // ); - // return config; - // }, }; module.exports = nextConfig; diff --git a/src/components/container/RequestContainer/index.tsx b/src/components/container/RequestContainer/index.tsx index 9345904..65d0fee 100644 --- a/src/components/container/RequestContainer/index.tsx +++ b/src/components/container/RequestContainer/index.tsx @@ -2,6 +2,7 @@ import { Tag } from "@/components/elements/Tag"; import { ProgressTracker } from "@/components/module/common/ProgressTracker"; import { useToast } from "@/components/ui/use-toast"; +import { useAuth } from "@/providers/AuthProvider"; function coordinateOnCircle( radius: number, angleDegrees: number @@ -30,6 +31,7 @@ function coordinateOnCircle( * */ export const RequestContainer = () => { + const { accessToken } = useAuth(); const numberOfDots = 12; const radius = 8.2; // Half of 16.4rem const adjustedRadius = radius; // Adjust for dot size @@ -37,6 +39,9 @@ export const RequestContainer = () => { const requestOnClick = () => { fetch("/api/requests/ems-to-er", { method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + }, }) .then((res) => res.json()) .then((res: { is_success: boolean; message: string }) => { diff --git a/src/components/container/RequestInfoContainer/index.tsx b/src/components/container/RequestInfoContainer/index.tsx index dcf0032..1a996f1 100644 --- a/src/components/container/RequestInfoContainer/index.tsx +++ b/src/components/container/RequestInfoContainer/index.tsx @@ -3,6 +3,8 @@ import { RequestInfoPageHeader } from "@/components/module/RequestInfo/Header"; import { RequestTable } from "@/components/module/RequestInfo/Table"; export const RequestInfoContainer = () => { + // const { setRequestList, sort, ...rest } = useRequestSocket(); + // console.log(rest.socket); return (
diff --git a/src/components/module/Evaluation/OpqrstEvaluation/OpqrstEvaluationForm.tsx b/src/components/module/Evaluation/OpqrstEvaluation/OpqrstEvaluationForm.tsx index 74a1b86..e4e283b 100644 --- a/src/components/module/Evaluation/OpqrstEvaluation/OpqrstEvaluationForm.tsx +++ b/src/components/module/Evaluation/OpqrstEvaluation/OpqrstEvaluationForm.tsx @@ -3,6 +3,7 @@ import { Form, FormField } from "@/components/ui/form"; import { useToast } from "@/components/ui/use-toast"; import { usePatient } from "@/hooks/api/usePatient"; import { useEvaluationStep } from "@/hooks/useEvaluationStep"; +import { useAuth } from "@/providers/AuthProvider"; import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; @@ -99,7 +100,7 @@ export const OpqrstEvaluationForm = ({ formId }: OpqrstEvaluationFormProps) => { const { toast } = useToast(); const { patient } = usePatient(); const router = useRouter(); - + const { accessToken } = useAuth(); const { nextPage, steps } = useEvaluationStep(); const form = useForm>({ @@ -159,6 +160,7 @@ export const OpqrstEvaluationForm = ({ formId }: OpqrstEvaluationFormProps) => { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, }, body, }) diff --git a/src/components/module/Evaluation/SampleEvaluation/SampleEvaluationForm.tsx b/src/components/module/Evaluation/SampleEvaluation/SampleEvaluationForm.tsx index 8fee4cc..895b3d5 100644 --- a/src/components/module/Evaluation/SampleEvaluation/SampleEvaluationForm.tsx +++ b/src/components/module/Evaluation/SampleEvaluation/SampleEvaluationForm.tsx @@ -3,6 +3,7 @@ import { Form } from "@/components/ui/form"; import { useToast } from "@/components/ui/use-toast"; import { usePatient } from "@/hooks/api/usePatient"; import { useEvaluationStep } from "@/hooks/useEvaluationStep"; +import { useAuth } from "@/providers/AuthProvider"; import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; @@ -89,7 +90,7 @@ export const SampleEvaluationForm = ({ formId }: OpqrstEvaluationFormProps) => { const { nextPage, steps } = useEvaluationStep(); const router = useRouter(); const { patient } = usePatient(); - + const { accessToken } = useAuth(); const form = useForm>({ resolver: zodResolver(opqrstEvaluationSchema), defaultValues: { @@ -163,6 +164,7 @@ export const SampleEvaluationForm = ({ formId }: OpqrstEvaluationFormProps) => { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, }, body, }) diff --git a/src/components/module/Evaluation/VsEvaluation/VsEvaluationForm.tsx b/src/components/module/Evaluation/VsEvaluation/VsEvaluationForm.tsx index cd5136f..c90838c 100644 --- a/src/components/module/Evaluation/VsEvaluation/VsEvaluationForm.tsx +++ b/src/components/module/Evaluation/VsEvaluation/VsEvaluationForm.tsx @@ -3,6 +3,7 @@ import { useToast } from "@/components/ui/use-toast"; import { usePatient } from "@/hooks/api/usePatient"; import { useEvaluationStep } from "@/hooks/useEvaluationStep"; +import { useAuth } from "@/providers/AuthProvider"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { SymptomLabel } from "../../common/SymptomLabeledInput"; @@ -17,7 +18,7 @@ export const VsEvalutaionForm = ({ formId }: VsEvalutaionFormProps) => { const router = useRouter(); const { toast } = useToast(); const { nextPage, steps } = useEvaluationStep(); - + const { accessToken } = useAuth(); const [temperature, setTemperature] = useState("36.5"); const [heartRate, setHeartRate] = useState(80); const [respiration, setRespiration] = useState(12); @@ -63,6 +64,7 @@ export const VsEvalutaionForm = ({ formId }: VsEvalutaionFormProps) => { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, }, body, }) diff --git a/src/components/module/My/ChangePasswordForm.tsx b/src/components/module/My/ChangePasswordForm.tsx index e94d9b3..1f54d5b 100644 --- a/src/components/module/My/ChangePasswordForm.tsx +++ b/src/components/module/My/ChangePasswordForm.tsx @@ -8,6 +8,7 @@ import { FormLabel, } from "@/components/ui/form"; import { useToast } from "@/components/ui/use-toast"; +import { useAuth } from "@/providers/AuthProvider"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -34,13 +35,14 @@ export const ChangePasswordForm = ({ password: "", }, }); - + const { accessToken } = useAuth(); const onSubmitHandler = (e: React.FormEvent) => { e.preventDefault(); fetch("api/ems/employees", { method: "PATCH", headers: { "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify(form.getValues()), }) diff --git a/src/components/module/PatientInfo/GuardianInfoForm.tsx b/src/components/module/PatientInfo/GuardianInfoForm.tsx index cec65b0..21c1582 100644 --- a/src/components/module/PatientInfo/GuardianInfoForm.tsx +++ b/src/components/module/PatientInfo/GuardianInfoForm.tsx @@ -9,6 +9,7 @@ import { } from "@/components/ui/form"; import { useToast } from "@/components/ui/use-toast"; import { useEvaluationStep } from "@/hooks/useEvaluationStep"; +import { useAuth } from "@/providers/AuthProvider"; import { useEveluationStepStore } from "@/store/evaluationStep.store"; import { usePatientStore } from "@/store/patient.store"; import { @@ -39,6 +40,7 @@ export const GuardianInfoForm = () => { const { nextPage } = useEvaluationStep(); const { rapidEvaluation, check } = useEveluationStepStore(); const { patient, setGuardian, setPatient } = usePatientStore(); + const { accessToken } = useAuth(); const form = useForm>({ resolver: zodResolver(guardianSchema), defaultValues: { @@ -107,6 +109,7 @@ export const GuardianInfoForm = () => { method: "POST", headers: { "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify(body), }) diff --git a/src/components/module/RequestInfo/Table.tsx b/src/components/module/RequestInfo/Table.tsx index ef1d45b..88e813f 100644 --- a/src/components/module/RequestInfo/Table.tsx +++ b/src/components/module/RequestInfo/Table.tsx @@ -1,3 +1,4 @@ +"use client"; import { Tag } from "@/components/elements/Tag"; import { useRequest } from "@/hooks/api/useRequest"; import { useRequestStore } from "@/store/request.store"; @@ -7,6 +8,12 @@ import { useEffect, useRef, useState } from "react"; export const RequestTable = () => { const ref = useRef(null); + const [componentMounted, setComponentMounted] = useState(false); + + // 컴포넌트가 마운트되면 useRequestSocket 훅을 호출 + useEffect(() => { + setComponentMounted(true); + }, []); const { requests, rejectedRequests, requestedRequests } = useRequest(); const { pageStatus } = useRequestStore(); const [request_list, setRequestList] = useState([]); @@ -24,6 +31,7 @@ export const RequestTable = () => { ref.current.scrollTo(0, 0); } }, [pageStatus, requests, rejectedRequests, requestedRequests, ref]); + if (!componentMounted) return null; return (
diff --git a/src/hooks/api/api.ts b/src/hooks/api/api.ts index b0d2a4d..7faec69 100644 --- a/src/hooks/api/api.ts +++ b/src/hooks/api/api.ts @@ -1,3 +1,4 @@ +import { useAuth } from "@/providers/AuthProvider"; import { Init, MethodPaths, Response } from "@/types/api"; import { ApiResponse, @@ -41,17 +42,27 @@ export function useGetApi

>( const [isLoading, setIsLoading] = useState(false); const [data, setData] = useState>>(); const loader = useLoading(); - + const { accessToken } = useAuth(); const { data: data_, error, isLoading: isLoadingSWR, mutate, - } = useSWR({ url, init }, async (obj: { url: P; init: Init<"get", P> }) => { - const { data, error } = await client.GET(obj.url, ...init); - if (error) throw new Error(error as string); - return data; - }); + } = useSWR( + { + url, + init, + }, + async (obj: { url: P; init: Init<"get", P> }) => { + const { data, error } = await client({ + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }).GET(obj.url, ...init); + if (error) throw new Error(error as string); + return data; + } + ); useEffect(() => { if (!isLoadingSWR && data_) @@ -72,12 +83,17 @@ export function usePostApi

>( const [isLoading, setIsLoading] = useState(false); const [data, setData] = useState>>(); + const { accessToken } = useAuth(); const loader = useLoading(); const mutate = async (...body: Init<"post", P>) => { if (options?.useLoader) loader.on(); setIsLoading(true); - const { data: data_, error } = await client.POST(url, ...body); + const { data: data_, error } = await client({ + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }).POST(url, ...body); const data = data_ as ApiResponse; //모든 response는 Success | Fail정보를 따름 const result = commonLogic(data, error); @@ -97,13 +113,18 @@ export function usePatchApi

>( if (options?.useLoader === undefined) options = { useLoader: true }; const [isLoading, setIsLoading] = useState(false); + const { accessToken } = useAuth(); const [data, setData] = useState>>(); const loader = useLoading(); const mutate = async (...body: Init<"patch", P>) => { if (options?.useLoader) loader.on(); setIsLoading(true); - const { data: data_, error } = await client.PATCH(url, ...body); + const { data: data_, error } = await client({ + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }).PATCH(url, ...body); const data = data_ as ApiResponse; //모든 response는 Success | Fail정보를 따름 const result = commonLogic(data, error); @@ -123,13 +144,18 @@ export function usePutApi

>( if (options?.useLoader === undefined) options = { useLoader: true }; const [isLoading, setIsLoading] = useState(false); + const { accessToken } = useAuth(); const [data, setData] = useState>>(); const loader = useLoading(); const mutate = async (...body: Init<"put", P>) => { if (options?.useLoader) loader.on(); setIsLoading(true); - const { data: data_, error } = await client.PUT(url, ...body); + const { data: data_, error } = await client({ + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }).PUT(url, ...body); const data = data_ as ApiResponse; //모든 response는 Success | Fail정보를 따름 const result = commonLogic(data, error); diff --git a/src/hooks/api/useEmergencyCenterList.tsx b/src/hooks/api/useEmergencyCenterList.tsx index fee7bd6..f9119fa 100644 --- a/src/hooks/api/useEmergencyCenterList.tsx +++ b/src/hooks/api/useEmergencyCenterList.tsx @@ -1,3 +1,4 @@ +import { useAuth } from "@/providers/AuthProvider"; import { EmergencyCenter, useEmergencyCenterListStore, @@ -15,13 +16,25 @@ interface GetEmergencyCenterListResponse { message: string; } -const fetcher = (url: string) => fetch(url).then((res) => res.json()); +const fetcherWithToken = ({ + url, + accessToken, +}: { + url: string; + accessToken: string | null; +}) => + fetch(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }).then((res) => res.json()); export const useEmergencyCenterList = () => { const laction = useGeoLocation(); const { emergencyCenters, query, setPageLimit, setEmergencyCenters } = useEmergencyCenterListStore(); const latitude = laction?.[0].toString() || "0"; const longitude = laction?.[1].toString() || "0"; + const { accessToken } = useAuth(); const queryParam = new URLSearchParams(); @@ -34,10 +47,15 @@ export const useEmergencyCenterList = () => { queryParam.append("emergency_center_type", type) ); + // `/api/er/emergency-centers?${queryParam.toString()}`, const { data, error, isLoading } = useSWR( `/api/er/emergency-centers?${queryParam.toString()}`, - fetcher + (url: string) => fetcherWithToken({ url, accessToken }) ); + // const { data, error, isLoading } = useSWR( + // `/api/er/emergency-centers?${queryParam.toString()}`, + // fetcher + // ); useEffect(() => { if (data) { diff --git a/src/hooks/api/usePatient.ts b/src/hooks/api/usePatient.ts index aa73a5d..682638c 100644 --- a/src/hooks/api/usePatient.ts +++ b/src/hooks/api/usePatient.ts @@ -1,17 +1,23 @@ +import { useAuth } from "@/providers/AuthProvider"; import { usePatientStore } from "@/store/patient.store"; import { Patient } from "@/types/model/patient"; import { useEffect } from "react"; import useSWR from "swr"; +const fetcher = (url: string, accessToken: string | null) => + fetch(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + .then((res) => res.json()) + .catch((err) => console.log(err)); export const usePatient = () => { const { patient, setPatient } = usePatientStore(); - const fetcher = (url: string) => - fetch(url) - .then((res) => res.json()) - .catch((err) => console.log(err)); + const { accessToken } = useAuth(); const { data } = useSWR<{ result: { patient_list: Patient[] } }>( `/api/ems/patients?patient_status=PENDING&patient_status=REQUESTED&patient_status=ACCEPTED`, - fetcher + (url: string) => fetcher(url, accessToken) ); useEffect(() => { diff --git a/src/hooks/socket/useRequestRealtime.tsx b/src/hooks/socket/useRequestRealtime.tsx index cdbd7c2..62d3145 100644 --- a/src/hooks/socket/useRequestRealtime.tsx +++ b/src/hooks/socket/useRequestRealtime.tsx @@ -1,46 +1,33 @@ -import { useRequestStore } from "@/store/request.store"; import { RequestInfo } from "@/types/model/request"; import { useEffect } from "react"; -import io from "socket.io-client"; +import { io } from "socket.io-client"; import { usePatient } from "../api/usePatient"; +import { useRequest } from "../api/useRequest"; export const useRequestSocket = () => { - const socket = io(`/request`); + const socket = io("/request"); const { patient } = usePatient(); - const { setRequestList } = useRequestStore(); + const { setRequestList, sort, ...rest } = useRequest(); useEffect(() => { socket.on("connect", () => { console.log("socket connected"); + console.log(socket); }); // 응답 관련 + socket.on("ems-request-er-response", (data) => { console.log("request", data); }); - // 응답 업데이트관련 - socket.on("ems-request-er-update", (data) => { - console.log("request", data); - }); + socket.on("ems-request-er", (data: RequestInfo) => { console.log("request", data); - // setRequestList((prev) => { - // const index = prev.findIndex( - // (request) => request.emergency_center_id === data.emergency_center_id - // ); - // if (orderBy && index === -1) { - // return [...prev, data]; - // } - // if (!orderBy && index === -1) { - // return [...prev, data]; - // } - - // return [...prev]; - // }); + setRequestList((prev) => [...prev, data]); }); socket.on("disconnect", () => {}); return () => { socket.disconnect(); }; }, [socket, patient, setRequestList]); - return {}; + return { setRequestList, sort, socket, ...rest }; }; diff --git a/src/lib/api.ts b/src/lib/api.ts index d6fdae8..94ad475 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,5 +1,8 @@ import { paths } from "@/types/api/api"; -import createClient, { defaultQuerySerializer } from "openapi-fetch"; +import createClient, { + HeadersOptions, + defaultQuerySerializer, +} from "openapi-fetch"; export async function api( input: RequestInfo | URL, @@ -10,10 +13,11 @@ export async function api( }); } -export const client = createClient({ - baseUrl: "/api", - querySerializer: (q) => - defaultQuerySerializer(q.query) +export const client = (option?: { headers?: HeadersOptions }) => + createClient({ + baseUrl: "/api", + querySerializer: (q) => defaultQuerySerializer(q.query), + ...(option ?? {}), // new URLSearchParams( // Object.fromEntries( // Object.entries( @@ -22,4 +26,4 @@ export const client = createClient({ // ).map(([key, value]) => [key, value.toString()]) // ) // ).toString(), -}); \ No newline at end of file + }); diff --git a/src/lib/utils/IO.ts b/src/lib/utils/IO.ts new file mode 100644 index 0000000..e2f12a7 --- /dev/null +++ b/src/lib/utils/IO.ts @@ -0,0 +1,13 @@ +import { io } from "socket.io-client"; + +const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; +const protocol = process.env.NEXT_PUBLIC_PROTOCOL; +const socketSubdomain = process.env.NEXT_PUBLIC_SOCKET_SUBDOMAIN; + +// issue: next.config rewrite를 통새 소켓서버와 연결을 시도를 하려하였지만, 실패. +// 에러가 많이발생하였고, 이를 해결하기위해 IO 함수를 만들어서 사용하였습니다. +export const IO = (uri: string) => { + return io(`${protocol}://${socketSubdomain}.${domain}${uri}`, { + transports: ["websocket"], + }); +}; diff --git a/src/providers/AuthProvider.tsx b/src/providers/AuthProvider.tsx index 4a57d97..5a416b9 100644 --- a/src/providers/AuthProvider.tsx +++ b/src/providers/AuthProvider.tsx @@ -35,6 +35,7 @@ interface IAuthContext { role: "ADMIN" | "DRIVER" | "EMERGENCY_MEDICAL_TECHNICIAN" | "DISPATCHER"; employee_name: string; } | null; + accessToken: string | null; signIn: (loginParam: LoginParam) => Promise; signOut: () => void; } @@ -65,10 +66,12 @@ function useProvideAuth() { const fetcher = async (input: RequestInfo | URL, init?: RequestInit) => { const res = await api(input, init); if (!res.is_success || !res.result.is_login) return false; - return res.result.employee; + return res.result; }; const [user, setUser] = useState(null); + const [accessToken, setAccessToken] = + useState(null); const { data, error } = useSWR("/api/ems/auth", fetcher); const [status, setStatus] = useState("loading"); @@ -77,7 +80,9 @@ function useProvideAuth() { setStatus("success"); } if (data) { - setUser({ ...data }); + const { employee, access_token } = data; + setUser({ ...employee }); + setAccessToken(access_token); setStatus("success"); } if (error) { @@ -87,14 +92,15 @@ function useProvideAuth() { }, [data, status, error]); const signIn = async (loginParam: LoginParam) => { - const result = await loginApi(loginParam); - if (!result.is_success || !result.result.is_login) return false; - const user = result.result.employee; - setUser({ ...user }); + const res = await loginApi(loginParam); + if (!res.is_success || !res.result.is_login) return false; + const { employee, access_token } = res.result; + setUser({ ...employee }); + setAccessToken(access_token); return true; }; const signOut = () => { - fetch("api/ems/auth/logout", { method: "GET" }); + fetch("api/ems/auth/logout", { method: "POST" }); setUser(null); }; @@ -103,6 +109,7 @@ function useProvideAuth() { signIn, signOut, status, + accessToken, }; } const publicPageList = ["/login"]; @@ -114,7 +121,7 @@ const isDevPage = (pathname: string) => const AuthProvider = ({ children }: PropsWithChildren) => { const router = useRouter(); const pathname = usePathname(); - const { user, signIn, signOut, status } = useProvideAuth(); + const { user, signIn, signOut, status, accessToken } = useProvideAuth(); const [isLoading, setIsLoading] = useState( status ? status === "loading" : true ); @@ -149,7 +156,7 @@ const AuthProvider = ({ children }: PropsWithChildren) => { if (isPublicPage(pathname)) return ( {children} @@ -158,7 +165,9 @@ const AuthProvider = ({ children }: PropsWithChildren) => { if (!user) router.push(`/login?callbackURL=${pathname}`); return ( - + {children} ); diff --git a/src/types/api/auth.ts b/src/types/api/auth.ts index e88b67a..f82540f 100644 --- a/src/types/api/auth.ts +++ b/src/types/api/auth.ts @@ -8,6 +8,7 @@ export type LoginResponse = { id_card: string; role: "ADMIN" | "DRIVER" | "EMERGENCY_MEDICAL_TECHNICIAN" | "DISPATCHER"; }; + access_token: string; }; request_to_response: number; http_status_code: number; From 1ff80922c0c0b3c6bc7be8c50878afa9391f3591 Mon Sep 17 00:00:00 2001 From: Denovo Date: Thu, 23 Nov 2023 23:43:06 +0900 Subject: [PATCH 3/4] chore: socket, api server constant --- next.config.js | 9 --------- .../emergency-center/[emergency_center_id]/page.tsx | 8 +++----- src/constant/index.ts | 12 ++++++++++++ src/hooks/socket/useRequestRealtime.tsx | 12 +++++++----- src/lib/utils/IO.ts | 12 ++++++------ 5 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 src/constant/index.ts diff --git a/next.config.js b/next.config.js index 925a306..45ce22d 100644 --- a/next.config.js +++ b/next.config.js @@ -3,9 +3,6 @@ const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; const protocol = process.env.NEXT_PUBLIC_PROTOCOL; const apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX; -const socketSubdomain = process.env.NEXT_PUBLIC_SOCKET_SUBDOMAIN; -console.log(`${protocol}://api.${domain}/${apiPrefix}/:path*`); -console.log(`${protocol}://${socketSubdomain}.${domain}/socket.io/:path*/`); const nextConfig = { reactStrictMode: true, experimental: { instrumentationHook: true }, @@ -21,12 +18,6 @@ const nextConfig = { ? `${protocol}://api.${domain}/${apiPrefix}/:path*` : "http://localhost:8000/api/:path*", }, - { - source: "/socket.io/:path*", - destination: domain - ? `${protocol}://${socketSubdomain}.${domain}/socket.io/:path*/` - : "http://localhost:8001/socket.io/:path*/", - }, ]; }, webpack: (config, { webpack }) => { diff --git a/src/app/(main)/emergency-center/[emergency_center_id]/page.tsx b/src/app/(main)/emergency-center/[emergency_center_id]/page.tsx index 0607c21..fb6de01 100644 --- a/src/app/(main)/emergency-center/[emergency_center_id]/page.tsx +++ b/src/app/(main)/emergency-center/[emergency_center_id]/page.tsx @@ -1,5 +1,6 @@ import { TabWrapper } from "@/components/layout/TabWrapper"; import { EmergencyCenterDetailPrototype } from "@/components/prototypes/EmergencyCenter/Detail"; +import { API_SERVER } from "@/constant"; import { GetEmergencyCenterDetailResponse } from "@/types/emergencyCenter.type"; import { ChevronLeft } from "lucide-react"; import Link from "next/link"; @@ -20,14 +21,11 @@ interface GetEmergencyCenterDetailResponseFailDto { export default async function Page({ params }: { params: Params }) { const { emergency_center_id } = params; - const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; - const protocol = process.env.NEXT_PUBLIC_PROTOCOL; - const apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX; - const url = `${protocol}://api.${domain}/${apiPrefix}/`; + const data: | GetEmergencyCenterDetailResponseSuccessDto | GetEmergencyCenterDetailResponseFailDto = await fetch( - `${url}/er/emergency-centers/${emergency_center_id}`, + `${API_SERVER}/er/emergency-centers/${emergency_center_id}`, { next: { revalidate: 10, diff --git a/src/constant/index.ts b/src/constant/index.ts new file mode 100644 index 0000000..9c2f09b --- /dev/null +++ b/src/constant/index.ts @@ -0,0 +1,12 @@ +// SOCKET CONSTANTS +export const SOCKET_SERVER = process.env.NEXT_PUBLIC_SOCKET_SERVER; +export const EMS_REQUEST_ER_RESPONSE = "ems-request-er-response"; +export const EMS_REQUEST_ER = "ems-request-er"; +export const EMS_REQUEST_ER_UPDATE = "ems-request-er-update"; + +// SSR API CONSTANTS + +const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; +const protocol = process.env.NEXT_PUBLIC_PROTOCOL; +const apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX; +export const API_SERVER = `${protocol}://api.${domain}/${apiPrefix}`; diff --git a/src/hooks/socket/useRequestRealtime.tsx b/src/hooks/socket/useRequestRealtime.tsx index 62d3145..c5d18d6 100644 --- a/src/hooks/socket/useRequestRealtime.tsx +++ b/src/hooks/socket/useRequestRealtime.tsx @@ -1,11 +1,14 @@ +import { EMS_REQUEST_ER, EMS_REQUEST_ER_RESPONSE } from "@/constant"; +import { IO } from "@/lib/utils/IO"; +import { useAuth } from "@/providers/AuthProvider"; import { RequestInfo } from "@/types/model/request"; import { useEffect } from "react"; -import { io } from "socket.io-client"; import { usePatient } from "../api/usePatient"; import { useRequest } from "../api/useRequest"; export const useRequestSocket = () => { - const socket = io("/request"); + const { accessToken } = useAuth(); + const socket = IO("/request", accessToken); const { patient } = usePatient(); const { setRequestList, sort, ...rest } = useRequest(); useEffect(() => { @@ -14,13 +17,12 @@ export const useRequestSocket = () => { console.log(socket); }); // 응답 관련 - - socket.on("ems-request-er-response", (data) => { + socket.on(EMS_REQUEST_ER_RESPONSE, (data) => { console.log("request", data); }); // 응답 업데이트관련 - socket.on("ems-request-er", (data: RequestInfo) => { + socket.on(EMS_REQUEST_ER, (data: RequestInfo) => { console.log("request", data); setRequestList((prev) => [...prev, data]); }); diff --git a/src/lib/utils/IO.ts b/src/lib/utils/IO.ts index e2f12a7..1138c3f 100644 --- a/src/lib/utils/IO.ts +++ b/src/lib/utils/IO.ts @@ -1,13 +1,13 @@ +import { SOCKET_SERVER } from "@/constant"; import { io } from "socket.io-client"; -const domain = process.env.NEXT_PUBLIC_ROOT_DOMAIN; -const protocol = process.env.NEXT_PUBLIC_PROTOCOL; -const socketSubdomain = process.env.NEXT_PUBLIC_SOCKET_SUBDOMAIN; - // issue: next.config rewrite를 통새 소켓서버와 연결을 시도를 하려하였지만, 실패. // 에러가 많이발생하였고, 이를 해결하기위해 IO 함수를 만들어서 사용하였습니다. -export const IO = (uri: string) => { - return io(`${protocol}://${socketSubdomain}.${domain}${uri}`, { +export const IO = (uri: string, token: string | null) => { + return io(`${SOCKET_SERVER}${uri}`, { transports: ["websocket"], + auth: { + token: token, + }, }); }; From 703c053ad74db3cdc6793ac9d393f50a4a26f2dd Mon Sep 17 00:00:00 2001 From: Denovo Date: Fri, 24 Nov 2023 00:53:10 +0900 Subject: [PATCH 4/4] chore: lib/constant -> envValidation --- src/instrumentation.ts | 2 +- src/lib/{constants.ts => utils/envValidation.ts} | 0 src/providers/AuthProvider.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/lib/{constants.ts => utils/envValidation.ts} (100%) diff --git a/src/instrumentation.ts b/src/instrumentation.ts index e153601..de3ad2e 100644 --- a/src/instrumentation.ts +++ b/src/instrumentation.ts @@ -1,4 +1,4 @@ -import { validateEnv } from "./lib/constants"; +import { validateEnv } from "./lib/utils/envValidation"; export function register() { validateEnv(); diff --git a/src/lib/constants.ts b/src/lib/utils/envValidation.ts similarity index 100% rename from src/lib/constants.ts rename to src/lib/utils/envValidation.ts diff --git a/src/providers/AuthProvider.tsx b/src/providers/AuthProvider.tsx index 5a416b9..5e40712 100644 --- a/src/providers/AuthProvider.tsx +++ b/src/providers/AuthProvider.tsx @@ -11,7 +11,7 @@ interface IAuthProviderProps {} import Spinner from "@/components/Spinner"; import { api } from "@/lib/api"; -import { env } from "@/lib/constants"; +import { env } from "@/lib/utils/envValidation"; import { Loader2 } from "lucide-react"; import useSWR from "swr"; import { LoginResponse } from "../types/api";