Merge branch 'features/media-lib' of github.com:strapi/strapi into media-lib/listview-fixes

This commit is contained in:
Ky 2020-04-01 18:49:58 +02:00
commit c76769ee3d
21 changed files with 350 additions and 117 deletions

View File

@ -22,12 +22,12 @@
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.4.3",
"@buffetjs/core": "3.0.1",
"@buffetjs/custom": "3.0.1",
"@buffetjs/hooks": "3.0.0",
"@buffetjs/icons": "3.0.0",
"@buffetjs/styles": "3.0.0",
"@buffetjs/utils": "3.0.0",
"@buffetjs/core": "3.0.2",
"@buffetjs/custom": "3.0.2",
"@buffetjs/hooks": "3.0.2",
"@buffetjs/icons": "3.0.2",
"@buffetjs/styles": "3.0.2",
"@buffetjs/utils": "3.0.2",
"@fortawesome/fontawesome-free": "^5.11.2",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-brands-svg-icons": "^5.11.2",

View File

@ -37,14 +37,15 @@ const getFilterType = type => {
id: 'components.FilterOptions.FILTER_TYPES._containss',
value: '_containss',
},
{
id: 'components.FilterOptions.FILTER_TYPES._in',
value: '_in',
},
{
id: 'components.FilterOptions.FILTER_TYPES._nin',
value: '_nin',
},
// These lines are commented until we support the array in the admin
// {
// id: 'components.FilterOptions.FILTER_TYPES._in',
// value: '_in',
// },
// {
// id: 'components.FilterOptions.FILTER_TYPES._nin',
// value: '_nin',
// },
];
case 'integer':
case 'biginteger':

View File

