From 2354d9f9d0332aa8287e37fa27ca8e80548efeb3 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Fri, 24 Nov 2023 23:13:51 +0900 Subject: [PATCH 01/10] feat: add auth slice --- .eslintrc.json | 5 ++- src/components/Auth/Auth.tsx | 77 ++++++++++++++++++++++++++++++++++ src/components/Board/Board.tsx | 8 ++-- src/redux/auth/authSlice.ts | 23 ++++++++++ src/redux/auth/loginThunk.ts | 16 +++++++ src/redux/rootReducer.ts | 13 ++++++ src/redux/store.ts | 4 +- 7 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 src/components/Auth/Auth.tsx create mode 100644 src/redux/auth/authSlice.ts create mode 100644 src/redux/auth/loginThunk.ts create mode 100644 src/redux/rootReducer.ts diff --git a/.eslintrc.json b/.eslintrc.json index 74f3319..13b7681 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -102,7 +102,10 @@ "prefer": "type-imports" } ], - "arrow-body-style": ["warn"] + "arrow-body-style": ["warn"], + "jsx-a11y/label-has-associated-control": [ 2, { + "some": [ "nesting", "id" ] + }] }, "settings": { "import/parsers": { diff --git a/src/components/Auth/Auth.tsx b/src/components/Auth/Auth.tsx new file mode 100644 index 0000000..c334daf --- /dev/null +++ b/src/components/Auth/Auth.tsx @@ -0,0 +1,77 @@ +import { type ChangeEvent, useState } from "react"; +import { useAppDispatch, useAppSelector } from "@/types"; +import { type RootState } from "@/redux/store"; +import { Board } from "@/components/Board"; +import { loginThunk } from "@/redux/auth/loginThunk"; +import style from "./Auth.module.scss"; + +export const AuthPage = () => { + const [deviceId, setDeviceId] = useState(""); + const [pin, setPin] = useState(""); + + const isLoggedIn = useAppSelector( + (state: RootState) => state.auth.isLoggedIn, + ); + + const onDeviceIdChange = (e: ChangeEvent) => { + setDeviceId(e.target.value); + }; + + const onPinChange = (e: ChangeEvent) => { + setPin(e.target.value); + }; + + const dispatch = useAppDispatch(); + + const onSubmitHandler = () => { + dispatch(loginThunk(deviceId, pin)); + setDeviceId(""); + setPin(""); + }; + + if (isLoggedIn) { + return ; + } + + return ( +
+
+

Zabo Board Login

+
+

+ Zabo Board 서비스를 이용하기 위해서는 등록된 device Id와 PIN을 + 입력하세요. +
+ Zabo Board 서비스를 등록하기 위해서는 zabo.sparcs.org 채널톡으로 + 문의해주세요. +

+
+ + +
+ +
+
+ ); +}; diff --git a/src/components/Board/Board.tsx b/src/components/Board/Board.tsx index 9dc8d45..99ffcca 100644 --- a/src/components/Board/Board.tsx +++ b/src/components/Board/Board.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import { useInterval } from "@/hooks"; -import { moveToNext, type ZaboListState } from "@/redux/zabos/zaboSlice"; +import { moveToNext } from "@/redux/zabos/zaboSlice"; import { fetchZaboThunk } from "@/redux/zabos/fetchZaboThunk"; import { useAppSelector, useAppDispatch, type ZaboJson } from "@/types"; import { Zabo } from "@/components/Zabo"; @@ -9,12 +9,14 @@ import { Background } from "@/components/Background"; import { Qr } from "@/components/Qr"; import { Logo } from "@/components/Logo"; import { TRANSITION_INTERVAL } from "@/config"; +import { type RootState } from "@/redux/store"; + import style from "./Board.module.scss"; export const Board = () => { - const zaboList = useAppSelector((state: ZaboListState) => state.zaboList); + const zaboList = useAppSelector((state: RootState) => state.zabo.zaboList); const leftOverZaboLength = useAppSelector( - (state: ZaboListState) => state.leftoverLength, + (state: RootState) => state.zabo.leftoverLength, ); const dispatch = useAppDispatch(); diff --git a/src/redux/auth/authSlice.ts b/src/redux/auth/authSlice.ts new file mode 100644 index 0000000..c747bad --- /dev/null +++ b/src/redux/auth/authSlice.ts @@ -0,0 +1,23 @@ +import { createSlice } from "@reduxjs/toolkit"; + +export interface AuthState { + isLoggedIn: boolean; +} + +const initialState: AuthState = { + isLoggedIn: false, +}; + +const authSlice = createSlice({ + name: "auth", + initialState, + reducers: { + setIsLoggedIn: (state, action) => { + state.isLoggedIn = action.payload; + }, + }, +}); + +export const { setIsLoggedIn } = authSlice.actions; + +export const authReducer = authSlice.reducer; diff --git a/src/redux/auth/loginThunk.ts b/src/redux/auth/loginThunk.ts new file mode 100644 index 0000000..ee76662 --- /dev/null +++ b/src/redux/auth/loginThunk.ts @@ -0,0 +1,16 @@ +import { type AppDispatch } from "@/redux/store"; +import { setIsLoggedIn } from "./authSlice"; + +// send device id and pin to server and get session cookie +export const loginThunk = + (deviceId: string, pin: string) => async (dispatch: AppDispatch) => { + // request zabo board login and get response + console.log(deviceId); + console.log(pin); + + // mock api request time + setTimeout(() => {}, 1000); + + // if success, then set zabo store's isLogin to true + dispatch(setIsLoggedIn(true)); + }; diff --git a/src/redux/rootReducer.ts b/src/redux/rootReducer.ts new file mode 100644 index 0000000..5f04338 --- /dev/null +++ b/src/redux/rootReducer.ts @@ -0,0 +1,13 @@ +import { combineReducers } from "@reduxjs/toolkit"; +import { type ZaboListState, zaboReducer } from "./zabos/zaboSlice"; +import { type AuthState, authReducer } from "./auth/authSlice"; + +export interface RootState { + zabo: ZaboListState; + auth: AuthState; +} + +export const rootReducer = combineReducers({ + zabo: zaboReducer, + auth: authReducer, +}); diff --git a/src/redux/store.ts b/src/redux/store.ts index 19bee35..86043f1 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,8 +1,8 @@ import { configureStore } from "@reduxjs/toolkit"; -import { zaboReducer } from "./zabos/zaboSlice"; +import { rootReducer } from "./rootReducer"; export const store = configureStore({ - reducer: zaboReducer, + reducer: rootReducer, }); // Infer the `RootState` and `AppDispatch` types from the store itself From 044a1e29f33f409276bac0170a3162f29e43fdda Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Fri, 24 Nov 2023 23:14:24 +0900 Subject: [PATCH 02/10] feat: add authentication to board --- src/App.tsx | 4 +-- src/components/Auth/Auth.module.scss | 41 ++++++++++++++++++++++++++++ src/components/Auth/index.ts | 1 + src/components/index.ts | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/components/Auth/Auth.module.scss create mode 100644 src/components/Auth/index.ts diff --git a/src/App.tsx b/src/App.tsx index b815355..b1c4dc2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,10 @@ -import { Board } from "@/components"; +import { AuthPage } from "@/components"; import { Provider } from "react-redux"; import { store } from "@/redux/store"; const App = () => ( - + ); diff --git a/src/components/Auth/Auth.module.scss b/src/components/Auth/Auth.module.scss new file mode 100644 index 0000000..854a15d --- /dev/null +++ b/src/components/Auth/Auth.module.scss @@ -0,0 +1,41 @@ +@import "src/styles"; + +.modalWrapper { + display: flex; + justify-content: center; + align-items: center; + padding-top: 500px; +} + +.modalContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 300px; + max-width: 1800px; +} + +.modalTitle { + font-size: 100px; +} + +.modalContent { + font-size: 50px; +} + +.form { + display: flex; + flex-direction: column; + margin-top: 100px; +} + +.input { + font-size: 50px; + margin: 10px; +} + +.submit { + margin-top: 20px; + font-size: 50px; +} \ No newline at end of file diff --git a/src/components/Auth/index.ts b/src/components/Auth/index.ts new file mode 100644 index 0000000..c6e4863 --- /dev/null +++ b/src/components/Auth/index.ts @@ -0,0 +1 @@ +export * from "./Auth"; diff --git a/src/components/index.ts b/src/components/index.ts index 800a0a3..37c186f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1 +1,2 @@ export * from "./Board"; +export * from "./Auth"; From cadc934a1c29f4c4d5522fece7ac55306c02e951 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Mon, 18 Mar 2024 16:35:10 +0900 Subject: [PATCH 03/10] add login feature for boards --- .gitignore | 2 ++ src/redux/auth/loginThunk.ts | 14 ++++++-- src/vite-env.d.ts | 16 +++++++++ yarn.lock | 69 ++++-------------------------------- 4 files changed, 36 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 5da5f14..274c332 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ### Node ### # Logs +.idea +.uuid logs *.log npm-debug.log* diff --git a/src/redux/auth/loginThunk.ts b/src/redux/auth/loginThunk.ts index ee76662..6b25039 100644 --- a/src/redux/auth/loginThunk.ts +++ b/src/redux/auth/loginThunk.ts @@ -1,4 +1,5 @@ import { type AppDispatch } from "@/redux/store"; +import axios from "axios"; import { setIsLoggedIn } from "./authSlice"; // send device id and pin to server and get session cookie @@ -9,8 +10,17 @@ export const loginThunk = console.log(pin); // mock api request time - setTimeout(() => {}, 1000); + const response = await axios.post(`/api/board/login`, { + name: deviceId, + password: pin, + }); + // TODO + // add api request to login + + const isLoginSuccess = response.data.success; // if success, then set zabo store's isLogin to true - dispatch(setIsLoggedIn(true)); + if (isLoginSuccess) { + dispatch(setIsLoggedIn(true)); + } }; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe..93e7ba0 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,17 @@ /// + +// https://vitejs.dev/config/ +import { API_SERVER_URL } from "@/config"; + +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + "/api": { + target: API_SERVER_URL, + changeOrigin: true, + rewrite: path => path.replace(/^\/api/, ""), + }, + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index c9c9dab..7dfd0c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -463,13 +463,6 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/http-proxy@^1.17.8": - version "1.17.13" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.13.tgz#dd3a4da550580eb0557d4c7128a2ff1d1a38d465" - integrity sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw== - dependencies: - "@types/node" "*" - "@types/json-schema@^7.0.12": version "7.0.14" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" @@ -480,10 +473,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*": - version "20.8.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.9.tgz#646390b4fab269abce59c308fc286dcd818a2b08" - integrity sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg== +"@types/node@^20.8.10": + version "20.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.0.tgz#bfcdc230583aeb891cf51e73cfdaacdd8deae298" + integrity sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw== dependencies: undici-types "~5.26.4" @@ -1097,16 +1090,6 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dotenv-expand@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" - integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== - -dotenv@^16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -1125,11 +1108,6 @@ enhanced-resolve@^5.12.0: graceful-fs "^4.2.4" tapable "^2.2.0" -envsafe@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/envsafe/-/envsafe-2.0.3.tgz#cb88a9f750a67455638ebfc16f4cd5296bb5e1a0" - integrity sha512-51lek8h5btjXhXUDW//Pno7HPydM1WQzltOb1/ONRKdwB2QkfAURnN4Qn0cJ5LzGvX2Km1ReR2O3K3Bqq9sBMg== - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1492,11 +1470,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" @@ -1591,7 +1564,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.0.0, follow-redirects@^1.15.0: +follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== @@ -1804,26 +1777,6 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react- dependencies: react-is "^16.7.0" -http-proxy-middleware@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== - dependencies: - "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - human-signals@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" @@ -2004,11 +1957,6 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -2257,7 +2205,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@4.0.5, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -2658,11 +2606,6 @@ regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: define-properties "^1.2.0" set-function-name "^2.0.0" -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - reselect@^4.1.8: version "4.1.8" resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" From d7abe2d1e468d38541c5e8bd0658458b259ad965 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Mon, 18 Mar 2024 19:37:49 +0900 Subject: [PATCH 04/10] feat: make error message popup when login fails --- src/App.module.css | 1 + src/components/Auth/Auth.module.scss | 5 +++++ src/components/Auth/Auth.tsx | 5 +++++ src/redux/auth/authSlice.ts | 7 ++++++- src/redux/auth/loginThunk.ts | 4 +++- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/App.module.css b/src/App.module.css index b9d355d..da78883 100644 --- a/src/App.module.css +++ b/src/App.module.css @@ -40,3 +40,4 @@ .read-the-docs { color: #888; } + diff --git a/src/components/Auth/Auth.module.scss b/src/components/Auth/Auth.module.scss index 854a15d..54a1a9c 100644 --- a/src/components/Auth/Auth.module.scss +++ b/src/components/Auth/Auth.module.scss @@ -38,4 +38,9 @@ .submit { margin-top: 20px; font-size: 50px; +} + +.errorBox { + font-size: 50px; + color: red; } \ No newline at end of file diff --git a/src/components/Auth/Auth.tsx b/src/components/Auth/Auth.tsx index c334daf..bd73856 100644 --- a/src/components/Auth/Auth.tsx +++ b/src/components/Auth/Auth.tsx @@ -13,6 +13,10 @@ export const AuthPage = () => { (state: RootState) => state.auth.isLoggedIn, ); + const errorMessage = useAppSelector( + (state: RootState) => state.auth.errorMessage, + ); + const onDeviceIdChange = (e: ChangeEvent) => { setDeviceId(e.target.value); }; @@ -71,6 +75,7 @@ export const AuthPage = () => { > Submit +
{errorMessage}
); diff --git a/src/redux/auth/authSlice.ts b/src/redux/auth/authSlice.ts index c747bad..494e7f8 100644 --- a/src/redux/auth/authSlice.ts +++ b/src/redux/auth/authSlice.ts @@ -2,10 +2,12 @@ import { createSlice } from "@reduxjs/toolkit"; export interface AuthState { isLoggedIn: boolean; + errorMessage: string; } const initialState: AuthState = { isLoggedIn: false, + errorMessage: "", }; const authSlice = createSlice({ @@ -15,9 +17,12 @@ const authSlice = createSlice({ setIsLoggedIn: (state, action) => { state.isLoggedIn = action.payload; }, + setErrorMessage: (state, action) => { + state.errorMessage = action.payload; + }, }, }); -export const { setIsLoggedIn } = authSlice.actions; +export const { setIsLoggedIn, setErrorMessage } = authSlice.actions; export const authReducer = authSlice.reducer; diff --git a/src/redux/auth/loginThunk.ts b/src/redux/auth/loginThunk.ts index 6b25039..f9418b2 100644 --- a/src/redux/auth/loginThunk.ts +++ b/src/redux/auth/loginThunk.ts @@ -1,6 +1,6 @@ import { type AppDispatch } from "@/redux/store"; import axios from "axios"; -import { setIsLoggedIn } from "./authSlice"; +import { setIsLoggedIn, setErrorMessage } from "./authSlice"; // send device id and pin to server and get session cookie export const loginThunk = @@ -22,5 +22,7 @@ export const loginThunk = // if success, then set zabo store's isLogin to true if (isLoginSuccess) { dispatch(setIsLoggedIn(true)); + } else { + dispatch(setErrorMessage(response.data.error)); } }; From 864bc98b86324b4f499598e482c141c7b1ca91e8 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Mon, 18 Mar 2024 19:39:26 +0900 Subject: [PATCH 05/10] misc: remove console log --- src/redux/auth/loginThunk.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/redux/auth/loginThunk.ts b/src/redux/auth/loginThunk.ts index f9418b2..eabd09a 100644 --- a/src/redux/auth/loginThunk.ts +++ b/src/redux/auth/loginThunk.ts @@ -6,8 +6,6 @@ import { setIsLoggedIn, setErrorMessage } from "./authSlice"; export const loginThunk = (deviceId: string, pin: string) => async (dispatch: AppDispatch) => { // request zabo board login and get response - console.log(deviceId); - console.log(pin); // mock api request time const response = await axios.post(`/api/board/login`, { From 78c6b8ba054575f864d313e71f0a2a4357c256d2 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Fri, 22 Mar 2024 21:26:47 +0900 Subject: [PATCH 06/10] misc: make init content configurable --- src/redux/zabos/zaboSlice.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/redux/zabos/zaboSlice.ts b/src/redux/zabos/zaboSlice.ts index 56273d9..2d468c9 100644 --- a/src/redux/zabos/zaboSlice.ts +++ b/src/redux/zabos/zaboSlice.ts @@ -1,5 +1,6 @@ import { type ZaboJson, ZaboState } from "@/types"; import { createSlice } from "@reduxjs/toolkit"; +import { INIT_CONTENT1, INIT_CONTENT2 } from "@/config"; export interface ZaboListState { zaboList: ZaboJson[]; @@ -9,15 +10,14 @@ export interface ZaboListState { const initialState: ZaboListState = { zaboList: [ { - id: "0000000000000", - title: "ZABO BOARD 2023 LAUNCHING", + id: "INIT CONTENT 1", + title: "ZABO BOARDS", owner: "SPARCS", - description: "HELLO WORLD", + description: "INIT CONTENT 1", scheduleType: null, scheduleDate: null, - qrUrl: "https://zabo.sparcs.org/s/86f104", - imageUrl: - "https://sparcs-kaist-zabo-prod.s3.ap-northeast-2.amazonaws.com/zabo/zabo-136421683085229471?a=1sdf23tg", + qrUrl: "https://zabo.sparcs.org", + imageUrl: INIT_CONTENT1, state: ZaboState.CURRENT_STATE, score: 0, views: 0, @@ -25,15 +25,14 @@ const initialState: ZaboListState = { likes: 0, }, { - id: "11111111111", - title: "ZABO BOARD 2023 LAUNCHING", + id: "INIT CONTENT 2", + title: "ZABO BOARDS", owner: "SPARCS", - description: "HELLO WORLD", + description: "INIT CONTENT 2", scheduleType: null, scheduleDate: null, - qrUrl: "https://zabo.sparcs.org/s/86f104", - imageUrl: - "https://sparcs-kaist-zabo-prod.s3.ap-northeast-2.amazonaws.com/zabo/zabo-136421683085229471?a=1sdf23tg", + qrUrl: "https://zabo.sparcs.org", + imageUrl: INIT_CONTENT2, state: ZaboState.BEFORE_STATE, score: 0, views: 0, From 5eb1ccd9444d3daacd5bf1d3c250abd86b4a186d Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Fri, 22 Mar 2024 21:27:22 +0900 Subject: [PATCH 07/10] feat: use /api/board api instead of /api/zabo api. --- .env.example | 6 +++++- src/config.ts | 3 +++ src/redux/zabos/fetchZaboThunk.ts | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 711d599..fbb47a5 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,8 @@ ## VITE_API_SERVER_URL should end without "/" VITE_API_SERVER_URL= VITE_ANIMATION_DURATION= -VITE_TRANSITION_INTERVAL= \ No newline at end of file +VITE_TRANSITION_INTERVAL= + +## put s3 url for initial content for zabo boards +VITE_INIT_CONTENT1= +VITE_INIT_CONTENT2= \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 5d78af6..e466d34 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,6 @@ export const API_SERVER_URL = import.meta.env.VITE_API_SERVER_URL; export const ANIMATION_DURATION = import.meta.env.VITE_ANIMATION_DURATION; export const TRANSITION_INTERVAL = import.meta.env.VITE_TRANSITION_INTERVAL; + +export const INIT_CONTENT1 = import.meta.env.VITE_INIT_CONTENT1; +export const INIT_CONTENT2 = import.meta.env.VITE_INIT_CONTENT2; diff --git a/src/redux/zabos/fetchZaboThunk.ts b/src/redux/zabos/fetchZaboThunk.ts index f6e6933..6c2767f 100644 --- a/src/redux/zabos/fetchZaboThunk.ts +++ b/src/redux/zabos/fetchZaboThunk.ts @@ -21,15 +21,15 @@ export const fetchZaboThunk = () => async (dispatch: AppDispatch) => { // important thing is that, we have to set lastSeen params to get zabos after lastSeen // so we can not do 5 requests in parallel. it should be sequential let zabosData: ZaboJson[] = []; - for (let i = 0; i < 5; i += 1) { + for (let i = 0; i < 1; i += 1) { const lastFetched = zabosData.length > 0 ? zabosData[zabosData.length - 1].id : null; // fetch zabos based on lastSeen(lastFetched) const { data } = lastFetched === null - ? await axios.get("/api/zabo/list") - : await axios.get("/api/zabo/list", { + ? await axios.get("/api/board/list") + : await axios.get("/api/board/list", { params: { lastSeen: lastFetched, }, From 25fa5a1e581acfe931c64b6e3d0590533d3c02e7 Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Sat, 23 Mar 2024 00:36:48 +0900 Subject: [PATCH 08/10] refactor: separate diff pages to diff file --- src/components/Auth/Auth.tsx | 56 ++++++------------------------- src/components/Auth/LoginPage.tsx | 53 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 46 deletions(-) create mode 100644 src/components/Auth/LoginPage.tsx diff --git a/src/components/Auth/Auth.tsx b/src/components/Auth/Auth.tsx index bd73856..39ef6c6 100644 --- a/src/components/Auth/Auth.tsx +++ b/src/components/Auth/Auth.tsx @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from "@/types"; import { type RootState } from "@/redux/store"; import { Board } from "@/components/Board"; import { loginThunk } from "@/redux/auth/loginThunk"; -import style from "./Auth.module.scss"; +import { LoginPage } from "@/components/Auth/LoginPage"; export const AuthPage = () => { const [deviceId, setDeviceId] = useState(""); @@ -33,50 +33,14 @@ export const AuthPage = () => { setPin(""); }; - if (isLoggedIn) { - return ; - } - - return ( -
-
-

Zabo Board Login

-
-

- Zabo Board 서비스를 이용하기 위해서는 등록된 device Id와 PIN을 - 입력하세요. -
- Zabo Board 서비스를 등록하기 위해서는 zabo.sparcs.org 채널톡으로 - 문의해주세요. -

-
- - -
- -
{errorMessage}
-
-
+ return isLoggedIn ? ( + + ) : ( + ); }; diff --git a/src/components/Auth/LoginPage.tsx b/src/components/Auth/LoginPage.tsx new file mode 100644 index 0000000..f3b2da7 --- /dev/null +++ b/src/components/Auth/LoginPage.tsx @@ -0,0 +1,53 @@ +import type { ChangeEvent } from "react"; +import style from "./Auth.module.scss"; + +type LoginPageProps = { + onDeviceIdChange: (e: ChangeEvent) => void; + onPinChange: (e: ChangeEvent) => void; + onSubmitHandler: () => void; + errorMessage: string; +}; + +export const LoginPage = ({ + onDeviceIdChange, + onPinChange, + onSubmitHandler, + errorMessage, +}: LoginPageProps) => ( +
+
+

Zabo Board Login

+
+

+ Zabo Board 서비스를 이용하기 위해서는 등록된 device Id와 PIN을 + 입력하세요. +
+ Zabo Board 서비스를 등록하기 위해서는 zabo.sparcs.org 채널톡으로 + 문의해주세요. +

+
+ + +
+ +
{errorMessage}
+
+
+); From 70ca6f347a34dce3235b85a5af6a82ee7d9bee9c Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Sat, 23 Mar 2024 00:40:16 +0900 Subject: [PATCH 09/10] refactor: remove unnecessary loop --- src/redux/zabos/fetchZaboThunk.ts | 105 ++++++++++++++---------------- 1 file changed, 50 insertions(+), 55 deletions(-) diff --git a/src/redux/zabos/fetchZaboThunk.ts b/src/redux/zabos/fetchZaboThunk.ts index 6c2767f..0b04233 100644 --- a/src/redux/zabos/fetchZaboThunk.ts +++ b/src/redux/zabos/fetchZaboThunk.ts @@ -16,66 +16,61 @@ export const fetchZaboThunk = () => async (dispatch: AppDispatch) => { "".concat("?t=", `${timestamp}`, "&idx=", `${idx}`); // fetch zabos from server - // single zabo list get api gets 20 zabos. so we will call it 5 times to fetch total 50 zabos - // we will circulate top 50 zabos - // important thing is that, we have to set lastSeen params to get zabos after lastSeen - // so we can not do 5 requests in parallel. it should be sequential + // single board list get api gets 20 zabos let zabosData: ZaboJson[] = []; - for (let i = 0; i < 1; i += 1) { - const lastFetched = - zabosData.length > 0 ? zabosData[zabosData.length - 1].id : null; + const lastFetched = + zabosData.length > 0 ? zabosData[zabosData.length - 1].id : null; - // fetch zabos based on lastSeen(lastFetched) - const { data } = - lastFetched === null - ? await axios.get("/api/board/list") - : await axios.get("/api/board/list", { - params: { - lastSeen: lastFetched, - }, - }); + // fetch zabos based on lastSeen(lastFetched) + const { data } = + lastFetched === null + ? await axios.get("/api/board/list") + : await axios.get("/api/board/list", { + params: { + lastSeen: lastFetched, + }, + }); - // parse fetched zabos to ZaboJson - // remove unnecessary fields - // add state field - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const parsedZabos = data.map((zabo: any, index: number) => { - const { title, description, likesCount, views, score, effectiveViews } = - zabo; - /* eslint-disable-next-line no-underscore-dangle */ - const id = zabo._id; - const likes = likesCount; - const owner = zabo.owner.name; - const scheduleType = - zabo.schedules.length > 0 ? zabo.schedules[0].eventType : null; - const scheduleDate = - zabo.schedules.length > 0 - ? dayjs(zabo.schedules[0].startAt).format("MM.DD") - : null; - const qrUrl = "".concat(ZABO_SHARE_URL, id.substring(id.length - 6)); - const imageUrl = - zabo.photos.length > 0 ? zabo.photos[0].url + postFix(index) : null; - const state = ZaboState.PENDING_STATE; + // parse fetched zabos to ZaboJson + // remove unnecessary fields + // add state field + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const parsedZabos = data.map((zabo: any, index: number) => { + const { title, description, likesCount, views, score, effectiveViews } = + zabo; + /* eslint-disable-next-line no-underscore-dangle */ + const id = zabo._id; + const likes = likesCount; + const owner = zabo.owner.name; + const scheduleType = + zabo.schedules.length > 0 ? zabo.schedules[0].eventType : null; + const scheduleDate = + zabo.schedules.length > 0 + ? dayjs(zabo.schedules[0].startAt).format("MM.DD") + : null; + const qrUrl = "".concat(ZABO_SHARE_URL, id.substring(id.length - 6)); + const imageUrl = + zabo.photos.length > 0 ? zabo.photos[0].url + postFix(index) : null; + const state = ZaboState.PENDING_STATE; - return { - id, - title, - owner, - description, - scheduleType, - scheduleDate, - qrUrl, - imageUrl, - state, - likes, - views, - score, - effectiveViews, - }; - }); + return { + id, + title, + owner, + description, + scheduleType, + scheduleDate, + qrUrl, + imageUrl, + state, + likes, + views, + score, + effectiveViews, + }; + }); - zabosData = zabosData.concat(parsedZabos); - } + zabosData = zabosData.concat(parsedZabos); // we have to remove zabos that have no image url zabosData = zabosData.filter((zabo: ZaboJson) => zabo.imageUrl !== null); From d7e6283be26e22a6ae46ce2133b958f1a75187ea Mon Sep 17 00:00:00 2001 From: Jinho-Choi123 Date: Sat, 23 Mar 2024 00:40:36 +0900 Subject: [PATCH 10/10] misc: remove finished todo comment --- src/redux/auth/loginThunk.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/redux/auth/loginThunk.ts b/src/redux/auth/loginThunk.ts index eabd09a..a6d642a 100644 --- a/src/redux/auth/loginThunk.ts +++ b/src/redux/auth/loginThunk.ts @@ -12,8 +12,6 @@ export const loginThunk = name: deviceId, password: pin, }); - // TODO - // add api request to login const isLoginSuccess = response.data.success;