refactor: clean session06 notes
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
## `SearchResultsPage`
|
||||||
|
|
||||||
|
This page is responsible for:
|
||||||
|
1. **Reading route parameters and query strings from the URL**
|
||||||
|
2. **Sending those values as a form to the backend**
|
||||||
|
3. **Showing the result (or loading/error message)**
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
### ✅ 1. **Route Parameters**
|
||||||
|
When you define this route in `App.jsx`:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
|
||||||
|
```
|
||||||
|
|
||||||
|
It means the URL will look like:
|
||||||
|
```
|
||||||
|
/1/21/45
|
||||||
|
```
|
||||||
|
Those values are extracted using:
|
||||||
|
```js
|
||||||
|
const { vehicleId, fromCityId, toCityId } = useParams();
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 `useParams()` comes from React Router and gives you access to the dynamic parts of the URL.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ✅ 2. **Query String Parameters**
|
||||||
|
Suppose your full URL is:
|
||||||
|
|
||||||
|
```
|
||||||
|
/1/21/45?departing=2025-06-01&arriving=2025-06-10
|
||||||
|
```
|
||||||
|
|
||||||
|
These extra values after the `?` are **query string parameters**. They're accessed using:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const query = useQuery(); // Custom helper
|
||||||
|
const departing = query.get("departing");
|
||||||
|
const arriving = query.get("arriving");
|
||||||
|
```
|
||||||
|
|
||||||
|
The helper `useQuery()` is:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function useQuery() {
|
||||||
|
return new URLSearchParams(useLocation().search);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This uses React Router's `useLocation()` to access the full URL, and then parses the query string.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ✅ 3. **Parsing and Converting Values**
|
||||||
|
|
||||||
|
React Router gives you everything as strings. So:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const vehicleTypeId = vehicleId ? parseInt(vehicleId, 10) : 1;
|
||||||
|
const fromId = fromCityId ? parseInt(fromCityId, 10) : undefined;
|
||||||
|
const toId = toCityId ? parseInt(toCityId, 10) : undefined;
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures you have **numbers**, not strings, when building your form object.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ✅ 4. **Building the Search Form and Fetching Data**
|
||||||
|
|
||||||
|
Now all data is combined into one `form` object:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const form = {
|
||||||
|
vehicleTypeId,
|
||||||
|
fromCityId: fromId,
|
||||||
|
toCityId: toId,
|
||||||
|
startDate: departing || null,
|
||||||
|
endDate: arriving || null,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then it sends that to the backend:
|
||||||
|
|
||||||
|
```js
|
||||||
|
agent.TransportationSearch.search(form)
|
||||||
|
.then(setResults)
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
```
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
|
||||||
# Branching
|
# 🛠️ Task Checklist
|
||||||
|
|
||||||
|
## 🚧Branching
|
||||||
- [ ] Create the feature/navbar branch based on develop
|
- [ ] Create the feature/navbar branch based on develop
|
||||||
|
|
||||||
# Adding a Navbar
|
## Adding a Navbar
|
||||||
## 🔹 What Is a Navbar?
|
### 🔹 What Is a Navbar?
|
||||||
- A **navigation bar (navbar)** is a UI element typically placed at the **top** or **side** of a web app.
|
- A **navigation bar (navbar)** is a UI element typically placed at the **top** or **side** of a web app.
|
||||||
|
|
||||||
- [ ] Use a `<nav>` with `flex`, `justify-between`, `items-center`.
|
- [ ] Use a `<nav>` with `flex`, `justify-between`, `items-center`.
|
||||||
@@ -11,54 +13,41 @@
|
|||||||
|
|
||||||
use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/shared/components)
|
use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/shared/components)
|
||||||
|
|
||||||
|
📂 Suggested Folder for navbar component: `src/shared/components/Navbar`
|
||||||
|
📂 Suggested Folder for images: `public/images/`
|
||||||
|
|
||||||
link to project:
|
## 🚧Merge
|
||||||
📂 Suggested Folder for navbar component: src/shared/components/Navbar
|
|
||||||
📂 Suggested Folder for images: public/images/
|
|
||||||
|
|
||||||
# Merge
|
|
||||||
- [ ] Create a PR and merge the current branch with develop
|
- [ ] Create a PR and merge the current branch with develop
|
||||||
# Branching
|
## 🚧Branching
|
||||||
- [ ] Create the feature/routing branch based on develop
|
- [ ] Create the feature/routing branch based on develop
|
||||||
|
|
||||||
# Modifying Project From Single Component to Routed Pages
|
## Converting Search Functionality From Single Component to Routed Pages
|
||||||
|
|
||||||
|
Originally, transportation search logic and UI may have all been inside one component — which quickly becomes messy and hard to manage as your app grows.
|
||||||
Originally, your transportation search logic and UI may have all been inside one component — which quickly becomes messy and hard to manage as your app grows.
|
|
||||||
|
|
||||||
Now we’ve **split the logic into two proper pages**:
|
Now we’ve **split the logic into two proper pages**:
|
||||||
|
|
||||||
### ✅ `SearchPage.jsx`
|
### ✅ `SearchPage.jsx`
|
||||||
|
|
||||||
|
|
||||||
- Responsible only for showing the **search form**.
|
- Responsible only for showing the **search form**.
|
||||||
- Clean and minimal.
|
- Clean and minimal.
|
||||||
- Uses the reusable `TransportationSearchForm` component.
|
- Uses the reusable `TransportationSearchForm` component.
|
||||||
- use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation/pages)
|
- Use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation/pages)
|
||||||
|
|
||||||
### ✅ `SearchResultsPage.jsx`
|
### ✅ `SearchResultsPage.jsx`
|
||||||
|
|
||||||
- Responsible for **fetching and showing results**.
|
- Responsible for **fetching and showing results**.
|
||||||
- Reads route parameters and query strings.
|
- Reads route parameters and query strings.
|
||||||
- Calls the backend using `agent`.
|
- Calls the backend using `agent`.
|
||||||
- Shows a loading state, handles empty results, and renders cards.
|
- Shows a loading state, handles empty results, and renders cards.
|
||||||
- use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation/pages)
|
- Use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation/pages)
|
||||||
|
|
||||||
|
|
||||||
This separation improves:
|
This separation improves:
|
||||||
|
|
||||||
- Routing and navigation
|
- Routing and navigation
|
||||||
- Code readability and maintainability
|
- Code readability and maintainability
|
||||||
- Reusability of components like the search form and result cards
|
- Reusability of components like the search form and result cards
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### **Create Pages into a Pages Folder**
|
||||||
|
|
||||||
|
|
||||||
## **Create Pages into a Pages Folder**
|
|
||||||
|
|
||||||
|
|
||||||
📂 Suggested Folder for navbar component
|
📂 Suggested Folder for navbar component
|
||||||
Inside your `features/transportation` folder:
|
Inside your `features/transportation` folder:
|
||||||
@@ -71,10 +60,7 @@ components/
|
|||||||
├── TransportationCard.jsx
|
├── TransportationCard.jsx
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. **Create `SearchPage`**
|
- [ ] Create `SearchPage`
|
||||||
|
|
||||||
Use:
|
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import TransportationSearchForm from "@/features/transportation/transportationSearchForm";
|
import TransportationSearchForm from "@/features/transportation/transportationSearchForm";
|
||||||
|
|
||||||
@@ -88,11 +74,9 @@ const SearchPage = () => {
|
|||||||
|
|
||||||
export default SearchPage;
|
export default SearchPage;
|
||||||
```
|
```
|
||||||
|
> Keep the form clean and layout minimal.
|
||||||
|
|
||||||
Keep the form clean and layout minimal.
|
- [ ] Create `SearchResultsPage`
|
||||||
|
|
||||||
### 3. **Create `SearchResultsPage`**
|
|
||||||
|
|
||||||
- Use `useParams()` for URL parameters (`vehicleId`, `fromCityId`, `toCityId`)
|
- Use `useParams()` for URL parameters (`vehicleId`, `fromCityId`, `toCityId`)
|
||||||
- Use `useLocation()` and `URLSearchParams` to read query strings (`departing`, `arriving`)
|
- Use `useLocation()` and `URLSearchParams` to read query strings (`departing`, `arriving`)
|
||||||
- Fetch data from backend using a shared `agent`
|
- Fetch data from backend using a shared `agent`
|
||||||
@@ -170,110 +154,15 @@ const SearchResultsPage = () => {
|
|||||||
export default SearchResultsPage;
|
export default SearchResultsPage;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 🔍 SearchResultsPage – Understanding Parameters and Arguments
|
#### 🔍 `SearchResultsPage` – Understanding Parameters and Arguments
|
||||||
|
- [ ] Check out [[Session06 Additional Info]]
|
||||||
This page is responsible for:
|
|
||||||
|
|
||||||
1. **Reading route parameters and query strings from the URL**
|
|
||||||
2. **Sending those values as a form to the backend**
|
|
||||||
3. **Showing the result (or loading/error message)**
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
### Modifying `TransportationSearchForm`:
|
||||||
|
> Use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation)
|
||||||
|
|
||||||
##### ✅ 1. **Route Parameters**
|
- [ ] Remove Search Result fetching from inside the component
|
||||||
|
- [ ] Update `handleSearch` to Navigate with Parameters
|
||||||
When you define this route in `App.jsx`:
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
|
|
||||||
```
|
|
||||||
|
|
||||||
It means the URL will look like:
|
|
||||||
```
|
|
||||||
/1/21/45
|
|
||||||
```
|
|
||||||
Those values are extracted using:
|
|
||||||
```js
|
|
||||||
const { vehicleId, fromCityId, toCityId } = useParams();
|
|
||||||
```
|
|
||||||
|
|
||||||
🔹 `useParams()` comes from React Router and gives you access to the dynamic parts of the URL.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### ✅ 2. **Query String Parameters**
|
|
||||||
|
|
||||||
Suppose your full URL is:
|
|
||||||
|
|
||||||
```
|
|
||||||
/1/21/45?departing=2025-06-01&arriving=2025-06-10
|
|
||||||
```
|
|
||||||
|
|
||||||
These extra values after the `?` are **query string parameters**. They're accessed using:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const query = useQuery(); // Custom helper
|
|
||||||
const departing = query.get("departing");
|
|
||||||
const arriving = query.get("arriving");
|
|
||||||
```
|
|
||||||
|
|
||||||
The helper `useQuery()` is:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function useQuery() {
|
|
||||||
return new URLSearchParams(useLocation().search);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This uses React Router's `useLocation()` to access the full URL, and then parses the query string.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### ✅ 3. **Parsing and Converting Values**
|
|
||||||
|
|
||||||
React Router gives you everything as strings. So:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const vehicleTypeId = vehicleId ? parseInt(vehicleId, 10) : 1;
|
|
||||||
const fromId = fromCityId ? parseInt(fromCityId, 10) : undefined;
|
|
||||||
const toId = toCityId ? parseInt(toCityId, 10) : undefined;
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures you have **numbers**, not strings, when building your form object.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### ✅ 4. **Building the Search Form and Fetching Data**
|
|
||||||
|
|
||||||
Now all data is combined into one `form` object:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const form = {
|
|
||||||
vehicleTypeId,
|
|
||||||
fromCityId: fromId,
|
|
||||||
toCityId: toId,
|
|
||||||
startDate: departing || null,
|
|
||||||
endDate: arriving || null,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Then it sends that to the backend:
|
|
||||||
|
|
||||||
```js
|
|
||||||
agent.TransportationSearch.search(form)
|
|
||||||
.then(setResults)
|
|
||||||
.catch(err => console.error(err))
|
|
||||||
.finally(() => setLoading(false));
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
### 4. **Modify `TransportationSearchForm`:**
|
|
||||||
use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation)
|
|
||||||
|
|
||||||
#### 1. **Removed Search Result Fetching from Inside the Component**
|
|
||||||
|
|
||||||
#### 2. **Updated `handleSearch` to Navigate with Parameters**
|
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
@@ -299,35 +188,24 @@ const handleSearch = () => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Changes made:**
|
**Changes made:**
|
||||||
|
|
||||||
- It **checks form validity** first.
|
- It **checks form validity** first.
|
||||||
|
|
||||||
- Then it builds a **URL using `URLSearchParams`** for `departing` and `arriving` dates.
|
- Then it builds a **URL using `URLSearchParams`** for `departing` and `arriving` dates.
|
||||||
|
|
||||||
- Then it calls `navigate(...)` to go to a **route like**:
|
- Then it calls `navigate(...)` to go to a **route like**:
|
||||||
|
|
||||||
```
|
```
|
||||||
/1/2/3?departing=2025-06-15T00%3A00%3A00.000Z&arriving=2025-06-18T00%3A00%3A00.000Z
|
/1/2/3?departing=2025-06-15T00%3A00%3A00.000Z&arriving=2025-06-18T00%3A00%3A00.000Z
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
> That route (`/vehicleTypeId/fromCityId/toCityId`) will be handled by your `SearchResultPage` via `react-router`.
|
> That route (`/vehicleTypeId/fromCityId/toCityId`) will be handled by your `SearchResultPage` via `react-router`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 3. **Used `useNavigate` from `react-router-dom`**
|
- [ ] Used `useNavigate` from `react-router-dom`
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
```
|
```
|
||||||
|
- [ ] Removed Result Display Section
|
||||||
|
|
||||||
#### 4. **Removed Result Display Section**
|
## Add Routing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Add Routing
|
|
||||||
- [ ] Change `App.tsx` as the following:
|
- [ ] Change `App.tsx` as the following:
|
||||||
```tsx
|
```tsx
|
||||||
import Navbar from "@/shared/components/navbar";
|
import Navbar from "@/shared/components/navbar";
|
||||||
@@ -359,38 +237,32 @@ function App() {
|
|||||||
export default App;
|
export default App;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Explanation of `App.jsx`
|
### Explanation of `App.jsx`
|
||||||
|
|
||||||
#### 🔁 `Router` & `Routes`:
|
#### 🔁 `Router` & `Routes`:
|
||||||
|
|
||||||
- Wraps the entire app in `<Router>` so that React Router can manage navigation.
|
- Wraps the entire app in `<Router>` so that React Router can manage navigation.
|
||||||
- `<Routes>` contains all the individual page routes.
|
- `<Routes>` contains all the individual page routes.
|
||||||
|
|
||||||
---
|
---
|
||||||
#### 📌 Routes:
|
#### 📌 Routes:
|
||||||
|
|
||||||
- `/`: Loads `SearchPage`. This is your home/search form.
|
- `/`: Loads `SearchPage`. This is your home/search form.
|
||||||
- `/:vehicleId/:fromCityId/:toCityId`: Loads `SearchResultsPage`. This URL carries parameters to display results based on user input.
|
- `/:vehicleId/:fromCityId/:toCityId`: Loads `SearchResultsPage`. This URL carries parameters to display results based on user input.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 🎯 Navbar Placement:
|
#### 🎯 Navbar Placement:
|
||||||
|
|
||||||
- Placed **outside** `<Routes>`, so it shows on **all pages**.
|
- Placed **outside** `<Routes>`, so it shows on **all pages**.
|
||||||
- The surrounding `<div className="pt-16">` adds space at the top so that page content isn’t hidden behind the navbar (assuming the navbar is fixed).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ Checklist for Setting Up Routing
|
## Setting Up Routing
|
||||||
|
|
||||||
### 1. **Install React Router** (If you haven't already)
|
- [ ] **Install React Router** (If you haven't already)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install react-router-dom
|
npm install react-router-dom
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. **Wrap Your App in Router**
|
- [ ] **Wrap Your App in Router**
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
<Router>
|
<Router>
|
||||||
@@ -401,12 +273,21 @@ npm install react-router-dom
|
|||||||
</Router>
|
</Router>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. **Define Routes**
|
- [ ] **Define Routes**
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
<Route path="/" element={<SearchPage />} />
|
<Route path="/" element={<SearchPage />} />
|
||||||
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
|
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
|
||||||
```
|
```
|
||||||
|
## 🚧Merge
|
||||||
# Merge
|
|
||||||
- [ ] Create a PR and merge the current branch with develop
|
- [ ] Create a PR and merge the current branch with develop
|
||||||
|
|
||||||
|
# 🧠 Hints & Notes
|
||||||
|
# 🙌 Acknowledgements
|
||||||
|
|
||||||
|
- ChatGPT for snippet refinement and explanations
|
||||||
|
# 🔍 References
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user