diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/clients/actions.ts | 49 | ||||
-rw-r--r-- | src/clients/api_client.ts | 16 | ||||
-rw-r--r-- | src/components/addresses_table.tsx | 6 | ||||
-rw-r--r-- | src/components/forms/address_form.tsx | 34 | ||||
-rw-r--r-- | src/components/forms/fields/select_field.tsx | 32 | ||||
-rw-r--r-- | src/lib/form_utils.ts | 17 | ||||
-rw-r--r-- | src/main.tsx | 12 | ||||
-rw-r--r-- | src/routes/account/addresses/create.tsx | 18 | ||||
-rw-r--r-- | src/routes/account/addresses/edit.tsx | 7 |
9 files changed, 139 insertions, 52 deletions
diff --git a/src/clients/actions.ts b/src/clients/actions.ts index bfddf54..cb5ba23 100644 --- a/src/clients/actions.ts +++ b/src/clients/actions.ts @@ -1,8 +1,22 @@ import { redirect } from "react-router-dom"; import { ApiClient } from "./api_client"; -import { clearSessionStorage, deleteEmptyFields, setFormErrorsInSessionStorage } from "../lib/form_utils"; +import { clearSessionStorage, deleteEmptyFields, setFormErrorsInSessionStorage, FormError } from "../lib/form_utils"; import Token from "../lib/token"; +function requestErrorsToArray(errors: any) { + const errors_array = new Array<FormError>(); + + for(const [field, error_message] of Object.entries(errors)) { + const e = { + field_name: field, + message: error_message.toString() + }; + errors_array.push(e); + } + + return errors_array; +} + export async function editAccount({ request }) { const client = new ApiClient(); let form_data = await request.formData(); @@ -25,23 +39,38 @@ export async function editAddress({ params, request }) { form_data = deleteEmptyFields(form_data); try { - - const response = await client.put(`/account/addresses/${params.addressId}`, form_data); - - if(response.status == 401 || response.status == 404) - return redirect("/products"); + await client.put(`/account/addresses/${params.addressId}`, form_data); return redirect("/account"); } catch(error) { if(error.response.status == 401) { - new Token().logout; + new Token().logout(); - return redirect("/products") + return redirect("/products"); } else { - setFormErrorsInSessionStorage(error.response.data.errors); + return requestErrorsToArray(error.response.data.errors); + } + } +} + +export async function createAddress({ request }) { + clearSessionStorage(); + const client = new ApiClient(); + const form_data = await request.formData(); + + try{ + await client.post('/account/addresses', form_data, client.authorizationHeaders()); - return redirect(`/account/addresses/${params.addressId}/edit`); + return redirect("/account"); + } catch(error) { + if(error.response.status == 401) { + new Token().logout(); + + return redirect("/products"); + } + else { + return requestErrorsToArray(error.response.data.errors); } } }
\ No newline at end of file diff --git a/src/clients/api_client.ts b/src/clients/api_client.ts index 4362c4d..81ae482 100644 --- a/src/clients/api_client.ts +++ b/src/clients/api_client.ts @@ -54,6 +54,14 @@ export class ApiClient { return [product_response, product_reviews]; } + authorizationHeaders() { + return { + headers: { + Authorization: `Bearer ${this.token.get()}` + } + }; + } + private async makeGetRequest(request_url: string, headers?: object) { try { const response = await axios.get(request_url, headers); @@ -63,12 +71,4 @@ export class ApiClient { return error; } } - - private authorizationHeaders() { - return { - headers: { - Authorization: `Bearer ${this.token.get()}` - } - }; - } }
\ No newline at end of file diff --git a/src/components/addresses_table.tsx b/src/components/addresses_table.tsx index ae46f21..ea7d840 100644 --- a/src/components/addresses_table.tsx +++ b/src/components/addresses_table.tsx @@ -10,6 +10,12 @@ export default function AddressesTable({ addresses }) { <h1 className="text-2xl my-2"> Direcciones de envío </h1> + <div className="absolute top-0 right-0"> + <a type="button" className="focus:outline-none text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800" + href="/account/addresses/new"> + Nueva dirección + </a> + </div> <table className="w-full text-sm text-left text-gray-500 dark:text-gray-400"> <thead className="bg-blue-arma text-xs text-white uppercase dark:bg-gray-700 dark:text-gray-400"> <tr> diff --git a/src/components/forms/address_form.tsx b/src/components/forms/address_form.tsx index 11b0191..53822bb 100644 --- a/src/components/forms/address_form.tsx +++ b/src/components/forms/address_form.tsx @@ -1,72 +1,72 @@ import { Form } from "react-router-dom"; -import { formHasErrors } from "../../lib/form_utils"; import countryList from "react-select-country-list"; import FieldProperties from "./fields/field_properties"; import Field from "./fields/field"; import SelectField from "./fields/select_field"; +import { useEffect } from "react"; +import { setFieldErrorMessages } from "../../lib/form_utils"; -function getFieldProperties(address: any) { +function getFieldProperties(address?: any) { const fields: Array<FieldProperties> = [ { id: "street-field", type: "text", name: "street", label: "Calle", - placeholder: address.street + placeholder: address?.street }, { id: "number-field", type: "number", name: "number", label: "Número", - placeholder: address.number + placeholder: address?.number }, { id: "zip-code-field", type: "number", name: "zip_code", label: "Código postal", - placeholder: address.zip_code + placeholder: address?.zip_code }, { id: "city-field", type: "text", name: "city", label: "Ciudad", - placeholder: address.city + placeholder: address?.city } ]; - for(const field of fields) { - if(sessionStorage.getItem(field.name)) { - field.error_message = sessionStorage.getItem(field.name); - } - } - return fields; } function getCountrySelectOptions(address: any, country_code: string) { - if(country_code == address.country) + if(address && country_code == address.country) return (<option key={ country_code } value={ country_code } selected>{ countryList().getLabel(country_code) }</option>); else return (<option key={ country_code } value={ country_code }>{ countryList().getLabel(country_code) }</option>); } -export default function AddressForm({ address }) { + +export default function AddressForm({ address = null, errors = null }) { + let field_properties = getFieldProperties(address); const select_field_properties: FieldProperties = { id: "country-field", type: "select", - name: "city", + name: "country", label: "Ciudad" } + if(errors) + field_properties = setFieldErrorMessages(field_properties, errors); + const options = countryList().getValues().map(country_code => getCountrySelectOptions(address, country_code) ); - const fields = getFieldProperties(address).map(field_properties => - <Field properties={field_properties}/> + const fields = field_properties.map(field_property => + <Field properties={field_property}/> ); return( diff --git a/src/components/forms/fields/select_field.tsx b/src/components/forms/fields/select_field.tsx index 75859d9..4d67754 100644 --- a/src/components/forms/fields/select_field.tsx +++ b/src/components/forms/fields/select_field.tsx @@ -1,10 +1,26 @@ export default function SelectField({ properties, options }) { - return( - <div className="mb-6"> - <label className="block mb-2 text-lg text-gray-900 dark:text-white">{properties.label}</label> - <select id={properties.id} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> - {options} - </select> - </div> - ); + let select_field; + + if(properties.error_message) { + select_field = ( + <div className="mb-6"> + <label className="block mb-2 text-lg text-red-700 dark:text-red-500">{properties.label}</label> + <select id={properties.id} name={properties.name} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> + {options} + </select> + <p className="mt-2 text-sm text-red-600 dark:text-red-500"> {properties.error_message }</p> + </div> + ); + } else { + select_field = ( + <div className="mb-6"> + <label className="block mb-2 text-lg text-gray-900 dark:text-white">{properties.label}</label> + <select id={properties.id} name={properties.name} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> + {options} + </select> + </div> + ); + } + + return select_field; }
\ No newline at end of file diff --git a/src/lib/form_utils.ts b/src/lib/form_utils.ts index af29463..43fe0fa 100644 --- a/src/lib/form_utils.ts +++ b/src/lib/form_utils.ts @@ -1,8 +1,8 @@ +import FieldProperties from "../components/forms/fields/field_properties"; import Token from "./token"; -export interface Error { - field_div_id: string; - field_input_id: string; +export interface FormError { + field_name: string; message: string; } @@ -23,6 +23,17 @@ export function setFormErrorsInSessionStorage(errors: Array<any>) { } } +export function setFieldErrorMessages(fields: FieldProperties[], errors: FormError[]) { + for(const field of fields) { + const error = errors.find(e => e.field_name == field.name) + if(error){ + field.error_message = error.message; + } + } + + return fields; +} + export function clearSessionStorage() { const token = new Token(); const stored_token = sessionStorage.getItem('token'); diff --git a/src/main.tsx b/src/main.tsx index b044f49..f1020b1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,10 +7,11 @@ import Companies from "./routes/companies/companies"; import Account from "./routes/account/account"; import { EditAccount } from './routes/account/edit'; import { Edit as EditAddress } from "./routes/account/addresses/edit"; -import { editAccount, editAddress } from './clients/actions'; +import { createAddress, editAccount, editAddress } from './clients/actions'; import Layout from "./components/layout"; import { accountLoader, loader, productLoader, addressLoader } from "./clients/loaders"; import './index.css'; +import { Create as CreateAddress } from './routes/account/addresses/create'; const routes = [ { @@ -46,6 +47,11 @@ const routes = [ action: editAddress, }, { + path: "/account/addresses/new", + element: <CreateAddress/>, + action: createAddress + }, + { path: '/', element: <Navigate to='/products'/> } @@ -54,9 +60,9 @@ const routes = [ const router = createBrowserRouter(routes); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - //<React.StrictMode> + <React.StrictMode> <Layout> <RouterProvider router={router}/> </Layout> - //</React.StrictMode> + </React.StrictMode> )
\ No newline at end of file diff --git a/src/routes/account/addresses/create.tsx b/src/routes/account/addresses/create.tsx new file mode 100644 index 0000000..5e33a2a --- /dev/null +++ b/src/routes/account/addresses/create.tsx @@ -0,0 +1,18 @@ +import { useActionData } from "react-router-dom"; +import AddressForm from "../../../components/forms/address_form"; +import MainContentLayout from "../../../components/main_content_layout"; + +export function Create() { + const errors = useActionData(); + + return ( + <MainContentLayout> + <div className="w-4/5 my-6"> + <h1 className="my-6 text-3xl"> + Nueva dirección + </h1> + <AddressForm errors={errors}/> + </div> + </MainContentLayout> + ); +}
\ No newline at end of file diff --git a/src/routes/account/addresses/edit.tsx b/src/routes/account/addresses/edit.tsx index 31bfaed..65c7b9a 100644 --- a/src/routes/account/addresses/edit.tsx +++ b/src/routes/account/addresses/edit.tsx @@ -1,17 +1,18 @@ import AddressForm from "../../../components/forms/address_form"; -import { useLoaderData } from "react-router-dom"; +import { useLoaderData, useActionData } from "react-router-dom"; import MainContentLayout from "../../../components/main_content_layout"; export function Edit() { const address = useLoaderData().data.data.attributes; + const errors = useActionData(); return( <MainContentLayout> <div className="w-4/5 my-6"> <h1 className="my-6 text-3xl"> - Editar cuenta + Editar dirección </h1> - <AddressForm address={address}/> + <AddressForm address={address} errors={errors}/> </div> </MainContentLayout> ); |