products-typescript kohteeseen master 3 vuotta sitten
| @@ -2,6 +2,7 @@ import { Typography } from '@mui/material'; | |||
| import { Box } from '@mui/system'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| import Image from 'next/image'; | |||
| const CompanyInfo: React.FC = () => { | |||
| const { t } = useTranslation('home'); | |||
| return ( | |||
| @@ -1,12 +1,13 @@ | |||
| import { Box } from '@mui/system'; | |||
| import ProductType from '../product-type/ProductType'; | |||
| import Sort from '../sort/Sort'; | |||
| import { SelectChangeEvent } from "@mui/material"; | |||
| interface FilterSortProps { | |||
| sort: string; | |||
| handleSortChange: () => void; | |||
| handleSortChange: (e: SelectChangeEvent) => void; | |||
| productType: string; | |||
| handleProductTypeChange: () => void; | |||
| handleProductTypeChange: (e: SelectChangeEvent) => void; | |||
| } | |||
| const FilterSort: React.FC<FilterSortProps> = ({ | |||
| @@ -14,9 +14,9 @@ import { LOGIN_PAGE } from '../../../constants/pages'; | |||
| import { forgotPasswordSchema } from '../../../schemas/forgotPasswordSchema'; | |||
| const ForgotPasswordForm = () => { | |||
| const { t } = useTranslation('forms', 'forgotPass', 'common'); | |||
| const { t } = useTranslation(['forms', 'forgotPass', 'common']); | |||
| const handleSubmit = (values) => { | |||
| const handleSubmit = (values: {email: string}) => { | |||
| console.log('Values', values); | |||
| }; | |||
| @@ -22,8 +22,13 @@ import { | |||
| import { loginSchema } from '../../../schemas/loginSchema'; | |||
| import ErrorMessageComponent from '../../mui/ErrorMessageComponent'; | |||
| interface Values { | |||
| username: string; | |||
| password: string; | |||
| } | |||
| const LoginForm = () => { | |||
| const { t } = useTranslation('forms', 'login'); | |||
| const { t } = useTranslation(['forms', 'login']); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| @@ -31,16 +36,16 @@ const LoginForm = () => { | |||
| const router = useRouter(); | |||
| const [error, setError] = useState({ hasError: false, errorMessage: '' }); | |||
| const submitHandler = async (values) => { | |||
| const submitHandler = async (values: Values) => { | |||
| const result = await signIn('credentials', { | |||
| redirect: false, | |||
| username: values.username, | |||
| password: values.password, | |||
| }); | |||
| if (!result.error) { | |||
| if (!result?.error) { | |||
| router.replace(BASE_PAGE); | |||
| } else { | |||
| setError({ hasError: true, errorMessage: result.error }); | |||
| setError({ hasError: true, errorMessage: result?.error }); | |||
| } | |||
| }; | |||
| @@ -19,8 +19,20 @@ import { createUser } from '../../../requests/accounts/accountRequests'; | |||
| import { registerSchema } from '../../../schemas/registerSchema'; | |||
| import ErrorMessageComponent from '../../mui/ErrorMessageComponent'; | |||
| interface Values { | |||
| fullName: string; | |||
| username: string; | |||
| email: string; | |||
| password: string; | |||
| address: string; | |||
| address2: string; | |||
| city: string; | |||
| country: string; | |||
| postcode: string; | |||
| } | |||
| const RegisterForm = () => { | |||
| const { t } = useTranslation('forms', 'register'); | |||
| const { t } = useTranslation(['forms', 'register']); | |||
| const router = useRouter(); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| @@ -35,7 +47,7 @@ const RegisterForm = () => { | |||
| const [error, setError] = useState({ hasError: false, errorMessage: '' }); | |||
| const submitHandler = async (values) => { | |||
| const submitHandler = async (values: Values) => { | |||
| try { | |||
| const result = await createUser( | |||
| values.fullName, | |||
| @@ -50,7 +62,9 @@ const RegisterForm = () => { | |||
| ); | |||
| router.push(LOGIN_PAGE); | |||
| } catch (error) { | |||
| setError({ hasError: true, errorMessage: error.message }); | |||
| if (error instanceof Error) { | |||
| setError({ hasError: true, errorMessage: error?.message }); | |||
| } | |||
| } | |||
| }; | |||
| @@ -1,6 +1,10 @@ | |||
| import { Grid } from '@mui/material'; | |||
| const GridItem = ({ children }) => { | |||
| interface Props { | |||
| children: JSX.Element | |||
| } | |||
| const GridItem: React.FC<Props> = ({ children }) => { | |||
| return ( | |||
| <Grid item md={4} sm={6} xs={12} sx={{ mb: '100px' }}> | |||
| {children} | |||
| @@ -5,7 +5,7 @@ import Image from 'next/image'; | |||
| import { useRouter } from 'next/router'; | |||
| import { PRODUCTS_PAGE } from '../../constants/pages'; | |||
| const Hero = () => { | |||
| const Hero: React.FC = () => { | |||
| const { t } = useTranslation('home'); | |||
| const router = useRouter(); | |||
| @@ -1,6 +1,6 @@ | |||
| const { CircularProgress, Box } = require('@mui/material'); | |||
| const LoadingSpinner = () => { | |||
| const LoadingSpinner: React.FC = () => { | |||
| return ( | |||
| <Box display="flex" justifyContent="center" sx={{ mt: 5 }}> | |||
| <CircularProgress /> | |||
| @@ -1,9 +1,9 @@ | |||
| import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'; | |||
| import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| interface ProductTypeProps { | |||
| productType: string; | |||
| handleProductTypeChange: () => void; | |||
| handleProductTypeChange: (e: SelectChangeEvent) => void; | |||
| } | |||
| const ProductType: React.FC<ProductTypeProps> = ({ productType, handleProductTypeChange }) => { | |||
| @@ -1,3 +1,4 @@ | |||
| import { SelectChangeEvent } from '@mui/material'; | |||
| import { Box } from '@mui/system'; | |||
| import Head from 'next/head'; | |||
| import { useState } from 'react'; | |||
| @@ -6,27 +7,21 @@ import FilterSort from '../filter-sort/FilterSort'; | |||
| import LoadingSpinner from '../loader/basic-spinner/LoadSpinner'; | |||
| import ProductsGrid from '../products-grid/ProductsGrid'; | |||
| import ProductsHero from '../products-hero/ProductsHero'; | |||
| import { SelectChangeEvent } from "@mui/material"; | |||
| const ProductsContent = () => { | |||
| const ProductsContent: React.FC = () => { | |||
| const [filter, setFilter] = useState<string>(''); | |||
| const [sort, setSort] = useState<string>(''); | |||
| const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = | |||
| useInfiniteProducts(filter, sort); | |||
| const handleProductTypeChange = () => { | |||
| return (event: SelectChangeEvent) => { | |||
| const filterText = event.target.value; | |||
| setFilter(filterText); | |||
| } | |||
| const handleProductTypeChange = (event: SelectChangeEvent) => { | |||
| const filterText = event.target.value; | |||
| setFilter(filterText); | |||
| }; | |||
| const handleSortChange = () => { | |||
| return (event: SelectChangeEvent) => { | |||
| const sort = event.target.value; | |||
| setSort(sort); | |||
| } | |||
| const handleSortChange = (event: SelectChangeEvent) => { | |||
| const sort = event.target.value; | |||
| setSort(sort); | |||
| }; | |||
| return ( | |||
| @@ -1,10 +1,30 @@ | |||
| import { Container, Grid } from '@mui/material'; | |||
| import { Box } from '@mui/system'; | |||
| import { ProductDataDB } from '../../utils/interface/productInterface'; | |||
| import LoadMore from '../buttons/load-more/LoadMore'; | |||
| import GridItem from '../grid-item/GridItem'; | |||
| import ProductCard from '../product-card/ProductCard'; | |||
| const ProductsGrid = ({ | |||
| interface PageProps { | |||
| message: string; | |||
| next: string; | |||
| prevous: string; | |||
| product: ProductDataDB[]; | |||
| productCount: number; | |||
| } | |||
| interface ProductsProps { | |||
| pages: PageProps[]; | |||
| } | |||
| interface Props { | |||
| allProducts: ProductsProps | |||
| hasNextPage: boolean; | |||
| isFetchingNextPage: boolean; | |||
| fetchNextPage: () => void; | |||
| } | |||
| const ProductsGrid: React.FC<Props> = ({ | |||
| allProducts, | |||
| hasNextPage, | |||
| fetchNextPage, | |||
| @@ -2,7 +2,7 @@ import { Container, Typography } from '@mui/material'; | |||
| import { Box } from '@mui/system'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| const ProductsHero = () => { | |||
| const ProductsHero: React.FC = () => { | |||
| const { t } = useTranslation('products'); | |||
| return ( | |||
| <Box | |||
| @@ -3,7 +3,6 @@ import { Box } from '@mui/system'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| import Image from 'next/image'; | |||
| import { useState } from 'react'; | |||
| import { ProductDataDB } from '../../../utils/interface/productInterface'; | |||
| interface DataProps { | |||
| name: string; | |||
| @@ -1,9 +1,9 @@ | |||
| import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'; | |||
| import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| interface SortProps { | |||
| sort: string; | |||
| handleSortChange: () => void; | |||
| handleSortChange: (e: SelectChangeEvent) => void; | |||
| } | |||
| const Sort: React.FC<SortProps> = ({ sort, handleSortChange }) => { | |||
| @@ -12,7 +12,7 @@ export const useInfiniteProducts = (category: string, filter: string) => { | |||
| ), | |||
| { | |||
| getNextPageParam: (lastPage, pages) => { | |||
| if (lastPage.next !== null) { | |||
| if (lastPage.next !== '') { | |||
| return pages.length + 1; | |||
| } | |||
| }, | |||
| @@ -11,9 +11,13 @@ import CompanyInfo from '../components/company-info/CompanyInfo'; | |||
| import { useUserUpdate } from '../store/user-context'; | |||
| import { getStorage } from '../utils/helpers/storage'; | |||
| import { useEffect } from 'react'; | |||
| import { ProductDataDB } from '../utils/interface/productInterface'; | |||
| interface Props { | |||
| data: { featuredProducts: ProductDataDB[], message: string }; | |||
| } | |||
| const Home: NextPage = ({ featuredProducts }) => { | |||
| const Home: NextPage<Props> = ({ data }) => { | |||
| const { data: session } = useSession(); | |||
| const { addUser } = useUserUpdate(); | |||
| @@ -33,7 +37,7 @@ const Home: NextPage = ({ featuredProducts }) => { | |||
| </Head> | |||
| <Hero /> | |||
| <FeaturedProductsList | |||
| featuredProducts={featuredProducts} | |||
| featuredProducts={data.featuredProducts} | |||
| ></FeaturedProductsList> | |||
| <Features /> | |||
| <CompanyInfo /> | |||
| @@ -44,19 +48,17 @@ const Home: NextPage = ({ featuredProducts }) => { | |||
| export async function getStaticProps({ locale }: any) { | |||
| try { | |||
| const { message, featuredProducts } = await getFeaturedProducts(); | |||
| const data = await getFeaturedProducts(); | |||
| return { | |||
| props: { | |||
| ...(await serverSideTranslations(locale, ["home"])), | |||
| message, | |||
| featuredProducts, | |||
| data: data | |||
| }, | |||
| }; | |||
| } catch (error) { | |||
| return { | |||
| props: { | |||
| ...(await serverSideTranslations(locale, ['home'])), | |||
| errorMessage: error, | |||
| featuredProducts: [], | |||
| }, | |||
| }; | |||
| @@ -14,30 +14,31 @@ import TabContent from '../../components/tab-content/TabContent'; | |||
| import { useFetchSingleProduct } from '../../hooks/useFetchProductData'; | |||
| import { getProductData } from '../../requests/products/producDataRequest'; | |||
| import { useStore, useStoreUpdate } from '../../store/cart-context'; | |||
| import { SingleProductResponseGet } from '../../utils/interface/productInterface'; | |||
| const SingleProduct: NextPage = () => { | |||
| interface Props { | |||
| data: SingleProductResponseGet; | |||
| } | |||
| const SingleProduct: NextPage<Props> = ({ data }) => { | |||
| const { t } = useTranslation('products'); | |||
| const { addCartValue } = useStoreUpdate(); | |||
| const { cartStorage } = useStore(); | |||
| const router = useRouter(); | |||
| const { customId } = router.query; | |||
| const customId = router.query.customId ? router.query.customId as string : undefined; | |||
| const { data, isLoading } = useFetchSingleProduct(customId); | |||
| // const { data, isLoading } = useFetchSingleProduct(customId); | |||
| const addProductToCart = (quantity) => addCartValue(data.product, quantity); | |||
| const addProductToCart = (quantity: number) => addCartValue(data?.product, quantity); | |||
| const inCart = | |||
| cartStorage?.length > 0 | |||
| ? cartStorage?.some((item) => item.product.customID === product.customID) | |||
| ? cartStorage?.some((item) => item.product.customID === data?.product.customID) | |||
| ? true | |||
| : false | |||
| : false; | |||
| if (isLoading) { | |||
| return <Loader loading={isLoading} />; | |||
| } | |||
| return ( | |||
| <Box | |||
| sx={{ | |||
| @@ -51,12 +52,12 @@ const SingleProduct: NextPage = () => { | |||
| fontSize="32px" | |||
| sx={{ mt: 25, height: '100%', color: 'primary.main' }} | |||
| > | |||
| {data.product.name} | |||
| {data?.product.name} | |||
| </Typography> | |||
| <Grid container spacing={2}> | |||
| <Grid sx={{ display: 'flex' }} item md={6} sm={12}> | |||
| <Image | |||
| src={data.product.image} | |||
| src={data?.product.image} | |||
| alt="product" | |||
| width={900} | |||
| height={700} | |||
| @@ -82,7 +83,7 @@ const SingleProduct: NextPage = () => { | |||
| {t('products:similar')} | |||
| </Typography> | |||
| <Grid container spacing={2}> | |||
| {data.similarProducts.map((product) => ( | |||
| {data?.similarProducts.map((product) => ( | |||
| <GridItem key={product._id}> | |||
| <ProductCard product={product} /> | |||
| </GridItem> | |||
| @@ -97,15 +98,22 @@ export const getServerSideProps = async (context: any) => { | |||
| const { params } = context; | |||
| const { customId } = params; | |||
| console.log(customId); | |||
| const queryClient = new QueryClient(); | |||
| await queryClient.prefetchQuery( | |||
| const data = await queryClient.fetchQuery( | |||
| ['product', customId], | |||
| async () => await getProductData(customId) | |||
| ); | |||
| await queryClient.fetchQuery( | |||
| ['product', customId], | |||
| async () => await getProductData(customId) | |||
| ); | |||
| return { | |||
| props: { | |||
| data: data, | |||
| dehydratatedState: dehydrate(queryClient), | |||
| ...(await serverSideTranslations(context.locale, ['products'])), | |||
| }, | |||
| @@ -42,7 +42,7 @@ export interface ProductsResponsePost { | |||
| product: ProductData; | |||
| } | |||
| interface SingleProductResponseGet { | |||
| export interface SingleProductResponseGet { | |||
| message: string; | |||
| product: ProductDataDB; | |||
| similarProducts: Array<ProductDataDB>; | |||
| @@ -58,5 +58,5 @@ export type ProductsResponse = | |||
| | ProductsResponseError; | |||
| export type FeaturedProductsResponse = | |||
| | FeaturedProductsResponseGet | |||
| FeaturedProductsResponseGet | |||
| | ProductsResponseError; | |||