Skip to content

Commit

Permalink
search products by dates
Browse files Browse the repository at this point in the history
  • Loading branch information
Sojabio committed Nov 8, 2024
1 parent 30b5dbe commit 44ff63d
Show file tree
Hide file tree
Showing 19 changed files with 416 additions and 253 deletions.
4 changes: 2 additions & 2 deletions backend/src/entities/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class Article extends BaseEntity {
@ManyToOne(() => Product, (product) => product.articles)
product: Product;

@Field(() => [Reservation]) // GraphQL
@Field(() => [Reservation], { nullable: true }) // GraphQL
@ManyToMany(() => Reservation, reservation => reservation.articles) // TypeORM
reservations: Reservation[];
reservations?: Reservation[];
}
34 changes: 1 addition & 33 deletions backend/src/resolvers/ArticleResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,14 @@ class EditArticleInput {
availability: boolean;
}

// data from range picket
@InputType()
class DateRangeInput {
@Field(() => Date)
startDate: Date

@Field(() => Date)
endDate: Date
}


@Resolver(Article)
class ArticleResolver {
@Query(() => [Article])
async getAllArticles() {
const article = await Article.find({ relations: { product: true, reservations: true } });
const article = await Article.find({ relations: { product: true, reservations: true } })
return article
}

@Query(() => [Article])
async getAvailableArticles(@Arg("dateRange") dateRange: DateRangeInput) {
const { startDate, endDate } = dateRange
const articles = await Article.find({ relations: { reservations: true } })

// Filter articles based on their reservation dates
const availableArticles = articles.filter(article => {
return article.reservations.every(reservation => {
const reservationStart = new Date(reservation.startDate)
const reservationEnd = new Date(reservation.endDate)

return (
reservationEnd < startDate || // Reservation ends before the start date I searched
reservationStart > endDate // Reservation starts after the end date I searched
});
});

return availableArticles;
}


@Authorized(Role.Admin)
@Mutation(() => Article)
async createNewArticle(@Arg("data") newArticleData: NewArticleInput) {
Expand Down
78 changes: 73 additions & 5 deletions backend/src/resolvers/ProductResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "type-graphql";
import { Role } from "../entities/user";


@InputType()
class NewProductInput implements Partial<Product> {
@Field()
Expand All @@ -27,14 +28,31 @@ class NewProductInput implements Partial<Product> {
price: number;
}

// data from range picker
@InputType()
class ProductDateRangeInput {
@Field(() => Date,{ nullable: true })
startDate: Date

@Field(() => Date ,{ nullable: true })
endDate: Date
}

@Resolver(Product)
class ProductResolver {
@Query(() => [Product])
async getAllProducts() {
const products = await Product.find({ relations: ["articles"] });
const products = await Product.find({
relations: {
articles: {
reservations: true,
},
},
});
return products;
}


@Authorized(Role.Admin)
@Mutation(() => Product)
async createNewProduct(@Arg("data") newProductData: NewProductInput) {
Expand All @@ -55,13 +73,63 @@ class ProductResolver {
}

@Query(() => [Product])
async searchProducts(@Arg("keyword") keyword: string) {
const products = await Product.find({
async searchAndFilterProducts(
@Arg("keyword", { nullable: true }) keyword?: string,
@Arg("dateRangeInput", { nullable: true }) dateRangeInput?: ProductDateRangeInput
) {
let products: Product[]


if (keyword) {
products = await Product.find({
where: [{ name: Like(`%${keyword}%`) }],
});
return products;
relations: {
articles: {
reservations: true,
},
},
})
} else {
products = await Product.find({
relations: {
articles: {
reservations: true,
},
},
})
}

if (!dateRangeInput) {
return products
}
const availableProducts = products.filter(product => {
// si un produit n'a pas d'article associé il n'est pas dispo pas dispo
if (!product.articles || product.articles.length === 0) {
return false
}

// si un produit a au moins un article dispo, on l'affiche
return product.articles.some(article => {
// si un article n'a pas de réservation associée, il est dispo
if (!article.reservations || article.reservations.length === 0) {
return true
}

// je regarde les réservations pour chaque article.
// la fonction renvoie false si, pour au moins une réservation :
return article.reservations.every(reservation => {
return (
reservation.endDate < dateRangeInput.startDate || // la date de début que j'ai choisie tombe avant la fin de la réservation
reservation.startDate > dateRangeInput.endDate // la date de fin que j'ai choisie tombe après le début de la réservation
)
})
})
})

return availableProducts
}


@Authorized(Role.Admin)
@Mutation(() => Product)
async editProduct(
Expand Down
10 changes: 10 additions & 0 deletions backend/src/resolvers/ReservationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ class ReservationResolver {
return reservation;
}

@Query(() => [Reservation])
async getReservationsByArticleId(@Arg("articleId") articleId: string) {
const reservations = await Reservation.find({
where: { articles: { id: Number(articleId) } },
relations: ["user", "articles", "articles.product"],
});
return reservations
}


@Query(() => ReservationWithTotal)
async getCurrentReservationByUserId(@Ctx() context: Context) {
if (context.id !== undefined) {
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import ProductDescription from "./pages/ProductDescription";
import Admin from "./pages/Admin";
import Login from "./pages/Login";
import SearchPage from "./pages/search/[searchKeywords]";
import SearchError from "./pages/search/SearchError";
import Profile from "./pages/Profile";
import { Cart } from "./pages/Cart";

Expand All @@ -21,8 +20,8 @@ const App = () => {
<Route path="profile" element={<Profile />} />
<Route path="login" element={<Login />} />
<Route path="/product/:productId" element={<ProductDescription />} />
<Route path="search/:keyword" element={<SearchPage />} />
<Route path="search/" element={<SearchError />} />
<Route path="search" element={<SearchPage />} />
<Route path="/search/:keyword" element={<SearchPage />} />
<Route path="register" element={<Register />} />
<Route path="cart" element={<Cart />} />
</Route>
Expand Down
48 changes: 0 additions & 48 deletions frontend/src/components/EditArticleDropdown.tsx

This file was deleted.

14 changes: 0 additions & 14 deletions frontend/src/components/ListProductsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,6 @@ function ListProductsTable() {
key: "price",
render: (price: number) => `${price.toFixed(2)} €`,
},
{
title: "Disponibles",
dataIndex: "articles",
key: "available",
render: (articles: ArticleProps[]) =>
articles.filter((article) => article.availability === true).length,
},
{
title: "Réservés",
dataIndex: "articles",
key: "unavailable",
render: (articles: ArticleProps[]) =>
articles.filter((article) => article.availability === false).length,
},
{
title: "Stock total",
dataIndex: "articles",
Expand Down
15 changes: 10 additions & 5 deletions frontend/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@ function Navbar() {
{ name: "Course", path: "/category/course" },
];

const onSearch = (value: string) => {
navigate(`/search/${value}`);
};
// console.log(userInfo);
const onSearch = (value?: string) => {
console.log("value", value)
if (value) {
navigate(`/search/${value}`)
} else {
navigate("/search")
}
}

return (
<div className="flex flex-col p-4 bg-lightBlue">
<div className="flex justify-between items-center">
Expand Down Expand Up @@ -58,7 +63,7 @@ function Navbar() {
</Button>
}
/>
<RangePicker />
<RangePicker onSearch={onSearch} />
</div>

<div className="flex items-center">
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/components/RangePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { DatePicker } from "antd"
import dayjs, { Dayjs } from "dayjs"
import { useState, useEffect } from "react"
import { useParams } from "react-router-dom"

export default function RangePicker({
onSearch,
} : {
onSearch: (value?: string | undefined) => void
}) {
const { RangePicker } = DatePicker
const { keyword } = useParams<{ keyword?: string }>();

export default function RangePicker() {
const { RangePicker } = DatePicker;

const [selectedDates, setSelectedDates] = useState<[Dayjs | null, Dayjs | null]>([
localStorage.getItem("startDate") ? dayjs(localStorage.getItem("startDate")) : null,
Expand All @@ -27,6 +34,11 @@ export default function RangePicker() {
localStorage.removeItem("endDate")
setSelectedDates([null, null])
}
if (keyword) {
onSearch(keyword);
} else {
onSearch();
}
};

useEffect(() => {
Expand Down
13 changes: 2 additions & 11 deletions frontend/src/components/ReservationButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,8 @@ export default function ReservationButton({
return;
}

// TO DO : à changer quand la recherche en amont sera mise en place
const availableArticles = articles.filter(
(article) => article.availability === true
);
if (availableArticles.length === 0 && userInfo.isLoggedIn === true) {
message.error("Cet article n'est pas disponible");
return;
}

// TO BE DISCUSSED : comment choisir l'article si plusieurs sont dispo ?
const firstAvailableArticleId = availableArticles[0].id;
// TO BE DISCUSSED : comment choisir l'article si plusieurs sont dispo ?
const firstAvailableArticleId = articles[0].id

handleReservation({
variables: {
Expand Down
Loading

0 comments on commit 44ff63d

Please sign in to comment.