| import Card from '@mui/material/Card'; | |||||
| import CardContent from '@mui/material/CardContent'; | |||||
| import Typography from '@mui/material/Typography'; | |||||
| import Image from 'next/image'; | |||||
| const DataDetailsCard = ({ data }) => { | |||||
| return ( | |||||
| <Card | |||||
| sx={{ | |||||
| maxWidth: 600, | |||||
| height: 200, | |||||
| marginX: 'auto', | |||||
| marginY: 20, | |||||
| boxShadow: 10, | |||||
| display: 'flex', | |||||
| }} | |||||
| > | |||||
| <Image | |||||
| src="https://www.business2community.com/wp-content/uploads/2017/08/blank-profile-picture-973460_640.png" | |||||
| alt="profile picture" | |||||
| width={600} | |||||
| height={500} | |||||
| /> | |||||
| <CardContent> | |||||
| <Typography | |||||
| gutterBottom | |||||
| variant="h5" | |||||
| component="div" | |||||
| sx={{ | |||||
| textAlign: 'center', | |||||
| marginTop: 1, | |||||
| marginBottom: 3, | |||||
| }} | |||||
| > | |||||
| {data.name}, {data.age} | |||||
| </Typography> | |||||
| <Typography variant="body2" color="text.secondary"> | |||||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur | |||||
| quis odio in libero fringilla pellentesque aliquet et mi. Quisque | |||||
| maximus lectus a neque luctus, tempus auctor ipsum ultrices. | |||||
| </Typography> | |||||
| </CardContent> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default DataDetailsCard; |
| const base = { | |||||
| profileData: { name: 'John Doe' }, | |||||
| }; | |||||
| export const mockDataDetailsCardProps = { | |||||
| base, | |||||
| }; |
| import DataDetailsCard from './DataDetailsCard'; | |||||
| import { mockDataDetailsCardProps } from './ProfileCard.mock'; | |||||
| const obj = { | |||||
| title: 'cards/DataDetailsCard', | |||||
| component: DataDetailsCard, | |||||
| // More on argTypes: https://storybook.js.org/docs/react/api/argtypes | |||||
| argTypes: {}, | |||||
| }; //eslint-disable-line | |||||
| export default obj; | |||||
| // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args | |||||
| const Template = (args) => <DataDetailsCard {...args} />; | |||||
| export const Base = Template.bind({}); | |||||
| // More on args: https://storybook.js.org/docs/react/writing-stories/args | |||||
| Base.args = { | |||||
| ...mockDataDetailsCardProps.base, | |||||
| }; |
| import { Box, Button, Grid, Paper, Typography } from '@mui/material'; | import { Box, Button, Grid, Paper, Typography } from '@mui/material'; | ||||
| import { useTranslation } from 'next-i18next'; | import { useTranslation } from 'next-i18next'; | ||||
| import { useRouter } from 'next/router'; | |||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import { SINGLE_DATA_PAGE } from '../../../constants/pages'; | |||||
| import useDebounce from '../../../hooks/use-debounce'; | import useDebounce from '../../../hooks/use-debounce'; | ||||
| import { usePagination } from '../../../hooks/use-pagination'; | import { usePagination } from '../../../hooks/use-pagination'; | ||||
| import { compare } from '../../../utils/helpers/sortHelpers'; | import { compare } from '../../../utils/helpers/sortHelpers'; | ||||
| const [sort, setSort] = useState(''); | const [sort, setSort] = useState(''); | ||||
| const { t } = useTranslation('pagination'); | const { t } = useTranslation('pagination'); | ||||
| const { data: paginationData } = usePagination(pageIndex); | const { data: paginationData } = usePagination(pageIndex); | ||||
| const router = useRouter(); | |||||
| const debouncedFilter = useDebounce(filter, 500); | const debouncedFilter = useDebounce(filter, 500); | ||||
| const handleFilterChange = (event) => { | const handleFilterChange = (event) => { | ||||
| setSort(sort); | setSort(sort); | ||||
| }; | }; | ||||
| const loadSingleDataHandler = (id) => { | |||||
| router.push(`${SINGLE_DATA_PAGE}${id}`); | |||||
| }; | |||||
| const dataToDisplay = paginationData?.data | const dataToDisplay = paginationData?.data | ||||
| .filter((item) => | .filter((item) => | ||||
| item.name.toLowerCase().startsWith(debouncedFilter.toLowerCase()) | item.name.toLowerCase().startsWith(debouncedFilter.toLowerCase()) | ||||
| .sort((a, b) => compare(a.name, b.name, sort)) | .sort((a, b) => compare(a.name, b.name, sort)) | ||||
| .map((item, index) => ( | .map((item, index) => ( | ||||
| // ! DON'T USE index for key, this is for example only | // ! DON'T USE index for key, this is for example only | ||||
| <Grid item sx={{ p: 2 }} xs={12} sm={6} md={4} lg={3} key={index}> | |||||
| <Grid | |||||
| item | |||||
| sx={{ p: 2 }} | |||||
| xs={12} | |||||
| sm={6} | |||||
| md={4} | |||||
| lg={3} | |||||
| key={index} | |||||
| onClick={loadSingleDataHandler.bind(null, item.customID)} | |||||
| > | |||||
| <DataCard data={item} t={t} /> | <DataCard data={item} t={t} /> | ||||
| </Grid> | </Grid> | ||||
| )); | )); |
| export const BASE_PAGE = "/"; | |||||
| export const LOGIN_PAGE = "/auth"; | |||||
| export const PROFILE_PAGE = "/profile"; | |||||
| export const REGISTER_PAGE = "/auth/register"; | |||||
| export const FORGOT_PASSWORD_PAGE = "/auth/forgot-password"; | |||||
| export const BASE_PAGE = '/'; | |||||
| export const LOGIN_PAGE = '/auth'; | |||||
| export const PROFILE_PAGE = '/profile'; | |||||
| export const REGISTER_PAGE = '/auth/register'; | |||||
| export const FORGOT_PASSWORD_PAGE = '/auth/forgot-password'; | |||||
| export const SINGLE_DATA_PAGE = '/single-data/'; |
| import { connectToDatabase } from '../../../utils/helpers/dbHelpers'; | |||||
| async function handler(req, res) { | |||||
| if (req.method !== 'GET') { | |||||
| return; | |||||
| } | |||||
| const dataId = req.query.dataId; | |||||
| let client; | |||||
| try { | |||||
| client = await connectToDatabase(); | |||||
| } catch (error) { | |||||
| res.status(500).json({ message: 'Connecting to the database failed!' }); | |||||
| return; | |||||
| } | |||||
| const db = client.db(); | |||||
| const singleData = await db | |||||
| .collection('randomData') | |||||
| .findOne({ customID: dataId }); | |||||
| if (!singleData) { | |||||
| res.status(422).json({ message: 'No data!' }); | |||||
| client.close(); | |||||
| return; | |||||
| } | |||||
| res.status(201).json({ | |||||
| message: 'Fetch single data successfull!', | |||||
| singleData: singleData, | |||||
| }); | |||||
| setTimeout(() => { | |||||
| client.close(); | |||||
| }, 1500); | |||||
| } | |||||
| export default handler; |
| import { connectToDatabase } from '../../../utils/helpers/dbHelpers'; | |||||
| async function handler(req, res) { | |||||
| if (req.method !== 'GET') { | |||||
| return; | |||||
| } | |||||
| const client = await connectToDatabase(); | |||||
| const db = client.db(); | |||||
| const dataFromDB = await db | |||||
| .collection('randomData') | |||||
| .find() | |||||
| .limit(4) | |||||
| .toArray(); | |||||
| if (!dataFromDB) { | |||||
| res.status(422).json({ message: 'No data!' }); | |||||
| client.close(); | |||||
| return; | |||||
| } | |||||
| const dataIds = dataFromDB.map((item) => item.customID); | |||||
| res.status(201).json({ | |||||
| message: 'Fetch ids successfull!', | |||||
| dataIds: dataIds, | |||||
| }); | |||||
| setTimeout(() => { | |||||
| client.close(); | |||||
| }, 1500); | |||||
| } | |||||
| export default handler; |
| import DataDetailsCard from '../../components/cards/data-details-card/DataDetailsCard'; | |||||
| import { getDataIds } from '../../requests/dataIdsRequest'; | |||||
| import { getSingleData } from '../../requests/singleDataRequest'; | |||||
| function SignelDataPage(props) { | |||||
| const data = props.selectedData; | |||||
| if (!data) { | |||||
| return <h1>No data!</h1>; | |||||
| } | |||||
| return <DataDetailsCard data={data.singleData} />; | |||||
| } | |||||
| export async function getStaticProps(context) { | |||||
| const dataId = context.params.dataId; | |||||
| try { | |||||
| const data = await getSingleData(dataId); | |||||
| return { | |||||
| props: { | |||||
| selectedData: data, | |||||
| }, | |||||
| revalidate: 60, | |||||
| }; | |||||
| } catch (error) { | |||||
| return { | |||||
| props: { | |||||
| selectedData: null, | |||||
| }, | |||||
| revalidate: 60, | |||||
| }; | |||||
| } | |||||
| } | |||||
| export async function getStaticPaths() { | |||||
| const firstPageDataIds = await getDataIds(); | |||||
| console.log(firstPageDataIds); | |||||
| const paths = firstPageDataIds.dataIds.map((id) => ({ | |||||
| params: { dataId: id }, | |||||
| })); | |||||
| return { | |||||
| paths: paths, | |||||
| fallback: 'blocking', | |||||
| }; | |||||
| } | |||||
| export default SignelDataPage; |
| createUser: '/api/auth/signup', | createUser: '/api/auth/signup', | ||||
| }, | }, | ||||
| data: '/api/data', | data: '/api/data', | ||||
| dataIds: '/api/single-data', | |||||
| singleData: '/api/single-data/', | |||||
| }; | }; |
| import apiEndpoints from './apiEndpoints'; | |||||
| export const getDataIds = async () => { | |||||
| const response = await fetch(`http://localhost:3000/${apiEndpoints.dataIds}`); | |||||
| const data = await response.json(); | |||||
| if (!response.ok) { | |||||
| throw new Error(data.message || 'Something went wrong!'); | |||||
| } | |||||
| return data; | |||||
| }; |
| import apiEndpoints from './apiEndpoints'; | |||||
| export const getSingleData = async (dataId) => { | |||||
| const response = await fetch( | |||||
| `http://localhost:3000/${apiEndpoints.singleData}${dataId}` | |||||
| ); | |||||
| const data = await response.json(); | |||||
| if (!response.ok) { | |||||
| throw new Error(data.message || 'Something went wrong!'); | |||||
| } | |||||
| return data; | |||||
| }; |