| @@ -1,14 +1,13 @@ | |||
| { | |||
| "extends": [ | |||
| "react-app", | |||
| "airbnb", | |||
| "prettier" | |||
| ], | |||
| "plugins": [ | |||
| "react", | |||
| "react-hooks", | |||
| "security" | |||
| ], | |||
| "extends": ["react-app", "airbnb", "prettier"], | |||
| "settings": { | |||
| "import/resolver": { | |||
| "node": { | |||
| "paths": ["src"] | |||
| } | |||
| } | |||
| }, | |||
| "plugins": ["react", "react-hooks", "security"], | |||
| "rules": { | |||
| "react/jsx-filename-extension": "off", | |||
| "react/jsx-props-no-spreading": "off", | |||
| @@ -21,8 +20,11 @@ | |||
| "no-use-before-define": "off", | |||
| "no-template-curly-in-string": "off", | |||
| "react-hooks/exhaustive-deps": "warn", | |||
| "prettier/prettier": ["error", { | |||
| "endOfLine":"auto" | |||
| }] | |||
| "prettier/prettier": [ | |||
| "error", | |||
| { | |||
| "endOfLine": "auto" | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| @@ -1,22 +1,23 @@ | |||
| { | |||
| "env": { | |||
| "browser": true, | |||
| "es2021": true | |||
| }, | |||
| "extends": [ | |||
| "eslint:recommended", | |||
| "plugin:react/recommended" | |||
| ], | |||
| "parserOptions": { | |||
| "ecmaFeatures": { | |||
| "jsx": true | |||
| }, | |||
| "ecmaVersion": 12, | |||
| "sourceType": "module" | |||
| }, | |||
| "plugins": [ | |||
| "react" | |||
| ], | |||
| "rules": { | |||
| "env": { | |||
| "browser": true, | |||
| "es2021": true | |||
| }, | |||
| "extends": ["eslint:recommended", "plugin:react/recommended"], | |||
| "settings": { | |||
| "import/resolver": { | |||
| "node": { | |||
| "paths": ["src"] | |||
| } | |||
| } | |||
| }, | |||
| "parserOptions": { | |||
| "ecmaFeatures": { | |||
| "jsx": true | |||
| }, | |||
| "ecmaVersion": 12, | |||
| "sourceType": "module" | |||
| }, | |||
| "plugins": ["react"], | |||
| "rules": {} | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "baseUrl": "src" | |||
| }, | |||
| "include": ["src"] | |||
| } | |||
| @@ -2,10 +2,10 @@ import React, { useEffect, useState, useRef } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { ErrorMessage } from 'formik'; | |||
| import IconButton from '../IconButton/IconButton'; | |||
| import { ReactComponent as Search } from '../../assets/images/svg/search.svg'; | |||
| import { ReactComponent as EyeOn } from '../../assets/images/svg/eye-on.svg'; | |||
| import { ReactComponent as EyeOff } from '../../assets/images/svg/eye-off.svg'; | |||
| import { ReactComponent as CapsLock } from '../../assets/images/svg/caps-lock.svg'; | |||
| import { ReactComponent as Search } from 'assets/images/svg/search.svg'; | |||
| import { ReactComponent as EyeOn } from 'assets/images/svg/eye-on.svg'; | |||
| import { ReactComponent as EyeOff } from 'assets/images/svg/eye-off.svg'; | |||
| import { ReactComponent as CapsLock } from 'assets/images/svg/caps-lock.svg'; | |||
| const BaseInputField = ({ | |||
| type, | |||
| @@ -1,7 +1,7 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { ReactComponent as Checked } from '../../assets/images/svg/checked.svg'; | |||
| import { ReactComponent as Unchecked } from '../../assets/images/svg/unchecked.svg'; | |||
| import { ReactComponent as Checked } from 'assets/images/svg/checked.svg'; | |||
| import { ReactComponent as Unchecked } from 'assets/images/svg/unchecked.svg'; | |||
| const Checkbox = ({ className, children, name, onChange, checked, field }) => ( | |||
| <label htmlFor={name} className={`c-checkbox ${className || ''}`}> | |||
| @@ -1,15 +1,15 @@ | |||
| import React, { useEffect, useRef } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { ErrorMessage, useField } from 'formik'; | |||
| import CurrencyInput from 'react-currency-input-field'; | |||
| import { formatMoneyNumeral } from '../../util/helpers/numeralHelpers'; | |||
| import React, { useEffect, useRef } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { ErrorMessage, useField } from "formik"; | |||
| import CurrencyInput from "react-currency-input-field"; | |||
| import { formatMoneyNumeral } from "util/helpers/numeralHelpers"; | |||
| import { | |||
| PLUS_SYMBOL, | |||
| MINUS_SYMBOL, | |||
| NUMPAD_MINUS_SYMBOL, | |||
| NUMPAD_PLUS_SYMBOL, | |||
| K_KEYCODE, | |||
| } from '../../constants/keyCodeConstants'; | |||
| } from "constants/keyCodeConstants"; | |||
| const CurrencyField = ({ | |||
| autoFocus, | |||
| @@ -23,7 +23,7 @@ const CurrencyField = ({ | |||
| const [field, meta] = useField(props); | |||
| const inputField = useRef(null); | |||
| function styles() { | |||
| let style = 'c-currency-field'; | |||
| let style = "c-currency-field"; | |||
| if (meta.error && meta.touched) { | |||
| style += ` c-currency-field--error`; | |||
| @@ -60,11 +60,11 @@ const CurrencyField = ({ | |||
| const prefix = formatMoneyNumeral(0); | |||
| const prefixSymbol = () => { | |||
| if (prefix.includes('CAD')) { | |||
| return 'CAD '; | |||
| if (prefix.includes("CAD")) { | |||
| return "CAD "; | |||
| } | |||
| return '$'; | |||
| return "$"; | |||
| }; | |||
| return ( | |||
| @@ -79,7 +79,7 @@ const CurrencyField = ({ | |||
| {...props} | |||
| prefix={prefixSymbol()} | |||
| onValueChange={(value) => { | |||
| onChange(value ? Number(value) : ''); | |||
| onChange(value ? Number(value) : ""); | |||
| }} | |||
| onKeyDown={(event) => onKeydownHandler(event)} | |||
| ref={inputField} | |||
| @@ -91,7 +91,7 @@ const CurrencyField = ({ | |||
| {...props} | |||
| prefix={prefixSymbol()} | |||
| onValueChange={(value) => { | |||
| onChange(value ? Number(value) : ''); | |||
| onChange(value ? Number(value) : ""); | |||
| }} | |||
| onKeyDown={(event) => onKeydownHandler(event)} | |||
| ref={inputField} | |||
| @@ -12,7 +12,7 @@ import { | |||
| NUMPAD_PLUS_SYMBOL, | |||
| DOWN_ARROW_KEYCODE, | |||
| UP_ARROW_KEYCODE, | |||
| } from '../../constants/keyCodeConstants'; | |||
| } from 'constants/keyCodeConstants'; | |||
| const NumberField = ({ | |||
| field, | |||
| @@ -1,7 +1,7 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { ReactComponent as RadioOn } from '../../assets/images/svg/radio-on.svg'; | |||
| import { ReactComponent as RadioOff } from '../../assets/images/svg/radio-off.svg'; | |||
| import { ReactComponent as RadioOn } from 'assets/images/svg/radio-on.svg'; | |||
| import { ReactComponent as RadioOff } from 'assets/images/svg/radio-off.svg'; | |||
| const Checkbox = ({ | |||
| className, | |||
| @@ -1,12 +1,11 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import BaseInputField from './BaseInputField'; | |||
| import BaseInputField from "./BaseInputField"; | |||
| const Search = ({ | |||
| field, | |||
| form, | |||
| label, | |||
| placeholder, | |||
| disabled, | |||
| className, | |||
| @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import Select, { components, createFilter } from 'react-select'; | |||
| import { ErrorMessage, useField } from 'formik'; | |||
| import { ReactComponent as FilledChevronDown } from '../../assets/images/svg/filled-chevron-down.svg'; | |||
| import { ReactComponent as FilledChevronDown } from 'assets/images/svg/filled-chevron-down.svg'; | |||
| const SelectField = ({ | |||
| label, | |||
| @@ -7,7 +7,7 @@ import { | |||
| TAB_KEYCODE, | |||
| RIGHT_ARROW_KEYCODE, | |||
| LEFT_ARROW_KEYCODE, | |||
| } from '../../constants/keyCodeConstants'; | |||
| } from 'constants/keyCodeConstants'; | |||
| const TextField = ({ | |||
| field, | |||
| @@ -1,183 +1,182 @@ | |||
| import React, { useEffect, useState } from 'react'; | |||
| import React, { useEffect, useState } from "react"; | |||
| import { | |||
| Paper, | |||
| Box, | |||
| Grid, | |||
| Typography, | |||
| Divider, | |||
| TablePagination, | |||
| TextField, | |||
| FormControl, | |||
| InputLabel, | |||
| Select, | |||
| MenuItem, | |||
| } from '@mui/material'; | |||
| Paper, | |||
| Box, | |||
| Grid, | |||
| Typography, | |||
| Divider, | |||
| TablePagination, | |||
| TextField, | |||
| FormControl, | |||
| InputLabel, | |||
| Select, | |||
| MenuItem, | |||
| } from "@mui/material"; | |||
| // import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch, useSelector, batch } from 'react-redux'; | |||
| import useDebounce from '../../../hooks/useDebounceHook'; | |||
| import { useDispatch, useSelector, batch } from "react-redux"; | |||
| import useDebounce from "hooks/useDebounceHook"; | |||
| import { | |||
| itemsSelector, | |||
| pageSelector, | |||
| itemsPerPageSelector, | |||
| countSelector, | |||
| sortSelector, | |||
| } from '../../../store/selectors/randomDataSelectors'; | |||
| itemsSelector, | |||
| pageSelector, | |||
| itemsPerPageSelector, | |||
| countSelector, | |||
| sortSelector, | |||
| } from "store/selectors/randomDataSelectors"; | |||
| import { | |||
| loadData, | |||
| updatePage, | |||
| updateItemsPerPage, | |||
| updateFilter, | |||
| updateSort, | |||
| } from '../../../store/actions/randomData/randomDataActions'; | |||
| loadData, | |||
| updatePage, | |||
| updateItemsPerPage, | |||
| updateFilter, | |||
| updateSort, | |||
| } from "store/actions/randomData/randomDataActions"; | |||
| const PagingSortingFilteringExample = () => { | |||
| const [filterText, setFilterText] = useState(''); | |||
| const [filterText, setFilterText] = useState(""); | |||
| const dispatch = useDispatch(); | |||
| // const { t } = useTranslation(); | |||
| const items = useSelector(itemsSelector); | |||
| const currentPage = useSelector(pageSelector); | |||
| const itemsPerPage = useSelector(itemsPerPageSelector); | |||
| const totalCount = useSelector(countSelector); | |||
| const sort = useSelector(sortSelector) || 'name-asc'; | |||
| const dispatch = useDispatch(); | |||
| // const { t } = useTranslation(); | |||
| const items = useSelector(itemsSelector); | |||
| const currentPage = useSelector(pageSelector); | |||
| const itemsPerPage = useSelector(itemsPerPageSelector); | |||
| const totalCount = useSelector(countSelector); | |||
| const sort = useSelector(sortSelector) || "name-asc"; | |||
| // Use debounce to prevent too many rerenders | |||
| const debouncedFilterText = useDebounce(filterText, 500); | |||
| // Use debounce to prevent too many rerenders | |||
| const debouncedFilterText = useDebounce(filterText, 500); | |||
| useEffect(() => { | |||
| dispatch(loadData(30)); | |||
| dispatch(updateSort(sort)); | |||
| }, []); | |||
| useEffect(() => { | |||
| dispatch(loadData(30)); | |||
| dispatch(updateSort(sort)); | |||
| }, []); | |||
| useEffect(() => { | |||
| batch(() => { | |||
| dispatch(updateFilter(filterText)); | |||
| currentPage > 0 && dispatch(updatePage(0)); | |||
| }); | |||
| }, [debouncedFilterText]); | |||
| useEffect(() => { | |||
| batch(() => { | |||
| dispatch(updateFilter(filterText)); | |||
| currentPage > 0 && dispatch(updatePage(0)); | |||
| }); | |||
| }, [debouncedFilterText]); | |||
| const handleFilterTextChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilterText(filterText); | |||
| }; | |||
| const handleFilterTextChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilterText(filterText); | |||
| }; | |||
| const handleSortChange = (event) => { | |||
| const sort = event.target.value; | |||
| dispatch(updateSort(sort)); | |||
| }; | |||
| const handleSortChange = (event) => { | |||
| const sort = event.target.value; | |||
| dispatch(updateSort(sort)); | |||
| }; | |||
| const handlePageChange = (event, newPage) => { | |||
| dispatch(updatePage(newPage)); | |||
| }; | |||
| const handlePageChange = (event, newPage) => { | |||
| dispatch(updatePage(newPage)); | |||
| }; | |||
| const handleItemsPerPageChange = (event) => { | |||
| const itemsPerPage = parseInt(event.target.value); | |||
| batch(() => { | |||
| dispatch(updateItemsPerPage(itemsPerPage)); | |||
| dispatch(updatePage(0)); | |||
| }); | |||
| }; | |||
| const handleItemsPerPageChange = (event) => { | |||
| const itemsPerPage = parseInt(event.target.value); | |||
| batch(() => { | |||
| dispatch(updateItemsPerPage(itemsPerPage)); | |||
| dispatch(updatePage(0)); | |||
| }); | |||
| }; | |||
| return ( | |||
| <Paper | |||
| sx={{ | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| justifyContent: 'start', | |||
| py: 2, | |||
| minHeight: 500, | |||
| }} | |||
| elevation={5} | |||
| > | |||
| <Typography sx={{ my: 4 }} variant="h4" gutterBottom align="center"> | |||
| Pagination, Filtering and Sorting Example Client Side | |||
| </Typography> | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| flexWrap: 'wrap', | |||
| mx: 2, | |||
| }} | |||
| > | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| width: '100%', | |||
| }} | |||
| > | |||
| {/* TODO Separate into SelectComponent */} | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="name-asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="name-desc">Name - Z-A</MenuItem> | |||
| <MenuItem value="price-asc">Price - Lowest to Highest</MenuItem> | |||
| <MenuItem value="price-desc">Price - Highest to Lowest</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filterText} | |||
| onChange={handleFilterTextChange} | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| <Grid container> | |||
| {items && | |||
| items.length > 0 && | |||
| items | |||
| .slice( | |||
| currentPage * itemsPerPage, | |||
| currentPage * itemsPerPage + itemsPerPage | |||
| ) | |||
| .map((product, index) => ( | |||
| // ! DON'T USE index for key, this is for example only | |||
| <Grid item sx={{ p: 2 }} xs={12} sm={6} md={4} lg={3} key={index}> | |||
| {/* TODO separate into component */} | |||
| <Paper sx={{ p: 3, height: '100%' }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>Name: </Typography> | |||
| <Typography display="inline"> {product.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Designer: </Typography> | |||
| <Typography display="inline"> {product.designer}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Type: </Typography> | |||
| <Typography display="inline"> {product.type}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Price: </Typography> | |||
| <Typography display="inline"> ${product.price}</Typography> | |||
| </Paper> | |||
| </Grid> | |||
| ))} | |||
| </Grid> | |||
| <Box sx={{ width: '100%' }}> | |||
| <TablePagination | |||
| component="div" | |||
| count={totalCount} | |||
| page={currentPage} | |||
| onPageChange={handlePageChange} | |||
| rowsPerPage={itemsPerPage} | |||
| onRowsPerPageChange={handleItemsPerPageChange} | |||
| rowsPerPageOptions={[12, 24, 48, 96]} | |||
| labelRowsPerPage="Items per page" | |||
| showFirstButton | |||
| showLastButton | |||
| /> | |||
| </Box> | |||
| </Paper> | |||
| ); | |||
| return ( | |||
| <Paper | |||
| sx={{ | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| justifyContent: "start", | |||
| py: 2, | |||
| minHeight: 500, | |||
| }} | |||
| elevation={5} | |||
| > | |||
| <Typography sx={{ my: 4 }} variant="h4" gutterBottom align="center"> | |||
| Pagination, Filtering and Sorting Example Client Side | |||
| </Typography> | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "space-between", | |||
| flexWrap: "wrap", | |||
| mx: 2, | |||
| }} | |||
| > | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "space-between", | |||
| width: "100%", | |||
| }} | |||
| > | |||
| {/* TODO Separate into SelectComponent */} | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="name-asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="name-desc">Name - Z-A</MenuItem> | |||
| <MenuItem value="price-asc">Price - Lowest to Highest</MenuItem> | |||
| <MenuItem value="price-desc">Price - Highest to Lowest</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filterText} | |||
| onChange={handleFilterTextChange} | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| <Grid container> | |||
| {items && | |||
| items.length > 0 && | |||
| items | |||
| .slice( | |||
| currentPage * itemsPerPage, | |||
| currentPage * itemsPerPage + itemsPerPage | |||
| ) | |||
| .map((product, index) => ( | |||
| // ! DON'T USE index for key, this is for example only | |||
| <Grid item sx={{ p: 2 }} xs={12} sm={6} md={4} lg={3} key={index}> | |||
| {/* TODO separate into component */} | |||
| <Paper sx={{ p: 3, height: "100%" }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>Name: </Typography> | |||
| <Typography display="inline"> {product.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Designer: </Typography> | |||
| <Typography display="inline"> {product.designer}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Type: </Typography> | |||
| <Typography display="inline"> {product.type}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Price: </Typography> | |||
| <Typography display="inline"> ${product.price}</Typography> | |||
| </Paper> | |||
| </Grid> | |||
| ))} | |||
| </Grid> | |||
| <Box sx={{ width: "100%" }}> | |||
| <TablePagination | |||
| component="div" | |||
| count={totalCount} | |||
| page={currentPage} | |||
| onPageChange={handlePageChange} | |||
| rowsPerPage={itemsPerPage} | |||
| onRowsPerPageChange={handleItemsPerPageChange} | |||
| rowsPerPageOptions={[12, 24, 48, 96]} | |||
| labelRowsPerPage="Items per page" | |||
| showFirstButton | |||
| showLastButton | |||
| /> | |||
| </Box> | |||
| </Paper> | |||
| ); | |||
| }; | |||
| export default PagingSortingFilteringExample; | |||
| @@ -1,159 +1,159 @@ | |||
| import React, { useEffect, useState } from 'react'; | |||
| import React, { useEffect, useState } from "react"; | |||
| import { | |||
| Paper, | |||
| Box, | |||
| Grid, | |||
| Typography, | |||
| Divider, | |||
| TablePagination, | |||
| TextField, | |||
| FormControl, | |||
| InputLabel, | |||
| Select, | |||
| MenuItem, | |||
| } from '@mui/material'; | |||
| Paper, | |||
| Box, | |||
| Grid, | |||
| Typography, | |||
| Divider, | |||
| TablePagination, | |||
| TextField, | |||
| FormControl, | |||
| InputLabel, | |||
| Select, | |||
| MenuItem, | |||
| } from "@mui/material"; | |||
| // import { useTranslation } from 'react-i18next'; | |||
| import Backdrop from '../BackdropComponent'; | |||
| import useDebounce from '../../../hooks/useDebounceHook'; | |||
| import { useRandomData } from '../../../context/RandomDataContext'; | |||
| import Backdrop from "../BackdropComponent"; | |||
| import useDebounce from "hooks/useDebounceHook"; | |||
| import { useRandomData } from "context/RandomDataContext"; | |||
| const PagingSortingFilteringExampleServerSide = () => { | |||
| const [filterText, setFilterText] = useState(''); | |||
| const { state, data } = useRandomData(); | |||
| const { items, loading, totalCount, currentPage, itemsPerPage, sort } = data; | |||
| const { setPage, setItemsPerPage, setSort, setFilter } = state; | |||
| // const { t } = useTranslation(); | |||
| const [filterText, setFilterText] = useState(""); | |||
| const { state, data } = useRandomData(); | |||
| const { items, loading, totalCount, currentPage, itemsPerPage, sort } = data; | |||
| const { setPage, setItemsPerPage, setSort, setFilter } = state; | |||
| // const { t } = useTranslation(); | |||
| // Use debounce to prevent too many rerenders | |||
| const debouncedFilterText = useDebounce(filterText, 500); | |||
| // Use debounce to prevent too many rerenders | |||
| const debouncedFilterText = useDebounce(filterText, 500); | |||
| useEffect(() => { | |||
| setFilter(filterText); | |||
| }, [debouncedFilterText]); | |||
| useEffect(() => { | |||
| setFilter(filterText); | |||
| }, [debouncedFilterText]); | |||
| const handleFilterTextChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilterText(filterText); | |||
| }; | |||
| const handleFilterTextChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilterText(filterText); | |||
| }; | |||
| const handleSortChange = (event) => { | |||
| const sort = event.target.value; | |||
| setSort(sort); | |||
| }; | |||
| const handleSortChange = (event) => { | |||
| const sort = event.target.value; | |||
| setSort(sort); | |||
| }; | |||
| const handlePageChange = (event, newPage) => { | |||
| setPage(newPage); | |||
| }; | |||
| const handlePageChange = (event, newPage) => { | |||
| setPage(newPage); | |||
| }; | |||
| const handleItemsPerPageChange = (event) => { | |||
| const itemsPerPage = parseInt(event.target.value); | |||
| setItemsPerPage(itemsPerPage); | |||
| setPage(0); | |||
| }; | |||
| const handleItemsPerPageChange = (event) => { | |||
| const itemsPerPage = parseInt(event.target.value); | |||
| setItemsPerPage(itemsPerPage); | |||
| setPage(0); | |||
| }; | |||
| return ( | |||
| <Paper | |||
| sx={{ | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| justifyContent: 'start', | |||
| py: 2, | |||
| minHeight: 500, | |||
| position: 'relative', | |||
| }} | |||
| elevation={5} | |||
| > | |||
| {loading && <Backdrop isLoading position="absolute" />} | |||
| <Typography sx={{ my: 4 }} variant="h4" gutterBottom align="center"> | |||
| Pagination, Filtering and Sorting Example Server Side | |||
| </Typography> | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| flexWrap: 'wrap', | |||
| mx: 2, | |||
| }} | |||
| > | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| width: '100%', | |||
| }} | |||
| > | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort || ''} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="">None</MenuItem> | |||
| <MenuItem value="name-asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="name-desc">Name - Z-A</MenuItem> | |||
| <MenuItem value="price-asc">Price - Lowest to Highest</MenuItem> | |||
| <MenuItem value="price-desc">Price - Highest to Lowest</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filterText} | |||
| onChange={handleFilterTextChange} | |||
| /> | |||
| </Box> | |||
| <Grid container sx={{ position: 'relative' }}> | |||
| {items && | |||
| items.length > 0 && | |||
| items.map((item) => ( | |||
| <Grid | |||
| item | |||
| sx={{ p: 2 }} | |||
| xs={12} | |||
| sm={6} | |||
| md={4} | |||
| lg={3} | |||
| key={item.id} | |||
| > | |||
| {/* TODO separate into component */} | |||
| <Paper sx={{ p: 3, height: '100%' }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>Name: </Typography> | |||
| <Typography display="inline"> {item.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Company: </Typography> | |||
| <Typography display="inline"> {item.company}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Color: </Typography> | |||
| <Typography display="inline"> {item.color}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Price: </Typography> | |||
| <Typography display="inline"> {item.price}</Typography> | |||
| </Paper> | |||
| </Grid> | |||
| ))} | |||
| </Grid> | |||
| <Box sx={{ width: '100%' }}> | |||
| <TablePagination | |||
| component="div" | |||
| count={totalCount} | |||
| page={currentPage} | |||
| onPageChange={handlePageChange} | |||
| rowsPerPage={itemsPerPage} | |||
| onRowsPerPageChange={handleItemsPerPageChange} | |||
| rowsPerPageOptions={[12, 24, 48, 96]} | |||
| labelRowsPerPage="Items per page" | |||
| showFirstButton | |||
| showLastButton | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| </Paper> | |||
| ); | |||
| return ( | |||
| <Paper | |||
| sx={{ | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| justifyContent: "start", | |||
| py: 2, | |||
| minHeight: 500, | |||
| position: "relative", | |||
| }} | |||
| elevation={5} | |||
| > | |||
| {loading && <Backdrop isLoading position="absolute" />} | |||
| <Typography sx={{ my: 4 }} variant="h4" gutterBottom align="center"> | |||
| Pagination, Filtering and Sorting Example Server Side | |||
| </Typography> | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "space-between", | |||
| flexWrap: "wrap", | |||
| mx: 2, | |||
| }} | |||
| > | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "space-between", | |||
| width: "100%", | |||
| }} | |||
| > | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort || ""} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="">None</MenuItem> | |||
| <MenuItem value="name-asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="name-desc">Name - Z-A</MenuItem> | |||
| <MenuItem value="price-asc">Price - Lowest to Highest</MenuItem> | |||
| <MenuItem value="price-desc">Price - Highest to Lowest</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filterText} | |||
| onChange={handleFilterTextChange} | |||
| /> | |||
| </Box> | |||
| <Grid container sx={{ position: "relative" }}> | |||
| {items && | |||
| items.length > 0 && | |||
| items.map((item) => ( | |||
| <Grid | |||
| item | |||
| sx={{ p: 2 }} | |||
| xs={12} | |||
| sm={6} | |||
| md={4} | |||
| lg={3} | |||
| key={item.id} | |||
| > | |||
| {/* TODO separate into component */} | |||
| <Paper sx={{ p: 3, height: "100%" }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>Name: </Typography> | |||
| <Typography display="inline"> {item.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Company: </Typography> | |||
| <Typography display="inline"> {item.company}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Color: </Typography> | |||
| <Typography display="inline"> {item.color}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>Price: </Typography> | |||
| <Typography display="inline"> {item.price}</Typography> | |||
| </Paper> | |||
| </Grid> | |||
| ))} | |||
| </Grid> | |||
| <Box sx={{ width: "100%" }}> | |||
| <TablePagination | |||
| component="div" | |||
| count={totalCount} | |||
| page={currentPage} | |||
| onPageChange={handlePageChange} | |||
| rowsPerPage={itemsPerPage} | |||
| onRowsPerPageChange={handleItemsPerPageChange} | |||
| rowsPerPageOptions={[12, 24, 48, 96]} | |||
| labelRowsPerPage="Items per page" | |||
| showFirstButton | |||
| showLastButton | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| </Paper> | |||
| ); | |||
| }; | |||
| export default PagingSortingFilteringExampleServerSide; | |||
| @@ -21,9 +21,9 @@ import Brightness4Icon from "@mui/icons-material/Brightness4"; | |||
| import Brightness7Icon from "@mui/icons-material/Brightness7"; | |||
| import MenuList from "./MenuListComponent"; | |||
| import Drawer from "./DrawerComponent"; | |||
| import { ColorModeContext } from "../../context/ColorModeContext"; | |||
| import { ColorModeContext } from "context/ColorModeContext"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { logoutUser } from "../../store/actions/login/loginActions"; | |||
| import { logoutUser } from "store/actions/login/loginActions"; | |||
| const NavbarComponent = () => { | |||
| const dispatch = useDispatch(); | |||
| @@ -1,9 +1,8 @@ | |||
| import React, { useEffect } from 'react'; | |||
| import { Redirect, Route } from 'react-router'; | |||
| import { useDispatch } from 'react-redux'; | |||
| import { authenticateUser } from '../../store/actions/login/loginActions'; | |||
| // import { selectIsUserAuthenticated } from '../../store/selectors/userSelectors'; | |||
| import { LOGIN_PAGE } from '../../constants/pages'; | |||
| import React, { useEffect } from "react"; | |||
| import { Redirect, Route } from "react-router"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { authenticateUser } from "store/actions/login/loginActions"; | |||
| import { LOGIN_PAGE } from "constants/pages"; | |||
| const PrivateRoute = ({ ...props }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -1,7 +1,7 @@ | |||
| import React, { createContext } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { ThemeProvider } from '@mui/material/styles'; | |||
| import useToggleColorMode from '../hooks/useToggleColorMode'; | |||
| import useToggleColorMode from 'hooks/useToggleColorMode'; | |||
| export const ColorModeContext = createContext(); | |||
| @@ -1,7 +1,7 @@ | |||
| import React, { createContext, useContext, useState } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import usePagingHook from '../hooks/usePagingHook'; | |||
| import { getRequest } from '../request/jsonServerRequest'; | |||
| import usePagingHook from 'hooks/usePagingHook'; | |||
| import { getRequest } from 'request/jsonServerRequest'; | |||
| const apiCall = (page, itemsPerPage, sort, sortDirection, filter) => | |||
| getRequest('/items', { | |||
| @@ -3,8 +3,8 @@ import { createTheme } from "@mui/material/styles"; | |||
| import { | |||
| authScopeSetHelper, | |||
| authScopeStringGetHelper, | |||
| } from "../util/helpers/authScopeHelpers"; | |||
| import selectedTheme from "../themes"; | |||
| } from "util/helpers/authScopeHelpers"; | |||
| import selectedTheme from "themes"; | |||
| const useToggleColorMode = () => { | |||
| const currentColorMode = authScopeStringGetHelper("colorMode") || "light"; | |||
| @@ -1,24 +1,26 @@ | |||
| import React from 'react'; | |||
| import ReactDOM from 'react-dom'; | |||
| import { Provider } from 'react-redux'; | |||
| import { HelmetProvider } from 'react-helmet-async'; | |||
| import React from "react"; | |||
| import ReactDOM from "react-dom"; | |||
| import { Provider } from "react-redux"; | |||
| import { HelmetProvider } from "react-helmet-async"; | |||
| import './main.scss'; | |||
| import App from './App'; | |||
| import store from './store'; | |||
| import "./main.scss"; | |||
| import App from "./App"; | |||
| import store from "./store"; | |||
| import './i18n'; | |||
| import ColorModeProvider from './context/ColorModeContext'; | |||
| import "./i18n"; | |||
| import ColorModeProvider from "./context/ColorModeContext"; | |||
| import { CssBaseline } from "@mui/material"; | |||
| ReactDOM.render( | |||
| <HelmetProvider> | |||
| <React.StrictMode> | |||
| <Provider store={store}> | |||
| <ColorModeProvider> | |||
| <App /> | |||
| <CssBaseline /> | |||
| <App /> | |||
| </ColorModeProvider> | |||
| </Provider> | |||
| </React.StrictMode> | |||
| </HelmetProvider>, | |||
| document.getElementById('root'), | |||
| document.getElementById("root") | |||
| ); | |||
| @@ -1,13 +1,13 @@ | |||
| import React, { useEffect } from "react"; | |||
| import { useLocation } from "react-router-dom"; | |||
| import { HOME_PAGE } from "../../constants/pages"; | |||
| import { HOME_PAGE } from "constants/pages"; | |||
| import PropTypes from "prop-types"; | |||
| import { useParams } from "react-router-dom"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { fetchAuthProvider } from "../../store/actions/authProvider/authProviderActions"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { AUTH_PROVIDER_SCOPE } from "../../store/actions/authProvider/authProviderActionConstants"; | |||
| import Backdrop from '../../components/MUI/BackdropComponent'; | |||
| import { fetchAuthProvider } from "store/actions/authProvider/authProviderActions"; | |||
| import { selectIsLoadingByActionType } from "store/selectors/loadingSelectors"; | |||
| import { AUTH_PROVIDER_SCOPE } from "store/actions/authProvider/authProviderActionConstants"; | |||
| import Backdrop from 'components/MUI/BackdropComponent'; | |||
| function AuthCallback({ history }) { | |||
| const dispatch = useDispatch(); | |||
| @@ -1,7 +1,7 @@ | |||
| import React from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import Button from '../../components/Button/Button'; | |||
| import Section from '../../components/Section/Section'; | |||
| import Button from 'components/Button/Button'; | |||
| import Section from 'components/Section/Section'; | |||
| const NotFoundPage = () => { | |||
| const { t } = useTranslation(); | |||
| @@ -3,12 +3,12 @@ import { Formik, Form, Field } from 'formik'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import * as Yup from 'yup'; | |||
| import i18next from 'i18next'; | |||
| import Auth from '../../components/Auth/Auth'; | |||
| import AuthCard from '../../components/AuthCards/AuthCard'; | |||
| import TextField from '../../components/InputFields/TextField'; | |||
| import Button from '../../components/Button/Button'; | |||
| import Auth from 'components/Auth/Auth'; | |||
| import AuthCard from 'components/AuthCards/AuthCard'; | |||
| import TextField from 'components/InputFields/TextField'; | |||
| import Button from 'components/Button/Button'; | |||
| import Section from '../../components/Section/Section'; | |||
| import Section from 'components/Section/Section'; | |||
| const forgotPasswordValidationSchema = Yup.object().shape({ | |||
| email: Yup.string().required( | |||
| @@ -1,88 +1,88 @@ | |||
| import React from 'react'; | |||
| import { useFormik } from 'formik'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import React from "react"; | |||
| import { useFormik } from "formik"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { | |||
| Box, | |||
| Container, | |||
| Typography, | |||
| Button, | |||
| TextField, | |||
| Link, | |||
| Grid, | |||
| } from '@mui/material'; | |||
| import Backdrop from '../../components/MUI/BackdropComponent'; | |||
| import { LOGIN_PAGE } from '../../constants/pages'; | |||
| import { NavLink } from 'react-router-dom'; | |||
| import forgotPasswordValidation from '../../validations/forgotPasswordValidation'; | |||
| import forgotPasswordInitialValues from '../../initialValues/forgotPasswordInitialValues'; | |||
| Box, | |||
| Container, | |||
| Typography, | |||
| Button, | |||
| TextField, | |||
| Link, | |||
| Grid, | |||
| } from "@mui/material"; | |||
| import Backdrop from "components/MUI/BackdropComponent"; | |||
| import { LOGIN_PAGE } from "constants/pages"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import forgotPasswordValidation from "validations/forgotPasswordValidation"; | |||
| import forgotPasswordInitialValues from "initialValues/forgotPasswordInitialValues"; | |||
| const ForgotPasswordPage = () => { | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation(); | |||
| const handleSubmit = (values) => { | |||
| console.log('Values', values); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| console.log("Values", values); | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: forgotPasswordInitialValues, | |||
| validationSchema: forgotPasswordValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| const formik = useFormik({ | |||
| initialValues: forgotPasswordInitialValues, | |||
| validationSchema: forgotPasswordValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| alignItems: 'center', | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t('forgotPassword.title')} | |||
| </Typography> | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: 'relative', mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={false} /> | |||
| <TextField | |||
| name="email" | |||
| label={t('login.forgotPasswordEmail')} | |||
| margin="normal" | |||
| value={formik.values.email} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.email && Boolean(formik.errors.email)} | |||
| helperText={formik.touched.email && formik.errors.email} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| > | |||
| {t('forgotPassword.label')} | |||
| </Button> | |||
| <Grid container justifyContent="center"> | |||
| <Link | |||
| to={LOGIN_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t('common.back')} | |||
| </Link> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| alignItems: "center", | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t("forgotPassword.title")} | |||
| </Typography> | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: "relative", mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={false} /> | |||
| <TextField | |||
| name="email" | |||
| label={t("login.forgotPasswordEmail")} | |||
| margin="normal" | |||
| value={formik.values.email} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.email && Boolean(formik.errors.email)} | |||
| helperText={formik.touched.email && formik.errors.email} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| > | |||
| {t("forgotPassword.label")} | |||
| </Button> | |||
| <Grid container justifyContent="center"> | |||
| <Link | |||
| to={LOGIN_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t("common.back")} | |||
| </Link> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| }; | |||
| export default ForgotPasswordPage; | |||
| @@ -1,12 +1,12 @@ | |||
| import React from "react"; | |||
| import { Box, Button, Grid } from "@mui/material"; | |||
| import Navbar from "../../components/MUI/NavbarComponent"; | |||
| import Modals from "../../components/MUI/Examples/ModalsExample"; | |||
| import DataGrid from "../../components/MUI/Examples/DataGridExample"; | |||
| import PagingSortingFiltering from "../../components/MUI/Examples/PagingSortingFilteringExample"; | |||
| import PagingSortingFilteringServerSide from "../../components/MUI/Examples/PagingSortingFilteringExampleServerSide"; | |||
| import RandomDataProvider from "../../context/RandomDataContext"; | |||
| import { getRequest } from "../../request"; | |||
| import Navbar from "components/MUI/NavbarComponent"; | |||
| import Modals from "components/MUI/Examples/ModalsExample"; | |||
| import DataGrid from "components/MUI/Examples/DataGridExample"; | |||
| import PagingSortingFiltering from "components/MUI/Examples/PagingSortingFilteringExample"; | |||
| import PagingSortingFilteringServerSide from "components/MUI/Examples/PagingSortingFilteringExampleServerSide"; | |||
| import RandomDataProvider from "context/RandomDataContext"; | |||
| import { getRequest } from "request"; | |||
| const HomePage = () => { | |||
| const getPosts = async () => { | |||
| @@ -1,32 +1,25 @@ | |||
| import React from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { Field, Form, Formik } from 'formik'; | |||
| import { useDispatch, useSelector } from 'react-redux'; | |||
| import { NavLink } from 'react-router-dom'; | |||
| import * as Yup from 'yup'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import i18next from 'i18next'; | |||
| import PasswordField from '../../components/InputFields/PasswordField'; | |||
| import Button from '../../components/Button/Button'; | |||
| import TextField from '../../components/InputFields/TextField'; | |||
| import Auth from '../../components/Auth/Auth'; | |||
| import AuthCard from '../../components/AuthCards/AuthCard'; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from '../../store/actions/login/loginActions'; | |||
| import { | |||
| selectLoginError, | |||
| } from '../../store/selectors/loginSelectors'; | |||
| import { | |||
| FORGOT_PASSWORD_PAGE, HOME_PAGE, | |||
| } from '../../constants/pages'; | |||
| import { selectIsLoadingByActionType } from '../../store/selectors/loadingSelectors'; | |||
| import { LOGIN_USER_LOADING } from '../../store/actions/login/loginActionConstants'; | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { Field, Form, Formik } from "formik"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import * as Yup from "yup"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import i18next from "i18next"; | |||
| import PasswordField from "components/InputFields/PasswordField"; | |||
| import Button from "components/Button/Button"; | |||
| import TextField from "components/InputFields/TextField"; | |||
| import Auth from "components/Auth/Auth"; | |||
| import AuthCard from "components/AuthCards/AuthCard"; | |||
| import { clearLoginErrors, fetchUser } from "store/actions/login/loginActions"; | |||
| import { selectLoginError } from "store/selectors/loginSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "constants/pages"; | |||
| import { selectIsLoadingByActionType } from "store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_LOADING } from "store/actions/login/loginActionConstants"; | |||
| const LoginValidationSchema = Yup.object().shape({ | |||
| username: Yup.string().required(i18next.t('login.usernameRequired')), | |||
| password: Yup.string().required(i18next.t('login.passwordRequired')), | |||
| username: Yup.string().required(i18next.t("login.usernameRequired")), | |||
| password: Yup.string().required(i18next.t("login.passwordRequired")), | |||
| }); | |||
| const LoginPage = ({ history }) => { | |||
| @@ -46,43 +39,39 @@ const LoginPage = ({ history }) => { | |||
| // }, [history, tokens]); | |||
| const isLoading = useSelector( | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING), | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING) | |||
| ); | |||
| const handleApiResponseSuccess =()=>{ | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| } | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| // destructure value as username. | |||
| const { username: Username } = values; | |||
| const { password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess | |||
| }, | |||
| ), | |||
| ); | |||
| // destructure value as username. | |||
| const { username: Username } = values; | |||
| const { password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| return ( | |||
| <Auth> | |||
| <AuthCard | |||
| title="Log In" | |||
| isLoading={isLoading} | |||
| > | |||
| <AuthCard title="Log In" isLoading={isLoading}> | |||
| <div className="c-login c-login--user"> | |||
| <div className="c-login__form"> | |||
| <Formik | |||
| initialValues={{ | |||
| username: '', | |||
| password: '', | |||
| username: "", | |||
| password: "", | |||
| }} | |||
| onSubmit={handleSubmit} | |||
| validationSchema={LoginValidationSchema} | |||
| @@ -92,7 +81,7 @@ const LoginPage = ({ history }) => { | |||
| {({ values }) => ( | |||
| <Form> | |||
| <Field | |||
| label={t('common.labelUsername')} | |||
| label={t("common.labelUsername")} | |||
| value={values.username.value} | |||
| component={TextField} | |||
| name="username" | |||
| @@ -100,15 +89,13 @@ const LoginPage = ({ history }) => { | |||
| <Field | |||
| label={ | |||
| <div className="c-login--password__label"> | |||
| {t('common.labelPassword')} | |||
| {t("common.labelPassword")} | |||
| </div> | |||
| } | |||
| link={ | |||
| <NavLink | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| > | |||
| {t('login.forgotYourPassword')} | |||
| </NavLink> | |||
| <NavLink to={FORGOT_PASSWORD_PAGE}> | |||
| {t("login.forgotYourPassword")} | |||
| </NavLink> | |||
| } | |||
| name="password" | |||
| component={PasswordField} | |||
| @@ -121,15 +108,15 @@ const LoginPage = ({ history }) => { | |||
| variant="primary" | |||
| type="submit" | |||
| > | |||
| {t('common.continue')} | |||
| {t("common.continue")} | |||
| </Button> | |||
| </Form> | |||
| )} | |||
| </Formik> | |||
| </div> | |||
| </div> | |||
| </AuthCard> | |||
| </Auth> | |||
| </AuthCard> | |||
| </Auth> | |||
| ); | |||
| }; | |||
| @@ -5,12 +5,13 @@ import { useFormik } from "formik"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { clearLoginErrors, fetchUser } from "store/actions/login/loginActions"; | |||
| import { selectLoginError } from "store/selectors/loginSelectors"; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from "../../store/actions/login/loginActions"; | |||
| import { selectLoginError } from "../../store/selectors/loginSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE, REGISTER_PAGE } from "../../constants/pages"; | |||
| FORGOT_PASSWORD_PAGE, | |||
| HOME_PAGE, | |||
| REGISTER_PAGE, | |||
| } from "constants/pages"; | |||
| import { | |||
| Box, | |||
| Button, | |||
| @@ -23,12 +24,12 @@ import { | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | |||
| import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "../../components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_SCOPE } from "../../store/actions/login/loginActionConstants"; | |||
| import loginValidation from "../../validations/loginValidation"; | |||
| import loginInitialValues from "../../initialValues/loginInitialValues"; | |||
| import Backdrop from "components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_SCOPE } from "store/actions/login/loginActionConstants"; | |||
| import loginValidation from "validations/loginValidation"; | |||
| import loginInitialValues from "initialValues/loginInitialValues"; | |||
| import GoogleIcon from "@mui/icons-material/Google"; | |||
| const LoginPage = ({ history }) => { | |||
| @@ -8,13 +8,9 @@ import { useTranslation } from "react-i18next"; | |||
| import { | |||
| clearRegisterErrors, | |||
| registerUser, | |||
| } from "../../store/actions/register/registerActions"; | |||
| import { selectRegisterError } from "../../store/selectors/registerSelectors"; | |||
| import { | |||
| FORGOT_PASSWORD_PAGE, | |||
| HOME_PAGE, | |||
| LOGIN_PAGE, | |||
| } from "../../constants/pages"; | |||
| } from "store/actions/register/registerActions"; | |||
| import { selectRegisterError } from "store/selectors/registerSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, LOGIN_PAGE } from "constants/pages"; | |||
| import { | |||
| Box, | |||
| Button, | |||
| @@ -27,15 +23,14 @@ import { | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | |||
| import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "../../components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_SCOPE } from "../../store/actions/login/loginActionConstants"; | |||
| import Backdrop from "components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "store/selectors/loadingSelectors"; | |||
| import GoogleIcon from "@mui/icons-material/Google"; | |||
| import registerInitialValues from "../../initialValues/registerInitialValues"; | |||
| import registerValidation from "../../validations/registerValidation"; | |||
| import { REGISTER_USER_SCOPE } from "../../store/actions/register/registerActionConstants"; | |||
| import { makeToastMessage } from "../../util/helpers/toastMessage"; | |||
| import registerInitialValues from "initialValues/registerInitialValues"; | |||
| import registerValidation from "validations/registerValidation"; | |||
| import { REGISTER_USER_SCOPE } from "store/actions/register/registerActionConstants"; | |||
| import { makeToastMessage } from "util/helpers/toastMessage"; | |||
| const RegisterPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -23,7 +23,6 @@ import { | |||
| REFRESH_TOKEN_CONST, | |||
| } from "../../constants/localStorage"; | |||
| import { | |||
| authScopeClearHelper, | |||
| authScopeStringGetHelper, | |||
| authScopeRemoveHelper, | |||
| authScopeSetHelper, | |||
| @@ -83,7 +82,9 @@ function* logoutUser() { | |||
| } catch (error) { | |||
| console.log(error); // eslint-disable-line | |||
| } finally { | |||
| yield call(authScopeClearHelper); | |||
| // yield call(authScopeClearHelper); | |||
| yield call(authScopeRemoveHelper, JWT_TOKEN); | |||
| yield call(authScopeRemoveHelper, JWT_REFRESH_TOKEN); | |||
| yield call(removeHeaderToken); | |||
| yield put(resetLoginState()); | |||
| yield call(history.replace, LOGIN_PAGE); | |||
| @@ -1,4 +1,4 @@ | |||
| import { SESSION_STORAGE_SCOPE } from '../../constants/sessionStorage'; | |||
| import { SESSION_STORAGE_SCOPE } from 'constants/sessionStorage'; | |||
| export function authScopeGetHelper(key) { | |||
| if (sessionStorage.getItem(SESSION_STORAGE_SCOPE)) { | |||