Files
ASP.NET/content/02-Project-Based/Session06/Session06 Frontend.md
T
2025-07-18 14:47:22 +03:30

294 lines
8.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🛠️ Task Checklist
## 🚧Branching
- [ ]  Create the feature/navbar branch based on develop
## Adding 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.
- [ ] Use a `<nav>` with `flex`, `justify-between`, `items-center`.
- [ ] Add buttons or links like Home, Search, About. (the links can be empty now)
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/`
## 🚧Merge
- [ ] Create a PR and merge the current branch with develop
## 🚧Branching
- [ ]  Create the feature/routing branch based on develop
## 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.
Now weve **split the logic into two proper pages**:
### ✅ `SearchPage.jsx`
- Responsible only for showing the **search form**.
- Clean and minimal.
- 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)
### ✅ `SearchResultsPage.jsx`
- Responsible for **fetching and showing results**.
- Reads route parameters and query strings.
- Calls the backend using `agent`.
- 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)
This separation improves:
- Routing and navigation
- Code readability and maintainability
- Reusability of components like the search form and result cards
---
### **Create Pages into a Pages Folder**
📂 Suggested Folder for navbar component
Inside your `features/transportation` folder:
```
pages/
├── SearchPage.jsx
├── SearchResultsPage.jsx
components/
├── TransportationSearchForm.jsx
├── TransportationCard.jsx
```
- [ ] Create `SearchPage`
```jsx
import TransportationSearchForm from "@/features/transportation/transportationSearchForm";
const SearchPage = () => {
  return (
    <div className="container mx-auto py-6">
      <TransportationSearchForm />
    </div>
  );
};
export default SearchPage;
```
> Keep the form clean and layout minimal.
- [ ] Create `SearchResultsPage`
- Use `useParams()` for URL parameters (`vehicleId`, `fromCityId`, `toCityId`)
- Use `useLocation()` and `URLSearchParams` to read query strings (`departing`, `arriving`)
- Fetch data from backend using a shared `agent`
- Display a loading state, empty message, and the result list
Code Example:
```jsx
import { useEffect, useState } from "react";
import { useLocation, useParams } from "react-router-dom";
import agent from "@/shared/api/agent";
import { TransportationSearchResult } from "@/shared/models/transportation/transportationSearchResult";
import TransportationCard from "@/features/transportation/transportationCard";
function useQuery() {
  return new URLSearchParams(useLocation().search);
}
const SearchResultsPage = () => {
  const { vehicleId, fromCityId, toCityId } = useParams();
  const vehicleTypeId = vehicleId ? parseInt(vehicleId, 1) : 1;
  const fromId = fromCityId ? parseInt(fromCityId, 1) : undefined;
  const toId = toCityId ? parseInt(toCityId, 1) : undefined;
  const query = useQuery();
  const departing = query.get("departing");
  const arriving = query.get("arriving");
  const [results, setResults] = useState<TransportationSearchResult[]>([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const form = {
      vehicleTypeId,
      fromCityId: fromId,
      toCityId: toId,
      startDate: departing || null,
      endDate: arriving || null,
    };
    agent.TransportationSearch.search(form)
      .then(setResults)
      .catch((err) => console.error(err))
      .finally(() => setLoading(false));
  }, [vehicleId, fromCityId, toCityId, departing, arriving]);
  return (
    <div className="container mx-auto py-6">
      <h2 className="text-2xl font-bold mb-4">Search Results</h2>
      {loading ? (
        <p>Loading...</p>
      ) : results.length === 0 ? (
        <p>No results found.</p>
      ) : (
        <div className="space-y-4">
          {results.map((r) => (
            <TransportationCard key={r.id} transportation={r} />
          ))}
        </div>
      )}
    </div>
  );
};
export default SearchResultsPage;
```
#### 🔍 `SearchResultsPage` Understanding Parameters and Arguments
- [ ] Check out [[Session06 Additional Info]]
---
### Modifying `TransportationSearchForm`:
> Use this as a reference: [link](https://github.com/MehrdadShirvani/AlibabaClone-Frontend/tree/develop/alibabaclone-frontend/src/features/transportation)
- [ ] Remove Search Result fetching from inside the component
- [ ] Update `handleSearch` to Navigate with Parameters
```tsx
const handleSearch = () => {
if (!form.fromCityId || !form.toCityId || !form.startDate || !form.vehicleTypeId)
return;
const params = new URLSearchParams();
if (form.startDate instanceof Date)
params.append("departing", form.startDate.toISOString());
else if (typeof form.startDate === "string")
params.append("departing", form.startDate);
if (form.endDate instanceof Date)
params.append("arriving", form.endDate.toISOString());
else if (typeof form.endDate === "string")
params.append("arriving", form.endDate);
navigate(
`/${form.vehicleTypeId}/${form.fromCityId}/${form.toCityId}?${params.toString()}`
);
};
```
**Changes made:**
- It **checks form validity** first.
- Then it builds a **URL using `URLSearchParams`** for `departing` and `arriving` dates.
- 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
```
> That route (`/vehicleTypeId/fromCityId/toCityId`) will be handled by your `SearchResultPage` via `react-router`.
---
- [ ] Used `useNavigate` from `react-router-dom`
```tsx
import { useNavigate } from "react-router-dom";
```
- [ ] Removed Result Display Section
## Add Routing
- [ ] Change `App.tsx` as the following:
```tsx
import Navbar from "@/shared/components/navbar";
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import SearchPage from "@/features/transportation/pages/SearchPage";
import SearchResultsPage from "@/features/transportation/pages/SearchResultsPage";
function App() {
  return (
    <Router>
      <Navbar /> {/* Navbar will show on all pages */}
      <div className="pt-16">
        {" "}
        {/* padding top if navbar is fixed */}
        <Routes>
          <Route path="/" element={<SearchPage />} />
          <Route
            path="/:vehicleId/:fromCityId/:toCityId"
            element={<SearchResultsPage />}
          />
        </Routes>
      </div>
    </Router>
  );
}
export default App;
```
### Explanation of `App.jsx`
#### 🔁 `Router` & `Routes`:
- Wraps the entire app in `<Router>` so that React Router can manage navigation.
- `<Routes>` contains all the individual page routes.
---
#### 📌 Routes:
- `/`: Loads `SearchPage`. This is your home/search form.
- `/:vehicleId/:fromCityId/:toCityId`: Loads `SearchResultsPage`. This URL carries parameters to display results based on user input.
---
#### 🎯 Navbar Placement:
- Placed **outside** `<Routes>`, so it shows on **all pages**.
---
## Setting Up Routing
- [ ] **Install React Router** (If you haven't already)
```bash
npm install react-router-dom
```
- [ ] **Wrap Your App in Router**
```jsx
<Router>
<Navbar />
<Routes>
{/* your routes here */}
</Routes>
</Router>
```
- [ ] **Define Routes**
```jsx
<Route path="/" element={<SearchPage />} />
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
```
## 🚧Merge
- [ ] Create a PR and merge the current branch with develop
# 🧠 Hints & Notes
# 🙌 Acknowledgements
- ChatGPT for snippet refinement and explanations
# 🔍 References