diff --git a/.gitignore b/.gitignore index 24cdedf..532eddc 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ npm-debug.log* yarn-debug.log* -yarn-error.log* \ No newline at end of file +yarn-error.log* + +.env \ No newline at end of file diff --git a/src/components/Router.tsx b/src/components/Router.tsx index 49eb1d5..9c54833 100644 --- a/src/components/Router.tsx +++ b/src/components/Router.tsx @@ -1,6 +1,7 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; import { Dashboard, Login, Main, User } from "pages"; +import UserDetail from "./user/UserDetail"; export default function Router() { return ( @@ -9,7 +10,9 @@ export default function Router() { } /> }> } /> - } /> + }> + } /> + 소중이들 미안! 404 에러야!} /> 소중이들 미안! 404 에러야!} /> diff --git a/src/components/dashboard/Summary.tsx b/src/components/dashboard/Summary.tsx index 6533da8..926d7af 100644 --- a/src/components/dashboard/Summary.tsx +++ b/src/components/dashboard/Summary.tsx @@ -1,12 +1,16 @@ import styled from "styled-components"; import { theme } from "styled-tools"; -export default function Summary({ - summaryIcon, - summaryText, - dataNumber, - dataVariation, -}) { +interface SummaryProps { + summaryIcon: string; + summaryText: string; + dataNumber: string | number; + dataVariation: number; +} + +export default function Summary(props: SummaryProps) { + const { summaryIcon, summaryText, dataNumber, dataVariation } = props; + return ( @@ -14,7 +18,13 @@ export default function Summary({

{summaryText}

{dataNumber} - {dataVariation} + {dataVariation ? ( + + {dataVariation > 0 + ? `( +${dataVariation} )` + : `( ${dataVariation} )`} + + ) : null}
diff --git a/src/components/dashboard/SummaryWrapper.tsx b/src/components/dashboard/SummaryWrapper.tsx index 9922429..53970fc 100644 --- a/src/components/dashboard/SummaryWrapper.tsx +++ b/src/components/dashboard/SummaryWrapper.tsx @@ -1,27 +1,71 @@ import Summary from "./Summary"; import styled from "styled-components"; import { joinUserIcon, totalUserIcon, totalPlantingIcon } from "../../assets"; +import { useSetRecoilState } from "recoil"; +import { userTotalNum } from "states"; +import { client } from "utils/api"; +import { useEffect, useState } from "react"; export default function SummaryWrapper() { + const [dashboardData, setDashboardData] = useState({ + todayUserTotal: 0, + yesterdayUserTotal: 0, + todayContactTotal: 0, + }); + + const setUserTotalCount = useSetRecoilState(userTotalNum); + + const convertDateToString = (date: Date) => { + return date.toISOString().split("T")[0]; + }; + + const todayDate = convertDateToString(new Date()); + const yesterdayDate = convertDateToString( + new Date(new Date().getTime() - 24 * 60 * 60 * 1000) + ); + + useEffect(() => { + (async function () { + const todayRes = await client(`/user/count?date=${todayDate}`); + const todayUserTotal = todayRes.data.data.totalCount; + setUserTotalCount(todayUserTotal); + const yesterdayRes = await client.get( + `/user/count?date=${yesterdayDate}` + ); + const yesterdayUserTotal = yesterdayRes.data.data.totalCount; + + const { data } = await client("/contact"); + + setDashboardData((current) => ({ + ...current, + todayUserTotal, + yesterdayUserTotal, + todayContactTotal: data.count, + })); + })(); + }, [todayDate, yesterdayDate, setUserTotalCount]); + return ( ); diff --git a/src/components/login/LoginForm.tsx b/src/components/login/LoginForm.tsx index bfc1f67..a648dae 100644 --- a/src/components/login/LoginForm.tsx +++ b/src/components/login/LoginForm.tsx @@ -1,23 +1,51 @@ -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { theme } from "styled-tools"; import { IdIcon, PwIcon } from "assets"; +import { useState } from "react"; export default function LoginWrapper() { + const [account, setAccount] = useState({ id: "", pwd: "" }); + const navigate = useNavigate(); + + const checkIsAdmin = (e) => { + e.preventDefault(); + console.log("account", account, process.env.REACT_APP_ADMIN_ID); + if ( + account.id === `${process.env.REACT_APP_ADMIN_ID}` && + account.pwd === `${process.env.REACT_APP_ADMIN_PWD}` + ) { + navigate("/main/dashboard"); + } + }; + return ( -
+ - + + setAccount((curr) => ({ ...curr, id: e.target.value })) + } + /> - + + setAccount((curr) => ({ ...curr, pwd: e.target.value })) + } + /> - - 로그인 - + 로그인
); } @@ -42,7 +70,7 @@ const StInput = styled.div` } `; -const StLoginButton = styled.p` +const StLoginButton = styled.button` display: flex; justify-content: center; align-items: center; diff --git a/src/components/user/PlantList.tsx b/src/components/user/PlantList.tsx index d82f4b7..5f81305 100644 --- a/src/components/user/PlantList.tsx +++ b/src/components/user/PlantList.tsx @@ -1,19 +1,12 @@ -import { useEffect, useState } from "react"; import styled from "styled-components"; import { theme } from "styled-tools"; +import { Plant } from "./UserDetail"; -import { getPlantList } from "utils"; -import { Plant } from "utils/tempData"; - -export default function PlantList() { - const [plantList, setPlantList] = useState([]); - - useEffect(() => { - (async function () { - const plantData: Plant[] = await getPlantList(); - setPlantList(plantData); - })(); - }, []); +interface PlantListProps { + plantList: Plant[]; +} +export default function PlantList(props: PlantListProps) { + const { plantList } = props; return ( @@ -25,12 +18,12 @@ export default function PlantList() { 최근 물주기 날짜 누적 물주기 - {plantList.map((plant) => ( -
    + {plantList.map((plant, idx) => ( +
      {plant.name} - {plant.period}일 - {plant.recentDate} - {plant.accumulated}회 + {plant.waterInterval}일 + {plant.lastWaterDate} + {plant.waterCount}회
    ))} diff --git a/src/components/user/UserDetail.tsx b/src/components/user/UserDetail.tsx index 4b458e6..259b543 100644 --- a/src/components/user/UserDetail.tsx +++ b/src/components/user/UserDetail.tsx @@ -1,36 +1,40 @@ import { useEffect, useState } from "react"; import styled from "styled-components"; -import { getUserInfo } from "utils"; -import { User } from "utils/tempData"; import { PlantList, UserInfo } from ".."; -import { plant1 } from "assets"; import { theme } from "styled-tools"; +import { useRecoilValue } from "recoil"; +import { userDatum, UserDatum } from "states"; +import { client } from "utils/api"; -const TEMP_USER_ID: number = 5; -const INITIAL_PROPS: User = { - id: 0, - image: "", - nickname: "temp", - email: "temp@temp.com", - phone: "0000", - count: "00", -}; +export interface Plant { + name: string; + waterInterval: number; + lastWaterDate: string; + waterCount: number; +} export default function UserDetail() { - const [userInfo, setUserInfo] = useState(INITIAL_PROPS); + const [plantList, setPlantList] = useState([]); + const user: UserDatum = useRecoilValue(userDatum); useEffect(() => { (async function () { - const userData: User = await getUserInfo(TEMP_USER_ID); - setUserInfo({ ...userData, image: plant1 }); + try { + const { + data: { plants }, + } = await client.get(`/user/user/${user.id}`); + setPlantList(plants); + } catch (err) { + console.log("err", err); + } })(); - }, []); + }, [user]); return ( - - + + ); } diff --git a/src/components/user/UserInfo.tsx b/src/components/user/UserInfo.tsx index f734a49..94bb109 100644 --- a/src/components/user/UserInfo.tsx +++ b/src/components/user/UserInfo.tsx @@ -1,13 +1,25 @@ +import { useEffect } from "react"; import styled from "styled-components"; import { theme } from "styled-tools"; -import { User } from "utils/tempData"; +interface UserInfoProps { + id: number; + nickname: string; + email: string; + thumbNail?: string; +} -export default function UserInfo(props: { userInfo: User }) { +export default function UserInfo(props: { userInfo: UserInfoProps }) { const userInfo = props.userInfo; + useEffect(() => { + console.log(userInfo); + }, [userInfo]); + return ( - userThumbNail-1 + {userInfo.thumbNail ? ( + userThumbNail + ) : null} {userInfo.nickname} {userInfo.email} diff --git a/src/components/userList/UserList.tsx b/src/components/userList/UserList.tsx index ab299e6..e4382d1 100644 --- a/src/components/userList/UserList.tsx +++ b/src/components/userList/UserList.tsx @@ -1,52 +1,61 @@ -import { useState, useEffect } from "react"; import styled from "styled-components"; import { theme } from "styled-tools"; -import { client } from "utils/api"; import { User } from "utils/tempData"; import { plant1, plant2, plant3, plant4, plant5 } from "assets"; -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { useSetRecoilState } from "recoil"; +import { userDatum } from "states"; -interface Data { - totalPages: number; - totalUserCount: number; - users: User[]; -} -export default function UserList(props: { pageCnt: number }) { - const [userList, setUserList] = useState([]); +export default function UserList(props: { userList: User[] }) { + const { userList } = props; const plantImages = [plant1, plant2, plant3, plant4, plant5]; + const navigate = useNavigate(); + + const setUserDatum = useSetRecoilState(userDatum); const getRandomNum = () => { const randomNum = Math.floor(Math.random() * 4); return randomNum; }; - useEffect(() => { - (async function () { - const { data } = await client.get("/user", { - params: { - offset: props.pageCnt, - count: 20, - }, - }); - setUserList(data.users); - })(); - }, [props.pageCnt]); + const navigateUserPage = (datum: User, image: string) => { + navigate(`/main/user`); + setUserDatum({ + id: datum.id, + nickname: datum.nickname, + email: datum.email, + thumbNail: image, + }); + }; return ( {userList && - userList.map((userInfo) => ( - - - 사용자 이미지 - {userInfo.nickname} - {userInfo.email} - {userInfo.phone} - {userInfo.count} + userList.map((userInfo) => { + const userThumbNail = plantImages[getRandomNum()]; + return ( + navigateUserPage(userInfo, userThumbNail)} + > + 사용자 이미지 + + {userInfo.nickname} + + + {userInfo.email} + + + {userInfo.phone} + + + {userInfo.count} + - - ))} + ); + })} ); } diff --git a/src/components/userList/UserListWrapper.tsx b/src/components/userList/UserListWrapper.tsx index 634d76e..204362b 100644 --- a/src/components/userList/UserListWrapper.tsx +++ b/src/components/userList/UserListWrapper.tsx @@ -1,13 +1,30 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { theme } from "styled-tools"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import UserList from "./UserList"; import { ArrowLeft, ArrowRight } from "../../assets"; +import { User } from "utils/tempData"; +import { client } from "utils/api"; +import { useRecoilValue } from "recoil"; +import { userTotalNum } from "states"; + +interface Data { + totalPages: number; + totalUserCount: number; + users: User[]; +} + +interface UserListWrapperProps { + shouldSetHeight: boolean; +} -export default function UserListWrapper() { - const MAX_PAGE = 5; +export default function UserListWrapper(props: UserListWrapperProps) { + const { shouldSetHeight } = props; + const [userList, setUserList] = useState([]); + + const MAX_PAGE = useRecoilValue(userTotalNum) / 20; const [pageCnt, setPageCnt] = useState(1); const [isLeftActive, setIsLeftActive] = useState(true); const [isRightActive, setIsRightActive] = useState(true); @@ -30,8 +47,20 @@ export default function UserListWrapper() { if (newCnt >= MAX_PAGE) setIsRightActive(false); }; + useEffect(() => { + (async function () { + const { data } = await client.get("/user", { + params: { + offset: pageCnt, + count: 20, + }, + }); + setUserList(data.users); + })(); + }, [pageCnt]); + return ( - + 사용자 목록 @@ -40,14 +69,18 @@ export default function UserListWrapper() { - + ); } -const StUserListWrapper = styled.article` +const StUserListWrapper = styled.article<{ shouldset: boolean }>` width: 100%; - height: 100%; + ${({ shouldset }) => + shouldset && + css` + height: 100%; + `} padding: 3.8rem; background-color: ${theme("colors.bgWhite")}; diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index f9bfe39..62ec621 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -14,7 +14,7 @@ export default function Dashboard() { - + diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 87774fc..4ecd00a 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,26 +1,9 @@ -import { useEffect, useState } from "react"; - import styled from "styled-components"; import { theme } from "styled-tools"; -import { client } from "utils/api"; import { logo } from "assets"; import LoginWrapper from "components/login/LoginForm"; export default function Login() { - const [temp, setTemp] = useState(); - - const fetch = async () => { - const data: any = client.get("/contact"); - setTemp(data); - }; - useEffect(() => { - fetch(); - }, []); - - useEffect(() => { - console.log(temp); - }, [temp]); - return ( diff --git a/src/pages/User.tsx b/src/pages/User.tsx index 7fce20c..b531ba1 100644 --- a/src/pages/User.tsx +++ b/src/pages/User.tsx @@ -6,7 +6,7 @@ import { UserDetail } from "components"; export default function User() { return ( - + ); diff --git a/src/states/index.ts b/src/states/index.ts index 549e326..4151075 100644 --- a/src/states/index.ts +++ b/src/states/index.ts @@ -1,5 +1,22 @@ -import { atom } from "recoil"; -// import { selector } from "recoil"; +import { logo } from "assets"; +import { atom, RecoilState } from "recoil"; + +export interface UserDatum { + id: number; + nickname: string; + email: string; + thumbNail: string; +} + +export const userDatum: RecoilState = atom({ + key: "userInfo", + default: { + id: -1, + nickname: "소중이", + email: "cherish@test.test", + thumbNail: logo, + }, +}); export type SelectedDate = { year: number; month: number }; @@ -16,10 +33,7 @@ export const waterSelectedDateAtom = atom({ default: defaultDate, }); -// export const isLikeAtom = selector({ -// key: "isLikeClicked", -// get: ({ get }) => { -// const { isLike, likeNumber } = get(articleAtom); -// return { status: isLike, number: likeNumber }; -// }, -// }); +export const userTotalNum: RecoilState = atom({ + key: "userInfo", + default: 0, +}); diff --git a/src/styles/global-style.ts b/src/styles/global-style.ts index e3a8857..d1834a8 100644 --- a/src/styles/global-style.ts +++ b/src/styles/global-style.ts @@ -20,7 +20,7 @@ export const GlobalStyle = createGlobalStyle` html { color: ${theme("colors.textBlack")}; font-family: NotoSansKR; - font-size: 10px; + font-size: 62.5%; } a { diff --git a/src/utils/api.ts b/src/utils/api.ts index bf74162..3013e17 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,7 +1,7 @@ import axios from "axios"; export const client = axios.create({ - baseURL: "http://localhost:5005", + baseURL: process.env.REACT_APP_BASE_URL, headers: { "Content-Type": "application/json", }, diff --git a/src/utils/index.ts b/src/utils/index.ts index e71f6b1..0194e18 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,17 +1,9 @@ -import { USER_LIST, DATE_JOIN_LIST, PLANT_LIST } from "./tempData"; - -export const getUserList = async () => { - return USER_LIST; -}; +import { DATE_JOIN_LIST, PLANT_LIST } from "./tempData"; export const getDateJoinList = async () => { return DATE_JOIN_LIST; }; -export const getUserInfo = async (id: number) => { - return USER_LIST.filter((user) => user.id === id)[0]; -}; - export const getPlantList = async () => { return PLANT_LIST; }; diff --git a/src/utils/tempData.ts b/src/utils/tempData.ts index ec9d74b..63f8159 100644 --- a/src/utils/tempData.ts +++ b/src/utils/tempData.ts @@ -1,6 +1,6 @@ export interface User { id: number; - image?: string; + image: string; nickname: string; email: string; phone: string; @@ -32,149 +32,6 @@ interface DateJoin { count: number; } -export const USER_LIST: User[] = [ - { - id: 1, - nickname: "01짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 2, - nickname: "02짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 3, - nickname: "03짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 4, - nickname: "04짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 5, - nickname: "05짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 6, - nickname: "06짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 7, - nickname: "07짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 8, - nickname: "08짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 9, - nickname: "09짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 10, - nickname: "10짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 11, - nickname: "11짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 12, - nickname: "12짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 13, - nickname: "13짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 14, - nickname: "14짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 15, - nickname: "15짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 16, - nickname: "16짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 17, - nickname: "17짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 18, - nickname: "18짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 19, - nickname: "19짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, - { - id: 20, - nickname: "20짱구엄마", - email: "temp@temp.com", - phone: "0000", - count: "00", - }, -]; - export const DATE_JOIN_LIST: DateJoin[] = [ { date: "21-07-01", count: 1 }, { date: "21-07-02", count: 1 },