From 8f85f8c93952170b609ca803bf89560766489f8c Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 12:04:29 +0100 Subject: [PATCH 01/14] Distinguish the uninitialized state --- src/store/groups.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/store/groups.js b/src/store/groups.js index 7027b0918..cb76b6cc5 100644 --- a/src/store/groups.js +++ b/src/store/groups.js @@ -6,6 +6,7 @@ export const initialState = { loading: false, rejected: false, fulfilled: false, + uninitialized: true, error: null, data: null }; @@ -15,7 +16,8 @@ export default applyReducerHash( [ACTION_TYPES.GROUPS_PENDING]: (state) => { return { ...state, - loading: true + loading: true, + uninitialized: false }; }, [ACTION_TYPES.GROUPS_FULFILLED]: (state, { payload }) => { @@ -23,18 +25,19 @@ export default applyReducerHash( ...state, loading: false, rejected: false, + uninitialized: false, fulfilled: true, data: payload }; }, - [ACTION_TYPES.GROUPS_REJECTED]: (state, { payload: { code, status } }) => { + [ACTION_TYPES.GROUPS_REJECTED]: (state, { payload }) => { return { ...state, loading: false, rejected: true, + uninitialized: false, fulfilled: false, - code, - status + error: payload }; } }, From 46580af10e2dac79165e7786eee5fbae188f861e Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 12:05:15 +0100 Subject: [PATCH 02/14] Support search and pagination in getGroups --- src/components/InventoryGroups/utils/api.js | 22 ++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/InventoryGroups/utils/api.js b/src/components/InventoryGroups/utils/api.js index ab7146bdd..d3c3eb504 100644 --- a/src/components/InventoryGroups/utils/api.js +++ b/src/components/InventoryGroups/utils/api.js @@ -1,7 +1,23 @@ import { instance } from '@redhat-cloud-services/frontend-components-utilities/interceptors/interceptors'; import { INVENTORY_API_BASE } from '../../../api'; +import PropTypes from 'prop-types'; -export const getGroups = () => { - // TODO: support parameters - return instance.get(`${INVENTORY_API_BASE}/groups`); +export const getGroups = (search = {}, pagination = { page: 1, perPage: 20 }) => { + const parameters = new URLSearchParams({ + ...search, + ...pagination + }).toString(); + + return instance.get(`${INVENTORY_API_BASE}/groups?${parameters}` /* , { headers: { Prefer: 'code=404' } } */); +}; + +getGroups.propTypes = { + search: PropTypes.shape({ + // eslint-disable-next-line camelcase + hostname_or_id: PropTypes.string + }), + pagination: PropTypes.shape({ + perPage: PropTypes.number, + page: PropTypes.number + }) }; From 40b84553775f32b982bef2903f17e83633a23d26 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 17:01:43 +0100 Subject: [PATCH 03/14] Add the groups list table --- config/cypress.webpack.config.js | 5 + cypress/fixtures/groups.json | 946 ++++++++++++++++++ cypress/support/commands.js | 21 +- cypress/support/component.js | 2 + src/components/GroupsTable/GroupsTable.cy.js | 238 +++++ src/components/GroupsTable/GroupsTable.js | 209 ++++ src/components/GroupsTable/index.js | 3 + .../InventoryGroups/InventoryGroups.js | 55 + src/components/InventoryGroups/index.js | 20 +- src/components/InventoryGroups/utils/api.js | 3 +- .../InventoryTable/NoSystemsTable.js | 14 +- src/components/InventoryTable/helpers.js | 6 + src/constants.js | 2 + 13 files changed, 1501 insertions(+), 23 deletions(-) create mode 100644 cypress/fixtures/groups.json create mode 100644 src/components/GroupsTable/GroupsTable.cy.js create mode 100644 src/components/GroupsTable/GroupsTable.js create mode 100644 src/components/GroupsTable/index.js create mode 100644 src/components/InventoryGroups/InventoryGroups.js diff --git a/config/cypress.webpack.config.js b/config/cypress.webpack.config.js index aa422d513..041f7a307 100644 --- a/config/cypress.webpack.config.js +++ b/config/cypress.webpack.config.js @@ -1,11 +1,16 @@ /* global module, __dirname */ const { resolve } = require('path'); +const webpack = require('webpack'); const config = require('@redhat-cloud-services/frontend-components-config'); const { config: webpackConfig, plugins } = config({ rootFolder: resolve(__dirname, '../') }); +plugins.push(new webpack.DefinePlugin({ + IS_DEV: true +})); + module.exports = { ...webpackConfig, plugins diff --git a/cypress/fixtures/groups.json b/cypress/fixtures/groups.json new file mode 100644 index 000000000..d909f060e --- /dev/null +++ b/cypress/fixtures/groups.json @@ -0,0 +1,946 @@ +{ + "count": 50, + "page": 1, + "per_page": 50, + "results": [ + { + "host_ids": null, + "updated_at": "2017-11-13T00:00:00.0Z", + "id": "bBEbFFB0D339fA46eD81cCA301d85AAF", + "created_at": "1991-03-22T23:00:00.0Z", + "name": "Ut occaecat" + }, + { + "host_ids": [ + "EAA29FDF-AF5F-4C19-8C36-F335fEec7618", + "91A7549e34766DEE652D338e21DcC8cD", + "fC2d75d3-48F9-EDaE-B61a-afFfb37eFaB8", + "Fb94F4cB-1bEB-D5B8-1d8e-729ff0965b7C", + "e7dAb595F4d0ABdAC7A10A9C7729bddE", + "6CccD3E1-7A0E-5cd8-B406-2EbDFc6d0B98", + "Eb1d77dC29FFD3c59bDcdb8b7e0d0E6b", + "6e5Aaf25-CB56-b0CC-D2DD-bDb7FbE31A98", + "818FEC0B-E25D-ee97-faaB-6f710fE5108D", + "Ba2DA694-4B09-ad9e-1Ee5-8EfD13Dbda7f", + "7c7AfEfA2Ac998A8b36Fd1acEC6aA2A4", + "CfACb9Cc-5baC-cAE4-3DeB-a2f7c2bf9BF0", + "d7BBcA4E-Ceba-CD3F-7B5c-54485848C6EC", + "c247ad09-8Cba-f88c-4EEF-8f7Aa663349B", + "4Bd5d3eb5Adc3F834a5cd98C8B2Be051", + "9EF91A57f45E2CbDc21F22bbcDf6d413", + "BA82eE6C16E8A4DE0ce5335cb4BCd22c", + "3D000c2fbDF1DABdcDEECd1Ee88255af", + "7b9C7689-028d-55e5-7b5f-a9dADe31E19c", + "E57EA1c1-D46a-BEdb-A281-dD5a6cAfeA1d" + ], + "id": "F66A94AC4E6d1B59F67cDf893D031F8e", + "name": "velit in proident in", + "updated_at": "1978-11-13T00:00:00.0Z", + "created_at": "1966-12-21T00:00:00.0Z" + }, + { + "host_ids": null, + "updated_at": "2012-07-06T22:00:00.0Z", + "id": "B9bcBF737A9BA20482f9bc9f5ACcbe04", + "created_at": "1944-12-04T23:00:00.0Z", + "name": "dolor labore dolore commodo" + }, + { + "updated_at": "1997-03-05T23:00:00.0Z", + "name": "deserunt laborum ipsum commodo qui", + "id": "9Ff0bf0A-a72a-0A3a-349d-F6cc428AfE53", + "created_at": "1994-08-09T22:00:00.0Z", + "host_ids": [ + "AF2AbE7Bc0bF0aAe76bD04c8DCd0eE63", + "970c3CBf4DF6C8BE1283907c7678Ae52", + "0e8CAabC1aC8e61f9Dc5AFEbcb19Fa0d", + "C46fB39D-87FD-e3EB-329B-Ff4b06a1bF3A", + "ed964532382A75d4119cBf466DbB571B", + "A9400605e2306ffAF8c7Dbff7B45bC0e", + "f8eFB9d2-a1Eb-2Fc2-b1e9-03e63AEE4b05", + "EFfb5b49-dECA-bcc9-2BC2-553606Ba0Ca5", + "BCdCbDca0d3188Df1bBb9A981AFC915E", + "4B78Eda1cAd170CA5efDF21cc291Cd14", + "c0ededA5c1C32a1d8cf08FD3884CE0A7", + "34384d84-Fa60-3807-305C-4F75BBAFe4E6", + "2Dad2cC0-8FcE-F74C-3c10-21ccae4E28fe", + "cDe00EC9-4b6a-e7e9-F5E7-77bfeBC287f1", + "EeE7fD5EcF3333E9c30AEecC43e9bd55", + "A7DCDBbE-a686-33BA-003E-6bAFcfa1D7Da", + "5F63E9aD2eDF0aA46cEf22fBeA5AD87d", + "e7ac9FEa61E7E6EC8cDFA86dD578aA75", + "FaC2b343-0Aee-43B9-aE03-eC8985caD19A", + "2Ec74fBb7DA36bD2e8883aF98DE2DbFc" + ] + }, + { + "created_at": "1963-08-01T23:00:00.0Z", + "updated_at": "1993-02-23T23:00:00.0Z", + "name": "qui aliqua Ut", + "host_ids": [ + "73DDebdDb56cCDaa0aB3921EDefB0949", + "52DaA88bcFe56D97F5af645D12b20F00", + "EEAf1b8ec218f04a10a1e5d594cB3eef", + "8E373c13-7CAa-B53D-CB14-eac50f9D27Aa", + "ECd5C0d1-BA6d-b69a-BeC2-bbD56FBc0ffD", + "c8E1B3f0-4f8e-Eff5-Be0a-4994Dc38202B", + "CCce488D-4bB8-634f-Ca67-2BdE84806Ac9", + "aC5ee3A057682aECF4A2ed7bef9C1BCd", + "0f00f1AA-F0C4-bCf7-8a47-C5b304b0d23D", + "798cc5Fdc9B8e430089b39d128DdB853", + "C3F48B91-1605-bBFB-B0db-fEe085304b9B", + "DCC2cbFabA0d1cD0cdeecd902cfeE79c", + "1Aa39bDcc28DA7d443cBa5BcE6E809e2", + "4dcCe33D-cB3F-efBe-78fb-405ad1d0cABE", + "A00ddE89-CBed-CBB3-b82A-c432D1AaB5ab", + "2C8BcfD4eDB1326Ae01BA309d7Ce58Aa", + "CB7E7CFA-9f0E-0fBd-4Db5-E7a30A65E5d0", + "F43b3EfCa46AbF7b70B3BAe7Ab8dA9EA", + "0ad6DFbD45cdA62ADDf6Ba2C2B45BaB8", + "57047ce6-bF9a-FD07-47FB-5B970d94a7CB" + ], + "id": "023AC4Bc-bCBc-6A31-5ef9-6C813BC47F5f" + }, + { + "host_ids": null, + "id": "Ad2dDeE2-a922-b815-b40C-Ba6bDa5C83Ad", + "created_at": "2004-02-28T23:00:00.0Z", + "name": "proident", + "updated_at": "2005-07-10T22:00:00.0Z" + }, + { + "name": "nisi adipisicing", + "created_at": "1955-09-21T23:00:00.0Z", + "host_ids": null, + "updated_at": "1974-09-14T23:00:00.0Z", + "id": "bf5a0Fbd-85c2-2Eff-D30A-0597Bb87c5AC" + }, + { + "id": "DD1ea62A-dBbB-09EC-acc1-45DfA0af4e1A", + "host_ids": null, + "name": "sit ullamco eu", + "created_at": "2006-10-19T00:00:00.0Z", + "updated_at": "1980-08-31T22:00:00.0Z" + }, + { + "name": "aliqua", + "host_ids": [ + "d963018f-ae5f-65C7-EfD6-fc49B66F3898", + "A506f443cB1de5Cfd0B42F2556751415", + "0BDaDCf1A286363F3ef98BcaC0Cbbf73", + "Fbe63F42-8DFc-DaDa-9fD9-f6ff80f9Dce2", + "6ffEA86d-EDFF-EE3b-f08E-E8fa6e593Bfd", + "B415d43DEedC3dEC9241c6FEd504cE61", + "E6AC740eEfA18FeFdF43BFe7F1CA1D6E", + "BaCFC5B7fc3bA96d145CCFa06B3AbfdF", + "Ff8aBccA-a430-b518-0Bdb-3D8FFDeeF30D", + "1E2d8bb8-bBc9-5dFB-2bfa-BA1D10f709cB", + "AfF6D4FF-AF0F-205c-bdD5-eCdAdBB99b9E", + "2Fa1E6B3-1E5A-e56a-558C-bacaE69f6Ef7", + "FDbDFB66bD8dA8FE7dB8ad4Dc4DEC88f", + "3cD2DF33-5D01-0859-51f2-2Bd39dF41c3a", + "aa61ead1-C5Bd-4aB4-89B5-daf63C3B41aa", + "29AFC1E0-cEDc-5589-C0fB-9F5B5059e634", + "FE70B4BF80eaE2E2a7D0B0d9F1185DFc", + "97DBbcCFaeDCa6AC6bcff8daCD1cD8BD", + "5B3502D7-ED80-51A5-F163-E5635EbF2c32", + "15CE3Da8-C0FC-68CE-eeAd-8ab936E66a22" + ], + "id": "fE5E884e-ad0A-6a1B-0bC6-FCAFCBeB82fe", + "created_at": "2018-12-07T23:00:00.0Z", + "updated_at": "1993-10-22T00:00:00.0Z" + }, + { + "host_ids": [ + "AdDb3Ec3ACfdb23e1bf70ffA8bCEA51f", + "Ae26CaeB90cbc4A2f72fB1E0da7f4B40", + "E2F9A4A3-EEaD-8F25-7e42-cA828fB38cab", + "e3Fd49A3-5cDD-d7b9-bEa3-1e8efcBa91Ac", + "AbbBA6eeab66ff70B67BA8885Af0B7b5", + "DeEfA6A3cAd3FC9AdaC20938aC03Fb14", + "baFB901c8BA36801F23F7d2A2d2BE4Af", + "7F1cc7De-acCd-950b-7dE6-e147ecdc2bDD", + "2e5003897ecDE8Ef562f65d99d8bCfdf", + "4dEdcf80Ac53fcFAc5C581C01c5c8ED2", + "E8b12Cb2-d033-6Bb8-c4dC-Db53feB8454A", + "F3a1FdBF4CEF80bF7a1d6302AEEf3eD2", + "2AF6Fc67-fb6b-17AD-4c2D-2aAebF504BAb", + "45D30054-2b3E-adcE-bA22-dF45de2EDc7C", + "Be02fab8-c5F7-EfFb-FF80-4837DA4d6dff", + "77cce23eBd6504dC6727d1ccaa4c7B9B", + "8EFbd122-0aed-93b0-52fd-2D0f3Ddd94De", + "daE8b5E7-0bab-A9ca-eCdA-AdfAB5D4CD6C", + "78C21852fdBF0ea6dCEBACcfcfBC4158", + "B8561A70-bCB1-8eDB-2f23-8D565eF321D3" + ], + "name": "conse", + "created_at": "1982-11-13T00:00:00.0Z", + "updated_at": "1990-08-06T22:00:00.0Z", + "id": "4aeacA7BA9d4A6a44cFB72822CF59cfc" + }, + { + "name": "dolore ", + "host_ids": null, + "id": "bd518cCc5dFE2D79ce0d8adD1cA5aAfE", + "created_at": "2004-08-22T22:00:00.0Z", + "updated_at": "1998-10-30T00:00:00.0Z" + }, + { + "name": "ipsum exercitation Excepteur voluptate officia", + "created_at": "1979-09-30T23:00:00.0Z", + "host_ids": [ + "aEaEd1Ea11cdBFE61f9dBFc9590C40aa", + "B8D0EDDdB2A0511BacF39Fea32CbdA84", + "da0bBfFB-0eb1-C9dd-CbA0-2Eb7B1B3c8Ab", + "d6635f91dB210316Eba77e6F1Fedb1bE", + "9d2E8fcc9e6C9a2F1f2EA65c020FC6c4", + "2cb7BE14-E258-d7D6-5F46-BEAF5C0F1ABa", + "934C972aE7215b088d321ca4aEc49B0B", + "F9fce675-F58B-f1Bd-a66C-6Ab2DCeCc39D", + "DDCE227E-8F02-71F2-186a-e5cE752abe45", + "560ACCaFE9F7b4Aef4f86D89eF835bEa", + "fBf8EBed-0E2d-DBE5-ae27-58e7c7F31e5D", + "7b9951099afDafD50FcFeBfbCa8ecE3B", + "9CAf5db6F7aAdC0bE7dCf1e0AAd72fdA", + "BD6Acda7-acDF-ffcc-2CA7-84D3a56C191d", + "79cC5ADd6c3B3cfB6Ec54E63B5fBff78", + "faB3dcd8fe1f8ebfBd87d4b2c04ED819", + "2208EEB76dB65Ce3d189d86adAaa24C0", + "d37B7ABc-C26d-EAeF-5f1B-D20e25cfA4Bb", + "bfeCCF5bcfa4BA6fec2ddbfBe9F5C40d", + "bF3cCaaBCdEd94Fd86dE12441dBbfFCF" + ], + "updated_at": "1988-02-16T23:00:00.0Z", + "id": "4aC8E5B1-65aF-22Ae-D9dc-E1D4DFBb62EE" + }, + { + "id": "69081dF1-eedF-52fc-9Dc1-DB0D3c1dFb2C", + "updated_at": "1998-06-24T22:00:00.0Z", + "host_ids": [ + "0F8789823040cfDDaEcc7AFf8a73aA81", + "C7FddA3fc6eA5D1cf06Fab7BDC3ff0fb", + "8aad1C55Aac3fEc30eCdB66dF8Ca691E", + "D7dBBd8F-f035-d45C-3DaD-d742e339734E", + "fED05d6C-1731-df78-4af9-F1E6bFCFb67E", + "a4aBbEc5-96dc-9437-a17B-245A9f5b46e3", + "Ef156edfb40dEDBfc34DAF9d97db4d0B", + "Dda7A1cd-a130-E93b-d8aC-79feC2abC4f0", + "3D1A8BfE-EaF9-135D-eEf6-410BC8Ce6D8B", + "AdA9cD5E-F008-9Eec-bEEB-4FA8b4E4BfC7", + "F7b64ab0EDe3a9F0E08Ade56eE77B2aD", + "9D96A4ed3AD672e1C49BEf07fFABD4A3", + "DD7b9a03ADCd90B9118A7FF3E0f75799", + "a9Bce3CC-9F06-5299-AA9f-Aa2a91CdF59C", + "a9C80DaB1CaFabA5a8F04565eBFDd2cC", + "cc526Fe4-1ffc-4C73-ECc4-38bFF64fFF5d", + "9c7d277D-AE26-9DBB-cd6E-5Ae1eEfE1952", + "7568bc15366fCFC943dCdFd5422C18E3", + "4f4ad2a5-072B-caba-beE1-1BdaBae4790F", + "e648cBdF-8124-DE4a-5cD9-e52178fECBf9" + ], + "created_at": "1993-06-21T22:00:00.0Z", + "name": "ut sint eu aute" + }, + { + "host_ids": null, + "id": "95943c18-A6AE-c85A-6CcE-cFA5BEFDCddB", + "created_at": "1996-11-24T00:00:00.0Z", + "updated_at": "1952-12-20T00:00:00.0Z", + "name": "tempor Excepteur exercitation non cillum" + }, + { + "created_at": "2014-05-17T22:00:00.0Z", + "id": "8b7Eace8-3cA1-7F5A-6f2C-FaE1baE9D05a", + "host_ids": null, + "name": "quis est ut", + "updated_at": "1999-05-05T22:00:00.0Z" + }, + { + "id": "adB66Deb-139e-AC2C-FDbf-6B1B15beebcE", + "updated_at": "1979-12-03T23:00:00.0Z", + "created_at": "1954-04-22T23:00:00.0Z", + "host_ids": null, + "name": "minim consequat" + }, + { + "host_ids": [ + "dB9860f2-Ad9E-a562-d1fd-2CA738b3c9B1", + "b74ddE2Ae9DCea314B7aD8dB6181C310", + "8bc2eCF2-Ad5e-Ae20-AAc7-DbDA2d8E939a", + "D92ef0c9CAd2AbBF68Dcad7ec0E0c5Fa", + "8079b312-c08B-1774-1Aaf-A6e9678DcbA8", + "DcBB29F061d88EF4894fB09A6DbbB8d9", + "E18c0F3B-bB7a-1DeA-2Bca-0fAe862CD7Ba", + "De1Cf6Ed6F1d75a0AcbbC4Fc27a69Fdf", + "a7574e43-cdc3-e307-a426-15baF3C09e53", + "51b8A08f6F6c7Bad18A9AfEa3c3E7DBC", + "94Abdb9B-Ac7B-b24e-DfEf-1cC9D92aAAac", + "dE306FB8B14A77b8BbD5Fe260D6425cE", + "aC262aEd-05BF-28aa-A052-73C0A66DD3c3", + "ebeEf833-38bD-E2Fc-D4fb-a6EA5D3c78D2", + "bbad9860E7C14d0E075bFB448Cc2Dace", + "b698800eec4a57499e4Be5798752F617", + "8a2c04ae7dcDee4c44590Dd846e70C06", + "cB5D6Fca-a7Ee-C4a4-dCd0-AD29Fa72Eba2", + "f44BDffbbB5db340D751DC3E06d8CC81", + "7A1cd92b9b42Fe5BAcF4eB2d31e3bAE6" + ], + "id": "ED5C54d9df6E5b2A4c7bDBDCaF8e5072", + "updated_at": "2006-11-08T23:00:00.0Z", + "created_at": "1984-03-30T22:00:00.0Z", + "name": "mollit eiusmod consectetur in" + }, + { + "updated_at": "2005-12-23T00:00:00.0Z", + "created_at": "1994-01-10T23:00:00.0Z", + "host_ids": [ + "C1bA82bb9c94afbFE1eeaFBBdcaFc5C0", + "dEbdcBb68BCDD4bCec0F931A4aE574BA", + "dE7467Da7aa199d005f6b8aaE648631c", + "ddbEe621-BC07-7c09-Bb31-8EEDcd7c6999", + "C02EefEC3bD660ff4B1C483eB4dC3ceA", + "6B91ee2f1EA57dA85DbdeDD6bc19B39C", + "7cfcfA6Ae3EcB57d8D2Cecd2b7886CD0", + "f0de3fD350289C76eAaF53de6Dac4BD2", + "bF01B215-dcbA-E6eA-E4A2-16f519B9Bbd0", + "5f5C331b-Ce31-FF64-C04D-9DEb7caFd70B", + "43663DE6eDFfBd503FB91B72f064450c", + "f8EdEf2a-4a0F-999C-8EaE-7EF4Ae0cfA48", + "3c9f50d0-Db75-abbb-1EE1-e7bAa9A9affB", + "dfdcFBaf859DeDC897504ffB6a496Cd8", + "f8ec1D4Abb84aAaD0A4AE75c8Eacc7F1", + "7B53cd3b-eefb-DccF-2ac1-fcCaEF87c782", + "c07ec0228DEbdbDdFc1852B8e92A9d6d", + "A36F0f84-def9-43fB-1eF9-A6a1cBCaD8Fc", + "e7A88263c025B6fEFBb3bCEBDdddec1b", + "DFFf70E81AD5B1CFCfeeFe2e0D44afc8" + ], + "name": "incididunt Ut est in", + "id": "AfdB24EE8aCfF26B5B8019eDC0bacbDd" + }, + { + "updated_at": "1974-05-27T23:00:00.0Z", + "id": "AbfCAcA210bfe502F826B5a1d97826DE", + "created_at": "1971-07-27T23:00:00.0Z", + "name": "Ut", + "host_ids": [ + "EAFcb4c636fe2f75C1A7E6ddCC65266F", + "Cd8B26d67e33Fe79bDFeeC4D81a6F9e7", + "7c7ACBDf-0EdA-EEfa-35ce-5FeC1e8963eA", + "eea13d6FfaF2F35E5dDAFE8beb0Cc8f0", + "CBf3dFecBC6eafaA0A18fA9248746AdC", + "2D209aDb-cEeb-D8Ee-68d5-EE9e1AC34CA1", + "BeEdd224-Fab2-e73C-ac7B-BfabDDF3544b", + "D32F5BBf-A425-bD60-ce7F-45bcf84B4f78", + "BD04AF2E-b7c4-650b-5Cff-bA9e9c5ef8aE", + "F1Acf68C-e87b-65E7-fdC2-063A67eCCDbD", + "1eda816C-d2ed-966a-b4CA-d79f78742501", + "FF3fEAB4-595c-A56C-A6E7-87e6Bb831FA5", + "e8E6fEcD13964efE06488A7CCAacBCbd", + "4bDE30966e239EdF6cBAa2bEe706410C", + "b5820Afb-95A9-463C-9C8e-3bFFBDef276B", + "eFdB4063DE833D312C83efaBcfaF7e3B", + "3Ce7F1b1E594BB008cEF1be0f3706BFE", + "50B362D93a5672B767CdA5b1bA1BB9Ba", + "2fEEc9bEf70fcCE19cBEb60EDbc8B8fe", + "CFE8A3ec-56Dd-F9d1-Be8F-1143ba51441f" + ] + }, + { + "id": "b13CEdDdff211ABbE8D8D8DdEBfa0C2e", + "updated_at": "1963-11-17T00:00:00.0Z", + "host_ids": null, + "name": "sint eiusmod nisi aliqua Excepteur", + "created_at": "1964-10-11T00:00:00.0Z" + }, + { + "created_at": "1992-04-11T22:00:00.0Z", + "updated_at": "2017-05-05T22:00:00.0Z", + "id": "c1795fe2-D4ec-e9de-AaAD-cea8ccEBBb43", + "name": "in sunt ea", + "host_ids": [ + "Ae671A61ECfe4b8aB818095f5efc4cdd", + "d58A4F974EBBCa8aa5FEc0CF0EbeCCB2", + "ae36005d-33e5-B80d-a5FE-9AC908a8DB78", + "bd1b5A2e-24FC-FeD1-8BF1-4Faa887Baf4d", + "dB44396Dbbd4a014E4ca32BA81E4Ce6E", + "fE632763-7A59-dacF-c44e-c824955b1b21", + "a7adcaf6-9bF5-1fa1-d05F-D72aD4D1af60", + "4dcED17ca7A62AA827e0f496Af7dA63c", + "bD60509Ee111E12AC4DE8f2124C19a94", + "e3149625-1adF-947c-38dd-C6aF022a96bc", + "d1C8d0cF-4Dd7-5C1C-1D2e-4A1ffE28EFB3", + "6feA8839-74e7-9E30-a377-F0a9AeA9a9C8", + "fd5A05D9-efC5-FF69-0D63-157377ACf7eE", + "e65daC9d-a01E-aD9B-D66C-dcFb3485DbD1", + "C0C61F6a80A3CeedeA3ab6EE1BA81ede", + "AA3D15c3CdAbc1f9a4Fb87CA36bEb09d", + "DaADc917-A4E4-a3Cf-8E2a-b4BdcAC5475C", + "dAE8dDbd-cd23-5be5-9b7a-e8C3daCDd853", + "D7CBc0ED-B20f-EBb6-81aD-9Ab337abD97C", + "6aeAf8ab-5276-BF63-61BA-8b6bd09A9Dc7" + ] + }, + { + "id": "35EfbDdCd8934E5dDBCfbEacdBAEa6fB", + "created_at": "1969-01-08T23:00:00.0Z", + "name": "ad qui incididunt anim ex", + "updated_at": "1993-08-18T22:00:00.0Z", + "host_ids": null + }, + { + "host_ids": [ + "FACD2Eb5-dca1-1e70-eCB1-590E9793f0B9", + "58F386aAD52bEDcDEdc70dEd2B0bE09c", + "dd0CF835-7843-c52B-2a8E-ecfCc9BdA0cD", + "19b0e998-71FA-C732-dC0b-2A61dbb090A2", + "c31Ba6CA0b6f61D73c79354c622e3daf", + "F997d7c7F950E8aEcDEbe5fA51d47522", + "e82ceC83564cb7AcfaA002abD9B4E8cf", + "B8aE38bF-A5Ac-aCC9-3C7f-e985CA5F8e33", + "46B19CfC-f3c4-aBed-1d29-9F519A2DadAF", + "E4A85Ea8-f203-eDBE-ECE2-fCb35aD777d5", + "fFfdbca2-6bc9-eCaA-AEE4-3badB3bba65e", + "aF4d6715-6D02-eDAB-efcC-c6518E848C8f", + "6D7B4CefeB6C35C2d1e90B6Af2c7b0bE", + "34f5Dc1e-6A11-111C-D9cF-ACddfA80aa6f", + "8D2Cc6E5acAFc99EDc1d1337e47fA3Cc", + "e3b063Dd-295c-4123-BE1F-A51DB11530D7", + "78D1ed06f8FB391A647e43E4D6EeBFA5", + "BE89Ff65-4b27-5feE-1Cdf-C7Bc0Ae5F27d", + "7451cC14D0ce04aDD84A1aFFd9eFAb82", + "7e0ea740-2DeB-374e-25Fb-fe493F1Df2BE" + ], + "created_at": "1966-05-12T23:00:00.0Z", + "name": "commodo Duis", + "updated_at": "1974-08-23T23:00:00.0Z", + "id": "D4bcFfeCDdFCb9D758Ddd55Bec5FFffd" + }, + { + "created_at": "1974-08-30T23:00:00.0Z", + "host_ids": [ + "A88b2baB-68a2-7AeA-bf12-2E51dFa9b876", + "aFADdC20-e7c3-99E2-8Fb6-1cf35eC5B0FF", + "8B0b06a8-11E5-EFB8-bDf6-A50eDdAdFaBe", + "69A7Ad05-5bb6-1515-ba96-ebcAaFC3caF8", + "F137B92f-0EEa-6Cda-E3Ab-3aC0Af58bCDd", + "61C36DF3-7eec-ebcD-8d86-dbECadb21B8c", + "9F8EF67fB0be51745a83F4b0f516a3fd", + "BCcD2f5A3FF4eB623Fba6DB0d52Fcfef", + "D3b83DaCAa0Ecf5DFD378c71Eab54C8D", + "3CeF60fe-592F-Fa42-2a7a-a5AB3D2798eA", + "6dFeF6bA502063e6F066BC0AcE63CCCE", + "5a42dE2f419Ab55CE3CEAc5DdA68BAB8", + "F2dC1ddB-9E8F-f006-aBDB-0657fCe5FFE0", + "95F0a5C7b3A87790766d56D44C0Db61A", + "DE998ccE-e13e-C38E-13f6-aaf5a0a5cACf", + "6ba717eE6e8575C2ab6AacFebd1fE2BA", + "cF2ce58c-Ff5B-802e-bFC3-0b5cD2A8aDa6", + "e2134372-864F-C3F3-c14F-ba1ecc4aaf74", + "3D677f0d-6e87-1489-C82f-6D1fcdDCfDe8", + "a69CE7309cf26825Fd9F5aBD0Ee6ab02" + ], + "id": "8F60a7c4-557E-eE3c-eF3B-5dfAECA896AF", + "name": "proident aute sed voluptate Excepteur", + "updated_at": "1980-05-10T22:00:00.0Z" + }, + { + "host_ids": [ + "2D1eEECBFCbe3CBA6efdD3aaab61aEc1", + "fedabba3-8F61-61fD-0D3d-FE4d37F498F1", + "25E7F7d467D7BE42Bf84CF62D2C34E9a", + "dCE6c0ce-8E4d-1cdB-9B9F-E23832791266", + "F53d2AFBc44dd682D5100FCc3b67fcbb", + "BecE41ad-f3eB-Eed6-CFA2-3f98Fa9E0Ea2", + "d4acCAcB754DCb3bABD2A2792d840ca9", + "1aacBfEdd7beBB3dBD65640FBB0A8012", + "fEBD80F60216ba3ff60779F379E8d252", + "2D3fF7DD-Cec5-d0AE-391d-AEe22579b8e4", + "1aB9AD77D55aD0923995Cd2a252E186A", + "ff6C968c-BF27-F5Aa-9af0-Fca5adDA0e12", + "b5FDe7684eBabCeA671b8136BcE0CC35", + "DB70efcB-759d-4314-EDBa-eEC9AFe71d2c", + "Fc3E4785-a5EA-B6cc-bE5A-ce779e47a5b8", + "bc6dedEFcA05f54BAACbe24ee7539B12", + "7cB1B8da-E3b3-e963-35D7-6c140252341C", + "be464E4e-F175-6805-EfdF-B2deD3CFede2", + "fA9EbBCec4cb5dCC25Cbd9a0EeCCad87", + "5910AC34-4C2E-B6c5-0e3F-3BDc43EC9cBB" + ], + "name": "nulla amet minim reprehenderit", + "created_at": "1953-02-12T23:00:00.0Z", + "updated_at": "1943-05-20T22:00:00.0Z", + "id": "8CEE49aC-E55B-74cE-97Fb-c183DdFa75D1" + }, + { + "updated_at": "1945-03-22T23:00:00.0Z", + "id": "f7b6c3ceE8BCB68aCFFC6bed33c9B278", + "name": "nulla", + "created_at": "1944-05-14T22:00:00.0Z", + "host_ids": null + }, + { + "name": "consectetur dolore", + "id": "d4f2b78ea0332e1eae26fCcbfac737Aa", + "host_ids": [ + "E7222C6D767F58B24aCF45B6ce6B03F1", + "5fcEAA207815bFf0D9DCD3f61eed8A3e", + "0f00aD8a-9BdF-8caE-EFD5-08a6C32bda43", + "9E39d30B-62D1-9EDD-ef15-57F9e93374ad", + "a5638826-F8a9-Fabb-fd26-Af6AE7be32aF", + "4e2B92bcbaAcab45af30c33C101cff8F", + "5831FCd1-8EE3-fC43-4F5a-2cf65eCFF558", + "FBB81F0d-caDA-AE2c-Ed21-842aDcA800aA", + "28A1aBFa-ABAB-172e-E9CF-F42A0dcb6e13", + "cfE7bAeA-eC4F-074A-E9fd-cf05eCb43A7C", + "Cc6cedcf2Fe513c73cc8fAeA610aD8E3", + "dcDD25BD-a0B7-c1Cd-11F7-eE33788Adc9a", + "a58a65Ed-fdB3-d7af-8Ff7-A6588F73BDb0", + "D7Ea5B3c-5d7A-aDB4-C65d-F76e28b3bf29", + "e45bc54f9DfccdFfcC0b4eED9F9ec8DD", + "3E6ACCBeAFF9Df39cEfFee70CdeE1fD3", + "c7761e33-6be8-be7A-E9D7-E3CC9ae49Dfb", + "a155EA39BE6640d60CCdd1d5e4e8fcd1", + "D025B4F5-9e3e-c9e8-4ECE-A52285d0097E", + "4adD5Ea4-1df2-1A3C-0F03-3ABa2Bc3eCCc" + ], + "created_at": "2015-12-18T00:00:00.0Z", + "updated_at": "1965-06-26T23:00:00.0Z" + }, + { + "host_ids": null, + "name": "mollit ", + "created_at": "1979-04-02T22:00:00.0Z", + "id": "B2BBFa8FB8DbfddDeba9dEf7B3fABd26", + "updated_at": "2001-01-09T23:00:00.0Z" + }, + { + "name": "enim q", + "created_at": "1961-03-06T23:00:00.0Z", + "id": "B1F7fe93-c6bC-15e8-c2FC-3F6b3c1FDeab", + "host_ids": [ + "aEBFBfCdbe7F0341cBE336FfcF797c5d", + "F2f480de-a13c-717d-b6Cf-B85F3FCD8fAa", + "77D4dEA8-fbE9-DCe9-E4Aa-1bfC26AbDC5A", + "96E990b2-FC0B-352C-0ECf-6cAF907B9B6f", + "f2114Dd1-ffBd-18b6-39dA-a0cc9fe7dB5F", + "aE2dcA6fdFFB3F33272Ffef2702d56cf", + "82214908-AdCD-fee6-BB9E-1d8dCB235B81", + "0bfb37860eAAF6095bF4Ec4ed02cB137", + "c334667D-20eD-4AE9-8EEF-8b7b7F46b465", + "fda8C3cE0CF119ED18BDDc6af7Db2F15", + "C37BdC89-7ee9-Ba31-1bF6-1a8fEFb9DeAa", + "DFcCfFd608fAF0E1a9bcfc5cC84647Ad", + "8cd7BebD-CC6d-7813-19E1-B815a1bF2bDf", + "b7dEff9dF9df13FB1236fB702164cCFE", + "7B59aaA9-1Ef0-20c8-5fAC-e72e3C7B2F3d", + "dEB7ECC2-a26B-F29f-369C-4aE75519E56d", + "dA0afA7f-05Fe-dD7E-AfF1-eE02A6B71D0B", + "cC52eEFA-EE5E-6aF7-4eDC-Dc1BF9ae5C72", + "E6f7BFb0AB3853eAf41AaA99c8faF049", + "7ce80b0a-fCe4-3be7-24fE-cD1da4bd2A49" + ], + "updated_at": "1973-07-09T23:00:00.0Z" + }, + { + "created_at": "2022-12-06T23:00:00.0Z", + "host_ids": [ + "d10576Ac-2187-1cEd-17F8-cEDACF2eCDC2", + "cb7Eb9aA-6831-0b3E-FBEa-9E653ca77F36", + "E19276AC-B4cB-Aa0D-21aB-194E6DEd0056", + "BEc31628AE87DDCf1be1Cc417B03936a", + "7AB6a43E4Fe8AdbFC07fbC6Cd96d91ef", + "E9c6cDBA7c0e4FbF7A93ADB50f9aa425", + "bA70711f-72a8-C9d4-aF4b-d4ECACCE6df0", + "1CD0cd75-Fe7E-BcDe-7c8E-791a3aff558b", + "F8DeECb8-Df3E-C3Df-48be-2Fafaa1e8430", + "3bFEfbbb-697c-1F01-6C07-0200A6Dc616d", + "EedcF47288efEe46b2adbDE1635ebf79", + "6FDdBDb3-0eD7-d0e7-e4A6-F3fCE6bEDF7b", + "7cE8b3bDA0A3ffE12Cc9dF8Ec03437cA", + "Ed9d2f5Df83B096279dB1ac341Cc11c0", + "515473AFCaffEDF9DBF1ff86f0BBF5cc", + "BecBdd0a-AAc1-BBd0-aA8F-4fc5e73CAF1B", + "c0D5Fa2B-1af1-a2Ac-CcaE-7C5C2fD5Ae07", + "dc325Fb2-CE45-0836-B78e-1de0aA39Dd84", + "8AeDAF41-9f11-Be8E-f1bB-5F841bc70A3B", + "D1e4c030-c255-537D-2BA3-53804DCbcd08" + ], + "name": "proident fugiat reprehenderit tempor", + "updated_at": "1971-01-12T23:00:00.0Z", + "id": "aAB7eadfa8e5f91B6cAcBCd53bAfECAB" + }, + { + "updated_at": "1973-07-27T23:00:00.0Z", + "id": "CBd04bFB-5d02-DCb2-eC2d-ed1deBcE1318", + "name": "anim quis cillum nulla ", + "host_ids": [ + "D9adB1bc-05CA-6Db9-39BE-ddAA08B505bE", + "a67a11eb-2C4B-CE22-FC16-4ABE18cbdaB8", + "fEBD4118710f0dCc2Ba4F9bD0444aDEc", + "dbb866DB-B25F-2af3-bFAd-3584A5cAb8cD", + "EC8A34F87e84fFC2ce1A3757f7c5342a", + "3Ee591a41bb2fA1cA7bCcd0291e53B0e", + "Ca53be53c119A6F1e23eBA75dBbf27D9", + "38b9C21fF9ACE7d1452D301aFEbcA7cc", + "aEbfEB55-AFD9-ef81-0eAc-1c6aCD5874F6", + "FFdd83AB-8fc9-4edc-e122-e7Ebe36bDBb1", + "168b6817Bb9CCAD15f129Ba1aeDB59d5", + "BD2745Ed-0b96-A0C3-Dfd2-Ff1CfEC485de", + "6487cf986f820af9FFFC79A5476AC8eA", + "Ef49FDEf-1dCf-FFec-4bb3-CB6ddFB8D53e", + "1e29c912f9FcAbcF30D9e1EF6425fC4C", + "C5aFd4EA41a7cb9e5BBdf0fffabBc96A", + "14CbecE7-a6e3-b319-CD1b-e6E56b82A451", + "fcfAaBa5-4AfB-eE25-cE3c-9ae69F58ccd8", + "e2e11921-BBD9-220E-fa27-ecab2aEAdD3e", + "6AC07943-ce69-c46A-3DCE-b7CeaAaE9A32" + ], + "created_at": "2001-01-07T23:00:00.0Z" + }, + { + "updated_at": "1991-05-05T22:00:00.0Z", + "created_at": "2020-07-24T22:00:00.0Z", + "id": "dAd6bBb1-B4c1-CaF4-88C6-bB47Cb04c117", + "name": "voluptate nulla", + "host_ids": null + }, + { + "created_at": "2004-08-10T22:00:00.0Z", + "name": "sit ci", + "updated_at": "2010-09-12T22:00:00.0Z", + "id": "7C7acDDB143DE75D0D2BD0fe6395f1e4", + "host_ids": null + }, + { + "created_at": "2018-10-13T00:00:00.0Z", + "id": "61B3d64f87bEdc9EDEf70d7b43C621E2", + "updated_at": "2005-03-09T23:00:00.0Z", + "name": "consequat veniam quis nulla", + "host_ids": null + }, + { + "created_at": "2003-08-03T22:00:00.0Z", + "host_ids": null, + "updated_at": "1970-01-29T23:00:00.0Z", + "id": "E1A6fa1fb5bdc073BF4aC5bAFDC98A0a", + "name": "pariatur adipisicing ut" + }, + { + "updated_at": "1978-04-08T23:00:00.0Z", + "created_at": "1967-03-06T23:00:00.0Z", + "id": "482da6aA-FCA1-FADC-A368-9ABB042DDbaf", + "name": "esse", + "host_ids": [ + "d5edfFbfe1C041A4A9C363D82eE2dAd8", + "D3a90CAbBcA82aBCAC2ae392C7C6272f", + "e754cc27-FFC4-fFC5-F182-19B9b8b5eaD8", + "ebAC5EFfC7Db6A7b44aC2EDF9b8704CC", + "B2735Eb5-7Fc9-5D7e-BDE0-11a399D66ceB", + "8e38ACFBFEC29D98C23439fDF2B23bE0", + "4667ccbEfB8B5810849daD75DA0Efd95", + "AAcD41B0-c4dA-EAe3-F6b0-235aaEbCE165", + "AFA3fd9d-1eC5-76e3-C7b5-DadDD2550D7b", + "fa8c1A0c-14dB-dEaF-BFd2-9D8b994c0e2f", + "FeDeE23f-D7Fc-e36d-A8ef-B6e3C275fb9C", + "ffE65c4b-75e5-0ccE-BE0a-ac8fb0B3baDF", + "0EcAaFC3206c5b36DFE49b7ff7A9DBc1", + "c3c04bdf-0Ce0-ed6F-aFED-8A3e3ED6558b", + "eC1CeBfb119Da87f92BDA27a0dae7dAA", + "B7Bacaa2cB3edb66104B7243e7DdF6A3", + "3C1896c6DeB3C23BE8B7F13e61bCDCE2", + "f4d3D0A5Aac9cC3bdea12B2a6920E0B0", + "F90E97F6-DF1e-e13E-d4D8-2B6d8Bd5CCfb", + "651Ed134-2F62-A72a-d1ff-F4bec8Fb7cfa" + ] + }, + { + "updated_at": "1956-04-22T23:00:00.0Z", + "id": "Bb9ABa408d2AF1e7A381ec57C3ff2FdC", + "host_ids": [ + "7fd74eCf-Cbee-eCec-dDe0-bAe2969a80CE", + "B2dbB4a4d3De810d2CeDEBAD4dbF2BbF", + "EE0fc1eB-AC3d-E8C5-37bC-bAA840dA9c33", + "3435DaFA-62De-12dC-cBD2-C4Bac0fCD9e1", + "1Abe2c2E-5aaB-0c74-C43f-De26d7e0ba0C", + "2Dcc49a1-f4eB-ad8D-c441-Dcf7Bb030Bc0", + "0Ca2Ae7741af1Ad6eca567C62f4cafC9", + "2089C1Bd57d8E5cA148F6fD879808Ffc", + "B9d1D976-91A9-674e-b9FE-4B5cA63bFCD1", + "AA45B3039f107E1FAca366A1aa1e09A3", + "e0570A64d182E12AEFAE1FfcCd4d8Bf8", + "A288c62E701BeC416C0aBaF67FC7BE1e", + "9cd02eBD-d9DA-c7FE-25ad-8FAb291048cb", + "1Fd33C51-C9D3-dAEc-a951-3D2bedaDA764", + "CAef594FA5a13c3Ae8bEdecFEDfBaA0E", + "6BdAAa04-8a7E-Ecac-91c1-BcF92EeCEda9", + "e39B6b2aFAAf6CF58bdECcFFBa3ed1c7", + "AbC5DB70b89eBCeca484eDA3bdBE0CaC", + "bb6e21Df-DB4E-de6D-4fed-5BCD8a606582", + "Ad0F7f8D-F089-c4c6-3D66-deEc6F0Bb7Fc" + ], + "created_at": "1948-10-29T00:00:00.0Z", + "name": "non irure qui" + }, + { + "name": "velit dolor officia Lorem tempor", + "host_ids": [ + "EfDBdfcC8C2C4b3eDeaabb4973c97D1a", + "98CEdd61FF2a5FFEdFD54D3fAa8Cd0c4", + "eD29fCa81fdfb0EeFd9cE5Cb3e97dA13", + "da450ADc-ee24-4e9f-dcc6-eEEF36FEBe5A", + "eB7D0f8B-015F-7BE4-5c44-0E939F0C5136", + "D2CcEbE1a55C3DB88CC4EB4e0942bF7f", + "6f8998Fcd8C5a633746823116AF7d9Eb", + "2e95F7bB-6ce3-2f6F-6c99-a776DfdCee19", + "F0db7AaE-ba4b-D0D4-aEBC-af1A43Ac7d3F", + "E5A9202d1aE2Fe61d5BB4d6ba9aE64B5", + "3c7a91FB-B0fe-339c-36cc-8bD67E12d1cA", + "b2E6a5ab-27D8-E37E-8Cfc-2Ff26Cde128a", + "D32fF332Fe9f6AF77a5C9aDF0ceeA07A", + "DF6076737e7DB621F7f1Dad7b3118A04", + "1EB95DFC-94E8-b3A0-8B5f-AeaE9bbb5dEC", + "050ed6AB-Eb8b-17BB-B254-2EC6B36CD39A", + "0d5a6eAB-c5C4-7cb2-1e17-B34FDDa9eFBe", + "23d9664E1fD846eeC7A5fB0c2C5eAabD", + "88CbE8aC-D257-F278-04AA-F26CB26a251b", + "5eDaa3dC-0D0f-08Be-272b-3215A97d76e5" + ], + "id": "93692deaA3fAbaE666caA2ad472A2196", + "updated_at": "1976-08-23T23:00:00.0Z", + "created_at": "1985-11-02T23:00:00.0Z" + }, + { + "name": "adipisicing esse", + "created_at": "2022-04-12T22:00:00.0Z", + "host_ids": [ + "EeCA9Cea-6a9D-dAEf-C717-eaf2ca015CdE", + "66Af591d-dE7a-EcCE-5e9F-1B9AbdCC5c05", + "4CD9daBb8ACAcE80eB4EAb4F25f88BD6", + "2a0626C3-e7A4-fB51-8130-AAc5eb8BD2B8", + "8bB8dD0e-c8Ef-4361-f2dc-54F33c8dd21d", + "AFFcc7ff925d32F1a33d8D52a5d86ceB", + "6f9ca923-119F-9dba-dbE4-6f657EDcB64D", + "Ce08cDdc-D002-F968-CeBe-C5a02107D366", + "71d7f69c-DEDc-9Efb-51Fb-1C5f0Bccc4FE", + "B8A20ce3-AAcA-bb7c-EFcb-FFCF80eda22E", + "f9C9a3D65353Cef59FeAcdd2EC802d4b", + "CEC8c2a231Ab3aA22EC66DbfDCDDB87C", + "4e597bCb-A2a0-55Aa-F7Ea-7a04cfaed822", + "AAfe2e23-0Ebf-3e74-88ad-A6c1cfBF6De9", + "B2adB826-076b-183E-51b7-ABDB6378735b", + "2e40093a-C6bF-e4e5-9F3D-eEfEaA43CA8f", + "C0C4B5Bf-3A79-c57f-1652-814dBA581a36", + "6160D4f1F2d3A8aB4BBaEc2b7261CCCC", + "8998EDAAEBfc4ABB927F89AcF2dc638A", + "6bf2FA50-B77d-8E4b-748D-9c894dBb04C5" + ], + "updated_at": "1997-08-10T22:00:00.0Z", + "id": "B7b37Eecda0dEfC71e72FD7923AF8Bdc" + }, + { + "id": "fAdcFeD365b0FE454e57dACcB69E0dfA", + "host_ids": null, + "name": "irure aliquip commodo", + "created_at": "1977-02-01T23:00:00.0Z", + "updated_at": "1952-01-05T23:00:00.0Z" + }, + { + "name": "cupidatat", + "created_at": "1996-07-15T22:00:00.0Z", + "host_ids": [ + "115907a96D39aED2915e5DB0AAF2524A", + "EA1cCe44-Bd0e-AbaC-4deD-146E7d6a9Ce9", + "4CF9fAC9-9AA4-FBB6-Df0c-c8Bc702dB10e", + "1aCa5d3F-4298-1a05-73b1-900206D4C8FE", + "EB47CB9c-8D4F-Abb2-E6F9-60c5eceafB3E", + "BBc2DfAAEeEf8fB70Fd2eb2FFEf7a5Fd", + "842c22B8-7203-de9C-449B-a5dfeE6ead7c", + "A1ea5eE4Ad6cDf1b28dDab8C050A00f5", + "afc72eC6-d88f-7BcD-78ab-Cc8321E886bE", + "Ca3dFA38-5e08-cA3A-E7b2-DcF7afaa827c", + "dc2F23da-FEBA-A389-AbB6-a36E617a5606", + "b1de4FAa2BDd61b03F7fE2c8FCdbC7cf", + "4Cd678a0036EFAd20e41D26e4A57bD1C", + "74A6916d82cd05383cFF45541bed0c2F", + "ab834ADcA81c8dA7e1c1f6DFdDFcb796" + ] + }, + { + "name": "nulla Ut adipisicing", + "updated_at": "1970-09-25T23:00:00.0Z", + "created_at": "1977-09-09T23:00:00.0Z", + "id": "715e3468-8D58-6E64-eFDa-B9E709a00B0C", + "host_ids": [ + "1D2edbCaaE1DE7A7Af40B6F538Fb8AEa", + "f29444fB0cF73Ad3fcaEbBFeBab94845", + "AF1Ee8bD-783b-D8FF-e383-A10E9B27FBC0", + "5D021Ca6-0Ffe-6035-9D7E-d4d39b4f1DcF", + "4fB3907d0130329cfc0fa3a5f85445de", + "0e1f3F1D-7BDa-ddFE-cb5b-487b4c9D0b9C", + "1A9FE2c8ae03EccCdFEA3C715D7CFbb0", + "Df1EdB144cd9e90aE1716F3ED5A1bAF4", + "3239ACFd-FD2b-a2C6-d33E-BEeFab8D2a3A", + "FcBf1c1B778009ac5cB87dF068d6d2Df", + "70D77A9d-28aB-f59d-Acff-2F3eFAfB9F6F", + "80477ABdC7046fA31a29580e3b1ABFb7", + "c1C1F50f-A62d-4117-E993-F5F4F3B75BEC", + "dEff7FA0d6FaDbeaB388eeE90d5D416c", + "7DFC9F898C98DBda8E98cfFD76CCBAbe", + "59bF4CBC-1Bcb-f999-bffC-b3Bb63D5c1f8", + "fC2e3288-927C-278F-d8Be-F492568AfdB6", + "19E4b5CcFed8e1aBEb47fb0BCd158cB6", + "FCEEFFb4F7A12bd2ad8DAA64eacE126a", + "28EA3e5E-4d33-aDfD-CFb4-91d3c66Ac8aD" + ] + }, + { + "id": "dbFd6B78-49Bd-a65b-36cb-9ea32EFD77BF", + "host_ids": null, + "name": "Ut deserunt dolore incididunt nulla", + "updated_at": "1980-03-07T23:00:00.0Z", + "created_at": "1965-07-22T23:00:00.0Z" + }, + { + "created_at": "1995-02-02T23:00:00.0Z", + "name": "laboris aliqua anim", + "updated_at": "1969-11-26T00:00:00.0Z", + "id": "8DdB68dd6B845f1704e50C2dccca807A", + "host_ids": [ + "D443Ac8D-c16c-a55C-dcf3-6e7Dd86FA4ED", + "4E1FE9Fa1366FBc9Ca16B9ff7dDC917C", + "a11ed5A06eFBA8E577E0ADc2C7Cf7237", + "27fDD70DeCa03EAfC72C40C72E32f0dA", + "Ce8f06eE-2FE8-c9c0-adBC-ED4414480a6b", + "13346F9eeB59c9a861F7E23D3ED64fc0", + "Fa422b84-EFE9-663d-3bd6-3B8dCd53ee4A", + "d1C3CE1a76dc16Ae6E62fe0bFf26739C", + "209e06A3B0dC1B4EAeF60832eeE68E00", + "baa66b5C-7500-2Eb6-8b5B-e3B2436fb15d", + "adECeC7A-2381-2B0d-f4fD-d41a94eb02D3", + "cdAFe98d-41Dd-cD6b-8e55-aecFe848bBF4", + "C104dDdE-e61A-0Ce2-1B9d-eF9BAEb671EF", + "A4a48BA8-e0c7-decC-8b3f-1BC1C5E14E6D", + "e7ebbBEA-0cf3-6b1f-4B7a-9fD6eC190A3F", + "1C77b00246C2d2667ddCbef74e2F548e", + "bd81f621f6f5c4CfFF2d2D1aF9acBf81", + "af8D28fDcf53B23CCB6Eba20849D7757", + "2E3Bd3aCA5028B60EDF8F4998ccfca18", + "CEc212D8-bA6c-4eed-0473-2Caa672eDCCC" + ] + }, + { + "name": "veniam anim", + "created_at": "2004-08-03T22:00:00.0Z", + "id": "fc85FF52-AD45-FBD0-E432-caA5Bb0FC98E", + "host_ids": null, + "updated_at": "1961-07-04T23:00:00.0Z" + }, + { + "updated_at": "2012-07-13T22:00:00.0Z", + "host_ids": [ + "2Ae38B9F-6cCa-fFCC-61Fb-7986772b5aD2", + "BFd43B7F-672D-73ad-A2cD-6b1ce3501F5d", + "90ca9F31-e8EA-1CcD-F3A4-b60bACDa78Fa", + "05472824BC7FFA3d2EF4Ca7eb9F77C2b", + "Dd8A05DB-e7AA-06AA-c562-aa8bacc79C09", + "c4b840B0B3DEddBd22A72b1a2B2fEFff", + "7CF31fbE4fEBD0F324DAf9FD5Ba7C3db", + "488fA7417cbd80fA4D66Cf3E0DBceaBc", + "137BBDAC92D22a8dAB1fcE9955Df0A4B", + "00B58767-cfe1-20B9-246c-0A16Ca4D1e0b", + "cB2E9A0161bd6aa04E56d76Aca98d700", + "A3A7F45Fc2D9548767dC5dcdef586FE2", + "aa5Cc45C0906df7965BC52B94e88aBfA", + "3cFF4a4def7Fdcf248646e7De5Cc4c1b", + "edfcE3F6-8f54-1b84-ebcB-15B7093bA7D3", + "f6AA5AA5A2F62758eEF84E8E8FccbB5b", + "909AA26c-e7cb-1e14-E1Ba-9bFe5D3F8E7F", + "8Eb03D5E-eEA6-a5d0-e2ec-45c300fBA4ff", + "88Bdb4F84AE6C8eB592461bEfBB3DA4c", + "a7BCB9B514AeA9B6eA000594dbd8D67e" + ], + "id": "c9DFA4Bf-dBcE-38A2-76B1-1f8ae941EF8E", + "created_at": "2006-10-22T00:00:00.0Z", + "name": "nostrud nisi sint U" + }, + { + "created_at": "1954-06-09T23:00:00.0Z", + "host_ids": [ + "eDf961e5718EBDf974d2d08be3CD6703", + "D86b6a4411c840ccdC2ccd6FeCc175bd", + "fBF0Dd88bc43D16c8E6A484E7eF4DCb1", + "3eE63AD4-93FE-bb2B-e8aD-e7Fa385d7bb0", + "4EEC3FF5cdAE9efC6Bd079Ecd1BEDd79", + "BBbeE598-eE3e-b248-0EcA-0bFd8C1E07e1", + "AF148Ed6-51Db-E5D0-a7fB-86edEF80AcFD", + "7DA6AfE5A6dc6Cd6874523dd747c4ecc", + "75CaC6db-541e-8e30-CEDE-aDbf00A0e4a2", + "D4192AD0-C71B-eA40-ea91-D44C48c3F8d6", + "2f4EBC4ED9e617Df9edCb3D1caaccAc2", + "d73e871D-5c00-559B-6DA3-FDcB5cfDDC1f", + "f713EF56FD91BCCbbA6155feaBFcE1Db", + "bcEB6Ff1eFfe52ddC5449eC2A931AE68", + "d9D34d55-5B6D-68AF-cA8e-b11BeDFefc63", + "EAa882daB9be49c4DcF905604CDd5a91", + "9AF20eD3-853d-af62-fF8D-4CCEFfa96F07", + "6a3bFC5aaaAadbef6aAFb7EaC0e4aee5", + "fBC8BCE9E2ebFCDCd9E92Bc9abe8e405", + "Dec2ab14-4cCC-B124-2Aad-0De5c80860D9" + ], + "id": "D11bECBbB6282D99Ece43d18df1BfbeB", + "updated_at": "1996-09-25T22:00:00.0Z", + "name": "non laboris" + }, + { + "created_at": "1981-12-28T00:00:00.0Z", + "name": "aliqua", + "id": "3F3E4CaD-aD9e-FCBE-aDcE-fCEa73ed4e67", + "updated_at": "2005-01-17T23:00:00.0Z", + "host_ids": null + }, + { + "name": "vol", + "created_at": "1943-12-16T00:00:00.0Z", + "updated_at": "1985-05-29T22:00:00.0Z", + "id": "CbBD1DCe-9855-B2D9-2c25-36f46ce16ED4", + "host_ids": null + }, + { + "updated_at": "1987-05-08T22:00:00.0Z", + "name": "do", + "id": "3ECf99c9842A81cA47Cd7A5380E64Fb8", + "host_ids": null, + "created_at": "1988-09-18T22:00:00.0Z" + }, + { + "name": "sunt sed dolore anim", + "id": "A2b64d6Db97b08eF4ACBae16EACceA5D", + "created_at": "1971-10-22T00:00:00.0Z", + "host_ids": [ + "f57d7dfFfBC9DCfda554dc4fF24d95aB", + "F2C5c2df-Dc37-efDb-1754-CE03A0812cAa", + "EAeFf0De-Eb4B-fEAA-D427-DaEA31dABdDb", + "e9EA62CC102BDfDeE6B02C8cbe652a03", + "6eEd6Bc1cFDdAB547DBA0B7eD5da6715", + "eE6315d6F26Af0EDb25eAC1Dc2f82348", + "EEEC2AB5-de5b-284e-E2a7-07F7c2feDed1", + "2D5Dc09201C413ecB58a0DaAdbbDeb93", + "B496aE97-B57C-32ee-3d85-aCfFed52fEfA", + "7CD7dDee8F4F4D0179a8D6ec66d4Db28", + "238CEea5-7cdA-b9C7-976E-1Bd8a4ec2b94", + "75BdeeEd-c04F-DcaC-aeA7-b74AB2AEFF1f", + "32D9FCA9-beF3-fD1E-54A7-b59326D7Fa10", + "bbfCe27AAAE0C396Ef25aafE68E64ddB", + "53Dd9a5134DcF5989Ad01260E38EDE75", + "33fBF1Ec-FA1D-d1A8-6dd3-F57de513A8Fa", + "F584Cb3C-C64A-c58D-B3C2-88FbaD9Cb6Be", + "16E5FABcDee394BCAedbBE2Ea9BDb789", + "abE6a1A5bAA28dBf748ffa292F500247", + "1b5acfDECa9eCD9dCC2eE51b68Cb8faE" + ], + "updated_at": "1961-06-19T23:00:00.0Z" + } + ], + "total": 60 +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 66ea16ef0..31ae217a7 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -22,4 +22,23 @@ // // // -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +Cypress.Commands.add( + 'ouiaId', + { prevSubject: 'optional' }, + (subject, item, el = '') => { + const attr = `${el}[data-ouia-component-id="${item}"]`; + return subject ? cy.wrap(subject).find(attr) : cy.get(attr); + } +); + +Cypress.Commands.add( + 'ouiaType', + { prevSubject: 'optional' }, + (subject, item, el = '') => { + const attr = `${el}[data-ouia-component-type="${item}"]`; + return subject ? cy.wrap(subject).find(attr) : cy.get(attr); + } +); + diff --git a/cypress/support/component.js b/cypress/support/component.js index e7c6d0c31..4ec910fd9 100644 --- a/cypress/support/component.js +++ b/cypress/support/component.js @@ -1,2 +1,4 @@ import '@patternfly/patternfly/patternfly.scss'; import '@cypress/code-coverage/support'; +import './commands'; + diff --git a/src/components/GroupsTable/GroupsTable.cy.js b/src/components/GroupsTable/GroupsTable.cy.js new file mode 100644 index 000000000..2fbe34a6a --- /dev/null +++ b/src/components/GroupsTable/GroupsTable.cy.js @@ -0,0 +1,238 @@ +/* eslint-disable camelcase */ +import { mount } from '@cypress/react'; +import { + changePagination, + checkEmptyState, + checkPaginationTotal, + checkPaginationValues, + checkTableHeaders, + CHIP, + CHIP_GROUP, + hasChip, + PAGINATION_VALUES, + SORTING_ORDERS, + TEXT_INPUT, + TOOLBAR, + TOOLBAR_FILTER +} from '@redhat-cloud-services/frontend-components-utilities'; +import _ from 'lodash'; +import React from 'react'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import fixtures from '../../../cypress/fixtures/groups.json'; +import { getStore } from '../../store'; +import GroupsTable from './GroupsTable'; + +const ORDER_TO_URL = { + ascending: 'ASC', + descending: 'DESC' +}; + +const DEFAULT_ROW_COUNT = 50; +const TABLE_HEADERS = ['Name', 'Total systems', 'Last modified']; +const ROOT = 'div[id="groups-table"]'; + +const interceptors = { + 'successful with some items': () => + cy.intercept('GET', '/api/inventory/v1/groups*', fixtures).as('getGroups'), + 'successful empty': () => + cy + .intercept('GET', '/api/inventory/v1/groups*', { + count: 0, + page: 1, + per_page: DEFAULT_ROW_COUNT, + total: 0 + }) + .as('getGroups'), + 'failed with server error': () => + { + Cypress.on('uncaught:exception', (err, runnable) => { + return false; + }); + cy + .intercept('GET', '/api/inventory/v1/groups*', { statusCode: 500 }) + .as('getGroups'); + } +}; + +const mountTable = () => + mount( + + + + + + ); + +before(() => { + cy.window().then( + (window) => + (window.insights = { + chrome: { + isProd: false, + auth: { + getUser: () => { + return Promise.resolve({}); + } + } + } + }) + ); +}); + +describe('renders correctly', () => { + beforeEach(() => { + interceptors['successful with some items'](); + mountTable(); + }); + + it('the root container is rendered', () => { + cy.get(ROOT).should('have.length', 1); + }); + + it('renders toolbar', () => { + cy.get(TOOLBAR).should('have.length', 1); + }); + + it('renders table header', () => { + checkTableHeaders(TABLE_HEADERS); + }); +}); + +describe('defaults', () => { + beforeEach(() => { + interceptors['successful with some items'](); + mountTable(); + }); + + it(`pagination is set to ${DEFAULT_ROW_COUNT}`, () => { + cy.wait('@getGroups'); + cy.get('.pf-c-options-menu__toggle-text') + .find('b') + .eq(0) + .should('have.text', `1 - ${DEFAULT_ROW_COUNT}`); + }); + + it('name filter is a default filter', () => { + cy.get(TOOLBAR_FILTER).find(TEXT_INPUT).should('exist'); + }); +}); + +describe('pagination', () => { + beforeEach(() => { + interceptors['successful with some items'](); + mountTable(); + }); + + it('shows correct total number of groups', () => { + checkPaginationTotal(fixtures.total); + }); + + it('values are expected ones', () => { + checkPaginationValues(PAGINATION_VALUES); + }); + + it('can change page limit', () => { + cy.wait('@getGroups').then(() => { + // first initial call + cy.wrap(PAGINATION_VALUES).each((el) => { + changePagination(el).then(() => { + cy.wait('@getGroups') + .its('request.url') + .should('include', `perPage=${el}`); + }); + }); + }); + }); +}); + +describe('sorting', () => { + beforeEach(() => { + interceptors['successful with some items'](); + mountTable(); + }); + + const checkSorting = (label, order, dataField) => { + // get appropriate locators + const header = `th[data-label="${label}"]`; + if (order === 'ascending') { + cy.get(header).find('button').click(); + } else { + cy.get(header).find('button').click().click(); + } + + cy.wait('@getGroups') + .its('request.url') + .should('include', `order_how=${ORDER_TO_URL[order]}`) + .and('include', `order_by=${dataField}`); + }; + + _.zip(['name', 'host_ids', 'updated_at'], TABLE_HEADERS).forEach( + ([category, label]) => { + SORTING_ORDERS.forEach((order) => { + it(`${order} by ${label}`, () => { + checkSorting(label, order, category); + }); + }); + } + ); +}); + +describe('filtering', () => { + beforeEach(() => { + interceptors['successful with some items'](); + mountTable(); + }); + + const applyNameFilter = () => { + cy.get('.ins-c-primary-toolbar__filter').find('input').type('lorem'); + }; + + it('renders filter chip', () => { + applyNameFilter(); + hasChip('Name', 'lorem'); + }); + + it('sends correct request', () => { + applyNameFilter(); + cy.wait('@getGroups') + .its('request.url') + .should('include', 'hostname_or_id=lorem'); + }); + + it('can remove the chip or reset filters', () => { + applyNameFilter(); + cy.get(CHIP_GROUP) + .find(CHIP) + .ouiaId('close', 'button') + .each(() => { + cy.get(CHIP_GROUP).find(CHIP).ouiaId('close', 'button'); + }); + cy.get('button').contains('Reset filters').click(); + cy.get(CHIP_GROUP).should('not.exist'); + }); +}); + +describe('edge cases', () => { + it('no groups match', () => { + interceptors['successful empty'](); + mountTable(); + + cy.wait('@getGroups').then(() => { + checkEmptyState('No matching systems found'); + checkPaginationTotal(0); + }); + }); + + it('failed request', () => { + interceptors['failed with server error'](); + mountTable(); + + cy.wait('@getGroups').then(() => { + cy.get('.pf-c-empty-state').find('h4').contains('Something went wrong'); + // the filter is disabled + cy.ouiaId('ConditionalFilter').should('have.attr', 'disabled'); + cy.ouiaId('pager').find('button').should('have.attr', 'disabled'); + }); + }); +}); diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js new file mode 100644 index 000000000..bd15c8e01 --- /dev/null +++ b/src/components/GroupsTable/GroupsTable.js @@ -0,0 +1,209 @@ +/* eslint-disable camelcase */ +import { Pagination, PaginationVariant } from '@patternfly/react-core'; +import { + fitContent, + sortable, + Table, + TableBody, + TableHeader, + TableVariant +} from '@patternfly/react-table'; +import { + DateFormat, + ErrorState, + PrimaryToolbar +} from '@redhat-cloud-services/frontend-components'; +import debounce from 'lodash/debounce'; +import upperCase from 'lodash/upperCase'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Link } from 'react-router-dom'; +import { TABLE_DEFAULT_PAGINATION } from '../../constants'; +import { fetchGroups } from '../../store/inventory-actions'; +import { generateLoadingRows } from '../InventoryTable/helpers'; +import NoSystemsTable from '../InventoryTable/NoSystemsTable'; + +const GROUPS_TABLE_INITIAL_STATE = { + perPage: TABLE_DEFAULT_PAGINATION, + page: 1 +}; + +const GROUPS_TABLE_COLUMNS = [ + { + title: 'Name', + transforms: [sortable] + }, + { + title: 'Total systems', + transforms: [sortable, fitContent] + }, + { + title: 'Last modified', + transforms: [sortable, fitContent] + } +]; + +const GROUPS_TABLE_COLUMNS_TO_URL = { + 0: 'name', + 1: 'host_ids', + 2: 'updated_at' +}; + +const REQUEST_DEBOUNCE_TIMEOUT = 500; + +const GroupsTable = () => { + const dispatch = useDispatch(); + const { rejected, uninitialized, loading, data } = useSelector( + (state) => state.groups + ); + const [filters, setFilters] = useState(GROUPS_TABLE_INITIAL_STATE); + const [rows, setRows] = useState([]); + const groups = useMemo(() => data?.results || [], [data]); + + const fetchData = useCallback( + debounce((filters) => { + const { perPage, page, sortIndex, sortDirection, ...search } = filters; + + if (sortIndex !== undefined && sortDirection !== undefined) { + const order_by = GROUPS_TABLE_COLUMNS_TO_URL[sortIndex]; + const order_how = upperCase(sortDirection); + return dispatch( + fetchGroups({ ...search, order_by, order_how }, { page, perPage }) + ); + } else { + return dispatch(fetchGroups(search, { page, perPage })); + } + }, REQUEST_DEBOUNCE_TIMEOUT), // wait the timeout before making the final fetch + [] + ); + + useEffect(() => { + fetchData(filters); + }, [filters]); + + useEffect(() => { + // update visible rows once new data obtained + const newRows = groups.map((group, index) => ({ + cells: [ + + {group.name || group.id} + , + {(group.host_ids || []).length.toString()}, + {} + ] + })); + setRows(newRows); + }, [groups]); + + // TODO: convert initial URL params to filters + + const onSort = (event, index, direction) => { + setFilters({ ...filters, sortIndex: index, sortDirection: direction }); + }; + + const filterConfigItems = [ + { + label: 'Name', + filterValues: { + key: 'name-filter', + onChange: (event, value) => + setFilters({ ...filters, hostname_or_id: value }), + value: filters.hostname_or_id, + placeholder: 'Filter by name', + isDisabled: rejected + } + } + ]; + + const activeFiltersConfig = { + showDeleteButton: !!filters.hostname_or_id, + deleteTitle: 'Reset filters', + filters: filters.hostname_or_id + ? [ + { + category: 'Name', + chips: [ + { name: filters.hostname_or_id, value: filters.hostname_or_id } + ] + } + ] + : [], + // always reset to initial filters since there is only one filter currently + onDelete: () => setFilters(GROUPS_TABLE_INITIAL_STATE) + }; + + const onSetPage = (event, page) => setFilters({ ...filters, page }); + + const onPerPageSelect = (event, perPage) => + setFilters({ ...filters, perPage, page: 1 }); // will also reset the page to first + + // TODO: use ouiaSafe to indicate the loading state for e2e tests + + return ( +
+ + */ + variant={TableVariant.compact} + cells={GROUPS_TABLE_COLUMNS} + rows={ + uninitialized || loading + ? generateLoadingRows(GROUPS_TABLE_COLUMNS.length, filters.perPage) + : (rejected || rows.length === 0) + ? [ + { + fullWidth: true, + cells: [ + { + title: rejected ? : , + props: { + colSpan: GROUPS_TABLE_COLUMNS.length + 1 + } + } + ] + } + ] + : rows + } + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection + }} + onSort={onSort} + isStickyHeader + > + + +
+ +
+ ); +}; + +export default GroupsTable; diff --git a/src/components/GroupsTable/index.js b/src/components/GroupsTable/index.js new file mode 100644 index 000000000..288be958c --- /dev/null +++ b/src/components/GroupsTable/index.js @@ -0,0 +1,3 @@ +import GroupsTable from './GroupsTable'; + +export default GroupsTable; diff --git a/src/components/InventoryGroups/InventoryGroups.js b/src/components/InventoryGroups/InventoryGroups.js new file mode 100644 index 000000000..b3aa2ff81 --- /dev/null +++ b/src/components/InventoryGroups/InventoryGroups.js @@ -0,0 +1,55 @@ +import { + ErrorState, + PageHeader, + PageHeaderTitle +} from '@redhat-cloud-services/frontend-components'; +import React, { useEffect, useState } from 'react'; + +import { Bullseye, Spinner } from '@patternfly/react-core'; +import GroupsTable from '../GroupsTable/GroupsTable'; +import { getGroups } from '../InventoryGroups/utils/api'; +import NoGroupsEmptyState from './NoGroupsEmptyState'; + +const InventoryGroups = () => { + const [isLoading, setIsLoading] = useState(true); + const [hasGroups, setHasGroups] = useState(false); + const [hasError, setHasError] = useState(false); + + useEffect(async () => { + // make initial request to check if there is at least one group available + try { + const { total } = await getGroups(); + + if (total > 0) { + setHasGroups(true); + } + } catch (error) { + setHasError(true); + } + + setIsLoading(false); + }, []); + + return ( + + + + +
+ {hasError ? ( + + ) : isLoading ? ( + + + + ) : hasGroups ? ( + + ) : ( + + )} +
+
+ ); +}; + +export default InventoryGroups; diff --git a/src/components/InventoryGroups/index.js b/src/components/InventoryGroups/index.js index 0e025381a..a6e3d7a04 100644 --- a/src/components/InventoryGroups/index.js +++ b/src/components/InventoryGroups/index.js @@ -1,21 +1,3 @@ -import React from 'react'; -import { - PageHeader, - PageHeaderTitle -} from '@redhat-cloud-services/frontend-components'; -import NoGroupsEmptyState from './NoGroupsEmptyState'; - -const InventoryGroups = () => { - return ( - - - - -
- -
-
- ); -}; +import InventoryGroups from './InventoryGroups'; export default InventoryGroups; diff --git a/src/components/InventoryGroups/utils/api.js b/src/components/InventoryGroups/utils/api.js index d3c3eb504..2920c7a21 100644 --- a/src/components/InventoryGroups/utils/api.js +++ b/src/components/InventoryGroups/utils/api.js @@ -1,8 +1,9 @@ import { instance } from '@redhat-cloud-services/frontend-components-utilities/interceptors/interceptors'; import { INVENTORY_API_BASE } from '../../../api'; +import { TABLE_DEFAULT_PAGINATION } from '../../../constants'; import PropTypes from 'prop-types'; -export const getGroups = (search = {}, pagination = { page: 1, perPage: 20 }) => { +export const getGroups = (search = {}, pagination = { page: 1, perPage: TABLE_DEFAULT_PAGINATION }) => { const parameters = new URLSearchParams({ ...search, ...pagination diff --git a/src/components/InventoryTable/NoSystemsTable.js b/src/components/InventoryTable/NoSystemsTable.js index 57b6086e6..7e67d94ce 100644 --- a/src/components/InventoryTable/NoSystemsTable.js +++ b/src/components/InventoryTable/NoSystemsTable.js @@ -1,11 +1,21 @@ import React from 'react'; -import { EmptyStateBody, EmptyState, EmptyStateVariant, Title } from '@patternfly/react-core'; +import { + EmptyStateBody, + EmptyState, + EmptyStateVariant, + Title +} from '@patternfly/react-core'; /** * Empty state stable when no systems are found. */ const NoSystemsTable = () => ( - + No matching systems found diff --git a/src/components/InventoryTable/helpers.js b/src/components/InventoryTable/helpers.js index f0f51f95a..862c4d1d7 100644 --- a/src/components/InventoryTable/helpers.js +++ b/src/components/InventoryTable/helpers.js @@ -4,6 +4,7 @@ import get from 'lodash/get'; import flatten from 'lodash/flatten'; import TitleColumn from './TitleColumn'; import { Fragment } from 'react'; +import { Skeleton } from '@patternfly/react-core'; export const buildCells = (item, columns, extra) => { return columns.map(({ key, composed, renderFunc }) => { @@ -85,3 +86,8 @@ export const createColumns = (columns, hasItems, rows, isExpandable) => ( })) ); +export const generateLoadingRows = (colsNumber, rowsNumber) => + Array(rowsNumber).fill({ + fullWidth: true, + cells: Array(colsNumber).fill({ title: }) + }); diff --git a/src/constants.js b/src/constants.js index fe589d457..3970ee0a5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -127,3 +127,5 @@ export const getSearchParams = () => { const perPage = searchParams.getAll('per_page'); return { status, source, tagsFilter, filterbyName, operatingSystem, rhcdFilter, updateMethodFilter, page, perPage }; }; + +export const TABLE_DEFAULT_PAGINATION = 50; // from UX table audit From 8073be1e6740acb20d84517cbfe66077f91dda75 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 17:30:51 +0100 Subject: [PATCH 04/14] Extract interceptors, add tests for the grous page --- cypress/support/interceptors.js | 33 ++++++++++ src/components/GroupsTable/GroupsTable.cy.js | 24 +------ .../InventoryGroups/InventoryGroups.cy.js | 66 +++++++++++++++++++ 3 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 cypress/support/interceptors.js create mode 100644 src/components/InventoryGroups/InventoryGroups.cy.js diff --git a/cypress/support/interceptors.js b/cypress/support/interceptors.js new file mode 100644 index 000000000..64f40d27b --- /dev/null +++ b/cypress/support/interceptors.js @@ -0,0 +1,33 @@ +/* eslint-disable camelcase */ +import { DEFAULT_ROW_COUNT } from '@redhat-cloud-services/frontend-components-utilities'; +import fixtures from '../fixtures/groups.json'; + +export const groupsInterceptors = { + 'successful with some items': () => + cy.intercept('GET', '/api/inventory/v1/groups*', fixtures).as('getGroups'), + 'successful empty': () => + cy + .intercept('GET', '/api/inventory/v1/groups*', { + count: 0, + page: 1, + per_page: DEFAULT_ROW_COUNT, + total: 0 + }) + .as('getGroups'), + 'failed with server error': () => { + Cypress.on('uncaught:exception', (err, runnable) => { + return false; + }); + cy.intercept('GET', '/api/inventory/v1/groups*', { statusCode: 500 }).as( + 'getGroups' + ); + }, + 'long responding': () => { + cy.intercept('GET', '/api/inventory/v1/groups*', (req) => { + req.reply({ + body: fixtures, + delay: 42000000 // milliseconds + }); + }).as('getGroups'); + } +}; diff --git a/src/components/GroupsTable/GroupsTable.cy.js b/src/components/GroupsTable/GroupsTable.cy.js index 2fbe34a6a..9e77919a4 100644 --- a/src/components/GroupsTable/GroupsTable.cy.js +++ b/src/components/GroupsTable/GroupsTable.cy.js @@ -20,6 +20,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; import fixtures from '../../../cypress/fixtures/groups.json'; +import { groupsInterceptors as interceptors } from '../../../cypress/support/interceptors'; import { getStore } from '../../store'; import GroupsTable from './GroupsTable'; @@ -32,29 +33,6 @@ const DEFAULT_ROW_COUNT = 50; const TABLE_HEADERS = ['Name', 'Total systems', 'Last modified']; const ROOT = 'div[id="groups-table"]'; -const interceptors = { - 'successful with some items': () => - cy.intercept('GET', '/api/inventory/v1/groups*', fixtures).as('getGroups'), - 'successful empty': () => - cy - .intercept('GET', '/api/inventory/v1/groups*', { - count: 0, - page: 1, - per_page: DEFAULT_ROW_COUNT, - total: 0 - }) - .as('getGroups'), - 'failed with server error': () => - { - Cypress.on('uncaught:exception', (err, runnable) => { - return false; - }); - cy - .intercept('GET', '/api/inventory/v1/groups*', { statusCode: 500 }) - .as('getGroups'); - } -}; - const mountTable = () => mount( diff --git a/src/components/InventoryGroups/InventoryGroups.cy.js b/src/components/InventoryGroups/InventoryGroups.cy.js new file mode 100644 index 000000000..90d41f7b7 --- /dev/null +++ b/src/components/InventoryGroups/InventoryGroups.cy.js @@ -0,0 +1,66 @@ +import { mount } from '@cypress/react'; +import React from 'react'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import { groupsInterceptors as interceptors } from '../../../cypress/support/interceptors'; +import { getStore } from '../../store'; +import InventoryGroups from './InventoryGroups'; + +const mountPage = () => mount( + + + + + +); + +before(() => { + cy.window().then( + (window) => + (window.insights = { + chrome: { + isProd: false, + auth: { + getUser: () => { + return Promise.resolve({}); + } + } + } + }) + ); +}); + +describe('groups table page', () => { + it('renders table if there is at least one group', () => { + interceptors['successful with some items'](); + mountPage(); + + cy.get('h1').contains('Groups'); + cy.get('#groups-table'); + }); + + it('renders only empty state when there are no groups', () => { + interceptors['successful empty'](); + mountPage(); + + cy.get('h1').contains('Groups'); + cy.get('#groups-table').should('not.exist'); + cy.get('.pf-c-empty-state').find('h4').contains('Create a system group'); + }); + + it('renders error message when request fails', () => { + interceptors['failed with server error'](); + mountPage(); + + cy.get('h1').contains('Groups'); + cy.get('#groups-table').should('not.exist'); + cy.get('.pf-c-empty-state').find('h4').contains('Something went wrong'); + }); + + it('renders spinner when loading', () => { + interceptors['long responding'](); + mountPage(); + + cy.get('[role=progressbar]').should('have.class', 'pf-c-spinner pf-m-xl'); + }); +}); From ec5865fcec71725f5c31345496a5b9070baf90ce Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 17:33:13 +0100 Subject: [PATCH 05/14] Add groups table (reference ESSNTL-4194 commit) Implements https://issues.redhat.com/browse/ESSNTL-4194. From 0bd89b50b1b2a9a93d154c5c504f9cb5de922128 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Tue, 14 Feb 2023 17:47:52 +0100 Subject: [PATCH 06/14] Fix lint error --- src/components/GroupsTable/GroupsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index bd15c8e01..a7b3df627 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -15,7 +15,7 @@ import { } from '@redhat-cloud-services/frontend-components'; import debounce from 'lodash/debounce'; import upperCase from 'lodash/upperCase'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; import { TABLE_DEFAULT_PAGINATION } from '../../constants'; From 7e39c47ff137c69bcf4b42a902684c3dd5adc7ea Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 12:07:02 +0100 Subject: [PATCH 07/14] Remove ouiaId unnecessary definition --- cypress/support/commands.js | 21 +-------------------- cypress/support/component.js | 1 - 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 31ae217a7..6665dff91 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -13,7 +13,7 @@ // Cypress.Commands.add('login', (email, password) => { ... }) // // -// -- This is a child command -- +// -- This iws a child command -- // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) // // @@ -23,22 +23,3 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) - -Cypress.Commands.add( - 'ouiaId', - { prevSubject: 'optional' }, - (subject, item, el = '') => { - const attr = `${el}[data-ouia-component-id="${item}"]`; - return subject ? cy.wrap(subject).find(attr) : cy.get(attr); - } -); - -Cypress.Commands.add( - 'ouiaType', - { prevSubject: 'optional' }, - (subject, item, el = '') => { - const attr = `${el}[data-ouia-component-type="${item}"]`; - return subject ? cy.wrap(subject).find(attr) : cy.get(attr); - } -); - diff --git a/cypress/support/component.js b/cypress/support/component.js index 4ec910fd9..5216e632c 100644 --- a/cypress/support/component.js +++ b/cypress/support/component.js @@ -1,4 +1,3 @@ import '@patternfly/patternfly/patternfly.scss'; import '@cypress/code-coverage/support'; -import './commands'; From 3a8aadd51cfe6d203f838083e6b0cd1e538fe2f7 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 12:12:11 +0100 Subject: [PATCH 08/14] Make the no match state render icon and optional link --- src/components/GroupsTable/GroupsTable.cy.js | 2 +- src/components/GroupsTable/GroupsTable.js | 20 ++- src/components/InventoryTable/EntityTable.js | 4 +- .../InventoryTable/NoEntitiesFound.js | 52 ++++++ .../InventoryTable/NoEntitiesFound.test.js | 23 +++ .../InventoryTable/NoSystemsTable.js | 28 ---- .../InventoryTable/NoSystemsTable.test.js | 11 -- .../NoEntitiesFound.test.js.snap | 155 ++++++++++++++++++ .../__snapshots__/NoSystemsTable.test.js.snap | 38 ----- 9 files changed, 247 insertions(+), 86 deletions(-) create mode 100644 src/components/InventoryTable/NoEntitiesFound.js create mode 100644 src/components/InventoryTable/NoEntitiesFound.test.js delete mode 100644 src/components/InventoryTable/NoSystemsTable.js delete mode 100644 src/components/InventoryTable/NoSystemsTable.test.js create mode 100644 src/components/InventoryTable/__snapshots__/NoEntitiesFound.test.js.snap delete mode 100644 src/components/InventoryTable/__snapshots__/NoSystemsTable.test.js.snap diff --git a/src/components/GroupsTable/GroupsTable.cy.js b/src/components/GroupsTable/GroupsTable.cy.js index 9e77919a4..7f8aa7b40 100644 --- a/src/components/GroupsTable/GroupsTable.cy.js +++ b/src/components/GroupsTable/GroupsTable.cy.js @@ -197,7 +197,7 @@ describe('edge cases', () => { mountTable(); cy.wait('@getGroups').then(() => { - checkEmptyState('No matching systems found'); + checkEmptyState('No matching groups found', true); checkPaginationTotal(0); }); }); diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index a7b3df627..64b7103ce 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -21,7 +21,7 @@ import { Link } from 'react-router-dom'; import { TABLE_DEFAULT_PAGINATION } from '../../constants'; import { fetchGroups } from '../../store/inventory-actions'; import { generateLoadingRows } from '../InventoryTable/helpers'; -import NoSystemsTable from '../InventoryTable/NoSystemsTable'; +import NoEntitiesFound from '../InventoryTable/NoEntitiesFound'; const GROUPS_TABLE_INITIAL_STATE = { perPage: TABLE_DEFAULT_PAGINATION, @@ -115,6 +115,8 @@ const GroupsTable = () => { } ]; + const onResetFilters = () => setFilters(GROUPS_TABLE_INITIAL_STATE); + const activeFiltersConfig = { showDeleteButton: !!filters.hostname_or_id, deleteTitle: 'Reset filters', @@ -129,7 +131,7 @@ const GroupsTable = () => { ] : [], // always reset to initial filters since there is only one filter currently - onDelete: () => setFilters(GROUPS_TABLE_INITIAL_STATE) + onDelete: onResetFilters }; const onSetPage = (event, page) => setFilters({ ...filters, page }); @@ -151,9 +153,8 @@ const GroupsTable = () => { isCompact: true, ouiaId: 'pager', isDisabled: rejected - }} - filterConfig={{ items: filterConfigItems }} + filterConfig={{ items: filterConfigItems }} activeFiltersConfig={activeFiltersConfig} /> { rows={ uninitialized || loading ? generateLoadingRows(GROUPS_TABLE_COLUMNS.length, filters.perPage) - : (rejected || rows.length === 0) + : rejected || rows.length === 0 ? [ { fullWidth: true, cells: [ { - title: rejected ? : , + title: rejected ? ( + + ) : ( + + ), props: { colSpan: GROUPS_TABLE_COLUMNS.length + 1 } diff --git a/src/components/InventoryTable/EntityTable.js b/src/components/InventoryTable/EntityTable.js index e174c7ac0..ce1cda30d 100644 --- a/src/components/InventoryTable/EntityTable.js +++ b/src/components/InventoryTable/EntityTable.js @@ -11,7 +11,7 @@ import { TableVariant } from '@patternfly/react-table'; import { SkeletonTable } from '@redhat-cloud-services/frontend-components/SkeletonTable'; -import NoSystemsTable from './NoSystemsTable'; +import NoEntitiesFound from './NoEntitiesFound'; import { createRows, createColumns } from './helpers'; import useColumns from './hooks/useColumns'; @@ -34,7 +34,7 @@ const EntityTable = ({ expandable: isExpandable, onRowClick, noDetail, - noSystemsTable = , + noSystemsTable = , showTags, columns: columnsProp, disableDefaultColumns, diff --git a/src/components/InventoryTable/NoEntitiesFound.js b/src/components/InventoryTable/NoEntitiesFound.js new file mode 100644 index 000000000..dceff15d8 --- /dev/null +++ b/src/components/InventoryTable/NoEntitiesFound.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { + EmptyStateBody, + EmptyState, + EmptyStateVariant, + Title, + EmptyStateIcon, + Button, + EmptyStatePrimary +} from '@patternfly/react-core'; +import PropTypes from 'prop-types'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; + +/** + * Empty state stable when no systems (or other entities) are found. + */ +const NoEntitiesFound = ({ entities, onClearAll, showIcon }) => ( + + {showIcon && } + + {`No matching ${entities} found`} + + + To continue, edit your filter settings and try again + + {onClearAll !== undefined && ( + + + + )} + +); + +NoEntitiesFound.propTypes = { + entities: PropTypes.string, + onClearAll: PropTypes.func, + showIcon: PropTypes.bool +}; + +NoEntitiesFound.defaultProps = { + entities: 'systems', + showIcon: true +}; + +export default NoEntitiesFound; diff --git a/src/components/InventoryTable/NoEntitiesFound.test.js b/src/components/InventoryTable/NoEntitiesFound.test.js new file mode 100644 index 000000000..5504c73e1 --- /dev/null +++ b/src/components/InventoryTable/NoEntitiesFound.test.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import NoEntitiesFound from './NoEntitiesFound'; + +describe('NoSystemsTable', () => { + it('should render correctly - no systems', () => { + const wrapper = mount(); + expect(toJson(wrapper)).toMatchSnapshot(); + expect(wrapper.find('h5').text()).toBe('No matching systems found'); + }); + + it('should render correctly - no groups', () => { + const wrapper = mount(); + expect(toJson(wrapper)).toMatchSnapshot(); + expect(wrapper.find('h5').text()).toBe('No matching groups found'); + }); + + it('renders link if callback provided', () => { + const wrapper = mount( 42}/>); + expect(wrapper.find('.pf-m-link').text()).toBe('Clear all filters'); + }); +}); diff --git a/src/components/InventoryTable/NoSystemsTable.js b/src/components/InventoryTable/NoSystemsTable.js deleted file mode 100644 index 7e67d94ce..000000000 --- a/src/components/InventoryTable/NoSystemsTable.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { - EmptyStateBody, - EmptyState, - EmptyStateVariant, - Title -} from '@patternfly/react-core'; - -/** - * Empty state stable when no systems are found. - */ -const NoSystemsTable = () => ( - - - No matching systems found - - - To continue, edit your filter settings and search again. - - -); - -export default NoSystemsTable; diff --git a/src/components/InventoryTable/NoSystemsTable.test.js b/src/components/InventoryTable/NoSystemsTable.test.js deleted file mode 100644 index 7401ea41a..000000000 --- a/src/components/InventoryTable/NoSystemsTable.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import toJson from 'enzyme-to-json'; -import NoSystemsTable from './NoSystemsTable'; - -describe('NoSystemsTable', () => { - it('should render correctly - no data', () => { - const wrapper = mount(); - expect(toJson(wrapper)).toMatchSnapshot(); - }); -}); diff --git a/src/components/InventoryTable/__snapshots__/NoEntitiesFound.test.js.snap b/src/components/InventoryTable/__snapshots__/NoEntitiesFound.test.js.snap new file mode 100644 index 000000000..61bc75c4f --- /dev/null +++ b/src/components/InventoryTable/__snapshots__/NoEntitiesFound.test.js.snap @@ -0,0 +1,155 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NoSystemsTable should render correctly - no groups 1`] = ` + + +
+
+ + + + + <h5 + className="pf-c-title pf-m-lg" + data-ouia-component-id="OUIA-Generated-Title-2" + data-ouia-component-type="PF4/Title" + data-ouia-safe={true} + > + No matching groups found + </h5> + + +
+ To continue, edit your filter settings and try again +
+
+
+
+
+
+`; + +exports[`NoSystemsTable should render correctly - no systems 1`] = ` + + +
+
+ + + + + <h5 + className="pf-c-title pf-m-lg" + data-ouia-component-id="OUIA-Generated-Title-1" + data-ouia-component-type="PF4/Title" + data-ouia-safe={true} + > + No matching systems found + </h5> + + +
+ To continue, edit your filter settings and try again +
+
+
+
+
+
+`; diff --git a/src/components/InventoryTable/__snapshots__/NoSystemsTable.test.js.snap b/src/components/InventoryTable/__snapshots__/NoSystemsTable.test.js.snap deleted file mode 100644 index 511101497..000000000 --- a/src/components/InventoryTable/__snapshots__/NoSystemsTable.test.js.snap +++ /dev/null @@ -1,38 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NoSystemsTable should render correctly - no data 1`] = ` - - -
-
- - <h5 - className="pf-c-title pf-m-lg" - data-ouia-component-id="OUIA-Generated-Title-1" - data-ouia-component-type="PF4/Title" - data-ouia-safe={true} - > - No matching systems found - </h5> - - -
- To continue, edit your filter settings and search again. -
-
-
-
-
-
-`; From 78bb7e4d87b1fb0be424280d75909f51b30d5c7d Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 12:30:23 +0100 Subject: [PATCH 09/14] Fix actions.test.js, update snapshots --- .../__snapshots__/EntityTable.test.js.snap | 19 ++++++++++++++++++- .../__snapshots__/InventoryList.test.js.snap | 19 ++++++++++++++++++- src/store/actions.test.js | 2 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap b/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap index ecd5862f3..340c86783 100644 --- a/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap +++ b/src/components/InventoryTable/__snapshots__/EntityTable.test.js.snap @@ -379,10 +379,27 @@ exports[`EntityTable DOM should render correctly - no rows 1`] = ` >
+
- To continue, edit your filter settings and search again. + To continue, edit your filter settings and try again
diff --git a/src/components/InventoryTable/__snapshots__/InventoryList.test.js.snap b/src/components/InventoryTable/__snapshots__/InventoryList.test.js.snap index e3a254ee9..8e8651a54 100644 --- a/src/components/InventoryTable/__snapshots__/InventoryList.test.js.snap +++ b/src/components/InventoryTable/__snapshots__/InventoryList.test.js.snap @@ -98,10 +98,27 @@ exports[`InventoryList should render correctly 1`] = ` >
+
- To continue, edit your filter settings and search again. + To continue, edit your filter settings and try again
diff --git a/src/store/actions.test.js b/src/store/actions.test.js index 128aa436b..e3bf3a6d0 100644 --- a/src/store/actions.test.js +++ b/src/store/actions.test.js @@ -64,7 +64,7 @@ describe('editAnsibleHost', () => { describe('fetchGroups', () => { it('should call correct endpoint', async () => { - mocked.onGet('/api/inventory/v1/groups').reply(() => { + mocked.onGet(new RegExp('/api/inventory/v1/groups*')).reply(() => { return [200, mockedGroups]; }); const { type, payload } = await fetchGroups(); From 7649a3ae0127a6291c047ab3e1d4390179967f76 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 12:34:58 +0100 Subject: [PATCH 10/14] Center the total systems column --- src/components/GroupsTable/GroupsTable.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index 64b7103ce..9e64bcde6 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -1,7 +1,7 @@ /* eslint-disable camelcase */ import { Pagination, PaginationVariant } from '@patternfly/react-core'; import { - fitContent, + cellWidth, sortable, Table, TableBody, @@ -31,15 +31,15 @@ const GROUPS_TABLE_INITIAL_STATE = { const GROUPS_TABLE_COLUMNS = [ { title: 'Name', - transforms: [sortable] + transforms: [sortable, cellWidth(40)] }, { title: 'Total systems', - transforms: [sortable, fitContent] + transforms: [sortable, cellWidth(20)] }, { title: 'Last modified', - transforms: [sortable, fitContent] + transforms: [sortable, cellWidth(20)] } ]; From 60cb8cd009cdcb8eacdcd4d18f1d91c6ad8071dc Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 15:50:22 +0100 Subject: [PATCH 11/14] Use SearchInput PF component --- src/components/GroupsTable/GroupsTable.cy.js | 2 +- src/components/GroupsTable/GroupsTable.js | 33 +++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/components/GroupsTable/GroupsTable.cy.js b/src/components/GroupsTable/GroupsTable.cy.js index 7f8aa7b40..f26832ec2 100644 --- a/src/components/GroupsTable/GroupsTable.cy.js +++ b/src/components/GroupsTable/GroupsTable.cy.js @@ -209,7 +209,7 @@ describe('edge cases', () => { cy.wait('@getGroups').then(() => { cy.get('.pf-c-empty-state').find('h4').contains('Something went wrong'); // the filter is disabled - cy.ouiaId('ConditionalFilter').should('have.attr', 'disabled'); + cy.ouiaId('name-filter').find('input').should('have.attr', 'disabled'); cy.ouiaId('pager').find('button').should('have.attr', 'disabled'); }); }); diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index 9e64bcde6..eff549963 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -1,5 +1,9 @@ /* eslint-disable camelcase */ -import { Pagination, PaginationVariant } from '@patternfly/react-core'; +import { + Pagination, + PaginationVariant, + SearchInput +} from '@patternfly/react-core'; import { cellWidth, sortable, @@ -103,14 +107,29 @@ const GroupsTable = () => { const filterConfigItems = [ { + type: 'custom', label: 'Name', filterValues: { - key: 'name-filter', - onChange: (event, value) => - setFilters({ ...filters, hostname_or_id: value }), - value: filters.hostname_or_id, - placeholder: 'Filter by name', - isDisabled: rejected + children: ( + { + const { hostname_or_id, ...fs } = filters; + return setFilters({ + ...fs, + ...(value.length > 0 ? { hostname_or_id: value } : {}) + }); + }} + onClear={() => { + const { hostname_or_id, ...fs } = filters; + return setFilters(fs); + }} + isDisabled={rejected} + /> + ) } } ]; From 90e5d8d41c1358cbd37d2b6c9aba55a241f5afd8 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Wed, 15 Feb 2023 15:51:33 +0100 Subject: [PATCH 12/14] Leave TODO note --- src/components/GroupsTable/GroupsTable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index eff549963..883130197 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -192,6 +192,7 @@ const GroupsTable = () => { cells: [ { title: rejected ? ( + // TODO: don't render the primary button (requires change in FF) ) : ( Date: Thu, 16 Feb 2023 11:24:05 +0100 Subject: [PATCH 13/14] Wrap filter config items with useMemo --- src/components/GroupsTable/GroupsTable.js | 57 ++++++++++++----------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index 883130197..6800a1f73 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -105,34 +105,37 @@ const GroupsTable = () => { setFilters({ ...filters, sortIndex: index, sortDirection: direction }); }; - const filterConfigItems = [ - { - type: 'custom', - label: 'Name', - filterValues: { - children: ( - { - const { hostname_or_id, ...fs } = filters; - return setFilters({ - ...fs, - ...(value.length > 0 ? { hostname_or_id: value } : {}) - }); - }} - onClear={() => { - const { hostname_or_id, ...fs } = filters; - return setFilters(fs); - }} - isDisabled={rejected} - /> - ) + const filterConfigItems = useMemo( + () => [ + { + type: 'custom', + label: 'Name', + filterValues: { + children: ( + { + const { hostname_or_id, ...fs } = filters; + return setFilters({ + ...fs, + ...(value.length > 0 ? { hostname_or_id: value } : {}) + }); + }} + onClear={() => { + const { hostname_or_id, ...fs } = filters; + return setFilters(fs); + }} + isDisabled={rejected} + /> + ) + } } - } - ]; + ], + [filters.hostname_or_id, rejected] + ); const onResetFilters = () => setFilters(GROUPS_TABLE_INITIAL_STATE); From f4ac110387d77f9d7fc7b2f317b8d98a67a1ef01 Mon Sep 17 00:00:00 2001 From: Georgii Karataev Date: Thu, 16 Feb 2023 11:33:23 +0100 Subject: [PATCH 14/14] Extract table rows to memoized function --- src/components/GroupsTable/GroupsTable.js | 58 ++++++++++++----------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/components/GroupsTable/GroupsTable.js b/src/components/GroupsTable/GroupsTable.js index 6800a1f73..c33176be7 100644 --- a/src/components/GroupsTable/GroupsTable.js +++ b/src/components/GroupsTable/GroupsTable.js @@ -161,6 +161,36 @@ const GroupsTable = () => { const onPerPageSelect = (event, perPage) => setFilters({ ...filters, perPage, page: 1 }); // will also reset the page to first + const tableRows = useMemo( + () => + uninitialized || loading + ? generateLoadingRows(GROUPS_TABLE_COLUMNS.length, filters.perPage) + : rejected || rows.length === 0 + ? [ + { + fullWidth: true, + cells: [ + { + title: rejected ? ( + // TODO: don't render the primary button (requires change in FF) + + ) : ( + + ), + props: { + colSpan: GROUPS_TABLE_COLUMNS.length + 1 + } + } + ] + } + ] + : rows, + [uninitialized, loading, rejected, rows, filters.perPage] + ); + // TODO: use ouiaSafe to indicate the loading state for e2e tests return ( @@ -185,33 +215,7 @@ const GroupsTable = () => { /* ouiaSafe={!loadingState}> */ variant={TableVariant.compact} cells={GROUPS_TABLE_COLUMNS} - rows={ - uninitialized || loading - ? generateLoadingRows(GROUPS_TABLE_COLUMNS.length, filters.perPage) - : rejected || rows.length === 0 - ? [ - { - fullWidth: true, - cells: [ - { - title: rejected ? ( - // TODO: don't render the primary button (requires change in FF) - - ) : ( - - ), - props: { - colSpan: GROUPS_TABLE_COLUMNS.length + 1 - } - } - ] - } - ] - : rows - } + rows={tableRows} sortBy={{ index: filters.sortIndex, direction: filters.sortDirection