Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/main page API 연동 완료 #16

Merged
merged 11 commits into from
Sep 6, 2024
2 changes: 1 addition & 1 deletion src/assets/images/main.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 20 additions & 20 deletions src/components/form/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ export interface DropDownProps {

export default function DropDown({ color, width, height, selectedMajor, setSelectedMajor }: DropDownProps) {
const majors = [
{ label: "📖 인문대학", value: "인문대학" },
{ label: "📋 사회과학대학", value: "사회과학대학" },
{ label: "🔬 자연과학대학", value: "자연과학대학" },
{ label: "📊 경상대학", value: "경상대학" },
{ label: "🛠️ 공과대학", value: "공과대학" },
{ label: "💻 IT대학", value: "IT대학" },
{ label: "🥕 농업생명과학대학", value: "농업생명과학대학" },
{ label: "🎨 예술대학", value: "예술대학" },
{ label: "📚 사범대학", value: "사범대학" },
{ label: "🥼 의과대학", value: "의과대학" },
{ label: "🦷 치과대학", value: "치과대학" },
{ label: "🐈 수의과대학", value: "수의과대학" },
{ label: "🧼 생활과학대학", value: "생활과학대학" },
{ label: "💉 간호대학", value: "간호대학" },
{ label: "💊 약학대학", value: "약학대학" },
{ label: "🚙 첨단기술융합대학", value: "첨단기술융합대학" },
{ label: "🪵 생태환경대학", value: "생태환경대학" },
{ label: "📡 과학기술대학", value: "과학기술대학" },
{ label: "🖋️ 행정학부", value: "행정학부" },
{ label: "💡 자율전공부", value: "자율전공부" },
{ label: "📖 인문대학", value: "humanities" },
{ label: "📋 사회과학대학", value: "social-sciences" },
{ label: "🔬 자연과학대학", value: "natural-sciences" },
{ label: "📊 경상대학", value: "economics" },
{ label: "🛠️ 공과대학", value: "engineering" },
{ label: "💻 IT대학", value: "it" },
{ label: "🥕 농업생명과학대학", value: "agriculture" },
{ label: "🎨 예술대학", value: "arts" },
{ label: "📚 사범대학", value: "teachers" },
{ label: "🥼 의과대학", value: "medicine" },
{ label: "🦷 치과대학", value: "dentisty" },
{ label: "🐈 수의과대학", value: "vet" },
{ label: "🧼 생활과학대학", value: "human-sciences" },
{ label: "💉 간호대학", value: "nursing" },
{ label: "💊 약학대학", value: "pharmacy" },
{ label: "🚙 첨단기술융합대학", value: "advanced-technology" },
{ label: "🪵 생태환경대학", value: "environment" },
{ label: "📡 과학기술대학", value: "science-technology" },
{ label: "🖋️ 행정학부", value: "administration" },
{ label: "💡 자율전공부", value: "undeclared" },
];

return (
Expand Down
36 changes: 36 additions & 0 deletions src/hooks/useAxios.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";

import axios, { AxiosRequestConfig } from "axios";

const instance = axios.create({
baseURL: "https://api.dongbti.com",
withCredentials: true,
});

export default function useAxios<T>(config: AxiosRequestConfig, dependencies: ReadonlyArray<any> = []) {
const [data, setData] = React.useState<T | null>(null);
const [loading, setLoading] = React.useState<boolean>(false);
const [error, setError] = React.useState<string | null>(null);

React.useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await instance(config);
setData(response.data);
console.log(response.data);
} catch (err: any) {
if (axios.isAxiosError(err)) {
setError(err.message || "Something went wrong");
} else {
setError("Something went wrong");
}
} finally {
setLoading(false);
}
};
fetchData();
}, dependencies);

return { data, loading, error };
}
26 changes: 25 additions & 1 deletion src/pages/AnalyticsPage.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,32 @@ export const Main = styled.div``;

export const Content = styled.div`
margin: 20px;
gap: 10px;
`;

export const MiddleSection = styled.div`
flex-grow: 0.8;
flex-grow: 1;
border-bottom: 2px solid #0000001f;
`;

