From b878d489e82520a557690384d8ebf69fc23a411d Mon Sep 17 00:00:00 2001 From: Benjamin POCHAT Date: Mon, 19 Aug 2024 01:14:23 +0200 Subject: [PATCH] convert loading with callbacks to react-router-loaders (...suite, producer area finished, customer area begin...) --- TODO | 3 +- frontend/app/src/api/mock/MockApi.ts | 15 +- frontend/app/src/api/mock/MockApiProducers.ts | 9 + .../domains/customer/CustomerController.tsx | 32 ---- .../domains/customer/views/CustomersList.tsx | 31 ++-- .../domains/producer/ProducerController.tsx | 171 ------------------ .../producer/views/ProducerAccountView.tsx | 79 ++++++++ .../components/BeefProductionCustomerCard.tsx | 19 +- .../beefProduction/BeefProductionView.tsx | 29 +-- .../app/src/domains/sale/SaleController.tsx | 77 -------- .../src/domains/sale/components/SaleCard.tsx | 22 ++- .../components/SaleCardBeefProduction.tsx | 25 +-- .../sale/components/SaleCustomerCard.tsx | 12 +- .../app/src/domains/sale/views/SalesList.tsx | 15 +- frontend/app/src/domains/welcome/Welcome.tsx | 27 +-- .../layouts/ViandeEnDirectRouterProvider.tsx | 6 +- .../src/layouts/customer/CustomerLayout.tsx | 78 +++----- .../customer/CustomerRouterFactory.tsx | 10 +- .../layouts/producer/AuthenticatedLayout.tsx | 13 +- .../producer/ProducerRouterFactory.tsx | 17 +- 20 files changed, 248 insertions(+), 442 deletions(-) delete mode 100644 frontend/app/src/domains/customer/CustomerController.tsx delete mode 100644 frontend/app/src/domains/producer/ProducerController.tsx create mode 100644 frontend/app/src/domains/producer/views/ProducerAccountView.tsx delete mode 100644 frontend/app/src/domains/sale/SaleController.tsx diff --git a/TODO b/TODO index ad99c75..8a41a15 100644 --- a/TODO +++ b/TODO @@ -1 +1,2 @@ -Faire fonctionner le la modification d'un abattage bovin \ No newline at end of file +Revoir le fonctionnement du contrôle producteur / client +Finir la migration avec react-router pour l'espace client \ No newline at end of file diff --git a/frontend/app/src/api/mock/MockApi.ts b/frontend/app/src/api/mock/MockApi.ts index a209d35..40757c1 100644 --- a/frontend/app/src/api/mock/MockApi.ts +++ b/frontend/app/src/api/mock/MockApi.ts @@ -69,8 +69,8 @@ export class MockApi { return this.mockApiProducers.getCustomers() } - getSales(args) { - this.log('getSales', args) + getSales() { + this.log('getSales', null) return this.mockApiSales.getSales() } @@ -103,4 +103,15 @@ export class MockApi { this.log('getProducer', args) return this.mockApiProducers.getProducer() } + + getStripeAccount(args) { + this.log('getStripeAccount', args) + return this.mockApiProducers.getStripeAccount() + } + + createStripeAccount(args) { + this.log('createStripeAccount', args) + return this.mockApiProducers.getStripeAccount() + } + } \ No newline at end of file diff --git a/frontend/app/src/api/mock/MockApiProducers.ts b/frontend/app/src/api/mock/MockApiProducers.ts index d76163d..bde6382 100644 --- a/frontend/app/src/api/mock/MockApiProducers.ts +++ b/frontend/app/src/api/mock/MockApiProducers.ts @@ -1,8 +1,17 @@ +import { StripeAccount } from "@viandeendirect/api/dist/models" import {Customer} from "@viandeendirect/api/dist/models/Customer" import {Producer} from "@viandeendirect/api/dist/models/Producer" import {Sale} from "@viandeendirect/api/dist/models/Sale" export class MockApiProducers { + getStripeAccount(): StripeAccount { + return { + id: 1, + accountLink: 'http://fake_stripe_account_link', + stripeId: '12345', + detailsSubmitted: true + } + } getProducer(): Producer { return { diff --git a/frontend/app/src/domains/customer/CustomerController.tsx b/frontend/app/src/domains/customer/CustomerController.tsx deleted file mode 100644 index 13dd5d3..0000000 --- a/frontend/app/src/domains/customer/CustomerController.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useState } from 'react' -import CustomersList from './views/CustomersList.tsx' -import React from 'react' -import { useKeycloak } from '@react-keycloak/web' -import { ProducerService } from '../commons/service/ProducerService.ts' -import Producer from '@viandeendirect/api/dist/models/Producer.js' -import AuthenticatedLayout from '../../layouts/producer/AuthenticatedLayout.tsx' - -export default function CustomerController() { - - const { keycloak } = useKeycloak() - const producerService = new ProducerService(keycloak) - const [producer, setProducer] = useState() - - const NONE = 'NONE' - - const [currentAction, setCurrentAction] = useState(NONE) - - useEffect(() => { - producerService.loadProducer(setProducer) - }) - - return <>{getContent()} - - function getContent() { - if(producer) { - switch (currentAction) { - case 'NONE': return - } - } - } -} diff --git a/frontend/app/src/domains/customer/views/CustomersList.tsx b/frontend/app/src/domains/customer/views/CustomersList.tsx index f9cf321..2586548 100644 --- a/frontend/app/src/domains/customer/views/CustomersList.tsx +++ b/frontend/app/src/domains/customer/views/CustomersList.tsx @@ -1,24 +1,16 @@ import React from 'react'; -import { useEffect, useState } from 'react' import { Typography } from "@mui/material" import { DataGrid, GridRowsProp, GridColDef, GridToolbar } from '@mui/x-data-grid'; -import { useKeycloak } from '@react-keycloak/web' -import { ApiInvoker } from '../../../api/ApiInvoker.ts'; +import { ApiBuilder } from '../../../api/ApiBuilder.ts'; +import { Producer } from '@viandeendirect/api/dist/models/Producer'; +import { ProducerService } from '../../commons/service/ProducerService.ts'; +import { useLoaderData } from 'react-router-dom'; +import { Customer } from '@viandeendirect/api/dist/models/Customer'; -export default function CustomersList({producer: producer}) { +export default function CustomersList() { - const [customers, setCustomers] = useState([]) - const { keycloak, initialized } = useKeycloak() - const apiInvoker = new ApiInvoker() - - useEffect(() => { - loadCustomers() - }, [keycloak]) - - function loadCustomers() { - apiInvoker.callApiAuthenticatedly(keycloak, api => api.getProducerCustomers, producer.id, setCustomers, console.error) - } + const customers: Array = useLoaderData() const rows: GridRowsProp = customers.map(customer => { return { @@ -52,4 +44,13 @@ export default function CustomersList({producer: producer}) { ) +} + +export async function loadCustomersListData(keycloak): Promise> { + const producerService = new ProducerService(keycloak) + const producer: Producer = await producerService.asyncLoadProducer() + const apiBuilder = new ApiBuilder(); + const api = await apiBuilder.getAuthenticatedApi(keycloak); + const customers = await api.getProducerCustomers({producerId: +producer.id}) + return customers } \ No newline at end of file diff --git a/frontend/app/src/domains/producer/ProducerController.tsx b/frontend/app/src/domains/producer/ProducerController.tsx deleted file mode 100644 index b57fb72..0000000 --- a/frontend/app/src/domains/producer/ProducerController.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React from 'react' -import { useState, useEffect } from 'react' - -import { Button, Link, Typography, CircularProgress } from "@mui/material" - -import { ApiInvoker } from '../../api/ApiInvoker.ts'; -import { useKeycloak } from '@react-keycloak/web'; -import Producer from '@viandeendirect/api/dist/models/Producer.js'; -import { AuthenticationService } from '../../authentication/service/AuthenticationService.ts'; -import AuthenticatedLayout from '../../layouts/producer/AuthenticatedLayout.tsx'; -import { ProducerService } from '../commons/service/ProducerService.ts'; - - -function ProducerController() { - - const apiInvoker = new ApiInvoker() - const {keycloak} = useKeycloak() - const producerService = new ProducerService(keycloak) - const authenticationService = new AuthenticationService(keycloak) - const [producer, setProducer] = useState() - const [stripeAccountCreationPending, setStripeAccountCreationPending] = useState(false) - - useEffect(() => { - producerService.loadProducer(producer => { - setProducer(producer) - if(producer.stripeAccount) { - loadStripeAccount(producer.id) - } - }) - }) - - return <> - Gestion du compte - {displayStripeAccount()} - - - function loadStripeAccount(producerId: number) { - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.getStripeAccount, - producerId, - stripeAccount => setProducer({...producer, stripeAccount: stripeAccount}), - console.error - ) - } - - function displayStripeAccount() { - if (producer?.stripeAccount) { - return <> -
Votre numéro de compte Stripe est {producer.stripeAccount.stripeId}
- {displayStripeAccountLink()} - - } else { - return - } - } - - function displayStripeAccountLink() { - if(producer?.stripeAccount) { - if (!producer.stripeAccount.detailsSubmitted) { - return - } else { - return <> - - - - } - } - } - - function displayStripeAccountCreationProgress() { - if (stripeAccountCreationPending) { - return - } - } - - function createStripeAccount() { - setStripeAccountCreationPending(true) - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.createStripeAccount, - producer?.id, - stripeAccount => { - setProducer({...producer, stripeAccount: stripeAccount}) - setStripeAccountCreationPending(false) - loadStripeAccount(producer.id) - }, - console.error) - } - -/* - const [accountCreatePending, setAccountCreatePending] = useState(false); - const [accountLinkCreatePending, setAccountLinkCreatePending] = useState(false); - const [stripeAccountId, setStripeAccountId] = useState(); - const [stripeError, setStripeError] = useState(); - - return <> - Gestion du compte - {!stripeAccountId &&

Get ready for take off

} - {!stripeAccountId &&

viandeendirect.eu is the world's leading air travel platform: join our team of pilots to help people travel faster.

} - {stripeAccountId &&

Add information to start accepting money

} - {stripeAccountId &&

Matt's Mats partners with Stripe to help you receive payments and keep your personal bank and details secure.

} - {!accountCreatePending && !stripeAccountId && ( - - )} - {stripeAccountId && !accountLinkCreatePending && ( - - )} - {stripeError &&

Something went wrong!

} - {(stripeAccountId || accountCreatePending || accountLinkCreatePending) && ( -
- {stripeAccountId &&

Your connected account ID is: {stripeAccountId}

} - {accountCreatePending &&

Creating a connected account...

} - {accountLinkCreatePending &&

Creating a new Account Link...

} -
- )} - - - async function createStripeAccount() { - setAccountCreatePending(true); - setStripeError(false); - const apiBuilder = new ApiBuilder() - const backendUrl = await apiBuilder.getBackendUrl() - fetch(backendUrl + "/payments/stripe/account", { - method: "POST", - }) - .then((response) => response.json()) - .then((json) => { - setAccountCreatePending(false); - const {account, error} = json - if (account) { - setStripeAccountId(account.accountId) - } - if (error) { - setStripeError(true) - } - }) - } - - async function finalizeStripeAccount() { - setAccountLinkCreatePending(true); - setStripeError(false); - const apiBuilder = new ApiBuilder() - const backendUrl = await apiBuilder.getBackendUrl() - fetch(backendUrl + "/payments/stripe/accountLink", { - method: "POST", - body: JSON.stringify({accountId: stripeAccountId}), - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => response.json()) - .then((json) => { - setAccountLinkCreatePending(false); - const {account, error} = json - if(account) { - window.location.href = account.onBoardingUrl - } - if (error) { - setStripeError(true) - } - }) - } - */ -} - - -export default ProducerController diff --git a/frontend/app/src/domains/producer/views/ProducerAccountView.tsx b/frontend/app/src/domains/producer/views/ProducerAccountView.tsx new file mode 100644 index 0000000..16a5d33 --- /dev/null +++ b/frontend/app/src/domains/producer/views/ProducerAccountView.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import { useState } from 'react' + +import { Button, Typography, CircularProgress } from "@mui/material" + +import { useKeycloak } from '@react-keycloak/web'; +import { Producer } from '@viandeendirect/api/dist/models/Producer.js'; +import { ProducerService } from '../../commons/service/ProducerService.ts'; +import { ApiBuilder } from '../../../api/ApiBuilder.ts'; +import { StripeAccount } from '@viandeendirect/api/dist/models/StripeAccount'; +import { useLoaderData } from 'react-router-dom'; + + +export default function ProducerAccountView() { + + const {keycloak} = useKeycloak() + const apiBuilder = new ApiBuilder() + const loadedProducer: Producer = useLoaderData() + const [producer, setProducer] = useState(loadedProducer) + const [stripeAccountCreationPending, setStripeAccountCreationPending] = useState(false) + + return <> + Gestion du compte + {displayStripeAccount()} + + + function displayStripeAccount() { + if (producer?.stripeAccount) { + return <> +
Votre numéro de compte Stripe est {producer.stripeAccount.stripeId}
+ {displayStripeAccountLink()} + + } else { + return + } + } + + function displayStripeAccountLink() { + if(producer?.stripeAccount) { + if (!producer.stripeAccount.detailsSubmitted) { + return + } else { + return <> + + + + } + } + } + + function displayStripeAccountCreationProgress() { + if (stripeAccountCreationPending) { + return + } + } + + async function createStripeAccount() { + setStripeAccountCreationPending(true) + const api = await apiBuilder.getAuthenticatedApi(keycloak) + const stripeAccount = await api.createStripeAccount({producerId: +producer.id}) + setProducer({...producer, stripeAccount: stripeAccount}) + setStripeAccountCreationPending(false) + } +} + +export async function loadProducerAccountViewData(keycloak) { + const producerService = new ProducerService(keycloak) + const producer: Producer = await producerService.asyncLoadProducer() + const apiBuilder = new ApiBuilder(); + const api = await apiBuilder.getAuthenticatedApi(keycloak); + if (producer.stripeAccount) { + const stripeAccount: StripeAccount = await api.getStripeAccount({producerId: +producer.id}) + return {...producer, stripeAccount: stripeAccount} + } + return producer +} \ No newline at end of file diff --git a/frontend/app/src/domains/production/components/BeefProductionCustomerCard.tsx b/frontend/app/src/domains/production/components/BeefProductionCustomerCard.tsx index 95cc8f6..c08ddce 100644 --- a/frontend/app/src/domains/production/components/BeefProductionCustomerCard.tsx +++ b/frontend/app/src/domains/production/components/BeefProductionCustomerCard.tsx @@ -3,23 +3,28 @@ import { useEffect, useState } from 'react' import { Typography } from "@mui/material" -import BeefProduction from '@viandeendirect/api/dist/models/BeefProduction.js' -import PackageLot from '@viandeendirect/api/dist/models/PackageLot.js' +import { BeefProduction } from '@viandeendirect/api/dist/models/BeefProduction.js' +import { PackageLot } from '@viandeendirect/api/dist/models/PackageLot.js' import { AnimalTypeUtils } from '../../../enum/AnimalType.ts' -import { ApiInvoker } from '../../../api/ApiInvoker.ts' import PieChart from '../../commons/components/PieChart.tsx' import PackageLotDescription from './PackageLotDescription.tsx' +import { ApiBuilder } from '../../../api/ApiBuilder.ts' export default function BeefProductionCustomerCard({production: production}) { const [beefProduction, setBeefProduction] = useState({}) const [percentageSold, setPercentageSold] = useState(0) - const [lots, setLots]= useState([]) - const apiInvoker = new ApiInvoker() + const apiBuilder = new ApiBuilder() useEffect(() => { - apiInvoker.callApiAnonymously(api => api.getBeefProduction, production.id, setBeefProduction) - apiInvoker.callApiAnonymously(api => api.getProductionPercentageSold, production.id, setPercentageSold) + const loadData = async () => { + const api = await apiBuilder.getAnonymousApi() + const loadedBeefProduction = await api.getBeefProduction({beefProductionId: production.id}) + setBeefProduction(loadedBeefProduction) + const loadedPercentageSold = await api.getProductionPercentageSold({productionId: production.id}) + setPercentageSold(+loadedPercentageSold) + } + loadData() }, []) diff --git a/frontend/app/src/domains/production/views/beefProduction/BeefProductionView.tsx b/frontend/app/src/domains/production/views/beefProduction/BeefProductionView.tsx index 136a630..acd89b6 100644 --- a/frontend/app/src/domains/production/views/beefProduction/BeefProductionView.tsx +++ b/frontend/app/src/domains/production/views/beefProduction/BeefProductionView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import React from 'react' import { useState } from 'react' import { useForm } from 'react-hook-form' import { Typography, ButtonGroup, Button, Tab, Tabs, Alert } from '@mui/material' @@ -6,11 +6,10 @@ import dayjs from 'dayjs' import BreedingPropertiesForm, { mapBreedingFormDataToBeefProduction } from './forms/BreedingPropertiesForm.tsx' import SlaughterPropertiesForm, { mapSlaughterFormDataToBeefProduction } from './forms/SlaughterPropertiesForm.tsx' import CuttingPropertiesForm, { mapCuttingFormDataToBeefProduction } from './forms/CuttingPropertiesForm.tsx' -import BeefProduction from "@viandeendirect/api/dist/models/BeefProduction.js" +import { BeefProduction } from "@viandeendirect/api/dist/models/BeefProduction.js" import PackageLotsCreator from '../PackageLotsCreator.tsx' -import { ApiInvoker } from '../../../../api/ApiInvoker.ts' import { useKeycloak } from '@react-keycloak/web' -import { useLoaderData, useNavigate, useParams } from 'react-router-dom' +import { useLoaderData, useNavigate } from 'react-router-dom' import { ApiBuilder } from '../../../../api/ApiBuilder.ts' export default function BeefProductionView() { @@ -20,10 +19,10 @@ export default function BeefProductionView() { const CUTTING_PROPERTIES_TAB = 2 const PRODUCTS_TAB = 3 - const apiInvoker = new ApiInvoker() const { keycloak } = useKeycloak() const navigate = useNavigate(); const loadedProduction = useLoaderData() + const apiBuilder = new ApiBuilder() const [currentTab, setCurrentTab] = useState(BREEDING_PROPERTIES_TAB) @@ -131,15 +130,19 @@ export default function BeefProductionView() { } } - function saveProduction(updatedProduction) { + async function saveProduction(updatedProduction) { setAlerts(undefined) - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.createBeefProduction, - updatedProduction, - () => setReadOnly(true), - error => setAlerts(error.response.body.message) - ) + const api = await apiBuilder.getAuthenticatedApi(keycloak) + try { + await api.createBeefProduction({beefProduction: updatedProduction}) + setReadOnly(true) + } catch (error) { + const reader = error.response.body.getReader() + const chunk = await reader.read() + const errorAsJson = new TextDecoder().decode(chunk.value); + const errorAsObject = JSON.parse(errorAsJson) + setAlerts(errorAsObject.message) + } } function cancelUpdate() { diff --git a/frontend/app/src/domains/sale/SaleController.tsx b/frontend/app/src/domains/sale/SaleController.tsx deleted file mode 100644 index 58ffda6..0000000 --- a/frontend/app/src/domains/sale/SaleController.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react' -import { useState, useEffect } from 'react' - -import SalesList from './views/SalesList.tsx' -import SaleForm from './views/SaleForm.tsx' -import OrdersList from './views/OrdersList.tsx' -import Sale from '@viandeendirect/api/dist/models/Sale' -import Order from '@viandeendirect/api/dist/models/Order' -import OrderView from './views/OrderView.tsx' -import ProducerOrderForm from './views/ProducerOrderForm.tsx' -import AuthenticatedLayout from '../../layouts/producer/AuthenticatedLayout.tsx' -import { useKeycloak } from '@react-keycloak/web' -import { ProducerService } from '../commons/service/ProducerService.ts' -import Producer from '@viandeendirect/api/dist/models/Producer.js' - -export default function SaleController() { - - const { keycloak, initialized } = useKeycloak() - const producerService = new ProducerService(keycloak) - const [producer, setProducer] = useState() - - const SALES_LIST_VIEW = 'SALES_LIST_VIEW' - const SALE_CREATION_VIEW = 'SALE_CREATION_VIEW' - const ORDERS_LIST_VIEW = 'ORDERS_LIST_VEW' - const ORDER_VIEW = 'ORDER_VIEW' - const ORDER_CREATION_VIEW = 'ORDER_CREATION_VIEW' - - const [currentView, setCurrentView] = useState(SALES_LIST_VIEW) - const [context, setContext] = useState(undefined) - - useEffect(() => { - producerService.loadProducer(setProducer) - }) - - return <>{getCurrentView()} - - function getCurrentView() { - if(producer) { - switch (currentView) { - case SALES_LIST_VIEW: return - case SALE_CREATION_VIEW: return - case ORDERS_LIST_VIEW: return createOrder(context)}/> - case ORDER_VIEW: return - case ORDER_CREATION_VIEW: return - } - } - } - - function displayOrdersList(sale: Sale) { - setContext(sale) - setCurrentView(ORDERS_LIST_VIEW) - } - - function displaySaleCreationForm() { - setContext(undefined) - setCurrentView(SALE_CREATION_VIEW) - } - - function displaySalesList() { - setContext(undefined) - setCurrentView(SALES_LIST_VIEW) - } - - function displayOrder(order: Order, sale: Sale) { - const orderContext = { - order: order, - sale: sale - } - setContext(orderContext) - setCurrentView(ORDER_VIEW) - } - - function createOrder(sale: Sale) { - setContext(sale) - setCurrentView(ORDER_CREATION_VIEW) - } -} diff --git a/frontend/app/src/domains/sale/components/SaleCard.tsx b/frontend/app/src/domains/sale/components/SaleCard.tsx index b394912..c3f8d8b 100644 --- a/frontend/app/src/domains/sale/components/SaleCard.tsx +++ b/frontend/app/src/domains/sale/components/SaleCard.tsx @@ -5,25 +5,31 @@ import dayjs from 'dayjs' import SaleCardBeefProduction from './SaleCardBeefProduction.tsx'; import { ApiInvoker } from '../../../api/ApiInvoker.ts'; import { useKeycloak } from '@react-keycloak/web'; -import Order from '@viandeendirect/api/dist/models/Order' -import Production from '@viandeendirect/api/dist/models/Production' import { useNavigate } from 'react-router-dom'; +import { ApiBuilder } from '../../../api/ApiBuilder.ts'; +import { Order } from '@viandeendirect/api/dist/models/Order' +import { Production } from '@viandeendirect/api/dist/models/Production' -export default function SaleCard({ sale: sale, manageOrdersCallback: manageOrdersCallback}) { +export default function SaleCard({sale: sale}) { const apiInvoker = new ApiInvoker() const [orders, setOrders] = useState>([]) const [productions, setProductions] = useState>([]) const {keycloak} = useKeycloak() const navigate = useNavigate() + const apiBuilder = new ApiBuilder() - useEffect(() => { - apiInvoker.callApiAuthenticatedly(keycloak, api => api.getSaleOrders, sale.id, setOrders, console.error) - }, [keycloak]) useEffect(() => { - apiInvoker.callApiAuthenticatedly(keycloak, api => api.getSaleProductions, sale.id, setProductions, console.error) + const loadData = async () => { + const api = await apiBuilder.getAuthenticatedApi(keycloak) + const loadedOrders = await api.getSaleOrders({saleId: sale.id}) + setOrders(loadedOrders) + const loadedProductions = await api.getSaleProductions({saleId: sale.id}) + setProductions(loadedProductions) + } + loadData() }, [keycloak]) return ( @@ -81,7 +87,7 @@ export default function SaleCard({ sale: sale, manageOrdersCallback: manageOrder function getProduction(production) { switch (production.productionType) { case 'BeefProduction': - return + return default: return <> diff --git a/frontend/app/src/domains/sale/components/SaleCardBeefProduction.tsx b/frontend/app/src/domains/sale/components/SaleCardBeefProduction.tsx index 38b2932..17bd567 100644 --- a/frontend/app/src/domains/sale/components/SaleCardBeefProduction.tsx +++ b/frontend/app/src/domains/sale/components/SaleCardBeefProduction.tsx @@ -7,6 +7,7 @@ import { AnimalTypeUtils } from '../../../enum/AnimalType.ts'; import PieChart from '../../commons/components/PieChart.tsx' import Production from '@viandeendirect/api/dist/models/BeefProduction' import './SaleCard.css' +import { ApiBuilder } from '../../../api/ApiBuilder.ts'; export default function SaleCardBeefProduction({production: production}) { @@ -14,23 +15,17 @@ export default function SaleCardBeefProduction({production: production}) { const [productionPercentageSold, setProductionPercentageSold] = useState([]) const { keycloak } = useKeycloak() const apiInvoker = new ApiInvoker() + const apiBuilder = new ApiBuilder() useEffect(() => { - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.getBeefProduction, - production.id, - setBeefProduction, - console.error) - }, [keycloak]) - - useEffect(() => { - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.getProductionPercentageSold, - production.id, - setProductionPercentageSold, - console.error) + const loadData = async () => { + const api = await apiBuilder.getAuthenticatedApi(keycloak) + const loadedBeefProduction = await api.getBeefProduction({beefProductionId: production.id}) + setBeefProduction(loadedBeefProduction) + const loadedPercentageSold = await api.getProductionPercentageSold({productionId: production.id}) + setProductionPercentageSold(+loadedPercentageSold) + } + loadData() }, [keycloak]) return <> diff --git a/frontend/app/src/domains/sale/components/SaleCustomerCard.tsx b/frontend/app/src/domains/sale/components/SaleCustomerCard.tsx index b98105f..d9947f5 100644 --- a/frontend/app/src/domains/sale/components/SaleCustomerCard.tsx +++ b/frontend/app/src/domains/sale/components/SaleCustomerCard.tsx @@ -1,14 +1,18 @@ import React from 'react' import { Button, Card, CardActions, CardContent } from '@mui/material' import dayjs from 'dayjs' +import { useNavigate } from 'react-router-dom'; +import ProductionCustomerCard from '../../production/components/ProductionCustomerCard.tsx'; +import { Production } from '@viandeendirect/api/dist/models/Production'; -import Production from '@viandeendirect/api/dist/models/Production.js' import './SaleCustomerCard.css' -import ProductionCustomerCard from '../../production/components/ProductionCustomerCard.tsx'; -export default function SaleCustomerCard({ sale: sale, createOrderCallback: createOrderCallBack }) { +export default function SaleCustomerCard({ sale: sale }) { + + const navigate = useNavigate() + return
@@ -33,7 +37,7 @@ export default function SaleCustomerCard({ sale: sale, createOrderCallback: crea {sale.productions.map(getProductionContent)} - + diff --git a/frontend/app/src/domains/sale/views/SalesList.tsx b/frontend/app/src/domains/sale/views/SalesList.tsx index 1d8629d..a8a40ca 100644 --- a/frontend/app/src/domains/sale/views/SalesList.tsx +++ b/frontend/app/src/domains/sale/views/SalesList.tsx @@ -12,23 +12,22 @@ import { Sale } from '@viandeendirect/api/dist/models/Sale'; export default function SalesList() { const navigate = useNavigate() - const data = useLoaderData() - const sales: Array = data.sales + const sales: Array = useLoaderData() return <> Ventes
- {sales.map(sale => )} + {sales.map(sale => )}
} -export async function loadSalesListData(keycloakClient): Promise<{sales: Array, producer: Producer}> { - const producerService = new ProducerService(keycloakClient) +export async function loadSalesListData(keycloak): Promise> { + const producerService = new ProducerService(keycloak) const producer: Producer = await producerService.asyncLoadProducer() const apiBuilder = new ApiBuilder() - const api = await apiBuilder.getAuthenticatedApi(keycloakClient) - const sales: Array = await api.getProducerSales({producerId: producer.id}) - return {sales: sales, producer: producer} + const api = await apiBuilder.getAuthenticatedApi(keycloak) + const sales: Array = await api.getProducerSales({producerId: +producer.id}) + return sales } \ No newline at end of file diff --git a/frontend/app/src/domains/welcome/Welcome.tsx b/frontend/app/src/domains/welcome/Welcome.tsx index 6a0160e..5d54b06 100644 --- a/frontend/app/src/domains/welcome/Welcome.tsx +++ b/frontend/app/src/domains/welcome/Welcome.tsx @@ -3,21 +3,17 @@ import { useEffect, useState } from 'react' import { AppBar, Box, CssBaseline, Toolbar, Typography } from '@mui/material' -import Sale from '@viandeendirect/api/dist/models/Sale.js' - -import { ApiInvoker } from '../../api/ApiInvoker.ts' -import SaleCustomerCard from '../../domains/sale/components/SaleCustomerCard.tsx' +import { useLoaderData } from 'react-router-dom'; +import { Sale } from '@viandeendirect/api/dist/models/Sale.js' -export default function Welcome({createOrderCallback: createOrderCallback}) { +import SaleCustomerCard from '../../domains/sale/components/SaleCustomerCard.tsx' +import { ApiBuilder } from '../../api/ApiBuilder.ts' - const [sales, setSales] = useState([]) - const apiInvoker = new ApiInvoker() - useEffect(() => { - apiInvoker.callApiAnonymously(api => api.getSales, null, setSales) - }, []) +export default function Welcome() { + const sales: Array = useLoaderData() return function getSaleCard(sale: Sale) { - return createOrderCallback(sale)}> + return } -} \ No newline at end of file +} + +export async function loadWelcomeData(): Promise> { + const apiBuilder = new ApiBuilder() + const api = await apiBuilder.getAnonymousApi() + const sales = await api.getSales() + return sales +} diff --git a/frontend/app/src/layouts/ViandeEnDirectRouterProvider.tsx b/frontend/app/src/layouts/ViandeEnDirectRouterProvider.tsx index a07073a..7080135 100644 --- a/frontend/app/src/layouts/ViandeEnDirectRouterProvider.tsx +++ b/frontend/app/src/layouts/ViandeEnDirectRouterProvider.tsx @@ -7,6 +7,8 @@ import {RouterFactory} from './RouterFactory.tsx' export default function ViandeEnDirectRouterProvider() { const {keycloak, initialized} = useKeycloak() const routerFactory = new RouterFactory() - - return + if (initialized) { + return + } + return <>Authentification en cours... } diff --git a/frontend/app/src/layouts/customer/CustomerLayout.tsx b/frontend/app/src/layouts/customer/CustomerLayout.tsx index 1a88929..3e3f4e4 100644 --- a/frontend/app/src/layouts/customer/CustomerLayout.tsx +++ b/frontend/app/src/layouts/customer/CustomerLayout.tsx @@ -1,23 +1,14 @@ import React from 'react' import { AppBar, Box, Button, CssBaseline, IconButton, Toolbar, Typography } from '@mui/material' -import { useEffect, useState } from 'react' - -import { useCookies } from 'react-cookie' import { useKeycloak } from '@react-keycloak/web' -import { Sale } from '@viandeendirect/api/dist/models/Sale' import { Customer } from '@viandeendirect/api/dist/models/Customer' -import { User } from '@viandeendirect/api/dist/models/User' -import { ApiInvoker } from '../../api/ApiInvoker.ts' import { AuthenticationService } from '../../authentication/service/AuthenticationService.ts' -import CustomerOrderForm from '../../domains/sale/views/CustomerOrderForm.tsx' -import CustomerCreationForm from '../../domains/customer/views/CustomerCreationForm.tsx' -import Welcome from '../../domains/welcome/Welcome.tsx' import { Login, Logout } from '@mui/icons-material' -import NotAuthorizedForProducers from '../../authentication/views/NotAuthorizedForProducers.tsx' -import { Outlet, useNavigate } from 'react-router-dom' +import { Navigate, Outlet, useLoaderData, useNavigate } from 'react-router-dom' +import { ApiBuilder } from '../../api/ApiBuilder.ts' export default function CustomerLayout() { @@ -26,52 +17,17 @@ export default function CustomerLayout() { const ORDER_CREATION = 'ORDER_CREATION' const NOT_AUTHORIZED_FOR_PRODUCER = 'NOT_AUTHORIZED_FOR_PRODUCER' - const apiInvoker = new ApiInvoker() - const {keycloak, initialized} = useKeycloak() + const {keycloak} = useKeycloak() const authenticationService = new AuthenticationService(keycloak) - const [mainContent, setMainContent] = useState(WELCOME) - const [context, setContext] = useState(undefined) - const [cookies, setCookie, removeCookie] = useCookies(['pendingOrder']); - const [customer, setCustomer] = useState(undefined) const navigate = useNavigate() - useEffect(() => { - if (initialized) { - if (authenticationService.isAuthenticated() && !customer) { - apiInvoker.callApiAuthenticatedly( - keycloak, - api => api.getCustomer, - {"email": authenticationService.getCurrentUserEmail()}, - aCustomer => initCustomer(aCustomer), - error => { - if (error.status === 409) { - setMainContent(NOT_AUTHORIZED_FOR_PRODUCER) - } else { - console.error(error) - } - }) - } - } - }, [initialized]) - - function initCustomer(customer :Customer) { - if (customer) { - setCustomer(customer) - } else { - const customer: Customer = {} - customer.user = {} - customer.user.lastName = authenticationService.getCurrentUserLastName() - customer.user.firstName = authenticationService.getCurrentUserFirstName() - customer.user.email = authenticationService.getCurrentUserEmail() - setCustomer(customer) - } - } + const data = useLoaderData() + const loggedAsProducer = data.loggedAsProducer + const customer = data.customer - function createOrder(sale: Sale) { - setContext(sale) - /*setMainContent(ORDER_CREATION)*/ - navigate('/order/creation') + if (loggedAsProducer) { + return } function displayAuthenticationButton(): React.ReactNode { @@ -108,3 +64,21 @@ export default function CustomerLayout() { ) } + +export async function loadCustomerLayoutData(keycloak) { + const authenticationService = new AuthenticationService(keycloak) + if (!authenticationService.isAuthenticated()) { + return {loggedAsProducer: false, customer: undefined} + } + try { + const userEmail = authenticationService.getCurrentUserEmail() + const apiBuilder = new ApiBuilder() + const api = await apiBuilder.getAuthenticatedApi(keycloak) + const customer: Customer = await api.getCustomer({'email': userEmail}) + return {loggedAsProducer: false, customer: customer} + } catch (error) { + if(error.response.status === 409) { + return {loggedAsProducer: true, customer: undefined} + } + } +} \ No newline at end of file diff --git a/frontend/app/src/layouts/customer/CustomerRouterFactory.tsx b/frontend/app/src/layouts/customer/CustomerRouterFactory.tsx index bc638a9..8a7fcbe 100644 --- a/frontend/app/src/layouts/customer/CustomerRouterFactory.tsx +++ b/frontend/app/src/layouts/customer/CustomerRouterFactory.tsx @@ -3,17 +3,18 @@ import React from "react"; import { createBrowserRouter, Navigate } from "react-router-dom"; import PaymentLayout from "./PaymentLayout.tsx"; -import CustomerLayout from "./CustomerLayout.tsx"; +import CustomerLayout, { loadCustomerLayoutData } from "./CustomerLayout.tsx"; import CustomerOrderForm from "../../domains/sale/views/CustomerOrderForm.tsx"; import NotAuthorizedForProducers from "../../authentication/views/NotAuthorizedForProducers.tsx"; -import Welcome from "../../domains/welcome/Welcome.tsx"; +import Welcome, { loadWelcomeData } from "../../domains/welcome/Welcome.tsx"; export class CustomerRouterFactory { - getRouter(keycloakClient) { + getRouter(keycloak) { return createBrowserRouter([ { path: "/", element: , + loader: async () => loadCustomerLayoutData(keycloak), children: [ { index: true, @@ -21,7 +22,8 @@ export class CustomerRouterFactory { }, { path: "/welcome", - element: + element: , + loader: async () => loadWelcomeData() }, { path: "/order/creation", diff --git a/frontend/app/src/layouts/producer/AuthenticatedLayout.tsx b/frontend/app/src/layouts/producer/AuthenticatedLayout.tsx index f1a4fc3..4a6e340 100644 --- a/frontend/app/src/layouts/producer/AuthenticatedLayout.tsx +++ b/frontend/app/src/layouts/producer/AuthenticatedLayout.tsx @@ -6,27 +6,16 @@ import {AppBar, Box, CssBaseline, IconButton, Toolbar, Typography} from '@mui/ma import {Close, Logout, Menu} from '@mui/icons-material' -import { Producer } from '@viandeendirect/api/dist/models/Producer.js'; import SideMenu from './SideMenu.jsx' import { AuthenticationService } from '../../authentication/service/AuthenticationService.ts'; -import { ProducerService } from '../../domains/commons/service/ProducerService.ts'; -import { Navigate, Outlet, useNavigate } from 'react-router-dom'; +import { Navigate, Outlet } from 'react-router-dom'; export default function AuthenticatedLayout() { const { keycloak } = useKeycloak() const [sideMenuOpen, setSideMenuOpen] = useState(false) - const [producer, setProducer] = useState() const [unauthorized, setUnauthorized] = useState(false) const authenticationService = new AuthenticationService(keycloak) - const producerService = new ProducerService(keycloak) - - useEffect(() => { - /* - producerService.loadProducer(setProducer, setUnauthorized) - */ - }, [keycloak]) - const sideMenuWidth = 240; diff --git a/frontend/app/src/layouts/producer/ProducerRouterFactory.tsx b/frontend/app/src/layouts/producer/ProducerRouterFactory.tsx index 7aee3d7..db27be7 100644 --- a/frontend/app/src/layouts/producer/ProducerRouterFactory.tsx +++ b/frontend/app/src/layouts/producer/ProducerRouterFactory.tsx @@ -3,7 +3,7 @@ import { createBrowserRouter, Navigate } from "react-router-dom"; import AuthenticatedLayout from "./AuthenticatedLayout.tsx"; import CustomerController from "../../domains/customer/CustomerController.tsx"; -import ProducerController from "../../domains/producer/ProducerController.tsx"; +import ProducerAccountView, { loadProducerAccountViewData } from "../../domains/producer/views/ProducerAccountView.tsx"; import Dashboard from "../../domains/dashboard/views/Dashboard.tsx"; import AnonymousLayout from "./AnonymousLayout.tsx"; import NotAuthorizedForCustomers from "../../authentication/views/NotAuthorizedForCustomers.tsx"; @@ -15,6 +15,7 @@ import SaleForm, { loadSaleFormData } from "../../domains/sale/views/SaleForm.ts import OrdersList, { loadOrdersListData } from "../../domains/sale/views/OrdersList.tsx"; import OrderView, { loadOrderViewData } from "../../domains/sale/views/OrderView.tsx"; import ProducerOrderForm, { loadProducerOrderFormData } from "../../domains/sale/views/ProducerOrderForm.tsx"; +import CustomersList, { loadCustomersListData } from "../../domains/customer/views/CustomersList.tsx"; export class ProducerRouterFactory { getRouter(keycloak) { @@ -29,7 +30,7 @@ export class ProducerRouterFactory { }, { path: '/dashboard', - element: + element: }, { path: '/productions', @@ -54,7 +55,7 @@ export class ProducerRouterFactory { { path: '/sales/creation', element: , - loader: async () => loadSaleFormData(keycloakClient) + loader: async () => loadSaleFormData(keycloak) }, { path: '/sale/:saleId/orders', @@ -64,20 +65,22 @@ export class ProducerRouterFactory { { path: '/sale/:saleId/order/:orderId', element: , - loader: async ({params}) => loadOrderViewData(+params.orderId, keycloakClient) + loader: async ({params}) => loadOrderViewData(+params.orderId, keycloak) }, { path: '/sale/:saleId/order/creation', element: , - loader: async ({params}) => loadProducerOrderFormData(+params.saleId, keycloakClient) + loader: async ({params}) => loadProducerOrderFormData(+params.saleId, keycloak) }, { path: '/customers', - element: + element: , + loader: async ({params}) => loadCustomersListData(keycloak) }, { path: '/account', - element: + element: , + loader: async ({params}) => loadProducerAccountViewData(keycloak) } ] },