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/이미지api #147 #156

Merged
merged 5 commits into from
Nov 20, 2024
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
7 changes: 4 additions & 3 deletions src/api/farmApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ const useGetFarm = () => {
});
};

const useGetFarms = () => {
const useGetFarms = (categoryId: number | null = 0) => {
const fetcher = () => defaultApi.get(`/api/farms`).then(({ data }) => data);
const categoryFetcher = () => defaultApi.get(`/api/farms/category/${categoryId}`).then(({ data }) => data);

return useQuery({
queryKey: ["farms"],
queryFn: fetcher,
queryKey: ["farms", categoryId],
queryFn: categoryId !== 0 ? categoryFetcher : fetcher,
});
};

Expand Down
63 changes: 56 additions & 7 deletions src/api/imageApi.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,68 @@
import { useMutation } from "@tanstack/react-query";
import { needAuthDefaultApi } from "@api/axiosInstance";
import { externalApi, needAuthDefaultApi } from "@api/axiosInstance";

type ImageKeyPrefix = "FARM" | "FARM_REVIEW" | "PRODUCT" | "PRODUCT_REVIEW" | "MEMBER_PROFILE" | "PRODUCT_INTRO";

const getImageUpdatePresignedUrl = async (keyPrefix: ImageKeyPrefix) =>
needAuthDefaultApi.get("/api/s3/presigned-url-put", { params: { keyPrefix } });

const putImage2S3 = async (presignedUrl: string, image: File) => {
const formData = new FormData();
formData.append("image", image as Blob);

return externalApi.put(presignedUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
};

const putImage = async (type: ImageKeyPrefix, image: File) => {
const getPresignedUrlResponse = await getImageUpdatePresignedUrl(type);
if (!getPresignedUrlResponse.data) {
throw new Error("Failed to get presigned URL");
}
const { presignedPutUrl, keyName, objectUrl } = getPresignedUrlResponse.data;
const putImageResponse = await putImage2S3(presignedPutUrl, image);
if (putImageResponse.status === 200) {
return {
imageUrl: objectUrl,
objectKey: keyName,
};
}
throw new Error("Failed to put image");
};

type PostImageProps = {
type: ImageKeyPrefix;
image: File;
referenceId: number;
};
const usePostImage = () => {
const fetcher = (img: string) => {
const formData = new FormData();
formData.append("image", img);
return needAuthDefaultApi.post("/api/images", formData).then(res => res.data);
};
const fetcher = async ({ type, image, referenceId }: PostImageProps) =>
needAuthDefaultApi.post("/api/images", {
...(await putImage(type, image)),
type,
referenceId,
});

return useMutation({
mutationFn: fetcher,
onSuccess: data => data,
});
};

const usePostImages = () => null;
const usePostImages = () => {
const fetcher = async (images: PostImageProps[]) => {
const promises = images.map(image => putImage(image.type, image.image));
const results = await Promise.all(promises);
return needAuthDefaultApi.post("/api/images", results);
};

return useMutation({
mutationFn: fetcher,
onSuccess: data => data,
});
};

export { usePostImage, usePostImages };
7 changes: 4 additions & 3 deletions src/api/productApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ type ProductData = {
subDesc3: string;
};

const useGetProducts = () => {
const useGetProducts = (categoryId = 0) => {
const fetcher = () => defaultApi.get(`/api/products`).then(({ data }) => data);
const categoryFetcher = () => defaultApi.get(`/api/categories/${categoryId}`).then(({ data }) => data);

return useQuery({
queryKey: ["products"],
queryFn: fetcher,
queryKey: ["products", categoryId],
queryFn: categoryId ? categoryFetcher : fetcher,
});
};

Expand Down
4 changes: 2 additions & 2 deletions src/components/features/MyPage/Order/CancelOrderModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import { Flex, ModalBody, ModalHeader, ModalCloseButton, Button, Text, Divider } from "@chakra-ui/react";
import { useGetProductDetail } from "@api/productApi";
import BasicModal from "@components/common/modal/BasicModal";
import useGetProduct from "./useGetProduct";

interface CancelOrderModalProps {
isOpen: boolean;
Expand All @@ -18,7 +18,7 @@ const CancelOrderModal: React.FC<CancelOrderModalProps> = ({
maxH = "800px",
productId,
}) => {
const { data: productData } = useGetProduct(productId);
const { data: productData } = useGetProductDetail(productId);

const [totalRefundAmount, setTotalRefundAmount] = useState(0);

Expand Down
4 changes: 2 additions & 2 deletions src/components/features/MyPage/Order/RefundModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import { Flex, ModalBody, ModalHeader, ModalCloseButton, Button, Text, Divider } from "@chakra-ui/react";
import { useGetProductDetail } from "@api/productApi";
import BasicModal from "@components/common/modal/BasicModal";
import useGetProduct from "./useGetProduct";

interface RefundModalProps {
isOpen: boolean;
Expand All @@ -12,7 +12,7 @@ interface RefundModalProps {
}

const RefundModal: React.FC<RefundModalProps> = ({ isOpen, onClose, maxW = "600px", maxH = "800px", productId }) => {
const { data: productData } = useGetProduct(productId);
const { data: productData } = useGetProductDetail(productId);

const [totalRefundAmount, setTotalRefundAmount] = useState(0);

Expand Down
14 changes: 0 additions & 14 deletions src/components/features/MyPage/Order/useGetProduct.tsx

This file was deleted.

5 changes: 4 additions & 1 deletion src/components/features/RegisterPage/Farmer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
import { Button, Text, Flex, Input, Image } from "@chakra-ui/react";
import { useCreateFarmer } from "@api/emailApi";
import call from "@assets/logo/Call.png";
import useLogin from "@hooks/useLogin";

type FarmerData = {
name: string;
Expand All @@ -26,10 +27,12 @@ const Farmer = () => {
const { mutateAsync: createFarmer } = useCreateFarmer();
const navigate = useNavigate();

const { refreshLogin } = useLogin();

const handleRegister = () => {
createFarmer(farmerData)
.then(() => {
navigate("/");
refreshLogin().then(() => navigate("/"));
})
.catch(() => {
alert("농부 등록에 실패했습니다.");
Expand Down
9 changes: 8 additions & 1 deletion src/components/features/SchedulePage/BestScheduleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ const BestScheduleSection = () => (
<Text py="3" fontSize="2xl" fontWeight="bold">
이번주 Best
</Text>
<GridView items={mockBestSchedule} columns={2} gap="20px" ItemComponent={ScheduleCard} />
<GridView
items={mockBestSchedule.map(item => ({
...item,
}))}
columns={2}
gap="20px"
ItemComponent={ScheduleCard}
/>
</Box>
);

Expand Down
16 changes: 10 additions & 6 deletions src/components/features/SchedulePage/ScheduleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import ImageCard, { ImageCardProps } from "@components/common/ImageCard";
import { Schedule } from "@type/index";

type ScheduleCardProps = ImageCardProps & {
item: Schedule;
item: Schedule & { link?: string };
};

const ScheduleCard = ({ item, ...props }: ScheduleCardProps) => (
<Box as={Link} to={`/schedule/${item.id}`}>
<Box {...(item.link ? { as: Link, to: item.link } : {})}>
<ImageCard
h="full"
{...props}
Expand All @@ -17,13 +17,17 @@ const ScheduleCard = ({ item, ...props }: ScheduleCardProps) => (
brightness: item?.mainImage ? 1 : 0.6,
}}
bgImg={item.mainImage}
_hover={{
transform: "scale(1.05)",
}}
_hover={
item.link
? {
transform: "scale(1.05)",
}
: {}
}
>
<Flex align="center" direction="column" w="full" mt="auto" mb="10">
<Text px="5" color="green" bg="white" borderRadius="xl">
{item.farm.address}
{item.address}
</Text>
<Text color="white" fontSize="2xl" fontWeight="bold">
{item.name}
Expand Down
10 changes: 5 additions & 5 deletions src/components/features/StorePage/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Link } from "react-router-dom";
import { Flex, Text } from "@chakra-ui/react";
import { Box, Flex, Text } from "@chakra-ui/react";
import Avatar from "@components/common/Avatar";
import Card, { CardProps } from "@components/common/Card";
import Image from "@components/common/Image";
import { Product } from "@type/index";

export type ProductCardProps = {
item: Product;
item: Product & { link?: string };
} & CardProps;

const ProductCard = ({ item, ...props }: ProductCardProps) => (
<Link to={`/store/${item.id}`}>
<Card {...props} _hover={{ transform: "translateY(-10px)" }}>
<Box {...(!item.link && { as: Link, to: item.link })}>
<Card {...props} _hover={item.link ? { transform: "translateY(-10px)" } : {}}>
<Image w="full" borderRadius="2xl" alt={item.name} aspectRatio="1" src={item.mainImage} />
<Flex py="3">
<Text maxW="70%" fontSize="lg" fontWeight="bold" isTruncated>
Expand All @@ -34,7 +34,7 @@ const ProductCard = ({ item, ...props }: ProductCardProps) => (
<Text ml="1">{item.farm.name}</Text>
</Flex>
</Card>
</Link>
</Box>
);

export default ProductCard;
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Divider, Flex, Text } from "@chakra-ui/react";
import { useGetProductCategories } from "@api/categoryApi";
import ProductGroupView from "@components/features/StorePage/ProductList/ProductFilter/ProductGroupView";
import { ProductFilterItem } from "@components/features/StorePage/ProductList/ProductFilter/type";
import productGroup from "@constants/productGroup";
import useFilters, { UseFilters } from "@hooks/useFilters";
import { encodeCategory } from "@utils/categoryParser";

type ProductFilterProps = {
items?: ProductFilterItem[];
filterState?: UseFilters;
};

const ProductFilter = ({ items = [], filterState = useFilters() }: ProductFilterProps) => (
<Flex direction="column" w="250px" h="100%" pt="10">
<Text w="full" py="5" fontSize="xl" fontWeight="bold">
필터
</Text>
<Divider />
{productGroup.map(group => (
<ProductGroupView key={group.group} group={group} items={items} filterState={filterState} />
))}
</Flex>
);
const ProductFilter = ({ filterState = useFilters() }: ProductFilterProps) => {
const { data: productCategories } = useGetProductCategories();

return (
<Flex direction="column" w="250px" h="100%" pt="10">
<Text w="full" py="5" fontSize="xl" fontWeight="bold">
필터
</Text>
<Divider />
{productGroup.map(group => (
<ProductGroupView
key={group.group}
group={group}
items={(productCategories || []).map(encodeCategory)}
filterState={filterState}
/>
))}
</Flex>
);
};

export default ProductFilter;
12 changes: 9 additions & 3 deletions src/components/features/StorePage/ProductList/ProductsView.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { useGetProducts } from "@api/productApi";
import GridView, { GridViewProps } from "@components/ItemView/GridView";
import ProductCard from "@components/features/StorePage/ProductCard";
import mockProducts from "@mocks/mockItem/mockProducts";
import { Product } from "@type/index";

type ProductsViewProps = {
filters: string[];
} & Omit<GridViewProps<Product>, "items" | "ItemComponent">;

const ProductsView = ({ filters, ...props }: ProductsViewProps) => {
const filteredProducts = mockProducts.filter(() => filters.length === 0);
const { data: products } = useGetProducts(filters[0] ? Number(filters[0]) : 0);

return <GridView items={filteredProducts} ItemComponent={ProductCard} {...props} />;
return (
<GridView
items={(products || []).map((p: Product) => ({ ...p, link: `/store/${p.id}` }))}
ItemComponent={ProductCard}
{...props}
/>
);
};

export default ProductsView;
4 changes: 1 addition & 3 deletions src/components/features/StorePage/ProductList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import { Flex } from "@chakra-ui/react";
import ProductFilter from "@components/features/StorePage/ProductList/ProductFilter";
import ProductsView from "@components/features/StorePage/ProductList/ProductsView";
import useFilters from "@hooks/useFilters";
import mockCategory from "@mocks/mockItem/mockCategory";
import { encodeCategory } from "@utils/categoryParser";

const ProductList = () => {
const productFilterState = useFilters();

return (
<Flex gap="5" w="100%" h="100vh">
<ProductFilter filterState={productFilterState} items={mockCategory.map(encodeCategory)} />
<ProductFilter filterState={productFilterState} />
<Flex overflow="scroll" w="100%" h="100%">
<ProductsView filters={productFilterState.filters} py="10" columns={3} gap="25px" />
</Flex>
Expand Down
Loading
Loading