瀏覽代碼

feat: added single data page (dynamic routing)

hover-contact
ntasicc 3 年之前
父節點
當前提交
147f3cfa9c

+ 47
- 0
components/cards/data-details-card/DataDetailsCard.jsx 查看文件

@@ -0,0 +1,47 @@
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;

+ 7
- 0
components/cards/data-details-card/DataDetailsCard.mock.js 查看文件

@@ -0,0 +1,7 @@
const base = {
profileData: { name: 'John Doe' },
};

export const mockDataDetailsCardProps = {
base,
};

+ 20
- 0
components/cards/data-details-card/DataDetailsCard.stories.jsx 查看文件

@@ -0,0 +1,20 @@
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,
};

+ 17
- 2
components/pagination/react-query/PaginationComponentRQ.jsx 查看文件

@@ -1,6 +1,8 @@
import { Box, Button, Grid, Paper, Typography } from '@mui/material';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { SINGLE_DATA_PAGE } from '../../../constants/pages';
import useDebounce from '../../../hooks/use-debounce';
import { usePagination } from '../../../hooks/use-pagination';
import { compare } from '../../../utils/helpers/sortHelpers';
@@ -13,7 +15,7 @@ const PaginationComponentRQ = () => {
const [sort, setSort] = useState('');
const { t } = useTranslation('pagination');
const { data: paginationData } = usePagination(pageIndex);
const router = useRouter();
const debouncedFilter = useDebounce(filter, 500);

const handleFilterChange = (event) => {
@@ -26,6 +28,10 @@ const PaginationComponentRQ = () => {
setSort(sort);
};

const loadSingleDataHandler = (id) => {
router.push(`${SINGLE_DATA_PAGE}${id}`);
};

const dataToDisplay = paginationData?.data
.filter((item) =>
item.name.toLowerCase().startsWith(debouncedFilter.toLowerCase())
@@ -33,7 +39,16 @@ const PaginationComponentRQ = () => {
.sort((a, b) => compare(a.name, b.name, sort))
.map((item, index) => (
// ! 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} />
</Grid>
));

+ 6
- 5
constants/pages.js 查看文件

@@ -1,5 +1,6 @@
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/';

+ 40
- 0
pages/api/single-data/[dataId].js 查看文件

@@ -0,0 +1,40 @@
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;

+ 35
- 0
pages/api/single-data/index.js 查看文件

@@ -0,0 +1,35 @@
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;

+ 49
- 0
pages/single-data/[dataId].js 查看文件

@@ -0,0 +1,49 @@
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;

+ 2
- 0
requests/apiEndpoints.js 查看文件

@@ -3,4 +3,6 @@ export default {
createUser: '/api/auth/signup',
},
data: '/api/data',
dataIds: '/api/single-data',
singleData: '/api/single-data/',
};

+ 13
- 0
requests/dataIdsRequest.js 查看文件

@@ -0,0 +1,13 @@
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;
};

+ 15
- 0
requests/singleDataRequest.js 查看文件

@@ -0,0 +1,15 @@
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;
};

Loading…
取消
儲存