Skip to content
Merged

Api #37

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
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ function App() {
<Route path="/payment" element={<PaymentComplete />} />
<Route path="/list" element={<ProjectList />} />
<Route path="/reserve" element={<Reserve />} />

<Route path="*" element={<NotFound />} /> {/* 404 페이지 처리 */}
<Route
path="/mypage"
element={isLoggedIn ? <Mypage /> : <Navigate to="/login" replace />}
/>
<Route path="/cropinfo/:projectId" element={<CropInfo />} />
<Route path="/cropinfo" element={<CropInfo />} />
<Route path="/cropdiary" element={<CropDiary />} />
</Routes>
Expand Down
65 changes: 65 additions & 0 deletions src/api/Home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,68 @@ export const fetchProductsReview2 = async (): Promise<ReviewApiResponse> => {
};
}
};

export interface ProjectResponse {
projectId: number;
projectTitle: string;
farmer: {
name: string;
location: string;
specialNote: string;
};
price: number;
productImageUrl: string;
}

export interface DiaryResponse {
diaryId: number;
content: string;
status: string;
imageUrl: string;
tag: string;
createdAt: string;
}

export interface ProjectsApiResponse {
success: boolean;
response: ProjectResponse[];
error: null | string;
}

export interface DiaryApiResponse {
success: boolean;
response: DiaryResponse[];
error: null | string;
}

export const fetchLatestProjects = async (): Promise<ProjectsApiResponse> => {
try {
const response = await axios.get("api/projects/latest");
console.log("Fetched latest projects:", response.data);
return response.data;
} catch (error) {
console.error("Latest projects API 에러:", error);
return {
success: false,
response: [],
error: "프로젝트 데이터를 불러올 수 없습니다.",
};
}
};

export const fetchProjectDiaries = async (
projectId: number
): Promise<DiaryApiResponse> => {
try {
const response = await axios.get(`api/products/${projectId}/diaries`);
console.log(`Fetched diaries for project ${projectId}:`, response.data);
return response.data;
} catch (error) {
console.error(`Project ${projectId} 일지 API 에러:`, error);
return {
success: false,
response: [],
error: "프로젝트 일지 데이터를 불러올 수 없습니다.",
};
}
};
Binary file added src/assets/agriculture-6749210_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/apples-6947409_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/apples-8212695_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/avocado-8498520_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/carrot-1565597_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/carrot.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/chinese-cabbage-5798137_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/corn.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/grapes-8306833_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/kimchi-7613319_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/kimchi-7613328_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/liver-3306262_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/peach-2632182_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/potatoe.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/shrimp-599792_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/soil-8080788_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/sweetpotato.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/tomato.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/watermelon-1808136_1280.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 84 additions & 7 deletions src/components/project-list/ProductCardList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import ProductCard from "./ProductCard";
import { fetchLatestProjects, type ProjectResponse } from "../../api/Home";

const dummyData = Array.from({ length: 33 }, (_, i) => ({
interface ProjectCardData {
id: number;
imageUrl: string;
title: string;
price: string;
weightPerBox: string;
daysLeft: number;
rating: number;
reviewCount: number;
farmer?: {
name: string;
location: string;
specialNote: string;
};
projectId?: number;
}

const dummyData: ProjectCardData[] = Array.from({ length: 33 }, (_, i) => ({
id: i,
imageUrl: "/test_img.png",
title: `title ${i + 1}`,
Expand All @@ -16,24 +35,82 @@ const ITEMS_PER_PAGE = 12;

export default function ProductCardList() {
const [currentPage, setCurrentPage] = useState(0);
const [projects, setProjects] = useState<ProjectResponse[]>([]);
const [loading, setLoading] = useState(true);
const navigate = useNavigate();

useEffect(() => {
const loadProjects = async () => {
try {
setLoading(true);
const apiResponse = await fetchLatestProjects();
if (apiResponse.success && apiResponse.response) {
setProjects(apiResponse.response);
} else {
console.error("프로젝트 데이터 로드 실패:", apiResponse.error);
}
} catch (error) {
console.error("프로젝트 로드 중 에러:", error);
} finally {
setLoading(false);
}
};

loadProjects();
}, []);

const goToNextPage = (i: number) => {
setCurrentPage(i);
window.scrollTo({ top: 20, behavior: "smooth" });
window.scrollTo({ top: 100, behavior: "smooth" });
};

const totalPages = Math.ceil(dummyData.length / ITEMS_PER_PAGE);
const pagedData = dummyData.slice(
const handleProjectClick = (projectId: number) => {
navigate(`/cropInfo/${projectId}`);
};

// API 데이터가 있으면 사용하고, 없으면 더미 데이터 사용
const dataToShow: ProjectCardData[] =
projects.length > 0
? projects.map((project) => ({
id: project.projectId,
imageUrl: project.productImageUrl,
title: project.projectTitle,
price: `${project.price.toLocaleString()}원`,
weightPerBox: "1박스",
daysLeft: Math.floor(Math.random() * 30),
rating: 4 + Math.random(),
reviewCount: Math.floor(Math.random() * 50),
farmer: project.farmer,
projectId: project.projectId,
}))
: dummyData;

const totalPages = Math.ceil(dataToShow.length / ITEMS_PER_PAGE);
const pagedData = dataToShow.slice(
currentPage * ITEMS_PER_PAGE,
(currentPage + 1) * ITEMS_PER_PAGE
);

if (loading) {
return (
<div className="flex justify-center items-center min-h-[400px]">
<div className="text-lg">프로젝트를 불러오는 중...</div>
</div>
);
}

return (
<div className="">
{/* 카드 리스트 */}
<div className="grid grid-cols-4 gap-10 my-10">
{pagedData.map((item) => (
<ProductCard key={item.id} {...item} />
<div
key={item.id}
onClick={() => handleProjectClick(item.projectId || item.id)}
className="cursor-pointer"
>
<ProductCard {...item} />
</div>
))}
</div>

Expand All @@ -45,7 +122,7 @@ export default function ProductCardList() {
onClick={() => goToNextPage(i)}
className={`px-3 py-1 rounded text-sm font-medium transition-colors ${
currentPage === i
? "bg-blue-500 text-white"
? "bg-green-700 text-white"
: "bg-gray-200 text-black hover:bg-gray-300"
}`}
>
Expand Down
Loading