Przeglądaj źródła

Merge branch 'feature/remove_unnecessary_files' of Neca/HRCenter into FE_dev

pull/167/head
safet.purkovic 3 lat temu
rodzic
commit
78f9cd689a
39 zmienionych plików z 87 dodań i 1409 usunięć
  1. 1
    0
      src/App.js
  2. 41
    6
      src/__tests__/UITests/candidatesPageUI.test.js
  3. 22
    5
      src/__tests__/UITests/dayDetailsComponentUI.test.js
  4. 0
    20
      src/components/Auth/Auth.js
  5. 0
    40
      src/components/InputFields/Checkbox.js
  6. 0
    123
      src/components/InputFields/CurrencyField.js
  7. 0
    33
      src/components/InputFields/EmailField.js
  8. 0
    74
      src/components/InputFields/NumberField.js
  9. 0
    45
      src/components/InputFields/PercentageField.js
  10. 0
    49
      src/components/InputFields/PhoneNumberField.js
  11. 0
    54
      src/components/InputFields/Radio.js
  12. 0
    37
      src/components/InputFields/Search.js
  13. 0
    122
      src/components/InputFields/SelectField.js
  14. 0
    26
      src/components/Loader/BlockSectionLoader.js
  15. 0
    11
      src/components/Loader/FullPageLoader.js
  16. 0
    57
      src/components/MUI/DialogComponent copy.js
  17. 0
    29
      src/components/MUI/Examples/DataGridExample.js
  18. 0
    64
      src/components/MUI/Examples/ModalsExample.js
  19. 0
    183
      src/components/MUI/Examples/PagingSortingFilteringExample.js
  20. 0
    159
      src/components/MUI/Examples/PagingSortingFilteringExampleServerSide.js
  21. 0
    26
      src/components/MUI/MenuListComponent.js
  22. 0
    35
      src/components/MUI/PopoverComponent.js
  23. 1
    1
      src/components/Schedules/DayDetailsComponent.js
  24. 1
    15
      src/constants/keyCodeConstants.js
  25. 1
    0
      src/constants/localStorage.js
  26. 1
    0
      src/constants/pages.js
  27. 0
    63
      src/context/RandomDataContext.js
  28. 0
    17
      src/hooks/useDebounceHook.js
  29. 0
    66
      src/hooks/usePagingHook.js
  30. 1
    0
      src/i18n/index.js
  31. 0
    13
      src/index.css
  32. 1
    0
      src/index.js
  33. 1
    0
      src/mockState.js
  34. 11
    5
      src/pages/CandidatesPage/CandidatesPage.js
  35. 3
    2
      src/reportWebVitals.js
  36. 1
    0
      src/store/index.js
  37. 0
    28
      src/util/helpers/dateHelpers.js
  38. 0
    1
      src/util/helpers/enumMappers.js
  39. 1
    0
      src/util/helpers/randomData.js

+ 1
- 0
src/App.js Wyświetl plik

@@ -6,6 +6,7 @@ import history from "./store/utils/history";
import MainContainer from "./components/Section/MainContainer";
import AppRoutes from "./AppRoutes";

/* istanbul ignore file */
function App() {
return (
<>

+ 41
- 6
src/__tests__/UITests/candidatesPageUI.test.js Wyświetl plik

@@ -5,6 +5,15 @@ import store from "../../store";
import { mockState } from "../../mockState";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import mediaQuery from "css-mediaquery";

function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, { width }),
addListener: () => {},
removeListener: () => {},
});
}

