In this Recap, you will collaborate to build the entire frontend of the TOURIO App.
This project will be completed in groups. To begin, one member of your group should create a repository using this template. Then, invite the rest of your group members as collaborators to this project.
As is standard practice, each feature should be developed on a separate branch.
-
State Initialization with Dummy Data
- In your
index.js
file located in the/pages
directory, import theinitialPlaces
array from@/lib/db.js
. - Initialize a state variable to hold this dummy data. You can use React's
useState
hook for state management.
- In your
-
Displaying Data
- Map over the state that contains your
initialPlaces
data to render the list of places on the homepage. - For each place, you should temporarily display its
name
andlocation
properties directly within your map function. This will be replaced by a card component in the following steps.
- Map over the state that contains your
-
Create a Card Component
- Create a new component named
PlaceCard
in a suitable directory (e.g.,components/PlaceCard.js
). - This component should accept props for a place's
image
,name
, andlocation
. - Feel free to style the component using styled components. (Basic styling is sufficient)
- Create a new component named
-
Integrate the Card Component
- Replace the temporary display of place details within your map function in
index.js
with thePlaceCard
component. - Pass the necessary data (
image
,name
, andlocation
) as props to eachPlaceCard
instance.
- Replace the temporary display of place details within your map function in
-
Whitelisting Image URLs
- If not already done so, modify the
PlaceCard
component to use theImage
component from Next.js for displaying the images. Ensure that you configure your Next.js project to allow images from the URLs you are using by editing thenext.config.js
file and adding the image domains to theimages.domains
array.
- If not already done so, modify the
-
Create a New Page for Adding Entries
- Add a new file named
create.js
within the/pages
directory. - This page will host a form that allows users to add new entries to your application.
- Add a link within the page that allows users to navigate back to the homepage.
- Add a new file named
-
Form Component for New Entries
- Create a form component that includes fields for
name
,location
,mapURL
,image
, anddescription
. You can structure this component in a directory likecomponents/Form.js
. - Include a submit button in the form.
- Basic styling is sufficient.
- Create a form component that includes fields for
-
Move State to
_app.js
- To share state across different pages, move the state management for your places (initially in
/pages/index.js
) to_app.js
. This will make the state accessible throughout your application.
- To share state across different pages, move the state management for your places (initially in
-
Implement the
handleAddPlace
Function- In your
_app.js
write a function namedhandleAddPlace
that will be responsible for adding a new place to the state. - This function should accept the form data as an argument, generate a unique ID for the new entry using
nanoid
, and then add the new place to the existing state.
- In your
-
State and Handler Propagation
- Ensure the shared state and the
handleAddPlace
function are properly passed down to the pages and components that require them. - In
create.js
, use these props to manage the form submission and to maintain the list of places.
- Ensure the shared state and the
-
Form Submission and Page Redirection
- Upon form submission, use the
handleAddPlace
function to add the new entry to your state. - After adding the entry, redirect the user to the homepage. This can be achieved by importing
useRouter
fromnext/router
and callingrouter.push('/')
after the form submission logic.
- Upon form submission, use the
-
Link to create page
- in
/pages/index.js
add a Link that forwards users to/pages/create.js
- this link should always be visible
- in
-
Create a Dynamic Route for Place Details
- In the
/pages/places
directory, create a dynamic route named[id]/index.js
. This file will serve as the detail page for each place. - The
[id]
in the file path is a placeholder that Next.js will replace with the actual ID of the place being viewed.
- In the
-
Display Place Information
- Inside your detail page component (
[id]/index.js
), use Next.js'suseRouter
hook to access the router object and retrieve theid
of the current place from the URL. - Use this
id
to find the corresponding place from your application's state using the.find()
method. - Display the detailed information of the found place, such as its
name
,location
,imageURL
,mapURL
, anddescription
.
- Inside your detail page component (
-
Implement a Back Link
- Include a link on the detail page that, when clicked, redirects the user back to the homepage (
/pages/index.js
).
- Include a link on the detail page that, when clicked, redirects the user back to the homepage (
-
Add Links to the Homepage for Detail Views
- Modify your homepage (
/pages/index.js
) to include links for each place entry that direct the user to the detail view of that place. - These links should route to
/places/[id]
, with[id]
replaced by the actual ID of the place. You can use Next.js'sLink
component for this purpose.
- Modify your homepage (
-
Implement
handleDeletePlace
Function in_app.js
- In the
_app.js
file, write a function namedhandleDeletePlace
that updates the state to remove an entry based on itsid
. - This function takes the
id
of the place to be deleted as a parameter.
- In the
-
Pass the Delete Function to Pages
- Make sure the
handleDeletePlace
function is passed down as a prop to all pages that need it.
- Make sure the
-
Add Delete Button in Place Detail Page
- In the detail page component for places (
/pages/places/[id]/index.js
), add a button that, when clicked, calls thehandleDeletePlace
function with the place'sid
as the argument. - This button allows users to delete the place they are currently viewing.
- In the detail page component for places (
-
Redirect to Homepage After Deletion
- After successfully deleting a place, use Next.js's
useRouter
hook to redirect the user back to the homepage. - Perform the redirection by calling
router.push('/')
after the state has been updated.
- After successfully deleting a place, use Next.js's
-
Create Edit Page for Places
- Add a new file under
/pages/places/[id]/edit.js
. This file will be responsible for rendering the edit form for a specific place, identified by itsid
.
- Add a new file under
-
Display Pre-filled Form
- In the edit page component, use Next.js's
useRouter
hook to extract theid
of the place from the URL. - Use this
id
to find the current data of the place from your application's state and pre-fill the form fields with this data. ThedefaultValue
attribute of form elements can be useful here. - Consider creating a separate component for the form to keep your code organized and reusable.
- In the edit page component, use Next.js's
-
Implement the
handleEditPlace
Function in_app.js
- Define a function named
handleEditPlace
in_app.js
that takes the updated place data and itsid
as arguments. This function should update the state by replacing the old place data with the new, updated data. - Utilize array manipulation methods, like
.map()
, to achieve this, ensuring that only the place with the matchingid
is updated.
- Define a function named
-
Handle Form Submission
- On form submission, prevent the default form action and call the
handleEditPlace
function with the updated place data and itsid
. - Ensure that the form gathers all necessary data to fully update the place's entry in the state.
- On form submission, prevent the default form action and call the
-
Redirect to Homepage After Submission
- After successfully updating the place's data, use Next.js's
useRouter
hook to redirect the user back to the homepage.
- After successfully updating the place's data, use Next.js's
-
Refactor to Use a Single Form Component
- Create a single form component that can be used for both adding a new place and editing an existing place. This component should be designed to handle the inputs required for a place, such as name, location, mapURL, imageURL, and description.
-
Pass a
formName
Prop to Identify the Form- Enhance the form component to accept a
formName
prop, which will be used to differentiate between the "create" and "edit" scenarios. Use this prop to conditionally render the form's heading and submit button text (e.g., "Add Place" for create and "Update Place" for edit).
- Enhance the form component to accept a
-
Conditional Default Values Using Optional Chaining
- Utilize optional chaining (
?.
) to make thedefaultValue
of form inputs conditional, based on whether the form is being used to edit an existing place. This allows for the inputs to be pre-filled when editing, while being empty for new entries.
- Utilize optional chaining (
-
Make the
handleSubmit
Function Generic- Design the
handleSubmit
function to be generic and adaptable to both creating and updating place data. This function should decide whether to add a new place or update an existing one based on the presence of anid
or any other distinguishing prop passed to the form.
- Design the
-
Ensure Proper Props are Passed in Usage
- When calling your form component within
/pages/create
and/pages/places/[id]/edit.js
, ensure that the correct props are passed. This includes theformName
, any necessary default values for editing, and a generichandleSubmit
function tailored to the context.
- When calling your form component within