Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Yongworry committed Apr 26, 2024
2 parents 56c0b9f + d7e6283 commit c5308e0
Show file tree
Hide file tree
Showing 20 changed files with 319 additions and 139 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## VITE_API_SERVER_URL should end without "/"
VITE_API_SERVER_URL=
VITE_ANIMATION_DURATION=
VITE_TRANSITION_INTERVAL=
VITE_TRANSITION_INTERVAL=

## put s3 url for initial content for zabo boards
VITE_INIT_CONTENT1=
VITE_INIT_CONTENT2=
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

### Node ###
# Logs
.idea
.uuid
logs
*.log
npm-debug.log*
Expand Down
1 change: 1 addition & 0 deletions src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@
.read-the-docs {
color: #888;
}

4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Board } from "@/components";
import { AuthPage } from "@/components";
import { Provider } from "react-redux";
import { store } from "@/redux/store";

const App = () => (
<Provider store={store}>
<Board />
<AuthPage />
</Provider>
);

Expand Down
46 changes: 46 additions & 0 deletions src/components/Auth/Auth.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@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;
}

.errorBox {
font-size: 50px;
color: red;
}
46 changes: 46 additions & 0 deletions src/components/Auth/Auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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 { LoginPage } from "@/components/Auth/LoginPage";

export const AuthPage = () => {
const [deviceId, setDeviceId] = useState("");
const [pin, setPin] = useState("");

const isLoggedIn = useAppSelector(
(state: RootState) => state.auth.isLoggedIn,
);

const errorMessage = useAppSelector(
(state: RootState) => state.auth.errorMessage,
);

const onDeviceIdChange = (e: ChangeEvent<HTMLInputElement>) => {
setDeviceId(e.target.value);
};

const onPinChange = (e: ChangeEvent<HTMLInputElement>) => {
setPin(e.target.value);
};

const dispatch = useAppDispatch();

const onSubmitHandler = () => {
dispatch(loginThunk(deviceId, pin));
setDeviceId("");
setPin("");
};

return isLoggedIn ? (
<Board />
) : (
<LoginPage
errorMessage={errorMessage}
onDeviceIdChange={onDeviceIdChange}
onPinChange={onPinChange}
onSubmitHandler={onSubmitHandler}
/>
);
};
53 changes: 53 additions & 0 deletions src/components/Auth/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { ChangeEvent } from "react";
import style from "./Auth.module.scss";

type LoginPageProps = {
onDeviceIdChange: (e: ChangeEvent<HTMLInputElement>) => void;
onPinChange: (e: ChangeEvent<HTMLInputElement>) => void;
onSubmitHandler: () => void;
errorMessage: string;
};

export const LoginPage = ({
onDeviceIdChange,
onPinChange,
onSubmitHandler,
errorMessage,
}: LoginPageProps) => (
<div className={style.modalWrapper}>
<div className={style.modalContainer}>
<h1 className={style.modalTitle}> Zabo Board Login</h1>
<br />
<p className={style.modalContent}>
Zabo Board 서비스를 이용하기 위해서는 등록된 device Id와 PIN을
입력하세요.
<br />
Zabo Board 서비스를 등록하기 위해서는 zabo.sparcs.org 채널톡으로
문의해주세요.
</p>
<div className={style.form}>
<label className={style.input}>
device Id
<input
className={style.input}
name="deviceId"
onChange={onDeviceIdChange}
/>
</label>
<label className={style.input}>
PIN
<input
className={style.input}
type="password"
name="PIN"
onChange={onPinChange}
/>
</label>
</div>
<button type="submit" className={style.submit} onClick={onSubmitHandler}>
Submit
</button>
<div className={style.errorBox}>{errorMessage}</div>
</div>
</div>
);
1 change: 1 addition & 0 deletions src/components/Auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Auth";
8 changes: 5 additions & 3 deletions src/components/Board/Board.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./Board";
export * from "./Auth";
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -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;
28 changes: 28 additions & 0 deletions src/redux/auth/authSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createSlice } from "@reduxjs/toolkit";

export interface AuthState {
isLoggedIn: boolean;
errorMessage: string;
}

const initialState: AuthState = {
isLoggedIn: false,
errorMessage: "",
};

const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setIsLoggedIn: (state, action) => {
state.isLoggedIn = action.payload;
},
setErrorMessage: (state, action) => {
state.errorMessage = action.payload;
},
},
});

export const { setIsLoggedIn, setErrorMessage } = authSlice.actions;

export const authReducer = authSlice.reducer;
24 changes: 24 additions & 0 deletions src/redux/auth/loginThunk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type AppDispatch } from "@/redux/store";
import axios from "axios";
import { setIsLoggedIn, setErrorMessage } 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

// mock api request time
const response = await axios.post(`/api/board/login`, {
name: deviceId,
password: pin,
});

const isLoginSuccess = response.data.success;

// if success, then set zabo store's isLogin to true
if (isLoginSuccess) {
dispatch(setIsLoggedIn(true));
} else {
dispatch(setErrorMessage(response.data.error));
}
};
13 changes: 13 additions & 0 deletions src/redux/rootReducer.ts
Original file line number Diff line number Diff line change
@@ -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,
});
4 changes: 2 additions & 2 deletions src/redux/store.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit c5308e0

Please sign in to comment.