From e6fa7db87b5f879de71bfc9e4f8afea9d722ab95 Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Fri, 26 May 2023 18:57:36 -0600 Subject: Añade creación de reseñas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/clients/actions.ts | 64 ++++++++++++++++++--------- src/components/forms/address_form.tsx | 2 +- src/components/forms/review_form.tsx | 48 ++++++++++++++++++++ src/components/review.tsx | 6 +-- src/components/user_account_dropdown_menu.tsx | 2 +- src/lib/session.ts | 7 +++ src/lib/token.ts | 9 ++++ src/main.tsx | 4 +- src/routes/products/product.tsx | 27 ++++++++--- 9 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 src/components/forms/review_form.tsx create mode 100644 src/lib/session.ts (limited to 'src') diff --git a/src/clients/actions.ts b/src/clients/actions.ts index 447d054..852e473 100644 --- a/src/clients/actions.ts +++ b/src/clients/actions.ts @@ -79,27 +79,6 @@ export async function create({ request }) { } } -export async function addToCart({ params, request }) { - const client = new ApiClient(); - const product = await client.get(`/products/${ params.productId }`); - const post_request_path = "/account/cart"; - const id = product.data.data.id; - const form = await request.formData(); - - form.append('product_id', id); - - if(form.get('quantity') == '') - form.set('quantity', 1); - - try { - await client.post(post_request_path, form, client.authorizationHeaders()); - } catch(error) { - return error.response.status; - } - - return 200; -} - export async function deleteFromCart({ request }) { const client = new ApiClient(); const form = await request.formData(); @@ -124,4 +103,47 @@ export async function placeOrder({ request }) { } catch(error) { return redirect('/account/cart') } +} + +export async function productAction({ params, request }) { + const client = new ApiClient(); + const product = await client.get(`/products/${ params.productId }`); + + if(!product) + redirect(`/products`); + + const product_id = product.data.data.id; + const form = await request.formData(); + + if(form.get('intent') == 'create_review') { + return await createReview(params.productId, form, client); + } + + return await addToCart(product_id, form, client); +} + +async function addToCart(product_id: string, form: FormData, client: ApiClient) { + const post_request_path = "/account/cart"; + form.append('product_id', product_id); + + if(form.get('quantity') == '') + form.set('quantity', 1); + + try { + await client.post(post_request_path, form, client.authorizationHeaders()); + } catch(error) { + return error.response.status; + } + + return 200; +} + +async function createReview(product_id: string, form: FormData, client: ApiClient) { + try { + await client.post(`/products/${product_id}/reviews`, form, client.authorizationHeaders()); + + return null; + } catch(error) { + return error.response; + } } \ No newline at end of file diff --git a/src/components/forms/address_form.tsx b/src/components/forms/address_form.tsx index 897b09b..2d3b8f4 100644 --- a/src/components/forms/address_form.tsx +++ b/src/components/forms/address_form.tsx @@ -54,7 +54,7 @@ export default function AddressForm({ address = null, errors = null }) { id: "country-field", type: "select", name: "country", - label: "Ciudad" + label: "País" } if(errors) diff --git a/src/components/forms/review_form.tsx b/src/components/forms/review_form.tsx new file mode 100644 index 0000000..8ed3848 --- /dev/null +++ b/src/components/forms/review_form.tsx @@ -0,0 +1,48 @@ +import { Form } from "react-router-dom"; +import StarPicker from 'react-star-picker'; +import "../stylesheets/shared.css"; +import { useState } from "react"; + +function getTextArea(errors) { + if(errors && errors.review) { + const error_messages = errors.review.map(message => +

{message}

+ ); + + return ( +
+ + {error_messages} +
+ ); + } + + return ( +
+ +
+ ); +} + +export default function ReviewForm({ product, errors }) { + const [rating, setRating] = useState(0); + const review_field = getTextArea(errors); + + const onChange = (value, name) => { + setRating(value); + }; + + return ( +
+ + + {review_field} +
+ +
+ + ); +} \ No newline at end of file diff --git a/src/components/review.tsx b/src/components/review.tsx index 153c202..d97e410 100644 --- a/src/components/review.tsx +++ b/src/components/review.tsx @@ -7,7 +7,7 @@ export default function Review({ review }) { return( <> -
+
{review.attributes.author_name} @@ -20,9 +20,9 @@ export default function Review({ review }) {
-
+
diff --git a/src/components/user_account_dropdown_menu.tsx b/src/components/user_account_dropdown_menu.tsx index 2bd0d28..e4ab023 100644 --- a/src/components/user_account_dropdown_menu.tsx +++ b/src/components/user_account_dropdown_menu.tsx @@ -1,5 +1,5 @@ import { PersonFill, DoorOpenFill, CardList } from "react-bootstrap-icons"; -import { BrowserRouter, Form } from "react-router-dom"; +import { BrowserRouter } from "react-router-dom"; import RedirectTo from '../lib/redirect_to'; import Token from "../lib/token"; import redirectTo from "../lib/redirect_to"; diff --git a/src/lib/session.ts b/src/lib/session.ts new file mode 100644 index 0000000..5d4967f --- /dev/null +++ b/src/lib/session.ts @@ -0,0 +1,7 @@ +import Token from "./token"; + +export function isUserAuthenticated() { + const session = new Token(); + + return session.get() != null && !session.expired(); +} \ No newline at end of file diff --git a/src/lib/token.ts b/src/lib/token.ts index 93ec0cf..f27df28 100644 --- a/src/lib/token.ts +++ b/src/lib/token.ts @@ -59,6 +59,15 @@ export default class Token { return this.decode()?.exp; } + expired() { + const exp = this.decode()?.exp; + + if(!exp) + return true; + + return exp < Math.floor(Date.now() / 1000); + } + private decode(): { data: string; aud: string; jti: string; exp: number; } | null { const token = this.get(); diff --git a/src/main.tsx b/src/main.tsx index 8f9c586..ab98f13 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -9,7 +9,7 @@ import Cart from "./routes/account/cart"; import Orders from "./routes/account/orders"; import { EditAccount } from './routes/account/edit'; import { Edit as EditAddress } from "./routes/account/addresses/edit"; -import { create, editAccount, authenticatedEdit, addToCart, deleteFromCart, placeOrder } from './clients/actions'; +import { create, editAccount, authenticatedEdit, deleteFromCart, placeOrder, productAction } from './clients/actions'; import Layout from "./components/layout"; import { accountLoader, loader, productLoader, addressLoader, cardLoader, cartLoader, ordersLoader } from "./clients/loaders"; import './index.css'; @@ -26,7 +26,7 @@ const routes = [ { path: "products/:productId", loader: productLoader, - action: addToCart, + action: productAction, element: , }, { diff --git a/src/routes/products/product.tsx b/src/routes/products/product.tsx index cca4073..fb883a8 100644 --- a/src/routes/products/product.tsx +++ b/src/routes/products/product.tsx @@ -2,17 +2,20 @@ import { useLoaderData, Form, useActionData } from "react-router-dom"; import { CartPlusFill } from "react-bootstrap-icons" import { InfoModal } from "../../components/info_modal"; import { Modal } from "flowbite"; +import { isUserAuthenticated } from "../../lib/session"; import ProductListing from "../../components/product_listing"; import MainContentLayout from "../../components/main_content_layout"; import Review from "../../components/review"; +import ReviewForm from "../../components/forms/review_form"; import "../../components/stylesheets/shared.css" import { useEffect } from "react"; export default function Product() { let quantity_field; + let review_form_error_messages; const response = useLoaderData(); - const response_status = useActionData(); + const action_response = useActionData(); const product = response[0].data; const reviews = response[1].data.data.map(review =>
  • @@ -33,12 +36,22 @@ export default function Product() { modal.hide(); }); - if(response_status == 200) + if(action_response == 200) modal.show(); - }, [response_status]); + }, [action_response]); + let review_form; - if(response_status == 422) { + if(isUserAuthenticated()) { + if(action_response?.status == 422) + review_form_error_messages = action_response.data.errors; + review_form = ; + } + else { + review_form = null; + } + + if(action_response == 422) { quantity_field = (
    {quantity_field} -
    +
    + {review_form} +
      {reviews}
    -- cgit v1.2.3