describe("CandidatesPage render tests", () => {
const cont = (
@@ -18,23 +27,40 @@ describe("CandidatesPage render tests", () => {
let spyOnUseSelector;

beforeEach(() => {
window.matchMedia = createMatchMedia(601);
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValueOnce(mockState.technologies.technologies)
spyOnUseSelector.mockReturnValueOnce(mockState.technologies.technologies);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should render first header because width of screen is greater than 600", () => {
render(cont);
expect(screen.getByTestId("candidates-header1")).toBeDefined();
});

it("Should render second header because width of screen is greater than 600", () => {
render(cont);
expect(screen.queryByTestId("candidates-header2")).toBeNull();
});

it("Should render", () => {
render(cont);
expect(screen.getByTestId("candidates-page")).toBeDefined();
});

it("Should render button responsible for showing different components inside page", () => {
it("Should render first button responsible for showing different components inside page because width of screen is greater than 600", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("all-white-btn")[0]).toBeDefined();
});

it("Should not render second button responsible for showing different components inside page because width of screen is greater than 600", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("candidate-btn")[0]).toBeDefined();
expect(
container.getElementsByClassName("candidate-btn-view-2")[0]
).toBeUndefined();
});

it("should be rendered button which is used for showing input responsible for searching by name", () => {
@@ -42,9 +68,18 @@ describe("CandidatesPage render tests", () => {
expect(container.getElementsByClassName("candidate-btn")[1]).toBeDefined();
});

it("Should render filter button", () => {
it("Should render first filter button because width is greater than 600", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("candidate-btn")[2]).toBeDefined();
expect(
container.getElementsByClassName("candidate-btn-filters1")[0]
).toBeDefined();
});

it("Should not render second filter button because width is greater than 600", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidate-btn-filters2")[0]
).toBeUndefined();
});

it("input for searching by name should not be shown when component is initialy rendered", () => {

+ 22
- 5
src/__tests__/UITests/dayDetailsComponentUI.test.js Wyświetl plik

@@ -7,14 +7,17 @@ import history from "../../store/utils/history";
import DayDetailsComponent from "../../components/Schedules/DayDetailsComponent";
import ColorModeProvider from "../../context/ColorModeContext";

const setCurrentlySelected = jest.fn();
const setCurrentlySelectedDay = jest.fn();

const props = {
selectedDate: "20.12.2023",
selectionProcesses: mockState.schedule.schedule,
open: jest.fn(),
onClose: jest.fn(),
setCurrentlySelected: jest.fn(),
setCurrentlySelectedDay: jest.fn(),
currentlySelectedDay: 1,
setCurrentlySelected: setCurrentlySelected,
setCurrentlySelectedDay: setCurrentlySelectedDay,
currentlySelectedDay: 20,
numberOfDaysInMonth: 31,
history: {
replace: jest.fn(),
@@ -45,12 +48,12 @@ describe("DayDetailsComponent render tests", () => {
expect(screen.getByTestId("day-component-dialog")).toBeDefined();
});

it("Should render left arrow as disabled because we set that currenlty selected day is first day of month", () => {
it("Should render left arrow as enabled because currenlty selected day is not 1", () => {
render(cont);
expect(screen.getAllByTestId("day-details-left-arrow")[0]).toBeDefined();
});

it("Should render right arrow as enabled because we set that currenlty selected day is first day of month", () => {
it("Should render right arrow as enabled because currently selected day is not 31", () => {
render(cont);
expect(screen.getAllByTestId("day-details-right-arrow")[0]).toBeDefined();
});
@@ -68,4 +71,18 @@ describe("DayDetailsComponent render tests", () => {
const arg = { pathname: "/candidates/1" };
expect(props.history.push).toHaveBeenCalledWith(arg);
});

it("Should call function when we press right arrow", () => {
render(cont);
fireEvent.click(screen.getAllByTestId("day-details-right-arrow")[0]);
expect(setCurrentlySelected.mock.calls).toHaveLength(1);
expect(setCurrentlySelectedDay.mock.calls).toHaveLength(1);
});

it("Should call function when we press right arrow", () => {
render(cont);
fireEvent.click(screen.getAllByTestId("day-details-left-arrow")[0]);
expect(setCurrentlySelected.mock.calls).toHaveLength(1);
expect(setCurrentlySelectedDay.mock.calls).toHaveLength(1);
});
});

+ 0
- 20
src/components/Auth/Auth.js Wyświetl plik

@@ -1,20 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';

const Auth = ({ children }) => {
const { t } = useTranslation();

return (
<div className="c-auth">
<h1 className="c-auth__title">{t(`app.title`)}</h1>
{children}
</div>
);
};

Auth.propTypes = {
children: PropTypes.node,
};

export default Auth;

+ 0
- 40
src/components/InputFields/Checkbox.js Wyświetl plik

@@ -1,40 +0,0 @@
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';

const Checkbox = ({ className, children, name, onChange, checked, field }) => (
<label htmlFor={name} className={`c-checkbox ${className || ''}`}>
<input
name={name}
id={name}
className="c-checkbox__field"
type="checkbox"
checked={checked}
{...field}
onChange={onChange || field.onChange}
/>

<div className="c-checkbox__indicator">
{checked ? (
<Checked className="c-checkbox__icon" />
) : (
<Unchecked className="c-checkbox__icon" />
)}
</div>
<div className="c-checkbox__text">{children}</div>
</label>
);

Checkbox.propTypes = {
children: PropTypes.node,
onChange: PropTypes.func,
checked: PropTypes.bool,
name: PropTypes.string,
field: PropTypes.shape({
onChange: PropTypes.func,
}),
className: PropTypes.string,
};

export default Checkbox;

+ 0
- 123
src/components/InputFields/CurrencyField.js Wyświetl plik

@@ -1,123 +0,0 @@
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';

const CurrencyField = ({
autoFocus,
notCentered,
notBold,
label,
onChange,
value,
...props
}) => {
const [field, meta] = useField(props);
const inputField = useRef(null);
function styles() {
let style = 'c-currency-field';

if (meta.error && meta.touched) {
style += ` c-currency-field--error`;
}

if (notCentered) {
style += ` c-currency-field--not-centered`;
}

if (notBold) {
style += ` c-currency-field--not-bold`;
}

return style;
}

useEffect(() => {
if (autoFocus) {
inputField.current.focus();
}
}, [autoFocus, inputField]);

const onKeydownHandler = (event) => {
if (
event.keyCode === MINUS_SYMBOL ||
event.keyCode === PLUS_SYMBOL ||
event.keyCode === NUMPAD_MINUS_SYMBOL ||
event.keyCode === NUMPAD_PLUS_SYMBOL ||
event.keyCode === K_KEYCODE
) {
event.preventDefault();
}
};

const prefix = formatMoneyNumeral(0);
const prefixSymbol = () => {
if (prefix.includes('CAD')) {
return 'CAD ';
}

return '$';
};

return (
<div className={styles()}>
{!!label && (
<label className="c-currency-field__label" htmlFor={field.name}>
{label}
</label>
)}
{value ? (
<CurrencyInput
{...props}
prefix={prefixSymbol()}
onValueChange={(value) => {
onChange(value ? Number(value) : '');
}}
onKeyDown={(event) => onKeydownHandler(event)}
ref={inputField}
defaultValue={0}
value={value}
/>
) : (
<CurrencyInput
{...props}
prefix={prefixSymbol()}
onValueChange={(value) => {
onChange(value ? Number(value) : '');
}}
onKeyDown={(event) => onKeydownHandler(event)}
ref={inputField}
/>
)}
<ErrorMessage name={field.name}>
{(errorMessage) => (
<span className="c-currency-field__error">{errorMessage}</span>
)}
</ErrorMessage>
</div>
);
};

CurrencyField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
onChange: PropTypes.func,
autoFocus: PropTypes.bool,
notCentered: PropTypes.bool,
notBold: PropTypes.bool,
value: PropTypes.number,
};

export default CurrencyField;

+ 0
- 33
src/components/InputFields/EmailField.js Wyświetl plik

@@ -1,33 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';

const EmailField = ({
field,
form,
label,
placeholder,
disabled,
...props
}) => (
<BaseInputField
type="email"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
{...props}
/>
);

EmailField.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
};

export default EmailField;

+ 0
- 74
src/components/InputFields/NumberField.js Wyświetl plik

@@ -1,74 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';
import {
PERIOD_SYMBOL,
COMMA_SYMBOL,
PLUS_SYMBOL,
MINUS_SYMBOL,
NUMPAD_PERIOD_SYMBOL,
NUMPAD_MINUS_SYMBOL,
NUMPAD_PLUS_SYMBOL,
DOWN_ARROW_KEYCODE,
UP_ARROW_KEYCODE,
} from '../../constants/keyCodeConstants';

const NumberField = ({
field,
form,
label,
placeholder,
disabled,
preventAllExceptNumbers,
...props
}) => {
const onKeydownHandler = (event) => {
if (preventAllExceptNumbers) {
if (
event.keyCode === PERIOD_SYMBOL ||
event.keyCode === COMMA_SYMBOL ||
event.keyCode === NUMPAD_PERIOD_SYMBOL ||
event.keyCode === DOWN_ARROW_KEYCODE ||
event.keyCode === UP_ARROW_KEYCODE
) {
event.preventDefault();
}
}

if (
event.keyCode === PLUS_SYMBOL ||
event.keyCode === MINUS_SYMBOL ||
event.keyCode === NUMPAD_MINUS_SYMBOL ||
event.keyCode === NUMPAD_PLUS_SYMBOL ||
event.keyCode === DOWN_ARROW_KEYCODE ||
event.keyCode === UP_ARROW_KEYCODE
) {
event.preventDefault();
}
};

return (
<BaseInputField
type="number"
label={label}
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
{...props}
onKeyDown={(event) => onKeydownHandler(event)}
/>
);
};

NumberField.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
disabled: PropTypes.bool,
preventAllExceptNumbers: PropTypes.bool,
};

export default NumberField;

+ 0
- 45
src/components/InputFields/PercentageField.js Wyświetl plik

@@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import NumberFormat from 'react-number-format';

import TextField from './TextField';

const PercentageField = ({ field, ...props }) => {
const handleOnChange = (percentageField) => {
const { floatValue } = percentageField;

if (!props.onChange) {
throw Error('Provide an onChange handler');
}
if (floatValue > 100) {
return props.onChange('100');
}

if (floatValue <= 0 || !floatValue) {
return props.onChange('0');
}

return props.onChange(floatValue.toString());
};

return (
<NumberFormat
format="###%"
value={field.value}
customInput={TextField}
field={field}
{...props}
onValueChange={handleOnChange}
onChange={() => {}}
/>
);
};

PercentageField.propTypes = {
onChange: PropTypes.func,
field: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
};

export default PercentageField;

+ 0
- 49
src/components/InputFields/PhoneNumberField.js Wyświetl plik

@@ -1,49 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ErrorMessage, useField } from 'formik';
import PhoneInput from 'react-phone-number-input';
import 'react-phone-number-input/style.css';

const PhoneNumberField = ({ label, ...props }) => {
const [field, meta] = useField(props);
const inputErrorClassName =
meta.error && meta.touched ? 'c-input--error' : '';

return (
<div className={`c-input c-phone-number ${inputErrorClassName}`}>
{!!label && (
<label className="c-input__label" htmlFor={field.name}>
{label}
</label>
)}
<PhoneInput
international
defaultCountry="US"
{...field}
{...props}
onChange={(value) => {
props.onPhoneChange(value);
}}
countryOptionsOrder={['US']}
/>
<ErrorMessage name={field.name}>
{(errorMessage) => (
<span className="c-input__error">{errorMessage}</span>
)}
</ErrorMessage>
</div>
);
};

PhoneNumberField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
onChange: PropTypes.func,
onPhoneChange: PropTypes.func,
};

export default PhoneNumberField;

+ 0
- 54
src/components/InputFields/Radio.js Wyświetl plik

@@ -1,54 +0,0 @@
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';

const Checkbox = ({
className,
children,
name,
checked,
field,
value,
selected,
id,
}) => (
<label
htmlFor={name}
className={`c-radio ${selected ? 'c-radio--selected' : ''} ${
className || ''
}`}
>
<input
name={name}
id={id}
className="c-radio__field"
type="radio"
checked={checked}
value={value}
{...field}
/>
<div className="c-radio__indicator">
{selected ? (
<RadioOn className="c-radio__icon" />
) : (
<RadioOff className="c-radio__icon" />
)}
</div>
<div className="c-radio__text">{children}</div>
</label>
);

Checkbox.propTypes = {
children: PropTypes.node,
checked: PropTypes.bool,
name: PropTypes.string,
field: PropTypes.shape({}),
form: PropTypes.shape({}),
className: PropTypes.string,
value: PropTypes.string,
selected: PropTypes.bool,
id: PropTypes.string,
};

export default Checkbox;

+ 0
- 37
src/components/InputFields/Search.js Wyświetl plik

@@ -1,37 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

import BaseInputField from './BaseInputField';

const Search = ({
field,
form,
label,
placeholder,
disabled,
className,
...props
}) => (
<BaseInputField
type="text"
label=""
placeholder={placeholder}
disabled={disabled}
form={form}
field={field}
isSearch
className={className}
{...props}
/>
);

Search.propTypes = {
field: PropTypes.shape({}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
placeholder: PropTypes.string,
disabled: PropTypes.bool,
className: PropTypes.string,
};

export default Search;

+ 0
- 122
src/components/InputFields/SelectField.js Wyświetl plik

@@ -1,122 +0,0 @@
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';

const SelectField = ({
label,
disabled,
options,
link,
defaultSelected = null,
dropdownFullHeight,
selectOption,
...props
}) => {
const [field, meta, helpers] = useField(props);

const filterConfig = {
ignoreCase: true,
ignoreAccents: true,
trim: true,
matchFrom: 'start',
};

useEffect(() => {
if (defaultSelected) {
helpers.setValue(defaultSelected);
}
}, [defaultSelected]); // eslint-disable-line

const DropdownIndicator = (props) =>
components.DropdownIndicator && (
<components.DropdownIndicator {...props}>
<FilledChevronDown />
</components.DropdownIndicator>
);

function styles() {
let style = 'c-input';

if (meta.error && meta.touched) {
style += ` c-input--error`;
}

if (dropdownFullHeight) {
style += ` c-input--dropdown-full-height`;
}

return style;
}

return (
<div className={styles()}>
{!!label && (
<label className="c-input__label" htmlFor={field.name}>
{label}
</label>
)}
{!!link && <div className="c-input__link">{link}</div>}
<Select
defaultValue={defaultSelected || options[0]}
components={{ DropdownIndicator }}
isSearchable={false}
classNamePrefix="c-select"
options={options}
isDisabled={disabled}
{...field}
{...props}
onBlur={(e) => {
helpers.setTouched(true);
field.onBlur(e);
}}
onChange={(selectedOption) => {
helpers.setValue(selectedOption);

if (props.onChange) {
props.onChange();
}

if (selectOption) {
selectOption(selectedOption);
}
}}
filterOption={createFilter(filterConfig)}
/>
<ErrorMessage name={field.name}>
{(errorMessage) => {
if (typeof errorMessage === 'string') {
return <span className="c-input__error">{errorMessage}</span>;
}
return <span className="c-input__error">{errorMessage.value}</span>;
}}
</ErrorMessage>
</div>
);
};

SelectField.propTypes = {
field: PropTypes.shape({
name: PropTypes.string,
}),
form: PropTypes.shape({}),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
disabled: PropTypes.bool,
options: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
),
onChange: PropTypes.func,
link: PropTypes.node,
defaultSelected: PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
dropdownFullHeight: PropTypes.bool,
selectOption: PropTypes.func,
};

export default SelectField;

+ 0
- 26
src/components/Loader/BlockSectionLoader.js Wyświetl plik

@@ -1,26 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';

const BlockSectionLoader = ({ children, isLoading, fullHeight, noShadow }) => (
<div
className={`c-loader__wrapper c-loader__wrapper--block ${
fullHeight ? 'c-loader__wrapper--full-height' : ''
} ${noShadow ? 'c-loader__wrapper--no-shadow' : ''}`}
>
{children}
{isLoading && (
<div className="c-loader">
<div className="c-loader__icon" />
</div>
)}
</div>
);

BlockSectionLoader.propTypes = {
children: PropTypes.node,
isLoading: PropTypes.bool,
fullHeight: PropTypes.bool,
noShadow: PropTypes.bool,
};

export default BlockSectionLoader;

+ 0
- 11
src/components/Loader/FullPageLoader.js Wyświetl plik

@@ -1,11 +0,0 @@
import React from 'react';

const FullPageLoader = () => {
return (
<div className="c-loader c-loader--page">
<div className="c-loader__icon" />
</div>
);
};

export default FullPageLoader;

+ 0
- 57
src/components/MUI/DialogComponent copy.js Wyświetl plik

@@ -1,57 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Dialog,
DialogContent,
DialogTitle,
DialogActions,
Button,
useMediaQuery,
useTheme,
} from '@mui/material';

const DialogComponent = ({
title,
content,
onClose,
open,
maxWidth,
fullWidth,
responsive,
}) => {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));

const handleClose = () => {
onClose();
};

return (
<Dialog
maxWidth={maxWidth}
fullWidth={fullWidth}
fullScreen={responsive && fullScreen}
onClose={handleClose}
open={open}
>
<DialogTitle>{title}</DialogTitle>
{content && <DialogContent>{content}</DialogContent>}
<DialogActions>
<Button onClick={handleClose}>OK</Button>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
);
};

DialogComponent.propTypes = {
title: PropTypes.any,
open: PropTypes.bool.isRequired,
content: PropTypes.any,
onClose: PropTypes.func.isRequired,
maxWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
fullWidth: PropTypes.bool,
responsive: PropTypes.bool,
};

export default DialogComponent;

+ 0
- 29
src/components/MUI/Examples/DataGridExample.js Wyświetl plik

@@ -1,29 +0,0 @@
import React from 'react';
import { Paper, Typography } from '@mui/material';
import { DataGrid } from '@mui/x-data-grid';

// Use these values from REDUX?
const rows = [
{ id: 1, col1: 'Example', col2: 'Row', col3: '1' },
{ id: 2, col1: 'Row', col2: 'Example', col3: '2' },
{ id: 3, col1: '3', col2: 'Row', col3: 'Example' },
];

const columns = [
{ field: 'col1', headerName: 'Column 1', flex: 1 },
{ field: 'col2', headerName: 'Column 2', flex: 1 },
{ field: 'col3', headerName: 'Column 2', flex: 1 },
];

const DataGridExample = () => {
return (
<Paper sx={{ p: 2 }} elevation={5}>
<Typography variant="h4" gutterBottom align="center">
DataGrid Example
</Typography>
<DataGrid autoHeight rows={rows} columns={columns} />
</Paper>
);
};

export default DataGridExample;

+ 0
- 64
src/components/MUI/Examples/ModalsExample.js Wyświetl plik

@@ -1,64 +0,0 @@
import React, { useState } from 'react';
import { Button, Divider, Paper, Typography } from '@mui/material';
import DialogComponent from '../DialogComponent';
import DrawerComponent from '../DrawerComponent';
import PopoverComponent from '../PopoverComponent';

const Modals = () => {
const [dialogOpen, setDialogOpen] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);

return (
<Paper
sx={{
p: 2,
display: 'flex',
flexDirection: 'column',
}}
elevation={5}
>
<Typography variant="h4" gutterBottom align="center">
Modals Example
</Typography>
<Divider />
<Button onClick={() => setDialogOpen(true)}>Open Dialog</Button>
<Button onClick={() => setDrawerOpen(true)}>Open Drawer</Button>
<Button
onClick={(e) => {
setPopoverOpen(true);
setAnchorEl(e.currentTarget);
}}
>
Open Popover
</Button>
<DialogComponent
title="Dialog Title"
content={<Typography>Dialog Content</Typography>}
open={dialogOpen}
onClose={() => setDialogOpen(false)}
maxWidth="md"
fullWidth
responsive
/>
<DrawerComponent
anchor="left"
content={<Typography sx={{ p: 2 }}>Drawer Content</Typography>}
open={drawerOpen}
toggleOpen={() => setDrawerOpen(!drawerOpen)}
/>
<PopoverComponent
anchorEl={anchorEl}
open={popoverOpen}
onClose={() => {
setPopoverOpen(false);
setAnchorEl(null);
}}
content={<Typography sx={{ p: 2 }}>Popover Content</Typography>}
/>
</Paper>
);
};

