diff --git a/dbinit/dbinit.sql b/dbinit/dbinit.sql new file mode 100644 index 0000000000000000000000000000000000000000..1070e073e94c76ab0f737dbb8caeac22222b9718 --- /dev/null +++ b/dbinit/dbinit.sql @@ -0,0 +1,4 @@ +LOAD DATA LOCAL INFILE '/dbinit/fluidprices.CSV' INTO TABLE prices +FIELDS TERMINATED BY ',' +LINES TERMINATED BY '\n' +IGNORE 1 ROWS; \ No newline at end of file diff --git a/dbinit/fluidprices.CSV b/dbinit/fluidprices.CSV new file mode 100644 index 0000000000000000000000000000000000000000..21670990623e0507be87e11a64779b23757ce9ef --- /dev/null +++ b/dbinit/fluidprices.CSV @@ -0,0 +1,84 @@ +fluid_type,price,start_date,end_date,id,created_at,updated_at +0,0.1256,2012-07-23T00:00:00Z,2013-07-31T23:59:59Z,1,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1329,2013-08-01T00:00:00Z,2014-10-31T23:59:59Z,2,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1401,2014-01-11T00:00:00Z,2015-07-31T23:59:59Z,3,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1437,2015-08-01T00:00:00Z,2016-07-31T23:59:59Z,4,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1503,2016-08-01T00:00:00Z,2017-07-31T23:59:59Z,5,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1546,2017-08-01T00:00:00Z,2018-01-31T23:59:59Z,6,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1555,2018-02-01T00:00:00Z,2018-07-31T23:59:59Z,7,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.145,2018-08-01T00:00:00Z,2019-05-31T23:59:59Z,8,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1531,2019-06-01T00:00:00Z,2019-07-31T23:59:59Z,9,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1524,2019-08-01T00:00:00Z,2020-01-31T23:59:59Z,10,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1546,2020-02-01T00:00:00Z,2020-07-31T23:59:59Z,11,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1557,2020-08-01T00:00:00Z,2021-01-31T23:59:59Z,12,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1582,2021-02-01T00:00:00Z,2021-07-31T23:59:59Z,13,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.1558,2021-08-01T00:00:00Z,2022-01-31T23:59:59Z,14,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +0,0.174,2022-02-01T00:00:00Z,,15,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.0030735,2012-01-01T00:00:00Z,2012-12-31T23:59:59Z,16,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.0031483,2013-01-01T00:00:00Z,2013-12-31T23:59:59Z,17,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.0031381,2014-01-01T00:00:00Z,2014-12-31T23:59:59Z,18,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00307,2015-01-01T00:00:00Z,2015-12-31T23:59:59Z,19,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.0031,2016-01-01T00:00:00Z,2016-12-31T23:59:59Z,20,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00311,2017-01-01T00:00:00Z,2017-12-31T23:59:59Z,21,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00313,2018-01-01T00:00:00Z,2018-12-31T23:59:59Z,22,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00313,2019-01-01T00:00:00Z,2019-12-31T23:59:59Z,23,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00315,2020-01-01T00:00:00Z,2020-12-31T23:59:59Z,24,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +1,0.00319,2021-01-01T00:00:00Z,,25,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0919,2017-01-01T00:00:00Z,2017-01-31T23:59:59Z,26,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0915,2017-02-01T00:00:00Z,2017-02-28T23:59:59Z,27,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0932,2017-03-01T00:00:00Z,2017-03-31T23:59:59Z,28,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0927,2017-04-01T00:00:00Z,2017-04-30T23:59:59Z,29,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0906,2017-05-01T00:00:00Z,2017-05-31T23:59:59Z,30,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0906,2017-06-01T00:00:00Z,2017-06-30T23:59:59Z,31,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0788,2017-07-01T00:00:00Z,2017-07-31T23:59:59Z,32,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0783,2017-08-01T00:00:00Z,2017-08-31T23:59:59Z,33,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0783,2017-09-01T00:00:00Z,2017-09-30T23:59:59Z,34,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0791,2017-10-01T00:00:00Z,2017-10-31T23:59:59Z,35,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0806,2017-11-01T00:00:00Z,2017-11-30T23:59:59Z,36,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0812,2017-12-01T00:00:00Z,2017-12-31T23:59:59Z,37,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0857,2018-01-01T00:00:00Z,2018-01-31T23:59:59Z,38,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0866,2018-02-01T00:00:00Z,2018-02-28T23:59:59Z,39,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0847,2018-03-01T00:00:00Z,2018-03-31T23:59:59Z,40,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0839,2018-04-01T00:00:00Z,2018-04-30T23:59:59Z,41,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0842,2018-05-01T00:00:00Z,2018-05-31T23:59:59Z,42,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0855,2018-06-01T00:00:00Z,2018-06-30T23:59:59Z,43,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0959,2018-07-01T00:00:00Z,2018-07-31T23:59:59Z,44,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0961,2018-08-01T00:00:00Z,2018-08-31T23:59:59Z,45,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0967,2018-09-01T00:00:00Z,2018-09-30T23:59:59Z,46,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0989,2018-10-01T00:00:00Z,2018-10-31T23:59:59Z,47,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.1031,2018-11-01T00:00:00Z,2018-11-30T23:59:59Z,48,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.1013,2018-12-01T00:00:00Z,2018-12-31T23:59:59Z,49,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0999,2019-01-01T00:00:00Z,2019-01-31T23:59:59Z,50,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0993,2019-02-01T00:00:00Z,2019-02-28T23:59:59Z,51,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0993,2019-03-01T00:00:00Z,2019-03-31T23:59:59Z,52,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0977,2019-04-01T00:00:00Z,2019-04-30T23:59:59Z,53,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0973,2019-05-01T00:00:00Z,2019-05-31T23:59:59Z,54,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0969,2019-06-01T00:00:00Z,2019-06-30T23:59:59Z,55,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0795,2019-07-01T00:00:00Z,2019-07-31T23:59:59Z,56,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0791,2019-08-01T00:00:00Z,2019-08-31T23:59:59Z,57,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0785,2019-09-01T00:00:00Z,2019-09-30T23:59:59Z,58,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.077,2019-10-01T00:00:00Z,2019-10-31T23:59:59Z,59,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0789,2019-11-01T00:00:00Z,2019-11-30T23:59:59Z,60,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0793,2019-12-01T00:00:00Z,2019-12-31T23:59:59Z,61,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0787,2020-01-01T00:00:00Z,2020-01-31T23:59:59Z,62,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0765,2020-02-01T00:00:00Z,2020-02-29T23:59:59Z,70,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0736,2020-03-01T00:00:00Z,2020-03-31T23:59:59Z,71,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.071,2020-04-01T00:00:00Z,2020-04-30T23:59:59Z,72,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0703,2020-05-01T00:00:00Z,2020-05-31T23:59:59Z,73,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0687,2020-06-01T00:00:00Z,2020-06-30T23:59:59Z,74,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0698,2020-07-01T00:00:00Z,2020-07-31T23:59:59Z,75,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0705,2020-08-01T00:00:00Z,2020-08-31T23:59:59Z,76,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0709,2020-09-01T00:00:00Z,2020-09-30T23:59:59Z,77,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0735,2020-10-01T00:00:00Z,2020-10-31T23:59:59Z,78,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0745,2020-11-01T00:00:00Z,2020-11-30T23:59:59Z,79,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0759,2020-12-01T00:00:00Z,2020-12-31T23:59:59Z,80,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.076,2021-01-01T00:00:00Z,2021-01-31T23:59:59Z,81,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0782,2021-02-01T00:00:00Z,2021-02-28T23:59:59Z,82,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0818,2021-03-01T00:00:00Z,2021-03-31T23:59:59Z,83,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.079,2021-04-01T00:00:00Z,2021-04-30T23:59:59Z,84,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0797,2021-05-01T00:00:00Z,2021-05-31T23:59:59Z,85,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0826,2021-06-01T00:00:00Z,2021-06-30T23:59:59Z,86,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0895,2021-07-01T00:00:00Z,2021-07-31T23:59:59Z,87,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.0934,2021-08-01T00:00:00Z,2021-08-31T23:59:59Z,88,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.1002,2021-09-01T00:00:00Z,2021-09-30T23:59:59Z,89,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z +2,0.1121,2021-10-01T00:00:00Z,,90,2000-01-01T00:00:00Z,2000-01-01T00:00:00Z \ No newline at end of file diff --git a/dbinit/init.md b/dbinit/init.md new file mode 100644 index 0000000000000000000000000000000000000000..21120af53c0319ce892155eb5c04ac5279b4c606 --- /dev/null +++ b/dbinit/init.md @@ -0,0 +1,25 @@ +# Init first prices data + +## Local + +If the script is not working (problem with secure_file_priv variable), use the import in phpMyAdmin: + +- Ignore the request for the first line +- Select format "CVS using LOAD DATA" +- Choose the right column separator "," +- Then launch the execution + +## Rec/Prod + +- Connect to mysql docker container + +``` +docker exec -it <container-id> bash +``` + +- Open mysql shell + +``` +mysql --local-infile=1 -uroot -p backoffice < /dbinit/dbinit.sql + +``` diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 14115d60a1bdcbb33531e9d135610bc82b4c861e..76db778aa3b523db2ad6eef0ad110eb143c980f8 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -12,8 +12,8 @@ services: depends_on: - backend # For linux users - # extra_hosts: - # - 'host.docker.internal:host-gateway' + extra_hosts: + - 'host.docker.internal:host-gateway' database-agent: image: mysql:5 @@ -27,6 +27,8 @@ services: interval: 5s timeout: 10s retries: 60 + volumes: + - ./dbinit:/dbinit backend: image: registry.forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server:dev diff --git a/docker-compose.yml b/docker-compose.yml index 0ef136a42b02ab4f8ba8cf852e11f78e6f58d281..ca6416695fbfdc7c02271f1b3c3468d602d1d42b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,8 @@ services: interval: 5s timeout: 10s retries: 60 + volumes: + - ./dbinit:/dbinit backend: image: registry.forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server:dev diff --git a/package.json b/package.json index 5dc3567c83d5412f1d1f0a144f3805588bf786d2..e20fdf70ba785b1020a3efa656ca7b477ec57cec 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@types/html-to-draftjs": "^1.4.0", "@types/react-draft-wysiwyg": "^1.13.3", "axios": "^0.21.1", + "dayjs": "^1.10.7", "draft-js": "^0.11.7", "draft-js-export-html": "^1.4.1", "html-to-draftjs": "^1.5.0", diff --git a/src/assets/icons/down-arrow.png b/src/assets/icons/down-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9bb76b55bb71faa4986fca57ca4974b39a76e836 Binary files /dev/null and b/src/assets/icons/down-arrow.png differ diff --git a/src/assets/icons/editing.png b/src/assets/icons/editing.png new file mode 100644 index 0000000000000000000000000000000000000000..5866258a45d4ca656122356afc42bcf6b35c4ae0 Binary files /dev/null and b/src/assets/icons/editing.png differ diff --git a/src/components/Prices/PriceRow.tsx b/src/components/Prices/PriceRow.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d3e5b0d0df9095cb8052560d1eee81748d16bce9 --- /dev/null +++ b/src/components/Prices/PriceRow.tsx @@ -0,0 +1,55 @@ +import React from 'react' +import { IPrice } from '../../models/price.model' +import editing from '../../assets/icons/editing.png' + +interface PriceSectionProps { + getDate: (date: string) => string + setPriceToSave: React.Dispatch<React.SetStateAction<IPrice>> + priceToSave: IPrice + price: IPrice + prices: IPrice[] + index: number + isNextPrice?: boolean +} + +const PriceRow: React.FC<PriceSectionProps> = ({ + getDate, + setPriceToSave, + priceToSave, + price, + prices, + index, + isNextPrice, +}: PriceSectionProps) => { + const editableLimit: number = 3 + + return ( + <> + <li + className={ + priceToSave.startDate === price.startDate + ? 'flex-bloc price-selected' + : 'flex-bloc' + } + > + <div className="prix"> + {price.price === '' ? '----' : price.price} € + </div> + <p> + à partir de :{' '} + <span className="capital">{getDate(price.startDate)}</span> + </p> + {index < editableLimit - 1 && ( + <img + src={editing} + onClick={() => setPriceToSave(isNextPrice ? price : prices[index])} + alt="edit-icon" + /> + )} + </li> + <hr></hr> + </> + ) +} + +export default PriceRow diff --git a/src/components/Prices/PriceSection.tsx b/src/components/Prices/PriceSection.tsx new file mode 100644 index 0000000000000000000000000000000000000000..20e70cb972cef633d27f0e3fa8b580ea719a8cbc --- /dev/null +++ b/src/components/Prices/PriceSection.tsx @@ -0,0 +1,215 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react' +import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' +import { FluidType } from '../../enum/fluidTypes' +import { FrequencyInMonth } from '../../enum/frequency.enum' +import { UserContext, UserContextProps } from '../../hooks/userContext' +import { IPrice } from '../../models/price.model' +import { PricesService } from '../../services/prices.service' +import arrowDown from '../../assets/icons/down-arrow.png' + +import Loader from '../Loader/Loader' +import './prices.scss' +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' +import timezone from 'dayjs/plugin/timezone' +import PriceRow from './PriceRow' +dayjs.extend(utc) +dayjs.extend(timezone) + +interface PriceSectionProps { + fluid: FluidType + frequency: FrequencyInMonth +} + +const PriceSection: React.FC<PriceSectionProps> = ({ + fluid, + frequency, +}: PriceSectionProps) => { + const [prices, setPrices] = useState<IPrice[]>([]) + const [nextPrice, setNextPrice] = useState<IPrice>() + const [isLoading, setIsLoading] = useState<boolean>(false) + const [refreshData, setRefreshData] = useState<boolean>(false) + const [showHistory, setShowHistory] = useState<boolean>(false) + const [showFullList, setShowFullList] = useState<boolean>(false) + const [priceToSave, setPriceToSave] = useState<IPrice>({ + fluidType: fluid, + price: '', + startDate: '', + endDate: null, + }) + const { user }: Partial<UserContextProps> = useContext(UserContext) + const maxPerList: number = 8 + + const handlePriceSelection = useCallback((val: string) => { + if (val === '') val = '0' + val = val.replace(/,/g, '.') + val = val.replace(/([^0-9.]+)/, '') + setPriceToSave((prev) => { + return { ...prev, price: val } + }) + }, []) + + const savePrice = useCallback(async () => { + if ( + priceToSave && + user && + priceToSave.price !== '0' && + priceToSave.price !== '' + ) { + const priceService = new PricesService() + const formattedPrice = { + ...priceToSave, + price: parseFloat(priceToSave.price as string), + } + await priceService.savePrice(formattedPrice, user.xsrftoken) + setRefreshData(true) + } + }, [priceToSave, user]) + + const toggleHistory = useCallback(() => { + setShowHistory((prev) => !prev) + }, []) + + const getDate = useCallback((isoString: string): string => { + const date = new Date(isoString) + const month = date.toLocaleString('fr', { month: 'long' }) + const year = date.toLocaleString('fr', { year: 'numeric' }) + return `${month} ${year}` + }, []) + + const toggleFullList = useCallback(() => { + setShowFullList((prev) => !prev) + }, []) + + useEffect(() => { + let subscribed = true + setIsLoading(true) + async function getPrices() { + const priceService = new PricesService() + const pricesByFluid = await priceService.getPricesByFluid(fluid) + if (pricesByFluid.length) { + const nextPriceToCreate: IPrice = { + fluidType: fluid, + price: '', + startDate: '', + endDate: null, + } + // Set the correct for the next price to create + const date: string = dayjs(pricesByFluid[0].startDate) + .utc(true) + .tz('Europe/Paris') + .add(frequency, 'month') + .startOf('day') + .format('YYYY-MM-DDTHH:mm:ss[Z]') + + nextPriceToCreate.startDate = date + if (subscribed) { + setPrices(pricesByFluid) + setPriceToSave(nextPriceToCreate) + setNextPrice(nextPriceToCreate) + } + setIsLoading(false) + } + } + getPrices() + + return () => { + subscribed = false + setRefreshData(false) + } + }, [refreshData, frequency, fluid]) + + if (isLoading) return <Loader></Loader> + if (!prices.length) return <section> Aucun prix trouvé</section> + return ( + <section> + <h2> + {fluid === FluidType.ELECTRICITY && 'Electricité'} + {fluid === FluidType.WATER && 'Eau'} + {fluid === FluidType.GAS && 'Gaz'} + </h2> + <hr className="price-separator" /> + <div className="flex-bloc"> + <p>Nouveau prix : </p> + <input + className="input-dark price-select" + type="text" + value={priceToSave.price.toString()} + onChange={(e) => handlePriceSelection(e.target.value)} + placeholder={priceToSave.price === '' ? 'Saisir le nouveau prix' : ''} + /> + <span className="euro">€</span> + + <div className="flex-bloc startDate"> + <p>A partir de : </p> + <p className="date"> + <span className="capital">{getDate(priceToSave.startDate)}</span> + </p> + </div> + </div> + <button + className="btnValid" + onClick={savePrice} + disabled={priceToSave.price === '0' || priceToSave.price === ''} + > + Sauvegarder + </button> + <div className="history"> + <button onClick={toggleHistory} className={showHistory ? 'active' : ''}> + <span>Voir l'historique</span> + <img + src={arrowDown} + className={showHistory ? 'icon-active' : ''} + alt="arrow-icon" + /> + </button> + {showHistory && ( + <ul className={showHistory ? 'active' : ''}> + {nextPrice && ( + <PriceRow + getDate={getDate} + priceToSave={priceToSave} + price={nextPrice} + prices={prices} + setPriceToSave={setPriceToSave} + index={0} + isNextPrice={true} + /> + )} + {prices.map((price, i) => { + return ( + <div + key={i} + className={ + i > maxPerList && !showFullList ? 'price-hidden' : '' + } + > + <PriceRow + getDate={getDate} + priceToSave={priceToSave} + price={price} + prices={prices} + setPriceToSave={setPriceToSave} + index={i} + /> + {i === maxPerList && !showFullList && ( + <button onClick={toggleFullList} className="showButton"> + En voir plus + </button> + )} + </div> + ) + })} + {showFullList && ( + <button onClick={toggleFullList} className="showButton"> + En voir moins + </button> + )} + </ul> + )} + </div> + </section> + ) +} + +export default PriceSection diff --git a/src/components/Prices/Prices.tsx b/src/components/Prices/Prices.tsx new file mode 100644 index 0000000000000000000000000000000000000000..beb1286ed8925914fbf84b68fa87479918b638a6 --- /dev/null +++ b/src/components/Prices/Prices.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' +import { FluidType } from '../../enum/fluidTypes' +import { FrequencyInMonth } from '../../enum/frequency.enum' +import './prices.scss' +import PriceSection from './PriceSection' +const Prices: React.FC = () => { + return ( + <> + <div className="header"> + <p className="title pagetitle">Prix des fluides</p> + </div> + <div className="prices"> + <PriceSection + fluid={FluidType.ELECTRICITY} + frequency={FrequencyInMonth.ELECTRICITY} + /> + <PriceSection + fluid={FluidType.WATER} + frequency={FrequencyInMonth.WATER} + /> + <PriceSection fluid={FluidType.GAS} frequency={FrequencyInMonth.GAS} /> + </div> + </> + ) +} + +export default Prices diff --git a/src/components/Prices/prices.scss b/src/components/Prices/prices.scss new file mode 100644 index 0000000000000000000000000000000000000000..0f0d367b909bda1b5261d7f54ddf888a5923411a --- /dev/null +++ b/src/components/Prices/prices.scss @@ -0,0 +1,119 @@ +@import '../../styles/config/typography.scss'; +@import '../../styles/config/layout.scss'; +@import '../../styles/config/breakpoints'; +@import '../../styles/config/colors'; + +.prices { + margin-top: $navigator-height; + padding: 1rem; + .title { + margin: 1rem 0; + } + .capital { + text-transform: capitalize; + } + h2 { + margin-bottom: 1rem; + color: $gold; + } + .price-separator { + margin: 1rem 0; + background: white; + } + .flex-bloc { + display: flex; + align-items: center; + .price-select { + position: relative; + } + .euro { + display: block; + margin-left: 0.5rem; + font-weight: bold; + } + .startDate { + margin-left: 1rem; + .date { + margin-left: 0.5rem; + color: $gold; + font-weight: bold; + } + } + } + section { + margin-top: 1rem; + margin-bottom: 2rem; + .history { + button { + @include baseButton(); + background: $grey-dark; + border: solid 1px $gold; + display: flex; + align-items: center; + &:hover { + background: $dark-background; + opacity: 0.8; + } + &.active { + border-radius: 5px 5px 0 0; + } + img { + width: 20px; + margin-left: 0.5rem; + } + span { + color: $gold; + } + } + ul { + transition: all 300ms ease; + background: $grey-dark; + border: solid 1px $gold; + padding: 1rem; + .price-hidden { + display: none; + } + li { + transition: all 300ms ease; + margin: 0.5rem 0; + padding: 0.5rem; + .prix { + transition: all 300ms ease; + font-weight: bold; + min-width: 120px; + } + p, + p span { + transition: all 300ms ease; + margin-left: 1rem; + } + img { + cursor: pointer; + width: 22px; + margin-left: auto; + transition: all 300ms ease; + } + } + .price-selected { + background: $gold; + .prix, + p, + p span { + color: black; + } + } + hr { + margin: 0; + background: white; + } + .showButton { + text-align: center; + color: $gold; + } + } + .icon-active { + transform: rotate(180deg); + } + } + } +} diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index a5c1b43a429265966d775277bcaf39f98fafcbd4..5a56426c21f671c0d8230b5dffde722030cc6147 100644 --- a/src/components/Routes/Routes.tsx +++ b/src/components/Routes/Routes.tsx @@ -3,6 +3,7 @@ import { Redirect, Route, Switch } from 'react-router-dom' import { UserContext } from '../../hooks/userContext' import Editing from '../Editing/Editing' import Login from '../Login/Login' +import Prices from '../Prices/Prices' import Settings from '../Settings/Settings' import PrivateRoute from './PrivateRoute' @@ -14,6 +15,7 @@ const Routes: React.FC = () => { {user && <Redirect path="/login" to="/editing" />} <Route path="/login" component={Login} /> <PrivateRoute path="/editing" component={Editing} exact /> + <PrivateRoute path="/prices" component={Prices} exact /> <PrivateRoute path="/settings" component={Settings} exact /> <Redirect path="*" to="/editing" /> </Switch> diff --git a/src/constants/routes.json b/src/constants/routes.json index 21414496797dd4d776127b0dda9b5ba1e32290a4..c0f67b8302a653c43c5c007178e3aae581bd338e 100644 --- a/src/constants/routes.json +++ b/src/constants/routes.json @@ -3,6 +3,10 @@ "label": "Edition", "path": "/editing" }, + { + "label": "Prix", + "path": "/prices" + }, { "label": "Paramètres", "path": "/settings" diff --git a/src/enum/fluidTypes.ts b/src/enum/fluidTypes.ts new file mode 100644 index 0000000000000000000000000000000000000000..62e46bca04b6df8cab23b1565acae31b43420545 --- /dev/null +++ b/src/enum/fluidTypes.ts @@ -0,0 +1,5 @@ +export enum FluidType { + ELECTRICITY = 0, + WATER = 1, + GAS = 2, +} diff --git a/src/enum/frequency.enum.ts b/src/enum/frequency.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..211ae18028fca746ba35a53728d77c56f92cd235 --- /dev/null +++ b/src/enum/frequency.enum.ts @@ -0,0 +1,5 @@ +export enum FrequencyInMonth { + ELECTRICITY = 6, + WATER = 12, + GAS = 1, +} diff --git a/src/models/price.model.ts b/src/models/price.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb1e52d1f0f6c564096216e293707ae10c715b42 --- /dev/null +++ b/src/models/price.model.ts @@ -0,0 +1,6 @@ +export interface IPrice { + fluidType: number + price: number | string + startDate: string + endDate: string | null +} diff --git a/src/services/prices.service.ts b/src/services/prices.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..723b1360f9fbdee1744f598701b40cd5288e83f6 --- /dev/null +++ b/src/services/prices.service.ts @@ -0,0 +1,36 @@ +import axios from 'axios' +import { toast } from 'react-toastify' +import { IPrice } from '../models/price.model' +export class PricesService { + /** + * Save the partnersInfo + * @param price + * @param token + */ + public savePrice = async (price: IPrice, token: string): Promise<void> => { + try { + await axios.put(`/api/admin/prices`, price, { + headers: { + 'XSRF-TOKEN': token, + }, + }) + toast.success('Price succesfully saved !') + } catch (e) { + toast.error('Failed to save price') + console.error(e) + } + } + + /** + * Gets the prices by fluid + */ + public getPricesByFluid = async (fluidType: number): Promise<IPrice[]> => { + try { + const { data } = await axios.get(`/api/common/prices/${fluidType}`) + return data + } catch (e) { + console.error('error', e) + return [] + } + } +} diff --git a/src/styles/config/_typography.scss b/src/styles/config/_typography.scss index 380fea218b22eeb87ecce61e4ec1cd350c88c748..5517824137581a2207b6c6bec75804d499f45844 100644 --- a/src/styles/config/_typography.scss +++ b/src/styles/config/_typography.scss @@ -46,6 +46,10 @@ $main-spacing: 4px; &:hover { background-color: #b89318; } + &:disabled { + opacity: 0.8; + cursor: not-allowed; + } } .btnValid { @include baseButton(); diff --git a/yarn.lock b/yarn.lock index 921c761b6f1db6348adbc08ef207e8e5a17e28fd..b713717b0a7a32a2a908ac1d8975cbcef7a9baa3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3716,6 +3716,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dayjs@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"