From 98671aba7af5089003e79984727aa0bc4d3bef39 Mon Sep 17 00:00:00 2001 From: JEFFREY-Bonson Date: Tue, 30 Jan 2024 20:58:08 +0530 Subject: [PATCH] Method list dialog conversion --- app/controllers/miq_ae_class_controller.rb | 27 +++ .../AeInlineMethod/FilterNamespace.jsx | 56 ++++++ .../AeInlineMethod/NamespaceSelector.jsx | 106 ++++++++++ .../components/AeInlineMethod/helper.js | 61 ++++++ .../components/AeInlineMethod/index.jsx | 190 ++++++++++++++++++ .../components/AeInlineMethod/style.scss | 79 ++++++++ .../components/miq-data-table/index.jsx | 4 +- .../miq-data-table/miq-table-cell.jsx | 6 +- .../packs/component-definitions-common.js | 2 + app/stylesheet/miq-data-table.scss | 4 + .../miq_ae_class/_embedded_methods.html.haml | 31 +-- config/routes.rb | 3 + package.json | 1 + spec/config/routes.pending.yml | 3 + yarn.lock | 123 ++++++++++-- 15 files changed, 651 insertions(+), 45 deletions(-) create mode 100644 app/javascript/components/AeInlineMethod/FilterNamespace.jsx create mode 100644 app/javascript/components/AeInlineMethod/NamespaceSelector.jsx create mode 100644 app/javascript/components/AeInlineMethod/helper.js create mode 100644 app/javascript/components/AeInlineMethod/index.jsx create mode 100644 app/javascript/components/AeInlineMethod/style.scss diff --git a/app/controllers/miq_ae_class_controller.rb b/app/controllers/miq_ae_class_controller.rb index d13ce6bb009..953e2afd164 100644 --- a/app/controllers/miq_ae_class_controller.rb +++ b/app/controllers/miq_ae_class_controller.rb @@ -520,6 +520,7 @@ def edit_method id = x_node.split('-') end @ae_method = find_record_with_rbac(MiqAeMethod, id[1]) + @embedded_methods = MiqAeMethod.where(:relative_path => @ae_method[:embedded_methods].map { |str| str.sub(/^\//, '') }) @selectable_methods = embedded_method_regex(@ae_method.fqname) if playbook_style_location?(@ae_method.location) # these variants are implemented in Angular @@ -1815,6 +1816,32 @@ def namespace render :json => find_record_with_rbac(MiqAeNamespace, params[:id]).attributes.slice('name', 'description', 'enabled') end + def ae_domains + domains = MiqAeDomain.where(:enabled => true).order("name").select("id, name") + render :json => {:domains => domains} + end + + def ae_methods + methods = MiqAeMethod + .name_path_search(params[:search]) + .where(params[:domain_id] ? {:domain_id => params[:domain_id]} : {}) + .where(params[:ids] ? {:id => params[:ids]&.split(',')} : {}) + .select("id, relative_path, name") + .order('name') + render :json => {:methods => methods} + end + + def ae_method_operations + ids = params[:ids].split(",") + @edit[:new][:embedded_methods] = MiqAeMethod.where(:id => ids).pluck(:relative_path).map { |path| "/#{path}" } + @changed = true + render :update do |page| + page << javascript_prologue + page << javascript_for_miq_button_visibility(@changed) + page << "miqSparkle(false);" + end + end + private def feature_by_action diff --git a/app/javascript/components/AeInlineMethod/FilterNamespace.jsx b/app/javascript/components/AeInlineMethod/FilterNamespace.jsx new file mode 100644 index 00000000000..70fb145ae9c --- /dev/null +++ b/app/javascript/components/AeInlineMethod/FilterNamespace.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Select, SelectItem, Search, +} from 'carbon-components-react'; +import { noSelect } from './helper'; + +const FilterNamespace = ({ domains, onSearch }) => { + /** Function to render the search text. */ + const renderSearchText = () => ( +
+ + onSearch({ searchText: noSelect })} + onChange={(event) => onSearch({ searchText: event.target.value || noSelect })} + /> +
+ ); + + /** Function to render the domain items in a drop-down list. */ + const renderDomainList = () => ( + + ); + + return ( +
+ {renderSearchText()} + {domains && renderDomainList()} +
+ ); +}; + +export default FilterNamespace; + +FilterNamespace.propTypes = { + domains: PropTypes.arrayOf(PropTypes.any), + onSearch: PropTypes.func.isRequired, +}; + +FilterNamespace.defaultProps = { + domains: undefined, +}; diff --git a/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx b/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx new file mode 100644 index 00000000000..8e6b9a39ccf --- /dev/null +++ b/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx @@ -0,0 +1,106 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { useQuery } from 'react-query'; +import { Loading } from 'carbon-components-react'; +import { debounce } from 'lodash'; +import FilterNamespace from './FilterNamespace'; +import MiqDataTable from '../miq-data-table'; +import NotificationMessage from '../notification-message'; +import { CellAction } from '../miq-data-table/helper'; +import { + methodSelectorHeaders, formatMethods, searchUrl, namespaceUrls, +} from './helper'; +import './style.scss'; + +const NamespaceSelector = ({ onSelectMethod, selectedIds }) => { + const [filterData, setFilterData] = useState({ searchText: '', selectedDomain: '' }); + + /** Loads the domains and stores in domainData for 60 seconds. */ + const { data: domainsData, isLoading: domainsLoading } = useQuery( + 'domainsData', + async() => (await http.get(namespaceUrls.aeDomainsUrl)).domains, + { + staleTime: 60000, + } + ); + + /** Loads the methods and stores in methodsData for 60 seconds. + * If condition works on page load + * Else part would work if there is a change in filterData. + */ + const { data, isLoading: methodsLoading } = useQuery( + ['methodsData', filterData.searchText, filterData.selectedDomain], + async() => { + if (!filterData.searchText && !filterData.selectedDomain) { + const response = await http.get(namespaceUrls.aeMethodsUrl); + return formatMethods(response.methods); + } + const url = searchUrl(filterData.selectedDomain, filterData.searchText); + const response = await http.get(url); + return formatMethods(response.methods); + }, + { + keepPreviousData: true, + refetchOnWindowFocus: false, + staleTime: 60000, + } + ); + + /** Debounce the search text by delaying the text input provided to the API. */ + const debouncedSearch = debounce((newFilterData) => { + setFilterData(newFilterData); + }, 300); + + /** Function to handle the onSearch event during a filter change event. */ + const onSearch = useCallback( + (newFilterData) => debouncedSearch(newFilterData), + [debouncedSearch] + ); + + /** Function to handle the click event of a cell in the data table. */ + const onCellClick = (selectedRow, cellType, checked) => { + const selectedItems = cellType === CellAction.selectAll + ? data && data.map((item) => item.id) + : [selectedRow]; + onSelectMethod({ selectedItems, cellType, checked }); + }; + + /** Function to render the list which depends on the data and selectedIds. + * List is memoized to prevent unnecessary re-renders when other state values change. */ + const renderContents = useMemo(() => { + if (!data || data.length === 0) { + return ; + } + + return ( + onCellClick(selectedRow, cellType, event.target.checked)} + /> + ); + }, [data, selectedIds]); + + return ( +
+ +
+ {(domainsLoading || methodsLoading) + ? + : renderContents} +
+
+ ); +}; + +NamespaceSelector.propTypes = { + onSelectMethod: PropTypes.func.isRequired, + selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired, +}; + +export default NamespaceSelector; diff --git a/app/javascript/components/AeInlineMethod/helper.js b/app/javascript/components/AeInlineMethod/helper.js new file mode 100644 index 00000000000..f846d4fe4df --- /dev/null +++ b/app/javascript/components/AeInlineMethod/helper.js @@ -0,0 +1,61 @@ +export const namespaceUrls = { + aeMethodsUrl: '/miq_ae_class/ae_methods', + aeMethodOperationsUrl: '/miq_ae_class/ae_method_operations', + aeDomainsUrl: '/miq_ae_class/ae_domains', +}; + +export const noSelect = 'NONE'; + +/** Headers needed for the data-table list. */ +export const methodSelectorHeaders = [ + { + key: 'name', + header: 'Name', + }, + { + key: 'path', + header: 'Relative path', + }, +]; + +export const methodListHeaders = [ + ...methodSelectorHeaders, + { key: 'remove', header: __('Remove'), actionCell: true }, +]; + +/** Function to format the method data needed for the data-table list. */ +export const formatMethods = (methods) => (methods.map((item) => ({ + id: item.id.toString(), + name: { text: item.name, icon: 'icon node-icon fa-ruby' }, + path: item.relative_path, +}))); + +const removeMethodButton = () => ({ + is_button: true, + actionCell: true, + title: __('Remove'), + text: __('Remove'), + alt: __('Remove'), + kind: 'danger', + callback: 'removeMethod', +}); + +export const formatListMethods = (methods) => (methods.map((item, index) => ({ + id: item.id.toString(), + name: { text: item.name, icon: 'icon node-icon fa-ruby' }, + path: item.relative_path, + remove: removeMethodButton(item, index), +}))); + +/** Function to return a conditional URL based on the selected filters. */ +export const searchUrl = (selectedDomain, text) => { + const queryParams = []; + if (selectedDomain && selectedDomain !== noSelect) { + queryParams.push(`domain_id=${selectedDomain}`); + } + if (text && text !== noSelect) { + queryParams.push(`search=${text}`); + } + const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; + return `${namespaceUrls.aeMethodsUrl}${queryString}`; +}; diff --git a/app/javascript/components/AeInlineMethod/index.jsx b/app/javascript/components/AeInlineMethod/index.jsx new file mode 100644 index 00000000000..09dc91e7b2a --- /dev/null +++ b/app/javascript/components/AeInlineMethod/index.jsx @@ -0,0 +1,190 @@ +import React, { useState, useEffect } from 'react'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import PropTypes from 'prop-types'; +import { + Modal, Button, ModalBody, Accordion, AccordionItem, +} from 'carbon-components-react'; +import { AddAlt16 } from '@carbon/icons-react'; +import NotificationMessage from '../notification-message'; +import MiqDataTable from '../miq-data-table'; +import NamespaceSelector from './NamespaceSelector'; +import { CellAction } from '../miq-data-table/helper'; +import { formatListMethods, methodListHeaders, namespaceUrls } from './helper'; + +/** Component to render a tree and to select an embedded method. */ +const AeInlineMethod = ({ type, selected }) => { + const queryClient = new QueryClient(); + + const [data, setData] = useState({ + isModalOpen: false, + selectedIds: selected ? selected.map((item) => item.id.toString()) : [], + rows: selected ? formatListMethods(selected) : [], + notification: false, + }); + + useEffect(() => { + setData({ ...data, notification: data.selectedIds.length > 20 }); + }, [data.selectedIds]); + + /** Function to show/hide the modal. */ + const showModal = (status) => setData({ ...data, isModalOpen: status }); + + /** Function to handle the select-all check-box click event. */ + const onSelectAll = (selectedItems, checked) => setData({ ...data, selectedIds: checked ? [...selectedItems] : [] }); + + /** Function to handle the list row selection events. + * selectedItem is passed as an array. */ + const onItemSelect = (selectedItems, checked) => { + if (checked) { + data.selectedIds.push(selectedItems[0].id); + } else { + data.selectedIds = data.selectedIds.filter((item) => item !== selectedItems[0].id); + } + setData({ ...data, selectedIds: [...data.selectedIds] }); + }; + + /** Function to add/remove an selected items. */ + const onSelectMethod = (selectedItems, cellType, checked) => { + switch (cellType) { + case CellAction.selectAll: onSelectAll(selectedItems, checked); break; + default: onItemSelect(selectedItems, checked); break; + } + }; + + /** Updates the ruby form with the selected methods. */ + const updateRubyForm = (ids) => $.get(`${namespaceUrls.aeMethodOperationsUrl}?&ids=${ids}`); + + /** Function to handle the click events for the list. */ + const onCellClickHandler = (item) => { + if (item && item.callbackAction && item.callbackAction === 'removeMethod') { + const ids = data.selectedIds.filter((id) => id !== item.id); + setData({ + rows: data.rows.filter((row) => row.id !== item.id), + selectedIds: ids, + }); + updateRubyForm(ids.map((str) => parseInt(str, 10))); + } + }; + + /** Function to handle the modal submit action. */ + const submitModal = () => { + if (data.selectedIds.length > 0) { + const ids = data.selectedIds.map((str) => parseInt(str, 10)); + http.get(`${namespaceUrls.aeMethodsUrl}?ids=${ids}`) + .then(({ methods }) => { + setData({ + ...data, rows: formatListMethods(methods), isModalOpen: false, + }); + updateRubyForm(ids); + }); + } else { + setData({ + ...data, rows: [], isModalOpen: false, + }); + } + }; + + /** Function to render the modal with namespace selector component. */ + const renderModalSelector = () => ( + showModal(false)} + onRequestSubmit={() => submitModal()} + onSecondarySubmit={() => showModal(false)} + primaryButtonDisabled={data.selectedIds.length > 20 || data.selectedIds.length === 0} + > + + { + data.isModalOpen + && ( + + { + data.notification && + } + onSelectMethod(selectedItems, cellType, checked)} + selectedIds={data.selectedIds} + /> + + ) + } + + + ); + + /** Function to render the contents of the list. */ + const renderList = () => (data.rows && data.rows.length > 0 + ? ( +
+ onCellClickHandler(selectedRow)} + /> +
+ ) + : ( +
+ +
+ )); + + const renderAddButton = () => ( +
+ { + data.selectedIds.length > 0 && ( +
+ {__('Listing')} + {` ${data.selectedIds.length} `} + {__('Method(s)')} +
+ ) + } + +
+ ); + + const renderAccordionContents = () => ( + + + {renderAddButton()} + {renderList()} + + + ); + + return ( +
+ {renderAccordionContents()} + {renderModalSelector()} +
+ ); +}; + +export default AeInlineMethod; + +AeInlineMethod.propTypes = { + type: PropTypes.string.isRequired, + selected: PropTypes.arrayOf(PropTypes.any), +}; + +AeInlineMethod.defaultProps = { + selected: undefined, +}; diff --git a/app/javascript/components/AeInlineMethod/style.scss b/app/javascript/components/AeInlineMethod/style.scss new file mode 100644 index 00000000000..1c89e69cb43 --- /dev/null +++ b/app/javascript/components/AeInlineMethod/style.scss @@ -0,0 +1,79 @@ +.ae-inline-method-modal { + .bx--modal-content { + margin-bottom: 0; + } + + .inline-method-selector { + display: flex; + flex-direction: column; + min-height: 520px; + + .inline-filters { + display: flex; + flex-direction: row; + align-items: end; + gap: 10px; + + .search-wrapper { + display: flex; + flex-grow: 1; + flex-direction: column; + align-items: flex-start; + } + } + + .inline-contents-wrapper { + display: flex; + flex-direction: column; + margin-top: 20px; + + .miq-inline-method-list { + margin-top: 0; + + .bx--data-table-content { + margin-bottom: 0; + + .bx--data-table--sticky-header { + max-height: 25rem; + } + } + } + + .miq-notification-message-container { + margin: 0; + } + } + } +} + + +.miq-custom-form-accordion +{ + border: 1px solid #e0e0e0; + + li button.bx--accordion__heading { + background: #e0e0e0; + } + .bx--accordion__item:last-child{ + border: 0; + } + + .bx--accordion__content { + padding: 20px; + margin: 0; + + .custom-form-buttons { + display: flex; + justify-content: flex-end; + + .custom-form-buttons-label { + flex-grow: 1; + font-size: 14px; + } + } + + .ae-inline-methods-notification { + margin-top: 20px; + } + } +} diff --git a/app/javascript/components/miq-data-table/index.jsx b/app/javascript/components/miq-data-table/index.jsx index ee1d1603eb1..a9d1332861d 100644 --- a/app/javascript/components/miq-data-table/index.jsx +++ b/app/javascript/components/miq-data-table/index.jsx @@ -100,7 +100,9 @@ const MiqDataTable = ({ isSortable={isSortable} isSortHeader={sortHeader} sortDirection={sortDirection} - className={classNames('miq-data-table-header', (header.contentButton ? 'header-button' : ''))} + className={ + classNames('miq-data-table-header', (header.contentButton ? 'header-button' : ''), (header.actionCell ? 'action-cell-holder' : '')) + } > {headerLabel(header.header)} diff --git a/app/javascript/components/miq-data-table/miq-table-cell.jsx b/app/javascript/components/miq-data-table/miq-table-cell.jsx index 70ce994af6e..709c1e0d6fc 100644 --- a/app/javascript/components/miq-data-table/miq-table-cell.jsx +++ b/app/javascript/components/miq-data-table/miq-table-cell.jsx @@ -248,7 +248,7 @@ const MiqTableCell = ({ cellClick && onCellClick(row, CellAction.itemClick, event)} - className={classNames(showText ? '' : 'no_text', wrapClass ? 'vertical_align_top' : '')} + className={classNames(showText ? '' : 'no_text', wrapClass ? 'vertical_align_top' : '', cell.data.actionCell ? 'action-cell-holder' : '')} > {component} @@ -259,7 +259,9 @@ export default MiqTableCell; MiqTableCell.propTypes = { onCellClick: PropTypes.func, - row: PropTypes.shape({}), + row: PropTypes.shape({ + actionCell: PropTypes.bool, + }), cell: PropTypes.shape({ id: PropTypes.string, value: PropTypes.string, diff --git a/app/javascript/packs/component-definitions-common.js b/app/javascript/packs/component-definitions-common.js index 42171651cc1..5f44a3e43f3 100644 --- a/app/javascript/packs/component-definitions-common.js +++ b/app/javascript/packs/component-definitions-common.js @@ -11,6 +11,7 @@ import { Toolbar } from '../components/toolbar'; import ActionForm from '../components/action-form'; import AddRemoveHostAggregateForm from '../components/host-aggregate-form/add-remove-host-aggregate-form'; import AddRemoveSecurityGroupForm from '../components/vm-cloud-add-remove-security-group-form'; +import AeInlineMethod from '../components/AeInlineMethod'; import AggregateStatusCard from '../components/aggregate_status_card'; import AnsibleCredentialsForm from '../components/ansible-credentials-form'; import AnsiblePlayBookEditCatalogForm from '../components/ansible-playbook-edit-catalog-form'; @@ -186,6 +187,7 @@ ManageIQ.component.addReact('ActionForm', ActionForm); ManageIQ.component.addReact('AddRemoveHostAggregateForm', AddRemoveHostAggregateForm); ManageIQ.component.addReact('AddRemoveSecurityGroupForm', AddRemoveSecurityGroupForm); ManageIQ.component.addReact('AggregateStatusCard', AggregateStatusCard); +ManageIQ.component.addReact('AeInlineMethod', AeInlineMethod); ManageIQ.component.addReact('AnsibleCredentialsForm', AnsibleCredentialsForm); ManageIQ.component.addReact('AnsiblePlayBookEditCatalogForm', AnsiblePlayBookEditCatalogForm); ManageIQ.component.addReact('AnsiblePlaybookWorkflow', AnsiblePlaybookWorkflow); diff --git a/app/stylesheet/miq-data-table.scss b/app/stylesheet/miq-data-table.scss index 53a66b56761..2097fba9820 100644 --- a/app/stylesheet/miq-data-table.scss +++ b/app/stylesheet/miq-data-table.scss @@ -310,6 +310,10 @@ table.miq_preview { width: 100px !important; } +.action-cell-holder { + max-width: 150px !important; +} + .reconfigure-form { .reconfigure-sub-form { display: flex; diff --git a/app/views/miq_ae_class/_embedded_methods.html.haml b/app/views/miq_ae_class/_embedded_methods.html.haml index bfce9a82565..aae0a544bfd 100644 --- a/app/views/miq_ae_class/_embedded_methods.html.haml +++ b/app/views/miq_ae_class/_embedded_methods.html.haml @@ -3,33 +3,4 @@ = embedded_method_list(@record[:embedded_methods]) - else %hr - %h3 - = _('Embedded Methods') - #automate-inline-method-select{'ng-controller' => 'aeInlineMethodSelectionController as vm', 'ng-init' => "vm.selectable = {key: '^aem-(?!#{@selectable_methods}$)'};"} - .pull-right - %button.btn.btn-primary{:type => "button", - 'ng-click' => 'vm.openModal();', - :align => "left"}= _('Add Method') - .clearfix - :javascript - miq_bootstrap('#automate-inline-method-select'); - - if !@edit[:new][:embedded_methods].nil? && !@edit[:new][:embedded_methods].empty? - %table.table.table-striped.table-hover.table-condensed.table-bordered - %thead - %th= _('Path') - %th= _('Actions') - %tbody - - @edit[:new][:embedded_methods].try(:each_with_index) do |method, i| - %tr - %td - %text_field_tag - = _(method.to_s) - %td{:class => "action-cell"} - = link_to(_('Remove'), - {:action => "embedded_methods_remove", :id => i}, - {"data-miq_sparkle_on" => true, - "data-miq_sparkle_off" => true, - 'data-method' => :post, - :remote => true, - :class => "btn btn-default btn-block btn-sm", - :title => _("Click to delete this input field from method")}) + = react('AeInlineMethod', {:type => "aeInlineMethod", :selected => @embedded_methods}) diff --git a/config/routes.rb b/config/routes.rb index ee0a80aa46b..8f705708af1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1907,6 +1907,9 @@ explorer method_form_fields namespace + ae_domains + ae_methods + ae_method_operations show ], :post => %w[ diff --git a/package.json b/package.json index edcf5fec2e1..abdd7b155d9 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "react-codemirror2": "^6.0.0", "react-dom": "~16.13.1", "react-markdown": "6.0.0", + "react-query": "^3.39.3", "react-redux": "^7.1.1", "react-router": "~5.1.2", "react-router-dom": "~5.1.2", diff --git a/spec/config/routes.pending.yml b/spec/config/routes.pending.yml index 2ac9ca1ab9b..f89eba359f4 100644 --- a/spec/config/routes.pending.yml +++ b/spec/config/routes.pending.yml @@ -722,6 +722,9 @@ MiqActionController: - x_search_by_name - x_show MiqAeClassController: +- ae_domains +- ae_methods +- ae_method_operations - adv_search_button - adv_search_clear - adv_search_load_choice diff --git a/yarn.lock b/yarn.lock index 299ee640857..b98a1ec548e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1282,7 +1282,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.1, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.1, @babel/runtime@npm:^7.9.2": version: 7.24.7 resolution: "@babel/runtime@npm:7.24.7" dependencies: @@ -4137,6 +4137,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.16": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10/4bc6ae152a96edc9f95020f5fc66b13d26a9ad9a021225a9f0213f7e3dc44269f423aa8c42e19d6ac4a63bb2b22140b95d10be8f9ca7a6d9aa1b22b330d1f514 + languageName: node + linkType: hard + "big.js@npm:^3.1.3": version: 3.2.0 resolution: "big.js@npm:3.2.0" @@ -4393,6 +4400,22 @@ __metadata: languageName: node linkType: hard +"broadcast-channel@npm:^3.4.1": + version: 3.7.0 + resolution: "broadcast-channel@npm:3.7.0" + dependencies: + "@babel/runtime": "npm:^7.7.2" + detect-node: "npm:^2.1.0" + js-sha3: "npm:0.8.0" + microseconds: "npm:0.2.0" + nano-time: "npm:1.0.0" + oblivious-set: "npm:1.0.0" + rimraf: "npm:3.0.2" + unload: "npm:2.2.0" + checksum: 10/ccf6be63c5ed03965f00c28f2cc55028ca3d6eb6f47cb430cc7a5e1ed404c54601c32bc87db24d11f229c80201fd2e606f5c9683543875a7e26ca06e23079782 + languageName: node + linkType: hard + "brorand@npm:^1.0.1, brorand@npm:^1.1.0": version: 1.1.0 resolution: "brorand@npm:1.1.0" @@ -6632,7 +6655,7 @@ __metadata: languageName: node linkType: hard -"detect-node@npm:^2.0.4": +"detect-node@npm:^2.0.4, detect-node@npm:^2.1.0": version: 2.1.0 resolution: "detect-node@npm:2.1.0" checksum: 10/832184ec458353e41533ac9c622f16c19f7c02d8b10c303dfd3a756f56be93e903616c0bb2d4226183c9351c15fc0b3dba41a17a2308262afabcfa3776e6ae6e @@ -10801,6 +10824,13 @@ __metadata: languageName: node linkType: hard +"js-sha3@npm:0.8.0": + version: 0.8.0 + resolution: "js-sha3@npm:0.8.0" + checksum: 10/a49ac6d3a6bfd7091472a28ab82a94c7fb8544cc584ee1906486536ba1cb4073a166f8c7bb2b0565eade23c5b3a7b8f7816231e0309ab5c549b737632377a20c + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -11762,6 +11792,7 @@ __metadata: react-codemirror2: "npm:^6.0.0" react-dom: "npm:~16.13.1" react-markdown: "npm:6.0.0" + react-query: "npm:^3.39.3" react-redux: "npm:^7.1.1" react-router: "npm:~5.1.2" react-router-dom: "npm:~5.1.2" @@ -11826,6 +11857,16 @@ __metadata: languageName: node linkType: hard +"match-sorter@npm:^6.0.2": + version: 6.3.4 + resolution: "match-sorter@npm:6.3.4" + dependencies: + "@babel/runtime": "npm:^7.23.8" + remove-accents: "npm:0.5.0" + checksum: 10/80b6cb04415b68b32cc7ec1242cc125f95ca6a8739a787dbc5e8058a120aaf5cb4ff8f8467b6f9949a8d8a6430a44d64659894d26138a03a8c0dd6ba239d5519 + languageName: node + linkType: hard + "mathml-tag-names@npm:^2.1.3": version: 2.1.3 resolution: "mathml-tag-names@npm:2.1.3" @@ -12021,6 +12062,13 @@ __metadata: languageName: node linkType: hard +"microseconds@npm:0.2.0": + version: 0.2.0 + resolution: "microseconds@npm:0.2.0" + checksum: 10/22bfa8553f92c7d95afff6de0aeb2aecf750680d41b8c72b02098ccc5bbbb0a384380ff539292dbd3788f5dfc298682f9d38a2b4c101f5ee2c9471d53934c5fa + languageName: node + linkType: hard + "miller-rabin@npm:^4.0.0": version: 4.0.1 resolution: "miller-rabin@npm:4.0.1" @@ -12383,6 +12431,15 @@ __metadata: languageName: node linkType: hard +"nano-time@npm:1.0.0": + version: 1.0.0 + resolution: "nano-time@npm:1.0.0" + dependencies: + big-integer: "npm:^1.6.16" + checksum: 10/eef8548546cc1020625f8e44751a7263e9eddf0412a6a1a6c80a8d2be2ea7973622804a977cdfe796807b85b20ff6c8ba340e8dd20effcc7078193ed5edbb5d4 + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -12845,6 +12902,13 @@ __metadata: languageName: node linkType: hard +"oblivious-set@npm:1.0.0": + version: 1.0.0 + resolution: "oblivious-set@npm:1.0.0" + checksum: 10/f31740ea9c3a8242ad2324e4ebb9a35359fbc2e6e7131731a0fc1c8b7b1238eb07e4c8c631a38535243a7b8e3042b7e89f7dc2a95d2989afd6f80bd5793b0aab + languageName: node + linkType: hard + "obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -14755,6 +14819,24 @@ __metadata: languageName: node linkType: hard +"react-query@npm:^3.39.3": + version: 3.39.3 + resolution: "react-query@npm:3.39.3" + dependencies: + "@babel/runtime": "npm:^7.5.5" + broadcast-channel: "npm:^3.4.1" + match-sorter: "npm:^6.0.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 10/17dc4eb75d2ebc262b685096dfaa203202a883ac43768d0faf80f3ad4bdf791dff0691569d6e8ec2ac81b9377c5477e0a1ebea9f51bae1482ca154f5dde50d2d + languageName: node + linkType: hard + "react-redux@npm:^7.1.1": version: 7.2.9 resolution: "react-redux@npm:7.2.9" @@ -15215,6 +15297,13 @@ __metadata: languageName: node linkType: hard +"remove-accents@npm:0.5.0": + version: 0.5.0 + resolution: "remove-accents@npm:0.5.0" + checksum: 10/4aa1a9d0c18468515a33c6760b0f8e28dfbceddcb846fac90b2189445445b27b11cc1df9fbceb97b4449438bc13250d77b27d4ab325b2d69933acc156d6c5b50 + languageName: node + linkType: hard + "remove-trailing-separator@npm:^1.0.1": version: 1.1.0 resolution: "remove-trailing-separator@npm:1.1.0" @@ -15524,25 +15613,25 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^2.5.4, rimraf@npm:^2.6.3": - version: 2.7.1 - resolution: "rimraf@npm:2.7.1" +"rimraf@npm:3.0.2, rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" dependencies: glob: "npm:^7.1.3" bin: - rimraf: ./bin.js - checksum: 10/4586c296c736483e297da7cffd19475e4a3e41d07b1ae124aad5d687c79e4ffa716bdac8732ed1db942caf65271cee9dd39f8b639611de161a2753e2112ffe1d + rimraf: bin.js + checksum: 10/063ffaccaaaca2cfd0ef3beafb12d6a03dd7ff1260d752d62a6077b5dfff6ae81bea571f655bb6b589d366930ec1bdd285d40d560c0dae9b12f125e54eb743d5 languageName: node linkType: hard -"rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" +"rimraf@npm:^2.5.4, rimraf@npm:^2.6.3": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" dependencies: glob: "npm:^7.1.3" bin: - rimraf: bin.js - checksum: 10/063ffaccaaaca2cfd0ef3beafb12d6a03dd7ff1260d752d62a6077b5dfff6ae81bea571f655bb6b589d366930ec1bdd285d40d560c0dae9b12f125e54eb743d5 + rimraf: ./bin.js + checksum: 10/4586c296c736483e297da7cffd19475e4a3e41d07b1ae124aad5d687c79e4ffa716bdac8732ed1db942caf65271cee9dd39f8b639611de161a2753e2112ffe1d languageName: node linkType: hard @@ -17769,6 +17858,16 @@ __metadata: languageName: node linkType: hard +"unload@npm:2.2.0": + version: 2.2.0 + resolution: "unload@npm:2.2.0" + dependencies: + "@babel/runtime": "npm:^7.6.2" + detect-node: "npm:^2.0.4" + checksum: 10/382f676f24b774dc84beaf424326a227929ecad0ea0f319d4fd0812376b3306ea6d7ccf7ea85c6663ed7be552e9e004f429146bad8faf976b43084e29e265d10 + languageName: node + linkType: hard + "unpipe@npm:1.0.0, unpipe@npm:~1.0.0": version: 1.0.0 resolution: "unpipe@npm:1.0.0"