export default Modals;

+ 0
- 183
src/components/MUI/Examples/PagingSortingFilteringExample.js Wyświetl plik

@@ -1,183 +0,0 @@
import React, { useEffect, useState } from 'react';
import {
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 {
itemsSelector,
pageSelector,
itemsPerPageSelector,
countSelector,
sortSelector,
} from '../../../store/selectors/randomDataSelectors';
import {
loadData,
updatePage,
updateItemsPerPage,
updateFilter,
updateSort,
} from '../../../store/actions/randomData/randomDataActions';

const PagingSortingFilteringExample = () => {
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';

// Use debounce to prevent too many rerenders
const debouncedFilterText = useDebounce(filterText, 500);

useEffect(() => {
dispatch(loadData(30));
dispatch(updateSort(sort));
}, []);

useEffect(() => {
batch(() => {
dispatch(updateFilter(filterText));
currentPage > 0 && dispatch(updatePage(0));
});
}, [debouncedFilterText]);

const handleFilterTextChange = (event) => {
const filterText = event.target.value;
setFilterText(filterText);
};

const handleSortChange = (event) => {
const sort = event.target.value;
dispatch(updateSort(sort));
};

const handlePageChange = (event, newPage) => {
dispatch(updatePage(newPage));
};

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>
);
};

