Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions src/entities/projects/api/projectsAPi.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,63 @@
import { collection, getDocs } from "firebase/firestore/lite";
import {
getCountFromServer,
collection,
getDocs,
limit,
orderBy,
query,
QueryDocumentSnapshot,
startAfter,
type DocumentData,
} from "firebase/firestore";

import type { ProjectListRes } from "@entities/projects/types/projects";

import { db } from "@shared/firebase/firebase";

/** projects의 total 수 */
export const getProjectsTotalCount = async (): Promise<number> => {
try {
const q = query(collection(db, "projects"));
const querySnapshot = await getCountFromServer(q);
return querySnapshot.data().count;
} catch (err) {
console.log(err);
return 0;
}
};

/** firebase project 목록 불러오기 */
export const getProjectList = async (): Promise<ProjectListRes[]> => {
export const getProjectList = async ({
pageSize = 6,
lastDoc = null,
}: {
pageSize?: number;
lastDoc: QueryDocumentSnapshot<DocumentData> | null;
}): Promise<{
projects: ProjectListRes[];
lastVisible: QueryDocumentSnapshot<DocumentData> | null;
}> => {
try {
const listRef = collection(db, "projects");
const querySnapshot = await getDocs(listRef);
const baseQuery = query(
collection(db, "projects"),
orderBy("createdAt", "desc"),
limit(pageSize)
);
const q = lastDoc ? query(baseQuery, startAfter(lastDoc)) : baseQuery;

const querySnapshot = await getDocs(q);

const posts = querySnapshot.docs.map((doc) => ({
const projects = querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
})) as ProjectListRes[];

return posts as ProjectListRes[];
return {
projects: projects,
lastVisible: querySnapshot.docs[querySnapshot.docs.length - 1] || null,
};
} catch (err) {
console.log(err);
return [];
return { projects: [], lastVisible: null };
}
};
73 changes: 73 additions & 0 deletions src/entities/projects/hook/useProjectPageNation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useState } from "react";

import useProjectList from "@entities/projects/queries/useProjectList";
import type { LastVisibleType } from "@entities/projects/types/firebase";
import type { ProjectListRes } from "@entities/projects/types/projects";

interface ReturnProjectPageNation {
projects: ProjectListRes[];
currentPage: number;
paging: {
prev: () => void;
next: () => void;
reset: () => void;
disablePrev: boolean;
disableNext: boolean;
};
}

const useProjectPageNation = ({
totalCount,
perPage = 6,
}: {
totalCount: number;
perPage?: number;
}): ReturnProjectPageNation => {
const [lastVisibleStack, setLastVisibleStack] = useState<LastVisibleType>([
null,
]);
const [currentPage, setCurrentPage] = useState(0);

const cursor = lastVisibleStack[currentPage] ?? null;
const { data: projects, isLoading } = useProjectList(cursor);

const disablePrev = currentPage === 0 || isLoading;
const disableNext =
currentPage === Math.floor(totalCount / perPage) || isLoading;

const pagingPrev = (): void => {
if (disablePrev) return;

setCurrentPage((prev) => prev - 1);
};

const pagingNext = (): void => {
if (disableNext) return;

if (projects?.lastVisible) {
if (currentPage === lastVisibleStack.length - 1) {
setLastVisibleStack((prev) => [...prev, projects.lastVisible]);
}
setCurrentPage((prev) => prev + 1);
}
};

const pagingReset = (): void => {
setLastVisibleStack([null]);
setCurrentPage(0);
};

return {
projects: projects?.projects || [],
currentPage,
paging: {
prev: pagingPrev,
next: pagingNext,
reset: pagingReset,
disablePrev,
disableNext,
},
};
};

export default useProjectPageNation;
12 changes: 9 additions & 3 deletions src/entities/projects/queries/useProjectList.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import type { DocumentData, QueryDocumentSnapshot } from "firebase/firestore";

import { getProjectList } from "@entities/projects/api/projectsAPi";
import type { ProjectListRes } from "@entities/projects/types/projects";

const useProjectList = (): UseQueryResult<ProjectListRes[]> => {
const useProjectList = (
lastDoc: QueryDocumentSnapshot<DocumentData> | null
): UseQueryResult<{
projects: ProjectListRes[];
lastVisible: QueryDocumentSnapshot<DocumentData> | null;
}> => {
return useQuery({
queryKey: ["project-list"],
queryFn: getProjectList,
queryKey: ["project-list", lastDoc?.id ?? "none"],
queryFn: () => getProjectList({ pageSize: 6, lastDoc }),
});
};

Expand Down
12 changes: 12 additions & 0 deletions src/entities/projects/queries/useProjectsTotalCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useQuery, type UseQueryResult } from "@tanstack/react-query";

import { getProjectsTotalCount } from "@entities/projects/api/projectsAPi";

const useProjectsTotalCount = (): UseQueryResult<number, Error> => {
return useQuery({
queryKey: ["projects-total-count"],
queryFn: getProjectsTotalCount,
});
};

