Skip to content

Commit c04d215

Browse files
authored
Merge pull request #29 from rezahedi/edit-stops
Add Note and Expense to each Stops in Plan Detail
2 parents d398766 + eb2c810 commit c04d215

31 files changed

+404
-1177
lines changed

src/App.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import {
1414
Bookmarked,
1515
Done,
1616
Profile,
17-
CreateNew,
18-
EditPlan,
1917
Settings,
2018
} from "@/pages/dashboard";
2119
import MainLayout from "@/Components/Layout/MainLayout";
@@ -54,12 +52,10 @@ function App() {
5452
}
5553
>
5654
<Route index element={<MyPlans />} />
57-
<Route path=":planId" element={<EditPlan />} />
5855
<Route path="bookmarked" element={<Bookmarked />} />
5956
<Route path="done" element={<Done />} />
6057
<Route path="profile" element={<Profile />} />
6158
<Route path="settings" element={<Settings />} />
62-
<Route path="create" element={<CreateNew />} />
6359
</Route>
6460
<Route path="*" element={<NotFound404 />} />
6561
</Route>

src/Components/Common/BookmarkButton.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ const BookmarkButton = ({
4343
};
4444

4545
return (
46-
<IconButton onClick={handleBookmark} variant="ghost" className={className}>
46+
<IconButton
47+
onClick={handleBookmark}
48+
variant="ghost"
49+
className={className}
50+
title={bookmark ? "Remove Bookmark" : "Add Bookmark"}
51+
>
4752
<BookmarkIcon
4853
className={bookmark ? "text-accent fill-accent" : "text-ring"}
4954
/>

src/Components/Common/CitiesSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import React, { useState, useEffect } from "react";
22
import { Skeleton } from "@/Components/ui/skeleton";
33
import { fetchData } from "@/util";
44
import { Link } from "react-router-dom";
5-
import { CityType } from "@/context/PlanTypes";
5+
import { CityDetail } from "@/types";
66

77
const CitiesSection = ({ title }: { title: string }) => {
8-
const [cities, setCities] = useState<CityType[]>([]);
8+
const [cities, setCities] = useState<CityDetail[]>([]);
99
const [loading, setLoading] = useState<boolean>(true);
1010
const [error, setError] = useState<string | null>(null);
1111

src/Components/Footer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React, { useEffect, useState } from "react";
22
import { fetchData } from "@/util";
3-
import { CityType } from "@/context/PlanTypes";
3+
import { CityDetail } from "@/types";
44
import { Link } from "react-router-dom";
55

66
function Footer() {
7-
const [cities, setCities] = useState<CityType[]>([]);
7+
const [cities, setCities] = useState<CityDetail[]>([]);
88

99
useEffect(() => {
1010
const fetchCities = async () => {
@@ -79,7 +79,7 @@ function Footer() {
7979
<div className="text-sm">
8080
<ul className="list-none p-0 m-0 text-sm flex flex-col gap-2">
8181
<li className="font-bold text-lg">Last Updated Cities</li>
82-
{cities.map((city: CityType) => (
82+
{cities.map((city: CityDetail) => (
8383
<Link key={city.placeId} to={`/city/${city.placeId}`}>
8484
<li>{city.name}</li>
8585
</Link>

src/context/ItineraryContext.tsx

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import React, { createContext, useContext, useEffect, useState } from "react";
2-
import { Place } from "@/types";
3-
import { PlanType } from "./PlanTypes";
2+
import { Place, PlanDTO, PlanWithDetail } from "@/types";
43
import { useAuth } from "./AuthContext";
54
import { useParams } from "react-router-dom";
65
import usePlanApi from "@/hooks/usePlanApi";
76
import usePlanOptimistic from "@/hooks/usePlanOptimistic";
87

98
type contextType = {
10-
plan: PlanType | null;
9+
plan: PlanWithDetail | null;
1110
setTitle: (title: string) => void;
1211
setDescription: (description: string) => void;
1312
addImage: (image: string) => void;
1413
setPolyline: (polyline: string) => void;
1514
addPlace: (place: Place) => void;
1615
removePlace: (placeId: string) => void;
17-
createPlan: (plan: PlanType) => void;
16+
setExpense: (placeId: string, expense: number) => void;
17+
setNote: (placeId: string, note: string) => void;
18+
createPlan: (plan: PlanDTO) => void;
1819
saving: boolean;
1920
loading: boolean;
2021
error: string | null;
@@ -25,7 +26,6 @@ const ItineraryContext = createContext<contextType | undefined>(undefined);
2526
const ItineraryProvider = ({ children }: { children: React.ReactNode }) => {
2627
const { plan, setPlan, saving, loading, createPlan, getPlan, updatePlan } =
2728
usePlanApi();
28-
// const [optimisticPlan, setOptimisticPlan] = useState<PlanType | null>(null);
2929
const { optimisticPlan, dispatch } = usePlanOptimistic(null);
3030
const [error, setError] = useState<string | null>(null);
3131
const { token } = useAuth();
@@ -50,7 +50,13 @@ const ItineraryProvider = ({ children }: { children: React.ReactNode }) => {
5050
(async () => {
5151
const originalPlan = plan;
5252
// Replace plan's properties with whatever is in optimisticPlan
53-
setPlan({ ...plan, ...optimisticPlan });
53+
setPlan({
54+
...plan,
55+
title: optimisticPlan.title || plan.title,
56+
description: optimisticPlan.description || plan.description,
57+
images: optimisticPlan.images || plan.images,
58+
stops: optimisticPlan.stops || plan.stops,
59+
});
5460
try {
5561
await updatePlan(planId, optimisticPlan);
5662
} catch (err: unknown) {
@@ -99,6 +105,26 @@ const ItineraryProvider = ({ children }: { children: React.ReactNode }) => {
99105
dispatch({ type: "removePlace", payload: placeId, init: plan.stops || [] });
100106
};
101107

108+
const setExpense = (placeId: string, expense: number) => {
109+
if (!plan || !placeId) return;
110+
111+
dispatch({
112+
type: "setExpense",
113+
payload: { placeId, expense },
114+
init: plan.stops || [],
115+
});
116+
};
117+
118+
const setNote = (placeId: string, note: string) => {
119+
if (!plan || !placeId) return;
120+
121+
dispatch({
122+
type: "setNote",
123+
payload: { placeId, note },
124+
init: plan.stops || [],
125+
});
126+
};
127+
102128
return (
103129
<ItineraryContext.Provider
104130
value={{
@@ -109,6 +135,8 @@ const ItineraryProvider = ({ children }: { children: React.ReactNode }) => {
109135
addImage,
110136
addPlace,
111137
removePlace,
138+
setExpense,
139+
setNote,
112140
createPlan,
113141
saving,
114142
loading,

src/context/PlanTypes.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/hooks/usePlanApi.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { useAuth } from "@/context/AuthContext";
2-
import { PlanType } from "@/context/PlanTypes";
2+
import { PlanDTO, PlanWithDetail } from "@/types";
33
import { useState } from "react";
44

55
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL}/api/v1`;
66

77
export default function usePlanApi() {
8-
const [plan, setPlan] = useState<PlanType | null>(null);
8+
const [plan, setPlan] = useState<PlanWithDetail | null>(null);
99
const [saving, setSaving] = useState<boolean>(false);
1010
const [loading, setLoading] = useState<boolean>(false);
1111
const { token } = useAuth();
1212

13-
const createPlan = async (plan: PlanType) => {
13+
const createPlan = async (plan: PlanDTO) => {
1414
if (!token) {
1515
setPlan(null);
1616
throw new Error("You must be logged in to create a plan");
@@ -65,7 +65,7 @@ export default function usePlanApi() {
6565
}
6666
};
6767

68-
const updatePlan = async (planId: string, planData: PlanType) => {
68+
const updatePlan = async (planId: string, planData: Partial<PlanDTO>) => {
6969
if (!token) {
7070
setPlan(null);
7171
throw new Error("You must be logged in to edit a plan");

src/hooks/usePlanOptimistic.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { PlaceType, PlanType } from "@/context/PlanTypes";
1+
import { Place } from "./../types/index";
2+
import { PlanDTO } from "@/types";
23
import { useReducer } from "react";
34

5+
type PartialPlanDTO = Partial<PlanDTO>;
6+
47
type Action =
58
| {
69
type: "setTitle" | "setDescription";
@@ -14,15 +17,28 @@ type Action =
1417
| {
1518
type: "removePlace";
1619
payload: string;
17-
init: PlaceType[];
20+
init: Place[];
1821
}
1922
| {
2023
type: "addPlace";
21-
payload: PlaceType;
22-
init: PlaceType[];
24+
payload: Place;
25+
init: Place[];
26+
}
27+
| {
28+
type: "setExpense";
29+
payload: { placeId: string; expense: number };
30+
init: Place[];
31+
}
32+
| {
33+
type: "setNote";
34+
payload: { placeId: string; note: string };
35+
init: Place[];
2336
};
2437

25-
const reducer = (state: PlanType | null, action: Action): PlanType | null => {
38+
const reducer = (
39+
state: PartialPlanDTO | null,
40+
action: Action,
41+
): PartialPlanDTO | null => {
2642
switch (action.type) {
2743
case "setTitle":
2844
return {
@@ -65,12 +81,44 @@ const reducer = (state: PlanType | null, action: Action): PlanType | null => {
6581
};
6682
}
6783

84+
case "setExpense": {
85+
let stops = [...(state?.stops || action.init)];
86+
87+
return {
88+
...state,
89+
stops: stops.map((p) =>
90+
p.placeId === action.payload.placeId
91+
? {
92+
...p,
93+
expense: action.payload.expense,
94+
}
95+
: p,
96+
),
97+
};
98+
}
99+
100+
case "setNote": {
101+
let stops = [...(state?.stops || action.init)];
102+
103+
return {
104+
...state,
105+
stops: stops.map((p) =>
106+
p.placeId === action.payload.placeId
107+
? {
108+
...p,
109+
note: action.payload.note,
110+
}
111+
: p,
112+
),
113+
};
114+
}
115+
68116
default:
69117
return state;
70118
}
71119
};
72120

73-
export default function usePlanOptimistic(init: PlanType | null) {
121+
export default function usePlanOptimistic(init: PlanDTO | null) {
74122
const [optimisticPlan, dispatch] = useReducer(reducer, init);
75123

76124
return { optimisticPlan, dispatch };

src/pages/CityPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { fetchData } from "@/util";
66
import Pagination from "@/Components/Common/Pagination";
77
import PlanCardSkeleton from "@/Components/Common/PlanCardSkeleton";
88
import { useAuth } from "@/context/AuthContext";
9-
import { Plan, City } from "@/types";
9+
import { Plan, CityDetail } from "@/types";
1010
import Title from "@/Components/Header/Title";
1111

1212
const PAGE_SIZE = 8;
@@ -18,7 +18,7 @@ const CityPage = () => {
1818
const [isLoading, setIsLoading] = useState<boolean>(true);
1919
const [plans, setPlans] = useState<Plan[]>([]);
2020
const [pagesCount, setPagesCount] = useState<number>(0);
21-
const [city, setCity] = useState<City | null>(null);
21+
const [city, setCity] = useState<CityDetail | null>(null);
2222
const [error, setError] = useState<string | null>(null);
2323
const { token } = useAuth();
2424

src/pages/HomePage/ExploreBlock.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react";
22
import { Link } from "react-router-dom";
33
import { ChevronRightIcon } from "lucide-react";
44
import PopularCities from "./PopularCities";
5-
import { CityType } from "@/context/PlanTypes";
65
import { fetchData } from "@/util";
6+
import { CityDetail } from "@/types";
77

88
const ExploreBlock = () => {
9-
const [cities, setCities] = useState<CityType[]>([]);
9+
const [cities, setCities] = useState<CityDetail[]>([]);
1010

1111
useEffect(() => {
1212
(async () => {

0 commit comments

Comments
 (0)