export default PagingSortingFilteringExample;

+ 0
- 159
src/components/MUI/Examples/PagingSortingFilteringExampleServerSide.js Wyświetl plik

@@ -1,159 +0,0 @@
import React, { useEffect, useState } from 'react';
import {
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';

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();

// Use debounce to prevent too many rerenders
const debouncedFilterText = useDebounce(filterText, 500);

useEffect(() => {
setFilter(filterText);
}, [debouncedFilterText]);

const handleFilterTextChange = (event) => {
const filterText = event.target.value;
setFilterText(filterText);
};

const handleSortChange = (event) => {
const sort = event.target.value;
setSort(sort);
};

const handlePageChange = (event, newPage) => {
setPage(newPage);
};

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>
);
};

export default PagingSortingFilteringExampleServerSide;

+ 0
- 26
src/components/MUI/MenuListComponent.js Wyświetl plik

@@ -1,26 +0,0 @@
import React, { useState } from 'react';
import { Button, Menu, MenuItem } from '@mui/material';

const MenuListComponent = () => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};

return (
<div>
<Button onClick={handleClick}>Menu List</Button>
<Menu id="menu-list" anchorEl={anchorEl} open={open} onClose={handleClose}>
<MenuItem onClick={handleClose}>Menu Item 1</MenuItem>
<MenuItem onClick={handleClose}>Menu Item 2</MenuItem>
<MenuItem onClick={handleClose}>Menu Item 3</MenuItem>
</Menu>
</div>
);
};