export default useProjectsTotalCount;
8 changes: 8 additions & 0 deletions src/entities/projects/types/firebase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type {
DocumentData,
QueryDocumentSnapshot,
Timestamp,
} from "firebase/firestore";

export type CreatedAt = Timestamp;
export type LastVisibleType = (QueryDocumentSnapshot<DocumentData> | null)[];
2 changes: 0 additions & 2 deletions src/entities/projects/types/projects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// 나중에 Project Owner 정보가 타입으로 들어가야할 것 같음 + expectedPeriod 타입 수정 or 포맷팅해서 DB 저장

import type { UserRole } from "@shared/user/types/user";

// 팀원 목록들도 타입으로 들어가야할 것 같음, 이미지도 타입으로 들어가야할 것 같음
Expand All @@ -23,7 +22,6 @@ export interface ProjectItemInsertReq {
positions: Positions[]; // 모집 포지션
applicants: string[]; // 지원자들
}

interface Positions {
position: string;
count: number;
Expand Down
13 changes: 11 additions & 2 deletions src/features/projects/api/projdectsApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { addDoc, collection, doc, setDoc } from "firebase/firestore/lite";
import {
addDoc,
collection,
doc,
serverTimestamp,
setDoc,
} from "firebase/firestore/lite";

import type { ProjectItemInsertReq } from "@entities/projects/types/projects";

Expand All @@ -10,7 +16,10 @@ export const insertProjectItem = async (
): Promise<{ success: boolean; message: string; id?: string }> => {
try {
const postsRef = collection(db, "projects");
const docRef = await addDoc(postsRef, projectItem);
const docRef = await addDoc(postsRef, {
...projectItem,
createdAt: serverTimestamp(),
});

return {
success: true,
Expand Down
4 changes: 0 additions & 4 deletions src/pages/home/ui/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ import type { JSX } from "react";

import Hero from "@widgets/hero/ui/Hero";

import useProjectList from "@entities/projects/queries/useProjectList";
import ProjectCard from "@entities/projects/ui/projects-card/ProjectCard";
import ProjectsStats from "@entities/projects/ui/projects-stats/ProjectsStats";

const HomePage = (): JSX.Element => {
const { data } = useProjectList();

console.log(data);
console.log("API_KEY: ", import.meta.env.VITE_API_KEY);

return (
Expand Down
33 changes: 33 additions & 0 deletions src/pages/project-list/ui/ProjectList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { JSX } from "react";

import useProjectPageNation from "@entities/projects/hook/useProjectPageNation";
import useProjectsTotalCount from "@entities/projects/queries/useProjectsTotalCount";

import PaginationBar from "@shared/ui/pagination";

const ProjectList = (): JSX.Element => {
const { data: totalCount = 0 } = useProjectsTotalCount();
const { projects, currentPage, paging } = useProjectPageNation({
totalCount,
});

return (
<div>
{projects.map((item, i) => {
return (
<div key={i}>
{item.id} - {item.title}
</div>
);
})}

<PaginationBar
totalCount={totalCount}
currentPage={currentPage}
{...paging}
/>
</div>
);
};

export default ProjectList;
9 changes: 8 additions & 1 deletion src/pages/project-list/ui/ProjectListPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { JSX } from "react";

import ProjectList from "@pages/project-list/ui/ProjectList";

const ProjectListPage = (): JSX.Element => {
return <div>ProjectListPage</div>;
return (
<div>
ProjectListPage
<ProjectList />
</div>
);
};

export default ProjectListPage;
2 changes: 1 addition & 1 deletion src/shared/firebase/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider, GithubAuthProvider } from "firebase/auth";
import { getFirestore } from "firebase/firestore/lite";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
apiKey: import.meta.env.VITE_API_KEY,
Expand Down
11 changes: 11 additions & 0 deletions src/shared/libs/utils/paginnation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const makePagingArr = (totalCount: number): number[] => {
const perPage = 6;
const lastPageNum =
totalCount % perPage > 0
? Math.floor(totalCount / perPage) + 1
: Math.floor(totalCount / perPage);

const pageArray = Array.from({ length: lastPageNum }, (_, i) => i + 1);

return pageArray;
};
47 changes: 47 additions & 0 deletions src/shared/ui/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Box } from "@mui/material";
import type { JSX } from "react";

import { makePagingArr } from "@shared/libs/utils/paginnation";

interface PaginationProps {
totalCount?: number;
currentPage: number;
prev: () => void;
next: () => void;
reset: () => void;
disablePrev: boolean;
disableNext: boolean;
}

const PaginationBar = ({
totalCount = 0,
currentPage,
prev,
next,
reset,
disablePrev,
disableNext,
}: PaginationProps): JSX.Element => {
const paginArray = makePagingArr(totalCount);

return (
<Box display={"flex"}>
<button onClick={prev} disabled={disablePrev}>
이전
</button>

{paginArray.map((num, i) => (
<Box key={i} sx={currentPage === i ? { color: "red" } : {}}>
{num}
</Box>
))}

<button onClick={next} disabled={disableNext}>
다음
</button>
<button onClick={reset}>리셋</button>
</Box>
);
};

export default PaginationBar;