@ -35,14 +35,15 @@ describe('HELPER PLUGIN | utils | getFilterType', () => {
id: 'components.FilterOptions.FILTER_TYPES._containss',
value: '_containss',
},
{
id: 'components.FilterOptions.FILTER_TYPES._in',
value: '_in',
},
{
id: 'components.FilterOptions.FILTER_TYPES._nin',
value: '_nin',
},
// Commenting until we support them
// {
// id: 'components.FilterOptions.FILTER_TYPES._in',
// value: '_in',
// },
// {
// id: 'components.FilterOptions.FILTER_TYPES._nin',
// value: '_nin',
// },
];
it('should generate the expected array if type is text', () => {

View File

@ -22,7 +22,6 @@ module.exports = {
const contentTypes = Object.keys(strapi.contentTypes)
.filter(uid => {
if (uid.startsWith('strapi::')) return false;
if (uid === 'plugins::upload.file') return false;
if (kind && _.get(strapi.contentTypes[uid], 'kind', 'collectionType') !== kind) {
return false;

View File

@ -81,13 +81,7 @@ const EditForm = forwardRef(
if (prefixedFileURL) {
setSrc(prefixedFileURL);
} else {
const reader = new FileReader();
reader.onloadend = () => {
setSrc(reader.result);
};
reader.readAsDataURL(fileToEdit.file);
setSrc(URL.createObjectURL(fileToEdit.file));
}
}
}, [isImg, isVideo, fileToEdit, prefixedFileURL]);

View File

@ -1,25 +1,34 @@
import React, { useReducer } from 'react';
import React, { useReducer, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Select } from '@buffetjs/core';
import { getFilterType } from 'strapi-helper-plugin';
import { getFilterType, request } from 'strapi-helper-plugin';
import { getTrad } from '../../../utils';
import reducer, { initialState } from './reducer';
import filtersForm from './utils/filtersForm';
import Wrapper from './Wrapper';
import InputWrapper from './InputWrapper';
import FilterButton from './FilterButton';
import FilterInput from './FilterInput';
const FiltersCard = ({ onChange, filters }) => {
// Not using the hook from buffet.js because it appears that when the component is closed we the hooks returns false
// Until we make a PR on @buffetjs/hooks I rather use this custom one
const isMounted = useRef(true);
const [state, dispatch] = useReducer(reducer, initialState);
const { name, filter, value } = state.toJS();
useEffect(() => {
if (isMounted.current) {
fetchTimestampNames();
}
return () => (isMounted.current = false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMounted]);
const { name, filter, filtersForm, value } = state.toJS();
const type = filtersForm[name].type;
const filtersOptions = getFilterType(type);
const options = ['image', 'video', 'file'].filter(
@ -36,7 +45,7 @@ const FiltersCard = ({ onChange, filters }) => {
};
const addFilter = () => {
onChange({ target: { value: state.toJS() } });
onChange({ target: { value: { name, filter, value } } });
dispatch({
type: 'RESET_FORM',
@ -61,6 +70,25 @@ const FiltersCard = ({ onChange, filters }) => {
});
};
const fetchTimestampNames = async () => {
try {
const result = await request('/content-manager/content-types/plugins::upload.file', {
method: 'GET',
});
if (isMounted.current) {
dispatch({
type: 'HANDLE_CUSTOM_TIMESTAMPS',
timestamps: result.data.contentType.schema.options.timestamps,
});
}
} catch (err) {
if (isMounted.current) {
strapi.notification.error('notification.error');
}
}
};
return (
<Wrapper>
<InputWrapper>

View File

@ -1,12 +1,13 @@
import { fromJS } from 'immutable';
import moment from 'moment';
import filters from './utils/filtersForm';
import filtersForm from './utils/filtersForm';
const initialState = fromJS({
name: 'created_at',
filter: '=',
value: moment(),
filtersForm,
});
function reducer(state, action) {
@ -17,14 +18,46 @@ function reducer(state, action) {
if (name === 'name') {
return state
.update(name, () => value)
.update('filter', () => filters[value].defaultFilter)
.update('value', () => defaultValue || filters[value].defaultValue);
.update('filter', () => state.getIn(['filtersForm', value, 'defaultFilter']))
.update(
'value',
() => defaultValue || state.getIn(['filtersForm', value, 'defaultValue'])
);
}
return state.update(name, () => value);
}
case 'HANDLE_CUSTOM_TIMESTAMPS': {
const {
timestamps: [created_at, updated_at],
} = action;
return state
.update('name', () => created_at)
.updateIn(['filtersForm'], object => {
return object.keySeq().reduce((acc, current) => {
if (current === 'created_at') {
return acc.set(created_at, object.get('created_at')).remove('created_at');
}
if (current === 'updated_at') {
return acc.set(updated_at, object.get('updated_at')).remove('updated_at');
}
return acc;
}, object);
});
}
case 'RESET_FORM':
return initialState;
return initialState
.set(
'name',
state
.get('filtersForm')
.keySeq()
.first()
)
.update('filtersForm', () => state.get('filtersForm'));
default:
return state;
}

View File

@ -1,3 +1,4 @@
import { fromJS } from 'immutable';
import reducer, { initialState } from '../reducer';
describe('Upload | components | FiltersCard | reducer', () => {
@ -30,6 +31,70 @@ describe('Upload | components | FiltersCard | reducer', () => {
expect(actual).toEqual(expected);
});
it('should return the updated filters form with custom timestamps', () => {
const state = fromJS({
name: 'created_at',
filter: '=',
value: 'test',
filtersForm: {
created_at: {
type: 'datetime',
defaultFilter: '=',
defaultValue: 'test1',
},
updated_at: {
type: 'datetime',
defaultFilter: '=',
defaultValue: 'test2',
},
size: {
type: 'integer',
defaultFilter: '=',
defaultValue: '0KB',
},
mime: {
type: 'enum',
defaultFilter: '_contains',
defaultValue: 'image',
},
},
});
const action = {
type: 'HANDLE_CUSTOM_TIMESTAMPS',
timestamps: ['createdAtCustom', 'updatedAtCustom'],
};
const expected = fromJS({
name: 'createdAtCustom',
filter: '=',
value: 'test',
filtersForm: {
createdAtCustom: {
type: 'datetime',
defaultFilter: '=',
defaultValue: 'test1',
},
updatedAtCustom: {
type: 'datetime',
defaultFilter: '=',
defaultValue: 'test2',
},
size: {
type: 'integer',
defaultFilter: '=',
defaultValue: '0KB',
},
mime: {
type: 'enum',
defaultFilter: '_contains',
defaultValue: 'image',
},
},
});
expect(reducer(state, action)).toEqual(expected);
});
it('should return the initialState on reset', () => {
const state = initialState.set('filter', '>');

View File

@ -20,7 +20,7 @@ import Input from '../Input';
const InputMedia = ({ label, onChange, name, attribute, value, type }) => {
const [modal, setModal] = useState({
isOpen: false,
step: null,
step: 'list',
fileToEdit: null,
});
const [fileToDisplay, setFileToDisplay] = useState(0);
@ -123,19 +123,17 @@ const InputMedia = ({ label, onChange, name, attribute, value, type }) => {
<Input type="file" name={name} />
</CardPreviewWrapper>
{modal.isOpen && (
<InputModalStepper
isOpen={modal.isOpen}
step={modal.step}
fileToEdit={modal.fileToEdit}
filesToUpload={modal.filesToUpload}
multiple={attribute.multiple}
onInputMediaChange={handleChange}
selectedFiles={value}
onToggle={handleClickToggleModal}
allowedTypes={attribute.allowedTypes}
/>
)}
<InputModalStepper
isOpen={modal.isOpen}
step={modal.step}
fileToEdit={modal.fileToEdit}
filesToUpload={modal.filesToUpload}
multiple={attribute.multiple}
onInputMediaChange={handleChange}
selectedFiles={value}
onToggle={handleClickToggleModal}
allowedTypes={attribute.allowedTypes}
/>
</Wrapper>
);
};

View File

@ -5,10 +5,11 @@ import PropTypes from 'prop-types';
import Wrapper from './Wrapper';
import { getTrad } from '../../utils';
const InputUploadURL = ({ onChange, value }) => {
const InputUploadURL = ({ errors, onChange, value }) => {
const { formatMessage } = useGlobalContext();
const label = formatMessage({ id: getTrad('input.url.label') });
const description = formatMessage({ id: getTrad('input.url.description') });
const error = errors ? formatMessage({ id: errors.id }, { number: errors.number }) : null;
return (
<Wrapper>
@ -16,6 +17,7 @@ const InputUploadURL = ({ onChange, value }) => {
<div className="col-12">
<Inputs
autoFocus
error={error}
type="textarea"
name="url"
onChange={onChange}
@ -30,11 +32,13 @@ const InputUploadURL = ({ onChange, value }) => {
};
InputUploadURL.defaultProps = {
errors: null,
onChange: () => {},
value: [],
};
InputUploadURL.propTypes = {
errors: PropTypes.object,
onChange: PropTypes.func,
value: PropTypes.arrayOf(PropTypes.string),
};

View File

@ -8,6 +8,7 @@ import ModalSection from '../ModalSection';
const UploadForm = ({
addFilesToUpload,
filesToDownload,
formErrors,
onChange,
setShouldDisplayNextButton,
}) => {
@ -32,7 +33,9 @@ const UploadForm = ({
{to => (
<ModalSection>
{to === 'computer' && <InputFileModal onChange={addFilesToUpload} />}
{to === 'url' && <InputUploadURL onChange={onChange} value={filesToDownload} />}
{to === 'url' && (
<InputUploadURL errors={formErrors} onChange={onChange} value={filesToDownload} />
)}
</ModalSection>
)}
</ModalNavWrapper>
@ -42,6 +45,7 @@ const UploadForm = ({
UploadForm.defaultProps = {
addFilesToUpload: () => {},
filesToDownload: [],
formErrors: null,
onChange: () => {},
setShouldDisplayNextButton: () => {},
};
@ -49,6 +53,7 @@ UploadForm.defaultProps = {
UploadForm.propTypes = {
addFilesToUpload: PropTypes.func,
filesToDownload: PropTypes.arrayOf(PropTypes.string),
formErrors: PropTypes.object,
onChange: PropTypes.func,
setShouldDisplayNextButton: PropTypes.func,
};

View File

@ -1,8 +1,8 @@
import React, { useReducer, useState, useEffect } from 'react';
import React, { useReducer, useState, useRef, useEffect } from 'react';
import { includes, isEmpty, toString } from 'lodash';
import { useHistory, useLocation } from 'react-router-dom';
import { Header } from '@buffetjs/custom';
import { useDebounce, useIsMounted } from '@buffetjs/hooks';
import { useDebounce } from '@buffetjs/hooks';
import {
HeaderSearch,
PageFooter,
@ -46,7 +46,7 @@ const HomePage = () => {
const [searchValue, setSearchValue] = useState(query.get('_q') || '');
const { push } = useHistory();
const { search } = useLocation();
const isMounted = useIsMounted();
const isMounted = useRef(true);
const { data, dataCount, dataToDelete, isLoading } = reducerState.toJS();
const pluginName = formatMessage({ id: getTrad('plugin.name') });
const paramsKeys = ['_limit', '_start', '_q', '_sort'];
@ -61,6 +61,7 @@ const HomePage = () => {
useEffect(() => {
fetchListData();
return () => (isMounted.current = false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [search]);
@ -72,7 +73,7 @@ const HomePage = () => {
method: 'DELETE',
});
} catch (err) {
if (isMounted) {
if (isMounted.current) {
strapi.notification.error('notification.error');
}
}
@ -83,7 +84,7 @@ const HomePage = () => {
const [data, count] = await Promise.all([fetchData(), fetchDataCount()]);
if (isMounted) {
if (isMounted.current) {
dispatch({
type: 'GET_DATA_SUCCEEDED',
data,
@ -103,7 +104,7 @@ const HomePage = () => {
return Promise.resolve(data);
} catch (err) {
if (isMounted) {
if (isMounted.current) {
dispatch({ type: 'GET_DATA_ERROR' });
strapi.notification.error('notification.error');
}
@ -122,7 +123,7 @@ const HomePage = () => {
return Promise.resolve(count);
} catch (err) {
if (isMounted) {
if (isMounted.current) {
dispatch({ type: 'GET_DATA_ERROR' });
strapi.notification.error('notification.error');
}

View File

@ -21,6 +21,7 @@ const InputModalStepper = ({ isOpen, onToggle, onInputMediaChange }) => {
filesToDownload,
filesToUpload,
fileToEdit,
formErrors,
goTo,
handleAbortUpload,
handleCancelFileToUpload,
@ -205,14 +206,16 @@ const InputModalStepper = ({ isOpen, onToggle, onInputMediaChange }) => {
const handleToggle = () => {
if (filesToUploadLength > 0 || selectedFiles.length > 0) {
// eslint-disable-next-line no-alert
const confirm = window.confirm(formatMessage({ id: getTrad('window.confirm.close-modal') }));
const confirm = window.confirm(
formatMessage({ id: getTrad('window.confirm.close-modal.files') })
);
if (!confirm) {
return;
}
}
onToggle();
onToggle(true);
};
const shouldDisplayNextButton = currentStep === 'browse' && displayNextButton;
@ -236,6 +239,7 @@ const InputModalStepper = ({ isOpen, onToggle, onInputMediaChange }) => {
filesToDownload={filesToDownload}
filesToUpload={filesToUpload}
fileToEdit={fileToEdit}
formErrors={formErrors}
isEditingUploadedFile={currentStep === 'edit'}
isFormDisabled={isFormDisabled}
onAbortUpload={handleAbortUpload}

View File

@ -1,14 +1,16 @@
import React, { useReducer, useEffect } from 'react';
import React, { useReducer, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { request, generateSearchFromFilters } from 'strapi-helper-plugin';
import { get } from 'lodash';
import { get, isEmpty } from 'lodash';
import axios from 'axios';
import pluginId from '../../pluginId';
import {
getFilesToDownload,
getRequestUrl,
getYupError,
compactParams,
createNewFilesToUploadArray,
urlSchema,
} from '../../utils';
import InputModalStepperContext from '../../contexts/InputModal/InputModalDataManager';
import init from './init';
@ -28,6 +30,7 @@ const InputModalStepperProvider = ({
selectedFiles,
step,
}) => {
const [formErrors, setFormErrors] = useState(null);
const [reducerState, dispatch] = useReducer(reducer, initialState, state =>
init({
...state,
@ -46,7 +49,7 @@ const InputModalStepperProvider = ({
},
})
);
const { params, filesToUpload, fileToEdit } = reducerState;
const { params, filesToDownload, filesToUpload, fileToEdit } = reducerState;
useEffect(() => {
if (isOpen) {
@ -104,11 +107,24 @@ const InputModalStepperProvider = ({
});
};
const handleClickNextButton = () => {
dispatch({
type: 'ADD_URLS_TO_FILES_TO_UPLOAD',
nextStep: 'upload',
});
const handleClickNextButton = async () => {
try {
await urlSchema.validate(
{ filesToDownload: filesToDownload.filter(url => !isEmpty(url)) },
{ abortEarly: false }
);
setFormErrors(null);
// Navigate to next step
dispatch({
type: 'ADD_URLS_TO_FILES_TO_UPLOAD',
nextStep: 'upload',
});
} catch (err) {
const formattedErrors = getYupError(err);
setFormErrors(formattedErrors.filesToDownload);
}
};
const handleFileToEditChange = ({ target: { name, value } }) => {
@ -116,6 +132,8 @@ const InputModalStepperProvider = ({
let type = 'ON_CHANGE';
if (name === 'url') {
setFormErrors(null);
val = value.split('\n');
type = 'ON_CHANGE_URLS_TO_DOWNLOAD';
}
@ -168,6 +186,8 @@ const InputModalStepperProvider = ({
};
const handleClose = () => {
setFormErrors(null);
dispatch({
type: 'RESET_PROPS',
});
@ -381,6 +401,7 @@ const InputModalStepperProvider = ({
addFilesToUpload,
downloadFiles,
fetchMediaLib,
formErrors,
goTo,
handleAbortUpload,
handleAllFilesSelection,

View File

@ -5,7 +5,7 @@ import { isEqual, isEmpty, get } from 'lodash';
import { Modal, ModalFooter, PopUpWarning, useGlobalContext, request } from 'strapi-helper-plugin';
import { Button } from '@buffetjs/core';
import pluginId from '../../pluginId';
import { getFilesToDownload, getTrad } from '../../utils';
import { getFilesToDownload, getTrad, getYupError, urlSchema } from '../../utils';
import ModalHeader from '../../components/ModalHeader';
import stepper from './stepper';
import init from './init';
@ -23,6 +23,7 @@ const ModalStepper = ({
const [isWarningDeleteOpen, setIsWarningDeleteOpen] = useState(false);
const [shouldDeleteFile, setShouldDeleteFile] = useState(false);
const [isFormDisabled, setIsFormDisabled] = useState(false);
const [formErrors, setFormErrors] = useState(null);
const [displayNextButton, setDisplayNextButton] = useState(false);
const [reducerState, dispatch] = useReducer(reducer, initialState, init);
const { currentStep, fileToEdit, filesToDownload, filesToUpload } = reducerState.toJS();
@ -150,6 +151,8 @@ const ModalStepper = ({
let type = 'ON_CHANGE';
if (name === 'url') {
setFormErrors(null);
val = value.split('\n');
type = 'ON_CHANGE_URLS_TO_DOWNLOAD';
}
@ -166,12 +169,24 @@ const ModalStepper = ({
toggleModalWarning();
};
const handleClickNextButton = () => {
// Navigate to next step
dispatch({
type: 'ADD_URLS_TO_FILES_TO_UPLOAD',
nextStep: next,
});
const handleClickNextButton = async () => {
try {
await urlSchema.validate(
{ filesToDownload: filesToDownload.filter(url => !isEmpty(url)) },
{ abortEarly: false }
);
setFormErrors(null);
// Navigate to next step
dispatch({
type: 'ADD_URLS_TO_FILES_TO_UPLOAD',
nextStep: next,
});
} catch (err) {
const formattedErrors = getYupError(err);
setFormErrors(formattedErrors.filesToDownload);
}
};
const handleClickDeleteFile = async () => {
@ -197,6 +212,7 @@ const ModalStepper = ({
onClosed();
setIsFormDisabled(false);
setDisplayNextButton(false);
setFormErrors(null);
dispatch({
type: 'RESET_PROPS',
@ -317,7 +333,7 @@ const ModalStepper = ({
}
}
onToggle();
onToggle(true);
};
const handleUploadFiles = async () => {
@ -416,6 +432,7 @@ const ModalStepper = ({
fileToEdit={fileToEdit}
filesToDownload={filesToDownload}
filesToUpload={filesToUpload}
formErrors={formErrors}
components={components}
isEditingUploadedFile={currentStep === 'edit'}
isFormDisabled={isFormDisabled}

View File

@ -1,6 +1,5 @@
import React, { useEffect, useReducer, useRef } from 'react';
import { Header, Inputs } from '@buffetjs/custom';
import { useIsMounted } from '@buffetjs/hooks';
import { isEqual } from 'lodash';
import { LoadingIndicatorPage, useGlobalContext, request } from 'strapi-helper-plugin';
@ -16,7 +15,7 @@ const SettingsPage = () => {
const { formatMessage } = useGlobalContext();
const [reducerState, dispatch] = useReducer(reducer, initialState, init);
const { initialData, isLoading, modifiedData } = reducerState.toJS();
const isMounted = useIsMounted();
const isMounted = useRef(true);
const getDataRef = useRef();
const abortController = new AbortController();
@ -25,7 +24,7 @@ const SettingsPage = () => {
const { signal } = abortController;
const { data } = await request(getRequestUrl('settings', { method: 'GET', signal }));
if (isMounted) {
if (isMounted.current) {
dispatch({
type: 'GET_DATA_SUCCEEDED',
data,
@ -41,6 +40,7 @@ const SettingsPage = () => {
return () => {
abortController.abort();
isMounted.current = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -52,7 +52,7 @@ const SettingsPage = () => {
body: modifiedData,
});
if (isMounted) {
if (isMounted.current) {
dispatch({
type: 'SUBMIT_SUCCEEDED',
});

View File

@ -14,6 +14,8 @@
"form.input.decription.file-alt": "This text will be displayed if the asset cant be shown.",
"form.input.label.file-caption": "Caption",
"form.input.label.file-name": "File name",
"form.upload-url.error.url.invalid": "One URL is invalid",
"form.upload-url.error.url.invalids": "{number} URLs are invalids",
"header.actions.upload-assets": "Upload assets",
"header.content.assets-empty": "No asset",
"header.content.assets-multiple": "{number} assets",

View File

@ -0,0 +1,17 @@
import { get } from 'lodash';
const getYupError = error => {
return get(error, 'inner', []).reduce((acc, curr) => {
acc[
curr.path
.split('[')
.join('.')
.split(']')
.join('')
] = { id: curr.message, number: curr.params.wrongURLsNumber };
return acc;
}, {});
};
export default getYupError;

View File

@ -13,4 +13,6 @@ export { default as getFilesToDownload } from './getFilesToDownload';
export { default as getRequestUrl } from './getRequestUrl';
export { default as getTrad } from './getTrad';
export { default as getType } from './getType';
export { default as getYupError } from './getYupError';
export { default as ItemTypes } from './ItemTypes';
export { default as urlSchema } from './urlYupSchema';

View File

@ -0,0 +1,41 @@
/* eslint-disable no-template-curly-in-string */
import * as yup from 'yup';
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
import getTrad from './getTrad';
const urlSchema = yup.object().shape({
filesToDownload: yup
.array()
.of(yup.string())
.test({
name: 'isUrlValid',
message: '${path}',
test(values) {
const filtered = values.filter(val => {
return !val.startsWith('http');
});
const filteredLength = filtered.length;
if (filteredLength === 0) {
return true;
}
const errorMessage =
filteredLength > 1
? 'form.upload-url.error.url.invalids'
: 'form.upload-url.error.url.invalid';
return this.createError({
path: this.path,
message: getTrad(errorMessage),
params: { wrongURLsNumber: filtered.length },
});
},
})
.min(0, errorsTrads.min)
.max(20, errorsTrads.max),
});
export default urlSchema;

View File

@ -844,15 +844,15 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@buffetjs/core@3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@buffetjs/core/-/core-3.0.1.tgz#d508415a34d31f187008245b2e209e242c27e019"
integrity sha512-OLtHynNaaUJ03YRundxHRiXVs//IdFwep/fS3Xt8aYJH/xtVhPO5nqLGAkN8T6jyLVn+fxYZ7zy+T9ccrta3TQ==
"@buffetjs/core@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/core/-/core-3.0.2.tgz#b7ab4600f142c581b7ec70fca2042168be820f05"
integrity sha512-2pqMusTjI3JB9YzPFAya7ixai3cOwolITaPgP6e1xJ/5/nCyJOX1k90Zg2eJvQPRIUWvQrk7dMHiUeBXHuJAZw==
dependencies:
"@buffetjs/hooks" "3.0.0"
"@buffetjs/icons" "3.0.0"
"@buffetjs/styles" "3.0.0"
"@buffetjs/utils" "3.0.0"
"@buffetjs/hooks" "3.0.2"
"@buffetjs/icons" "3.0.2"
"@buffetjs/styles" "3.0.2"
"@buffetjs/utils" "3.0.2"
"@fortawesome/fontawesome-svg-core" "^1.2.25"
"@fortawesome/free-regular-svg-icons" "^5.11.2"
"@fortawesome/free-solid-svg-icons" "^5.11.2"
@ -863,31 +863,31 @@
react-dates "^21.5.1"
react-moment-proptypes "^1.7.0"
"@buffetjs/custom@3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@buffetjs/custom/-/custom-3.0.1.tgz#41ced07dc35e0442ecc4ec263e304b181f576e8e"
integrity sha512-TmsO8iKsOZPFigwEoJ2xxwzk7w0qT26QbKldmKepV1Y9LZHjWaSjjHEJpSlpzgU1/5FxOhaeQmTJTzWHMrvcUQ==
"@buffetjs/custom@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/custom/-/custom-3.0.2.tgz#6bf426b61e98fc8455a225a66657cb055864fd69"
integrity sha512-UWuYxusniCih9TM/zfJHjcWiq8qIgto4cJ0NHQHNReDfpNG0DWvbZBrg8yqABrcve8yRBoYVrNiXs9AoapRM/Q==
dependencies:
"@buffetjs/core" "3.0.1"
"@buffetjs/styles" "3.0.0"
"@buffetjs/utils" "3.0.0"
"@buffetjs/core" "3.0.2"
"@buffetjs/styles" "3.0.2"
"@buffetjs/utils" "3.0.2"
moment "^2.24.0"
react-moment-proptypes "^1.7.0"
"@buffetjs/hooks@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@buffetjs/hooks/-/hooks-3.0.0.tgz#998a502a9acc419b49d0e437d60bc5d6df4727c5"
integrity sha512-4fi0DTgDElk5cKr7Z5jmRneasioQVUEWsv2oTXH+HA0IPiZS7Ob1dUcPsRltYcOFg/VygGHmbs0VPb6NCtl6Qw==
"@buffetjs/hooks@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/hooks/-/hooks-3.0.2.tgz#8c2530f38124c249606b5dc76bf110890c74cd43"
integrity sha512-Sf4PFGXM6CB/lB1qwDWwEyu75j6wsMJsuBheYhPeBHHnQgFS2v5gx3PaC+1Xm8DRxilsY6r6rT+07jntg67mKg==
"@buffetjs/icons@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@buffetjs/icons/-/icons-3.0.0.tgz#7c210df2cc741a303f3250bbac3e877da3d8bdad"
integrity sha512-6SFbizWbsTFzjTfIAK1/9PsnTJ3L5NwA3fqcgIZpGxSIdWH9+RIJXOPUfWaqFVpq9BChlul6HludcuZQex0Ceg==
"@buffetjs/icons@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/icons/-/icons-3.0.2.tgz#65b4c98b7f739d3a66f275c32d9bd31b88c953fc"
integrity sha512-aYV5drQNfzQk5RzZ7Gwgd4EmorEwJTjHv4lKqFRmEm20URPRsQkDhApE7IENKMw/G+hhKUJHXnAKQxgW8lkGHw==
"@buffetjs/styles@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@buffetjs/styles/-/styles-3.0.0.tgz#0c453adf264cac62404829d363a8daf953f1b79f"
integrity sha512-HSaQkYhkWRKIZq9vHnXrG+oBe43xw73BJd5DfmUdkDXPMQe6x5tkBZf4OhHtUlgemjuamiuR5qw4OYl+mmEp9Q==
"@buffetjs/styles@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/styles/-/styles-3.0.2.tgz#9f800460f95b31bdd50832104888d7c9f0973dcc"
integrity sha512-dKiNIEmN+eDxoRHU3IxcKgfcKa8C6T74yXekbTEtzUeBSMIly4BbTa2vVAKR89aSXeEg9KhG+F+9brlbi71BOA==
dependencies:
"@fortawesome/fontawesome-free" "^5.12.0"
"@fortawesome/fontawesome-svg-core" "^1.2.22"
@ -896,10 +896,10 @@
"@fortawesome/react-fontawesome" "^0.1.4"
react-dates "^21.1.0"
"@buffetjs/utils@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@buffetjs/utils/-/utils-3.0.0.tgz#ed78f60996c7486a745bd87669dd8f78add03901"
integrity sha512-xO/CQIRyYkWcMuHmGuullpJVUEUhXLq2pF46+x27XXxD3OzuDnoJQP6rKnqi4p2sZOeQSMTgo3yXBA+ZcSJLeA==
"@buffetjs/utils@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@buffetjs/utils/-/utils-3.0.2.tgz#dd18a85f50e1668a3e48f4533bd0c677bd026171"
integrity sha512-QUTFncBU0hViTDYfwaRi8aQvL/2hDBPStqtVOOe+MuhkeTlv8t1mdu51nA0+X7Z9Q6ygEpboaZT+N0QiBi0V2A==
dependencies:
yup "^0.27.0"