export default MenuListComponent;

+ 0
- 35
src/components/MUI/PopoverComponent.js Wyświetl plik

@@ -1,35 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Box, Popover } from '@mui/material';

const PopoverComponent = ({ open, anchorEl, onClose, content }) => {
const handleClose = () => {
onClose();
};

return (
<Box component="div">
<Popover
sx={{ p: 5 }}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
{content}
</Popover>
</Box>
);
};

PopoverComponent.propTypes = {
anchorEl: PropTypes.object,
open: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
content: PropTypes.any,
};

export default PopoverComponent;

+ 1
- 1
src/components/Schedules/DayDetailsComponent.js Wyświetl plik

@@ -155,13 +155,13 @@ const DayDetailsComponent = ({
{isLeftArrowDisabled === true ? (
<div
className="day-details-arrow-container"
data-testid="day-details-left-arrow"
>
<img src={arrowLeftDisabled} />
</div>
) : (
<div
className="day-details-arrow-container"
data-testid="day-details-left-arrow"
onClick={goBackOneDay}
>
<img src={arrowLeft} />

+ 1
- 15
src/constants/keyCodeConstants.js Wyświetl plik

@@ -1,16 +1,2 @@
export const PERIOD_SYMBOL = 190;
export const COMMA_SYMBOL = 188;
export const PLUS_SYMBOL = 187;
export const MINUS_SYMBOL = 189;
export const NUMPAD_PERIOD_SYMBOL = 110;
export const NUMPAD_MINUS_SYMBOL = 109;
export const NUMPAD_PLUS_SYMBOL = 107;
export const K_KEYCODE = 75;
export const DOWN_ARROW_KEYCODE = 38;
export const UP_ARROW_KEYCODE = 40;
export const RIGHT_ARROW_KEYCODE = 39;
export const LEFT_ARROW_KEYCODE = 37;
export const BACKSPACE_KEYCODE = 8;
export const TAB_KEYCODE = 9;

/* istanbul ignore file */
export const PAGE_SIZE_CANDIDATES = 9;

+ 1
- 0
src/constants/localStorage.js Wyświetl plik

@@ -1,3 +1,4 @@
/* istanbul ignore file */
export const JWT_TOKEN = 'JwtToken';
export const JWT_REFRESH_TOKEN = 'JwtRefreshToken';
export const REFRESH_TOKEN_CONST = 'RefreshToken';

+ 1
- 0
src/constants/pages.js Wyświetl plik

@@ -1,3 +1,4 @@
/* istanbul ignore file */
export const BASE_PAGE = '/';
export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const HOME_PAGE = '/home';

+ 0
- 63
src/context/RandomDataContext.js Wyświetl plik

@@ -1,63 +0,0 @@
import React, { createContext, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import usePagingHook from '../hooks/usePagingHook';
import { getRequest } from '../request/jsonServerRequest';

const apiCall = (page, itemsPerPage, sort, sortDirection, filter) =>
getRequest('/items', {
_page: page,
_limit: itemsPerPage,
// Conditionally add to params object if keys exist
...(sort && { _sort: sort }),
...(sortDirection && { _order: sortDirection }),
...(filter && { q: filter }),
});

const Context = createContext();
export const useRandomData = () => useContext(Context);

const RandomDataProvider = ({ children }) => {
const setPage = (page) => {
setState({ ...state, page });
};

const setItemsPerPage = (itemsPerPage) => {
setState({ ...state, itemsPerPage });
};

const setSort = (sort) => {
setState({ ...state, sort });
};

const setFilter = (filter) => {
setState({ ...state, filter });
};

const [state, setState] = useState({
page: 0,
setPage,
itemsPerPage: 12,
setItemsPerPage,
sort: '',
setSort,
filter: '',
setFilter,
});

const data = usePagingHook(
state.page,
state.itemsPerPage,
state.sort,
state.filter,
apiCall
);
return (
<Context.Provider value={{ state, data }}>{children}</Context.Provider>
);
};

RandomDataProvider.propTypes = {
children: PropTypes.node,
};

export default RandomDataProvider;

+ 0
- 17
src/hooks/useDebounceHook.js Wyświetl plik

@@ -1,17 +0,0 @@
import { useEffect, useState } from 'react';

const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay || 500);

return () => {
clearTimeout(timer);
};
}, [value, delay]);

return debouncedValue;
};

export default useDebounce;

+ 0
- 66
src/hooks/usePagingHook.js Wyświetl plik

@@ -1,66 +0,0 @@
import { useState, useCallback, useEffect } from 'react';
import { unstable_batchedUpdates } from 'react-dom';

const usePagingHook = (page, itemsPerPage, sort, filter, apiCallback) => {
const [items, setItems] = useState([]);
const [totalPages, setTotalPages] = useState(0);
const [currentPage, setCurrentPage] = useState(0);
const [loading, setLoading] = useState(false);
const [totalCount, setTotalCount] = useState(0);

const reload = useCallback(async () => {
setLoading(true);
try {
const [sortColumn, sortDirection] = sort.split('-');
const response = await apiCallback(
page,
itemsPerPage,
sortColumn,
sortDirection,
filter
);
if (response.status === 200) {
// Prevents multiple rerenders
unstable_batchedUpdates(() => {
setItems(response.data);
setTotalCount(parseInt(response.headers['x-total-count']));
setTotalPages(
Math.ceil(response.headers['x-total-count'] / itemsPerPage)
);
setCurrentPage(page);
});
}
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
}, [
setItems,
setLoading,
setTotalPages,
setCurrentPage,
apiCallback,
page,
itemsPerPage,
sort,
filter,
]);

useEffect(() => {
reload();
}, [reload]);

return {
items,
loading,
reload,
totalCount,
totalPages,
currentPage,
itemsPerPage,
sort,
};
};

export default usePagingHook;

+ 1
- 0
src/i18n/index.js Wyświetl plik

@@ -5,6 +5,7 @@ import { initReactI18next } from 'react-i18next';
import enTranslations from './resources/en';
import rsTranslations from './resources/rs';

/* istanbul ignore file */
i18n.use(initReactI18next).init({
lng: 'rs',
fallbackLng: 'en',

+ 0
- 13
src/index.css Wyświetl plik

@@ -1,13 +0,0 @@
/* body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
} */

+ 1
- 0
src/index.js Wyświetl plik

@@ -10,6 +10,7 @@ import store from './store';
import './i18n';
import ColorModeProvider from './context/ColorModeContext';

/* istanbul ignore file */
ReactDOM.render(
<HelmetProvider>
<React.StrictMode>

+ 1
- 0
src/mockState.js Wyświetl plik

@@ -1,3 +1,4 @@
/* istanbul ignore file */
export const mockState = {
user: {
user: {

+ 11
- 5
src/pages/CandidatesPage/CandidatesPage.js Wyświetl plik

@@ -80,9 +80,15 @@ const CandidatesPage = ({ history }) => {
<div className="r-b-rectangle"></div>
<div className="top-candidates-container">
{!matches ? (
<p className="candidates-header">Kandidati</p>
<p className="candidates-header" data-testid="candidates-header1">
Kandidati
</p>
) : (
<p className="candidates-header" style={{ fontSize: "22px" }}>
<p
className="candidates-header"
data-testid="candidates-header2"
style={{ fontSize: "22px" }}
>
Kandidati
</p>
)}
@@ -118,7 +124,7 @@ const CandidatesPage = ({ history }) => {
</IconButton>
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-view-2"
onClick={changeView}
>
Tablicni prikaz
@@ -156,7 +162,7 @@ const CandidatesPage = ({ history }) => {
)}
{!matches ? (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-filters1"
onClick={handleToggleFiltersDrawer}
>
Filteri
@@ -168,7 +174,7 @@ const CandidatesPage = ({ history }) => {
</IconButton>
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn-mobile"
className="c-btn c-btn--primary-outlined candidate-btn-mobile candidate-btn-filters2"
onClick={handleToggleFiltersDrawer}
>
<img

+ 3
- 2
src/reportWebVitals.js Wyświetl plik

@@ -1,6 +1,7 @@
const reportWebVitals = onPerfEntry => {
/* istanbul ignore file */
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);

+ 1
- 0
src/store/index.js Wyświetl plik

@@ -6,6 +6,7 @@ import loadingMiddleware from './middleware/loadingMiddleware';
import requestStatusMiddleware from './middleware/requestStatusMiddleware';
import internalServerErrorMiddleware from './middleware/internalServerErrorMiddleware';

/* istanbul ignore file */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const sagaMiddleware = createSagaMiddleware();
export default createStore(

+ 0
- 28
src/util/helpers/dateHelpers.js Wyświetl plik

@@ -1,6 +1,5 @@
import { format } from 'date-fns';
import { enUS } from 'date-fns/locale';
import i18next from 'i18next';

export function formatDate(date, fmt = 'dd.MM.y', locale = enUS) {
const dt = new Date(date);
@@ -17,33 +16,6 @@ export function formatDateTime(date) {
return format(dt, 'hh:mm dd.MM.y');
}

export function getDateDay(date) {
const dt = new Date(date);
return format(dt, 'dd');
}

export function getDateMonth(date) {
const dt = new Date(date);
return format(dt, 'MM');
}

export function getDateYear(date) {
const dt = new Date(date);
return format(dt, 'y');
}

export function formatDateTimeLocale(date) {
const dt = new Date(date);
return format(dt, 'MM/dd/y hh:mm aa');
}

// TODO add locale
export function formatDateRange(dates) {
const start = formatDate(dates.start);
const end = formatDate(dates.end);
return i18next.t('common.date.range', { start, end });
}

export function formatDateSrb(date) {
const dt = new Date(date);
return format(dt, 'dd.MM.');

+ 0
- 1
src/util/helpers/enumMappers.js Wyświetl plik

@@ -1 +0,0 @@
export const parseEnumType = (typeArray, index) => typeArray[index - 1];

+ 1
- 0
src/util/helpers/randomData.js Wyświetl plik

@@ -1,3 +1,4 @@
/* istanbul ignore file */
const random = (arr) => {
return arr[Math.floor(Math.random() * arr.length)];
};

Ładowanie…
Anuluj
Zapisz