Skip to content

Commit

Permalink
[Feat]로그인 유지 및 프로필 탭 구현
Browse files Browse the repository at this point in the history
Auth토큰이 로컬 스토리지에 있으면 페이지가 렌더링될 때마다 로그인 상태 유지
프로필 컨테이너 삭
프로필 컨테이너 삭제
프로필 모달에서 탭 위치 조정
Cash Hook, Cash Reducer에 담기는 변수이름 조정 cahAmount ->moneyAmount, cashId ->moneyId
Issues #70
  • Loading branch information
김병현 authored and 김병현 committed Sep 13, 2023
1 parent 6124396 commit 3ea8034
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 78 deletions.
29 changes: 15 additions & 14 deletions client/src/components/Profile/cashModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import styled from 'styled-components';
import { useSelector, useDispatch } from 'react-redux';
import { useCreateCash, useGetCash, useResetCash } from '../../hooks/useCash';
import { RootState } from '../../store/config';
import { setCashId, setCashAmount } from '../../reducer/cash/cashSlice';
import { setMoneyId, setMoneyAmount } from '../../reducer/cash/cashSlice';

const CashModal: React.FC<CashModalProps> = ({ onClose }) => {

Expand All @@ -14,38 +14,38 @@ const CashModal: React.FC<CashModalProps> = ({ onClose }) => {
const resetButtonText = "리셋";

const dispatch = useDispatch();
const cashId = useSelector((state: RootState) => state.cash.cashId);
const cashAmount = useSelector((state: RootState) => state.cash.cashAmount) || 0;
const moneyId = useSelector((state: RootState) => state.cash.moneyId);
const moneyAmount = useSelector((state: RootState) => state.cash.moneyAmount) || 0;

const createCashMutation = useCreateCash();
const { data: cashData, isLoading } = useGetCash(cashId);
const { data: cashData, isLoading } = useGetCash(moneyId);
const updateCashMutation = useResetCash();

const [cashInput, setCashInput] = useState<string>('');
const [cashInput, setCashInput] = useState<number>(0);
const [initialAmount, setInitialAmount] = useState<number>(0); // 현금 생성을 위한 상태 변수

// 현금 생성 및 cashId 전역 저장
const handleCreateCash = () => {
createCashMutation.mutate(initialAmount, {
onSuccess: (data) => {
dispatch(setCashId(data.data.cashId));
dispatch(setMoneyId(data.data.moneyId));
}
});
};

// 보유 현금량 조회 및 전역 저장
if (cashData && cashAmount !== cashData.data.cash) {
dispatch(setCashAmount(cashData.data.cash));
if (cashData && moneyAmount !== cashData.data.cash) {
dispatch(setMoneyAmount(cashData.data.cash));
}

// 현금을 입력한 금액으로 리셋하는 함수
const handleCashReset = () => {
if (cashId) {
const numericCashId = parseInt(cashId, 10); // cashId를 숫자로 변환
if (moneyId) {
const numericCashId = parseInt(moneyId, 10); // cashId를 숫자로 변환
const numericCashAmount = Number(cashInput); // cashInput을 숫자로 변환
updateCashMutation.mutate({ cashId: numericCashId, cashAmount: numericCashAmount }, {
updateCashMutation.mutate({ moneyId: numericCashId, money: numericCashAmount }, {
onSuccess: () => {
dispatch(setCashAmount(numericCashAmount)); // 현금 금액을 입력한 금액으로 리셋
dispatch(setMoneyAmount(numericCashAmount)); // 현금 금액을 입력한 금액으로 리셋
}
});
} else {
Expand All @@ -71,12 +71,12 @@ const CashModal: React.FC<CashModalProps> = ({ onClose }) => {
<CreateCashButton onClick={handleCreateCash}>{createCashButtonText}</CreateCashButton>
</div>

<p>현재 현금: {isLoading ? 'Loading...' : cashAmount.toLocaleString()}</p>
<p>현재 현금: {isLoading ? 'Loading...' : moneyAmount.toLocaleString()}</p>
<div>
<CashInput
type="number"
value={cashInput}
onChange={e => setCashInput(e.target.value)}
onChange={e => setCashInput(Number(e.target.value))}
placeholder={cashInputPlaceholder}
/>
<ReceiveButton onClick={handleCashReset}>{resetButtonText}</ReceiveButton>
Expand Down Expand Up @@ -106,6 +106,7 @@ const ModalBackground = styled.div`
`;

const ModalContainer = styled.div`
z-index: 11;
position: relative;
background-color: white;
padding: 20px;
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Profile/memberInfoModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const ModalBackground = styled.div`
`;

const ModalContainer = styled.div`
z-index: 11;
position: relative;
background-color: white;
padding: 20px;
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Profile/memberWithdrawalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const ModalBackground = styled.div`
`;

const ModalContainer = styled.div`
z-index: 11;
position: relative;
background-color: white;
padding: 20px;
Expand Down
75 changes: 38 additions & 37 deletions client/src/components/Profile/profileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,37 @@ const ProfileModal: React.FC<ProfileModalProps> = ({ onClose }) => {

const memberInfoText = "회원정보";
const cashText = "현금";
const memberWithdrawText = "회원탈퇴"
const [selectedTab, setSelectedTab] = useState<number>(1);
const cashId = useSelector((state: RootState) => state.cash.cashId); // Get cashId from Redux store
const memberWithdrawText = "회원탈퇴";
const cashId = useSelector((state: RootState) => state.cash.moneyId);

const handleTabChange = (tabNumber: number) => {
setSelectedTab(tabNumber);
};
const [selectedTab, setSelectedTab] = useState(1); // 1: MemberInfo, 2: CashModal, 3: WithdrawalModal

return (
<ModalBackground>
<ModalContainer>
<CloseButton onClick={onClose}>&times;</CloseButton>
<Tabs>
<TabButton active={selectedTab === 1} onClick={() => handleTabChange(1)}>{memberInfoText}</TabButton>
<TabButton active={selectedTab === 2} onClick={() => handleTabChange(2)}>{cashText}</TabButton>
<TabButton active={selectedTab === 3} onClick={() => handleTabChange(3)}>{memberWithdrawText}</TabButton>
<TabButton onClick={() => setSelectedTab(1)}>{memberInfoText}</TabButton>
<TabButton onClick={() => setSelectedTab(2)}>{cashText}</TabButton>
<TabButton onClick={() => setSelectedTab(3)}>{memberWithdrawText}</TabButton>
</Tabs>
<TabContent>
{selectedTab === 1 && <MemberInfoModal onClose={onClose}/>}
{selectedTab === 1 && <MemberInfoModal onClose={onClose} />}
{selectedTab === 2 && <CashModal onClose={onClose} cashId={cashId} />}
{selectedTab === 3 && <MemberWithdrawalModal onClose={onClose}/>}
{selectedTab === 3 && <MemberWithdrawalModal onClose={onClose} />}
</TabContent>
{/* <CloseButton onClick={onClose}>&times;</CloseButton> */}
</ModalContainer>
</ModalBackground>
);
}
};

interface ProfileModalProps {
onClose: () => void;
}


// 모달 배경 스타일
const ModalBackground = styled.div`
z-index:10;
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
Expand All @@ -56,53 +52,58 @@ const ModalBackground = styled.div`
background-color: rgba(0, 0, 0, 0.5);
`;

// 모달 컨테이너 스타일
const ModalContainer = styled.div`
z-index: 11;
z-index: 1001;
position: relative;
background-color: white;
padding: 20px;
width: 400px;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
background-color: transparent; // 배경색을 투명하게 설정
border: none; // 테두리를 없앱니다.
`;

const Tabs = styled.div`
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 20px;
z-index: 1002; // 이 값을 추가하여 Tabs를 최상위로 올립니다.
`;

// 모달 닫기 버튼 스타일
const CloseButton = styled.button`
position: absolute;
top: 10px;
right: 10px;
background: #FFFFFF;
border: 1px solid lightgray;
font-size: 1.5rem;
cursor: pointer;
`;
// // 모달 닫기 버튼 스타일
// const CloseButton = styled.button`
// position: absolute;
// top: 10px;
// right: 10px;
// background: #FFFFFF;
// border: 1px solid lightgray;
// font-size: 1.5rem;
// cursor: pointer;
// `;

interface TabButtonProps {
active?: boolean;
}

const TabButton = styled.button<TabButtonProps>`
const TabButton = styled.button`
flex: 1;
padding: 10px;
border: 1px solid lightgray;
border-radius: 5px;
cursor: pointer;
background-color: ${({ active }) => (active ? 'darkslategray' : '#FFFFFF')};
color: ${({ active }) => (active ? '#FFFFFF' : 'darkslategray')};
background-color: #FFFFFF;
color: darkslategray;
`;

const TabContent = styled.div`
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
overflow-y: auto;
position: relative;
min-height: 200px;
`;


export default ProfileModal;
export default ProfileModal;
16 changes: 8 additions & 8 deletions client/src/hooks/useCash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ const getAuthHeader = () => {
};

export const useCreateCash = () => {
return useMutation((initialAmount: number) => axios.post('http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash', { cash: initialAmount }, {
return useMutation((initialAmount: number) => axios.post('http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash', { "money": initialAmount }, {
headers: getAuthHeader()
}));

}

export const useGetCash = (cashId: string | null) => {
export const useGetCash = (moneyId: string | null) => {
const queryFn = () => {
if (!cashId) {
if (!moneyId) {
throw new Error("Cash ID is not provided.");
}
return axios.get(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${cashId}`, {
return axios.get(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${moneyId}`, {
headers: getAuthHeader()
});
};

const queryResult = useQuery(['cash', cashId], queryFn, {
enabled: !!cashId,
const queryResult = useQuery(['money', moneyId], queryFn, {
enabled: !!moneyId,
});

if (!cashId) {
if (!moneyId) {
return {
...queryResult,
data: null
Expand All @@ -41,7 +41,7 @@ export const useGetCash = (cashId: string | null) => {
}

export const useResetCash = () => {
return useMutation((data: { cashId: number, cashAmount: number }) => axios.patch(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${data.cashId}`, { cash: data.cashAmount }, {
return useMutation((data: { moneyId: number, money: number }) => axios.patch(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${data.moneyId}`, { "money": data.money }, {
headers: getAuthHeader()
}));
}
36 changes: 24 additions & 12 deletions client/src/page/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const MainPage = () => {
const [isWelcomeModalOpen, setWelcomeModalOpen] = useState(false);
const [isProfileModalOpen, setProfileModalOpen] = useState(false); //프로필 모달 보이기/숨기기


const openOAuthModal = useCallback(() => {
setOAuthModalOpen(true);
}, []);
Expand Down Expand Up @@ -95,17 +96,26 @@ const MainPage = () => {
// 🔴 로그인 지역 상태 제거 → 전역 상태로 대체 (지역 상태 관련된 코드 싹 다 지워야함... -> 전역 상태 만들었으니 전역 상태로 활용)
const dispatch = useDispatch();
const isLogin = useSelector((state: StateProps) => state.login);
const [isLoggedIn, setIsLoggedIn] = useState(false); // 로그인 상태 관리

// 🔴 새로고침 되면 로그인 해제되면서 액세스 토큰도 같이 삭제됨
useEffect(() => {
// const authToken = localStorage.getItem("authToken");

// if (authToken !== null) {
// dispatch(setLoginState());
// }
localStorage.removeItem("authToken");
}, []);
// 🔴 페이지 로드 시 로컬 스토리지의 토큰을 기반으로 로그인 상태를 확인합니다.
useEffect(() => {
const authToken = localStorage.getItem("authToken");
if (authToken !== null) {
dispatch(setLoginState());
} else {
dispatch(setLogoutState());
}
}, [dispatch]);

// // 🔴 새로고침 되면 로그인 해제되면서 액세스 토큰도 같이 삭제됨
// useEffect(() => {
// // const authToken = localStorage.getItem("authToken");

// // if (authToken !== null) {
// // dispatch(setLoginState());
// // }
// localStorage.removeItem("authToken");
// }, []);

//프로필 모달 열고닫는 매커니즘
const openProfileModal = useCallback(() => {
Expand All @@ -117,11 +127,12 @@ const MainPage = () => {
const handleLogin = () => {
closeEmailLoginModal();
setLoginConfirmationModalOpen(true);
dispatch(setLoginState());
};


const handleLoginConfirmationClose = () => {
setLoginConfirmationModalOpen(false);
setIsLoggedIn(true);
};

const [selectedMenu, setSelectedMenu] = useState<"관심목록" | "투자목록">("투자목록"); // Default menu is 관심목록
Expand All @@ -130,10 +141,11 @@ const MainPage = () => {
setSelectedMenu(menu);
};

// 🔴 로그 아웃 시 로컬데이터 토큰 제거
// 🔴 로그아웃 시 로컬스토리지에 있는 Auth 토큰 제거
const handleLogout = () => {
dispatch(setLogoutState());
localStorage.removeItem("authToken");

};

return (
Expand Down
14 changes: 7 additions & 7 deletions client/src/reducer/cash/cashSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import { createSlice } from '@reduxjs/toolkit';
const cashSlice = createSlice({
name: 'cash',
initialState: {
cashId: null,
cashAmount: null,
moneyId: null,
moneyAmount: null,
},
reducers: {
setCashId: (state, action) => {
state.cashId = action.payload;
setMoneyId: (state, action) => {
state.moneyId = action.payload;
},
setCashAmount: (state, action) => {
state.cashAmount = action.payload;
setMoneyAmount: (state, action) => {
state.moneyAmount = action.payload;
},
},
});

export const { setCashId, setCashAmount } = cashSlice.actions;
export const { setMoneyId, setMoneyAmount } = cashSlice.actions;
export default cashSlice.reducer;

0 comments on commit 3ea8034

Please sign in to comment.