export const TableContainer = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
margin: 10px;
`;

export const Card = styled.div`
padding: 16px;
`;

export const Rank = styled.div`
font-weight: bold;
height: 30px;
display: flex;
align-items: center;
`;

export const Type = styled.div`
color: #333;
`;
92 changes: 60 additions & 32 deletions src/pages/AnalyticsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,73 @@ import DropDown from "@/components/form/DropDown";
import { AppBar } from "@/components/navigation/AppBar";
import { Text } from "@/components/typography";

import useAxios from "@/hooks/useAxios";

import backIcon from "@/assets/back.svg";

import { TitleContainer, TitleTop, Main, Content, MiddleSection } from "./AnalyticsPage.style";
import { TitleContainer, TitleTop, Main, MiddleSection, TableContainer, Card, Rank, Type } from "./AnalyticsPage.style";

interface axiosProps {
top: Array<[number, string]>;
}

const renderData = (data: axiosProps | null, loading: boolean, error: any) => {
if (loading) return <Text size="m">Loading...</Text>;
if (error) return <Text size="m">Error: {error.message}</Text>;
if (data?.top.length == 0) {
return (
<Text size="m" color="secondary">
데이터가 존재하지 않습니다.
</Text>
);
}
if (data && data.top) {
return (
<TableContainer>
{data.top.map((value, index) => (
<Card key={index}>
<Rank>{index + 1}위</Rank>
<Type>{value[0]}</Type>
</Card>
))}
</TableContainer>
);
}
return null;
};

export default function AnalyticsPage() {
const [selectedMajor, setSelectedMajor] = React.useState<string>("");
const [selectedMajor, setSelectedMajor] = React.useState<string>("humanities");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selectedMajor 에 인문대학에 해당하는 값을 기본적으로 넣는것 보다, null 과 같은 값을 넣어두고,
department 를 선택하지 않은 경우, 에러 메시지를 띄우는 것이 좋아보입니다!

그리고 상태에 해당하는 값또한 string 보다는 enum 타입으로 따로 분리하면 좋을 것 같아요

Copy link
Collaborator Author

@2NNS-V 2NNS-V Sep 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 enum 타입을 사용하는 이유가 있을까요? API를 보낼 때마다, setDepartment() 마다 DropDown의 value(string)을 아래처럼 선언된 Department 타입으로 변경해야 하는데, enum을 사용할 필요가 있을까요? 아니면 서버에서 받기 쉬워서 그런건가요?

export enum Department {
    Humanities = "humanities",
    SocialSciences = "social-sciences",
    NaturalSciences = "natural-sciences",
    Economics = "economics",
    Engineering = "engineering",
    IT = "it",
    Agriculture = "agriculture",
    Arts = "arts",
    Teachers = "teachers",
    Medicine = "medicine",
    Dentistry = "dentisty",
    Vet = "vet",
    HumanSciences = "human-sciences",
    Nursing = "nursing",
    Pharmacy = "pharmacy",
    AdvancedTechnology = "advanced-technology",
    Environment = "environment",
    ScienceTechnology = "science-technology",
    Administration = "administration",
    Undeclared = "undeclared",
    default = "",
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 서버에서 받기 쉬운것보다 enum 타입으로 선언하면
Department.Humanities 로 IDE 나 코드 편집기에서 자동완성을 해주기 때문에,,, 자잘한 오류를 방지하기 위해서 주로 사용합니다!
여러 군데에서 해당 department 의 값을 사용할때 사용하면 좋을것 같아요~

const navigate = useNavigate();

const handleGoBack = () => {
navigate("/");
};

const {
data: allData,
loading: allDataLoading,
error: allDataError,
} = useAxios<axiosProps | null>(
{
url: `/stats/top/mbti`,
method: "GET",
},
[],
);

const {
data: majorData,
loading: majorDataLoading,
error: majorDataError,
} = useAxios<axiosProps | null>(
{
url: `/stats/top/department?key=${selectedMajor}`,
method: "GET",
},
[selectedMajor],
);

return (
<>
<img src={backIcon} onClick={handleGoBack} />
Expand All @@ -26,21 +81,8 @@ export default function AnalyticsPage() {
<Text size="m">어떤 유형이 가장 많을까요?</Text>
</TitleContainer>

<Main>
<Content>
<Text size="m">1위 </Text>
<Text size="m">무대를 좋아하는 연주가형</Text>
</Content>

<Content>
<Text size="m">2위 </Text>
<Text size="m">극락왕생을 원하는 부처님형</Text>
</Content>
<Content>
<Text size="m">3위 </Text>
<Text size="m">티칭의 대가 현우진형</Text>
</Content>
</Main>
<Main>{renderData(allData, allDataLoading, allDataError)}</Main>

<MiddleSection />

<TitleContainer>
Expand All @@ -57,21 +99,7 @@ export default function AnalyticsPage() {

<Text size="m">어떤 유형이 가장 많을까요?</Text>
</TitleContainer>
<Main>
<Content>
<Text size="m">1위 </Text>
<Text size="m">무대를 좋아하는 연주가형</Text>
</Content>

<Content>
<Text size="m">2위 </Text>
<Text size="m">극락왕생을 원하는 부처님형</Text>
</Content>
<Content>
<Text size="m">3위 </Text>
<Text size="m">티칭의 대가 현우진형</Text>
</Content>
</Main>
<Main>{renderData(majorData, majorDataLoading, majorDataError)}</Main>
</>
);
}
36 changes: 31 additions & 5 deletions src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Input } from "@/components/form/Input";
import { SideBar } from "@/components/navigation/SideBar";
import { Text } from "@/components/typography/Text";

import useAxios from "@/hooks/useAxios";

import menuIcon from "@/assets/icons/menu.svg";
import main from "@/assets/images/main.svg";

Expand All @@ -23,6 +25,10 @@ import {
} from "./HomePage.style";
import { useUserInfo } from "@/store/store";

interface axiosProps {
total_count: number;
}

export default function HomePage() {
const navigate = useNavigate();

Expand All @@ -37,6 +43,14 @@ export default function HomePage() {
setIsOpen(!isOpen);
};

const { data, loading, error } = useAxios<axiosProps>(
{
url: `/stats/total`,
method: "GET",
},
[],
);

React.useEffect(() => {
setMajor(selectedMajor);
}, [selectedMajor, setMajor]);
Expand Down Expand Up @@ -109,11 +123,23 @@ export default function HomePage() {
</ButtonContainer>

<TextContainer>
<Text size="xs">오늘까지 </Text>
<Text size="xs" color="primary" weight="bold">
39,239명
</Text>
<Text size="xs">이 참여했어요!</Text>
{loading && <Text size="xs">참여자 수를 불러오는 중...</Text>}

{error && (
<Text size="xs" color="red">
참여자 수를 불러오는데 실패했어요.
</Text>
)}

{data && (
<>
<Text size="xs">오늘까지 </Text>
<Text size="xs" color="primary" weight="bold">
{data.total_count}명
</Text>
<Text size="xs">이 참여했어요!</Text>
</>
)}
</TextContainer>
</Main>

Expand Down
Loading