From 4674262172d52ee08b699cb236fd510452e3b003 Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Wed, 1 Sep 2021 15:59:30 +1000 Subject: [PATCH] run prettier --- .prettierrc | 3 +- LICENSE | 338 ++-- cloudbuild.yaml | 68 +- craco.config.js | 34 +- createDotEnv.js | 16 +- firebase.json | 22 +- src/App.test.tsx | 6 +- src/App.tsx | 200 +-- src/Themes.tsx | 64 +- src/analytics.ts | 14 +- src/assets/Logo.tsx | 42 +- src/assets/icons/AddColumn.tsx | 10 +- src/assets/icons/AddRow.tsx | 10 +- src/assets/icons/Backburger.tsx | 10 +- src/assets/icons/CellResize.tsx | 10 +- src/assets/icons/CloudLogs.tsx | 10 +- src/assets/icons/ColumnPlusAfter.tsx | 10 +- src/assets/icons/ColumnPlusBefore.tsx | 10 +- src/assets/icons/ColumnRemove.tsx | 10 +- src/assets/icons/ConnectTable.tsx | 10 +- src/assets/icons/CopyCells.tsx | 10 +- src/assets/icons/Derivative.tsx | 10 +- src/assets/icons/Export.tsx | 10 +- src/assets/icons/Extension.tsx | 10 +- src/assets/icons/FileUpload.tsx | 10 +- src/assets/icons/Freeze.tsx | 16 +- src/assets/icons/Go.tsx | 2 +- src/assets/icons/Id.tsx | 10 +- src/assets/icons/Image.tsx | 10 +- src/assets/icons/Import.tsx | 10 +- src/assets/icons/Json.tsx | 10 +- src/assets/icons/MultiSelect.tsx | 10 +- src/assets/icons/Number.tsx | 10 +- src/assets/icons/Percentage.tsx | 10 +- src/assets/icons/RowHeight.tsx | 10 +- src/assets/icons/SingleSelect.tsx | 10 +- src/assets/icons/Slider.tsx | 10 +- src/assets/icons/Status.tsx | 10 +- src/assets/icons/SubTable.tsx | 10 +- src/assets/icons/Unfreeze.tsx | 16 +- src/assets/icons/Upload.tsx | 10 +- src/components/AppBar.tsx | 108 +- src/components/Auth/AuthLayout.tsx | 124 +- src/components/Auth/FirebaseUi.tsx | 408 ++--- src/components/BuilderInstaller.tsx | 106 +- src/components/ButtonWithStatus.tsx | 88 +- src/components/CodeEditor.tsx | 180 +- src/components/CodeEditorHelper/index.tsx | 148 +- src/components/Confirmation.tsx | 196 +- src/components/ConfirmationDialog/Context.ts | 2 +- src/components/ConfirmationDialog/Dialog.tsx | 136 +- .../ConfirmationDialog/Provider.tsx | 50 +- src/components/ConfirmationDialog/props.ts | 38 +- .../ConnectServiceSelect/PopupContents.tsx | 338 ++-- src/components/ConnectServiceSelect/index.tsx | 104 +- src/components/ConnectServiceSelect/styles.ts | 126 +- src/components/EmptyState.tsx | 166 +- src/components/ErrorBoundary.tsx | 100 +- src/components/FormattedChip.tsx | 54 +- src/components/Grid/AlgoliaFilters.tsx | 386 ++-- src/components/Grid/Card/index.tsx | 434 ++--- src/components/Grid/Card/styles.ts | 90 +- src/components/Grid/index.tsx | 126 +- src/components/HelperText.tsx | 26 +- src/components/HomeNavigation/NavDrawer.tsx | 196 +- src/components/HomeNavigation/index.tsx | 270 +-- src/components/Loading.tsx | 66 +- src/components/Modal/SlideTransition.tsx | 104 +- src/components/Modal/index.tsx | 194 +- src/components/Navigation/Breadcrumbs.tsx | 212 +-- src/components/Navigation/NavDrawer.tsx | 138 +- src/components/Navigation/NavDrawerItem.tsx | 198 +-- .../Navigation/Notifications/index.tsx | 176 +- src/components/Navigation/UpdateChecker.tsx | 196 +- src/components/Navigation/UserMenu.tsx | 400 ++--- src/components/Navigation/index.tsx | 138 +- src/components/ProjectSettings/form.tsx | 126 +- src/components/ProjectSettings/index.tsx | 86 +- src/components/RenderedHtml.tsx | 176 +- src/components/RichTextEditor.tsx | 208 +-- src/components/RichTooltip.tsx | 176 +- src/components/SideDrawer/Form/Autosave.tsx | 100 +- .../SideDrawer/Form/FieldSkeleton.tsx | 22 +- .../SideDrawer/Form/FieldWrapper.tsx | 200 +-- src/components/SideDrawer/Form/Label.tsx | 58 +- src/components/SideDrawer/Form/Reset.tsx | 62 +- src/components/SideDrawer/Form/index.tsx | 174 +- src/components/SideDrawer/Form/utils.ts | 70 +- src/components/SideDrawer/index.tsx | 258 +-- src/components/SideDrawer/useStyles.ts | 182 +- src/components/Snack.tsx | 166 +- src/components/StyledCard.tsx | 300 ++-- src/components/Table/BulkActions/index.tsx | 524 +++--- src/components/Table/CellValidation.tsx | 178 +- src/components/Table/ColumnHeader.tsx | 538 +++--- .../FieldSettings/DefaultValueInput.tsx | 288 +-- .../ColumnMenu/FieldSettings/FormAutosave.tsx | 20 +- .../Table/ColumnMenu/FieldSettings/index.tsx | 300 ++-- .../Table/ColumnMenu/FieldsDropdown.tsx | 114 +- .../Table/ColumnMenu/MenuContents.tsx | 142 +- .../Table/ColumnMenu/NameChange.tsx | 82 +- src/components/Table/ColumnMenu/NewColumn.tsx | 214 +-- .../Table/ColumnMenu/Subheading.tsx | 28 +- .../Table/ColumnMenu/TypeChange.tsx | 78 +- src/components/Table/ColumnMenu/index.tsx | 508 +++--- src/components/Table/EmptyTable.tsx | 252 +-- src/components/Table/Filters/Row.tsx | 2 +- src/components/Table/Filters/index.tsx | 898 +++++----- src/components/Table/FinalColumnHeader.tsx | 108 +- src/components/Table/HiddenFields.tsx | 224 +-- src/components/Table/HotKeys.tsx | 178 +- src/components/Table/Settings/Menu.tsx | 92 +- src/components/Table/Settings/Webhooks.tsx | 302 ++-- src/components/Table/Settings/index.tsx | 14 +- .../Table/Skeleton/HeaderRowSkeleton.tsx | 52 +- .../Table/Skeleton/TableHeaderSkeleton.tsx | 66 +- .../Table/TableHeader/Export/Download.tsx | 328 ++-- .../Table/TableHeader/Export/Export.tsx | 352 ++-- .../Table/TableHeader/Export/index.tsx | 222 +-- .../TableHeader/Extensions/ExtensionList.tsx | 382 ++-- .../Extensions/ExtensionMigration.tsx | 248 +-- .../TableHeader/Extensions/ExtensionModal.tsx | 970 +++++----- .../Table/TableHeader/Extensions/index.tsx | 494 +++--- .../Table/TableHeader/Extensions/utils.ts | 270 +-- .../Table/TableHeader/ImportCsv.tsx | 508 +++--- .../Table/TableHeader/ReExecute.tsx | 116 +- .../Table/TableHeader/RowHeight.tsx | 104 +- .../Table/TableHeader/TableHeaderButton.tsx | 38 +- .../Table/TableHeader/TableLogs.tsx | 992 +++++------ .../Table/TableHeader/TableSettings.tsx | 34 +- src/components/Table/TableHeader/index.tsx | 208 +-- src/components/Table/editors/CodeEditor.tsx | 444 ++--- src/components/Table/editors/NullEditor.tsx | 8 +- src/components/Table/editors/TextEditor.tsx | 172 +- src/components/Table/editors/styles.ts | 6 +- .../Table/editors/withSideDrawerEditor.tsx | 40 +- .../Table/formatters/FinalColumn.tsx | 172 +- src/components/Table/index.tsx | 406 ++--- src/components/Table/styles.ts | 182 +- src/components/TableSettings/form.tsx | 374 ++-- src/components/TableSettings/index.tsx | 454 ++--- src/components/Thumbnail.tsx | 168 +- src/components/Wizards/Cell.tsx | 160 +- src/components/Wizards/Column.tsx | 160 +- src/components/Wizards/FadeList.tsx | 74 +- .../Wizards/ImportCsvWizard/Step1Columns.tsx | 462 ++--- .../ImportCsvWizard/Step2NewColumns.tsx | 274 +-- .../Wizards/ImportCsvWizard/Step3Preview.tsx | 182 +- .../Wizards/ImportCsvWizard/index.tsx | 282 +-- .../Wizards/ImportWizard/Step1Columns.tsx | 344 ++-- .../Wizards/ImportWizard/Step2Rename.tsx | 230 +-- .../Wizards/ImportWizard/Step3Types.tsx | 240 +-- .../Wizards/ImportWizard/Step4Preview.tsx | 166 +- src/components/Wizards/ImportWizard/index.tsx | 200 +-- src/components/Wizards/ImportWizard/utils.ts | 116 +- src/components/Wizards/WizardDialog.tsx | 418 ++--- src/components/fields/Action/ActionFab.tsx | 242 +-- src/components/fields/Action/BasicCell.tsx | 2 +- .../fields/Action/FormDialog/Context.ts | 2 +- .../fields/Action/FormDialog/Dialog.tsx | 62 +- .../fields/Action/FormDialog/Provider.tsx | 50 +- .../fields/Action/FormDialog/props.ts | 28 +- src/components/fields/Action/Settings.tsx | 324 ++-- .../fields/Action/SideDrawerField.tsx | 116 +- src/components/fields/Action/TableCell.tsx | 84 +- src/components/fields/Action/index.tsx | 32 +- src/components/fields/Aggregate/Settings.tsx | 94 +- src/components/fields/Aggregate/index.tsx | 22 +- .../fields/Checkbox/SideDrawerField.tsx | 102 +- src/components/fields/Checkbox/TableCell.tsx | 110 +- src/components/fields/Checkbox/index.tsx | 40 +- src/components/fields/Code/BasicCell.tsx | 32 +- .../fields/Code/SideDrawerField.tsx | 68 +- src/components/fields/Code/index.tsx | 24 +- src/components/fields/Color/InlineCell.tsx | 80 +- src/components/fields/Color/PopoverCell.tsx | 8 +- .../fields/Color/SideDrawerField.tsx | 140 +- src/components/fields/Color/index.tsx | 30 +- .../ConnectServiceSelect/PopupContents.tsx | 336 ++-- .../ConnectServiceSelect/index.tsx | 110 +- .../ConnectServiceSelect/styles.ts | 126 +- .../fields/ConnectService/InlineCell.tsx | 126 +- .../fields/ConnectService/PopoverCell.tsx | 60 +- .../fields/ConnectService/Settings.tsx | 122 +- .../fields/ConnectService/SideDrawerField.tsx | 124 +- .../fields/ConnectService/index.tsx | 42 +- src/components/fields/ConnectService/utils.ts | 4 +- .../ConnectTable/ConnectTableSelect.tsx | 372 ++-- .../fields/ConnectTable/InlineCell.tsx | 132 +- .../fields/ConnectTable/PopoverCell.tsx | 64 +- .../fields/ConnectTable/Settings.tsx | 142 +- .../fields/ConnectTable/SideDrawerField.tsx | 118 +- src/components/fields/ConnectTable/index.tsx | 42 +- src/components/fields/ConnectTable/utils.ts | 4 +- src/components/fields/Date/BasicCell.tsx | 28 +- src/components/fields/Date/Settings.tsx | 26 +- .../fields/Date/SideDrawerField.tsx | 84 +- src/components/fields/Date/TableCell.tsx | 150 +- src/components/fields/Date/index.tsx | 40 +- src/components/fields/Date/utils.ts | 12 +- src/components/fields/DateTime/BasicCell.tsx | 28 +- .../fields/DateTime/SideDrawerField.tsx | 108 +- src/components/fields/DateTime/TableCell.tsx | 152 +- src/components/fields/DateTime/index.tsx | 34 +- src/components/fields/Derivative/Settings.tsx | 106 +- src/components/fields/Derivative/index.tsx | 26 +- .../fields/Duration/SideDrawerField.tsx | 44 +- src/components/fields/Duration/TableCell.tsx | 18 +- src/components/fields/Duration/index.tsx | 30 +- src/components/fields/Duration/utils.ts | 18 +- .../fields/Email/SideDrawerField.tsx | 60 +- src/components/fields/Email/index.tsx | 24 +- .../fields/File/SideDrawerField.tsx | 300 ++-- src/components/fields/File/TableCell.tsx | 298 ++-- src/components/fields/File/index.tsx | 26 +- src/components/fields/Id/SideDrawerField.tsx | 20 +- src/components/fields/Id/TableCell.tsx | 16 +- src/components/fields/Id/index.tsx | 22 +- .../fields/Image/SideDrawerField.tsx | 472 ++--- src/components/fields/Image/TableCell.tsx | 478 ++--- src/components/fields/Image/index.tsx | 38 +- src/components/fields/Json/BasicCell.tsx | 44 +- src/components/fields/Json/Settings.tsx | 28 +- .../fields/Json/SideDrawerField.tsx | 176 +- src/components/fields/Json/index.tsx | 42 +- src/components/fields/LongText/BasicCell.tsx | 28 +- .../fields/LongText/SideDrawerField.tsx | 58 +- src/components/fields/LongText/index.tsx | 28 +- .../MultiSelect/ConvertStringToArray.tsx | 42 +- .../fields/MultiSelect/InlineCell.tsx | 130 +- .../fields/MultiSelect/PopoverCell.tsx | 62 +- .../fields/MultiSelect/SideDrawerField.tsx | 112 +- src/components/fields/MultiSelect/index.tsx | 64 +- src/components/fields/MultiSelect/utils.ts | 4 +- src/components/fields/Number/BasicCell.tsx | 2 +- .../fields/Number/SideDrawerField.tsx | 56 +- src/components/fields/Number/index.tsx | 40 +- .../fields/Percentage/BasicCell.tsx | 58 +- .../fields/Percentage/SideDrawerField.tsx | 108 +- src/components/fields/Percentage/index.tsx | 44 +- .../fields/Phone/SideDrawerField.tsx | 62 +- src/components/fields/Phone/index.tsx | 26 +- src/components/fields/Rating/Settings.tsx | 68 +- .../fields/Rating/SideDrawerField.tsx | 76 +- src/components/fields/Rating/TableCell.tsx | 62 +- src/components/fields/Rating/index.tsx | 32 +- src/components/fields/Rating/styles.ts | 8 +- .../fields/RichText/SideDrawerField.tsx | 24 +- src/components/fields/RichText/TableCell.tsx | 146 +- src/components/fields/RichText/index.tsx | 30 +- src/components/fields/ShortText/Settings.tsx | 52 +- .../fields/ShortText/SideDrawerField.tsx | 54 +- src/components/fields/ShortText/index.tsx | 32 +- .../fields/SingleSelect/InlineCell.tsx | 80 +- .../fields/SingleSelect/PopoverCell.tsx | 62 +- .../fields/SingleSelect/Settings.tsx | 182 +- .../fields/SingleSelect/SideDrawerField.tsx | 66 +- src/components/fields/SingleSelect/index.tsx | 46 +- src/components/fields/SingleSelect/utils.ts | 6 +- src/components/fields/Slider/Settings.tsx | 90 +- .../fields/Slider/SideDrawerField.tsx | 146 +- src/components/fields/Slider/TableCell.tsx | 96 +- src/components/fields/Slider/index.tsx | 30 +- src/components/fields/Status/Settings.tsx | 408 ++--- .../fields/Status/SideDrawerField.tsx | 26 +- src/components/fields/Status/TableCell.tsx | 76 +- src/components/fields/Status/index.tsx | 34 +- src/components/fields/Status/styles.ts | 8 +- src/components/fields/SubTable/Settings.tsx | 44 +- .../fields/SubTable/SideDrawerField.tsx | 50 +- src/components/fields/SubTable/TableCell.tsx | 70 +- src/components/fields/SubTable/index.tsx | 38 +- src/components/fields/SubTable/utils.ts | 52 +- src/components/fields/Url/BasicCell.tsx | 26 +- src/components/fields/Url/SideDrawerField.tsx | 78 +- src/components/fields/Url/index.tsx | 24 +- .../fields/User/SideDrawerField.tsx | 94 +- src/components/fields/User/TableCell.tsx | 28 +- src/components/fields/User/index.tsx | 26 +- .../fields/_BasicCell/BasicCellName.tsx | 2 +- .../fields/_BasicCell/BasicCellNull.tsx | 2 +- .../fields/_BasicCell/BasicCellValue.tsx | 2 +- .../fields/_withTableCell/withBasicCell.tsx | 42 +- .../fields/_withTableCell/withHeavyCell.tsx | 140 +- .../fields/_withTableCell/withPopoverCell.tsx | 276 +-- src/components/fields/index.tsx | 98 +- src/components/fields/types.ts | 66 +- src/constants/fields.ts | 80 +- src/constants/routes.ts | 32 +- src/constants/wikiLinks.ts | 18 +- src/contexts/AppContext.tsx | 190 +- src/contexts/EditorContext.ts | 40 +- src/contexts/RowyContext.tsx | 328 ++-- src/contexts/SnackContext.tsx | 74 +- src/contexts/SnackLogContext.tsx | 66 +- src/firebase/callables.ts | 44 +- src/firebase/config.ts | 12 +- src/firebase/firebaseui.ts | 58 +- src/firebase/index.ts | 12 +- src/hooks/useCollection.ts | 244 +-- src/hooks/useDoc.ts | 154 +- src/hooks/useHotkeys.ts | 16 +- src/hooks/useKeyPress.ts | 50 +- src/hooks/useRouter.ts | 6 +- src/hooks/useRowy/index.ts | 196 +- src/hooks/useRowy/useTable.tsx | 666 +++---- src/hooks/useRowy/useTableConfig.ts | 286 +-- src/hooks/useRowy/useUploader.ts | 206 +-- src/hooks/useSettings.ts | 174 +- src/hooks/useWindowSize.ts | 34 +- src/pages/Auth/ImpersonatorAuth.tsx | 132 +- src/pages/Auth/JwtAuth.tsx | 64 +- src/pages/Auth/SetupGuide.tsx | 44 +- src/pages/Auth/SignOut.tsx | 44 +- src/pages/Auth/index.tsx | 10 +- src/pages/Grid.tsx | 44 +- src/pages/Home.tsx | 578 +++--- src/pages/Table.tsx | 100 +- src/pages/Test.tsx | 1578 ++++++++--------- src/serviceWorker.ts | 218 +-- src/theme/colors.ts | 290 +-- src/theme/components.tsx | 1204 ++++++------- src/theme/palette.ts | 278 +-- src/theme/typography.ts | 252 +-- src/utils/CustomBrowserRouter.tsx | 20 +- src/utils/PrivateRoute.tsx | 20 +- src/utils/auth.ts | 46 +- src/utils/color.ts | 6 +- src/utils/fns.ts | 208 +-- 329 files changed, 22103 insertions(+), 22104 deletions(-) diff --git a/.prettierrc b/.prettierrc index efe063ef..5b5bd993 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,3 @@ { - "trailingComma": "es5", - "proseWrap": "always" + "proseWrap": "always" } diff --git a/LICENSE b/LICENSE index 1d0e1c13..261eeb9e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 1. Definitions. + 1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. + APPENDIX: How to apply the Apache License to your work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [yyyy] [name of copyright owner] - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 75714cd4..c026f338 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -1,37 +1,37 @@ steps: - - name: node:10.17.0 - entrypoint: yarn - args: ["install"] - - name: node:10.17.0 - entrypoint: yarn - args: - - env - - "${_PROJECT_ID}" - - "${_FIREBASE_WEB_API_KEY}" - - "${_ALGOLIA_APP_ID}" - - "${_ALGOLIA_APP_KEY}" - - name: node:10.17.0 - entrypoint: yarn - args: ["build"] - - name: node:10.17.0 - entrypoint: yarn - args: - - "target" - - "${_HOSTING_TARGET}" - - --project - - "${_PROJECT_ID}" - - name: node:10.17.0 - entrypoint: yarn - args: - - deploy - - --project - - "${_PROJECT_ID}" - - --debug - - --token - - "${_FIREBASE_TOKEN}" - - --only - - hosting + - name: node:10.17.0 + entrypoint: yarn + args: ["install"] + - name: node:10.17.0 + entrypoint: yarn + args: + - env + - "${_PROJECT_ID}" + - "${_FIREBASE_WEB_API_KEY}" + - "${_ALGOLIA_APP_ID}" + - "${_ALGOLIA_APP_KEY}" + - name: node:10.17.0 + entrypoint: yarn + args: ["build"] + - name: node:10.17.0 + entrypoint: yarn + args: + - "target" + - "${_HOSTING_TARGET}" + - --project + - "${_PROJECT_ID}" + - name: node:10.17.0 + entrypoint: yarn + args: + - deploy + - --project + - "${_PROJECT_ID}" + - --debug + - --token + - "${_FIREBASE_TOKEN}" + - --only + - hosting substitutions: - _PROJECT_ID: "project-id" # default value + _PROJECT_ID: "project-id" # default value options: - machineType: "N1_HIGHCPU_8" + machineType: "N1_HIGHCPU_8" diff --git a/craco.config.js b/craco.config.js index 247de392..8b70a80b 100644 --- a/craco.config.js +++ b/craco.config.js @@ -1,21 +1,21 @@ const CracoSwcPlugin = require("craco-swc"); module.exports = { - plugins: [ - { - plugin: CracoSwcPlugin, - options: { - swcLoaderOptions: { - jsc: { - target: "es2019", - transform: { - react: { - runtime: "automatic", - }, - }, - }, - }, - }, - }, - ], + plugins: [ + { + plugin: CracoSwcPlugin, + options: { + swcLoaderOptions: { + jsc: { + target: "es2019", + transform: { + react: { + runtime: "automatic", + }, + }, + }, + }, + }, + }, + ], }; diff --git a/createDotEnv.js b/createDotEnv.js index e38b9c76..623ddb7f 100644 --- a/createDotEnv.js +++ b/createDotEnv.js @@ -1,18 +1,18 @@ const fs = require("fs"); const main = ( - projectID = "", - firebaseWebApiKey = "", - algoliaAppId = "", - algoliaSearhApiKey = "" + projectID = "", + firebaseWebApiKey = "", + algoliaAppId = "", + algoliaSearhApiKey = "" ) => { - return fs.writeFileSync( - ".env", - `REACT_APP_FIREBASE_PROJECT_ID = ${projectID} + return fs.writeFileSync( + ".env", + `REACT_APP_FIREBASE_PROJECT_ID = ${projectID} REACT_APP_FIREBASE_PROJECT_WEB_API_KEY = ${firebaseWebApiKey} REACT_APP_ALGOLIA_APP_ID = ${algoliaAppId} REACT_APP_ALGOLIA_SEARCH_API_KEY = ${algoliaSearhApiKey}` - ); + ); }; main(process.argv[2], process.argv[3], process.argv[4], process.argv[5]); diff --git a/firebase.json b/firebase.json index dd8815ef..a72a4d28 100644 --- a/firebase.json +++ b/firebase.json @@ -1,13 +1,13 @@ { - "hosting": { - "target": "rowy", - "public": "build", - "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], - "rewrites": [ - { - "source": "**", - "destination": "/index.html" - } - ] - } + "hosting": { + "target": "rowy", + "public": "build", + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } } diff --git a/src/App.test.tsx b/src/App.test.tsx index 635a333f..23c181f0 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom"; import App from "./App"; it("renders without crashing", () => { - const div = document.createElement("div"); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); + const div = document.createElement("div"); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); }); diff --git a/src/App.tsx b/src/App.tsx index d14bd13b..c06609d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,120 +23,120 @@ import SignOutView from "pages/Auth/SignOut"; import TestView from "pages/Test"; import { analytics } from "analytics"; const AuthSetupGuidePage = lazy( - () => import("pages/Auth/SetupGuide" /* webpackChunkName: "AuthSetupGuide" */) + () => import("pages/Auth/SetupGuide" /* webpackChunkName: "AuthSetupGuide" */) ); const HomePage = lazy( - () => import("./pages/Home" /* webpackChunkName: "HomePage" */) + () => import("./pages/Home" /* webpackChunkName: "HomePage" */) ); const TablePage = lazy( - () => import("./pages/Table" /* webpackChunkName: "TablePage" */) + () => import("./pages/Table" /* webpackChunkName: "TablePage" */) ); const ImpersonatorAuthPage = lazy( - () => - import( - "./pages/Auth/ImpersonatorAuth" /* webpackChunkName: "ImpersonatorAuthPage" */ - ) + () => + import( + "./pages/Auth/ImpersonatorAuth" /* webpackChunkName: "ImpersonatorAuthPage" */ + ) ); const JwtAuthPage = lazy( - () => import("./pages/Auth/JwtAuth" /* webpackChunkName: "JwtAuthPage" */) + () => import("./pages/Auth/JwtAuth" /* webpackChunkName: "JwtAuthPage" */) ); // const GridView = lazy( // () => import("./views/GridView" /* webpackChunkName: "GridView" */) // ); export default function App() { - return ( - - - - - - - - }> - - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - ( - - - } - /> - } - /> - } - /> - - - )} - /> + return ( + + + + + + + + }> + + } + /> + } + /> + } + /> + } + /> + } + /> + } /> + ( + + + } + /> + } + /> + } + /> + + + )} + /> - ( - - Go Home - - } - fullScreen - /> - )} - /> - - - - - - - - - - ); + ( + + Go Home + + } + fullScreen + /> + )} + /> + + + + + + + + + + ); } diff --git a/src/Themes.tsx b/src/Themes.tsx index 8a854720..aa0c906e 100644 --- a/src/Themes.tsx +++ b/src/Themes.tsx @@ -6,45 +6,45 @@ import { colorsLight, colorsDark } from "theme/colors"; import { components } from "theme/components"; export const customizableLightTheme = (customization: ThemeOptions) => { - const customizedLightThemeBase = createTheme( - _merge( - {}, - typography((customization?.typography as any) ?? {}), - colorsLight() - ) - ); + const customizedLightThemeBase = createTheme( + _merge( + {}, + typography((customization?.typography as any) ?? {}), + colorsLight() + ) + ); - return createTheme( - _merge( - {}, - customizedLightThemeBase, - components(customizedLightThemeBase), - customization - ) - ); + return createTheme( + _merge( + {}, + customizedLightThemeBase, + components(customizedLightThemeBase), + customization + ) + ); }; export const customizableDarkTheme = (customization: ThemeOptions) => { - const customizedDarkThemeBase = createTheme( - _merge( - {}, - typography((customization?.typography as any) ?? {}), - colorsDark() - ) - ); + const customizedDarkThemeBase = createTheme( + _merge( + {}, + typography((customization?.typography as any) ?? {}), + colorsDark() + ) + ); - return createTheme( - _merge( - {}, - customizedDarkThemeBase, - components(customizedDarkThemeBase), - customization - ) - ); + return createTheme( + _merge( + {}, + customizedDarkThemeBase, + components(customizedDarkThemeBase), + customization + ) + ); }; const Themes = { - light: customizableLightTheme, - dark: customizableDarkTheme, + light: customizableLightTheme, + dark: customizableDarkTheme, }; export default Themes; diff --git a/src/analytics.ts b/src/analytics.ts index 8b0c855a..bc30d268 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -2,13 +2,13 @@ import firebase from "firebase/app"; import "firebase/analytics"; const firebaseConfig = { - apiKey: "AIzaSyArABiYGK7dZgwSk0pw_6vKbOt6U1ZRPpc", - authDomain: "rowy-service.firebaseapp.com", - projectId: "rowy-service", - storageBucket: "rowy-service.appspot.com", - messagingSenderId: "305614947641", - appId: "1:305614947641:web:cb10467e7c11c93d6e14e8", - measurementId: "G-0VWE25LFZJ", + apiKey: "AIzaSyArABiYGK7dZgwSk0pw_6vKbOt6U1ZRPpc", + authDomain: "rowy-service.firebaseapp.com", + projectId: "rowy-service", + storageBucket: "rowy-service.appspot.com", + messagingSenderId: "305614947641", + appId: "1:305614947641:web:cb10467e7c11c93d6e14e8", + measurementId: "G-0VWE25LFZJ", }; // Initialize Firebase diff --git a/src/assets/Logo.tsx b/src/assets/Logo.tsx index 648f74be..98a32f4a 100644 --- a/src/assets/Logo.tsx +++ b/src/assets/Logo.tsx @@ -1,27 +1,27 @@ import { useTheme } from "@material-ui/core"; export default function Logo() { - const theme = useTheme(); + const theme = useTheme(); - return ( - - rowy + return ( + + rowy - - - - ); + + + + ); } diff --git a/src/assets/icons/AddColumn.tsx b/src/assets/icons/AddColumn.tsx index f0b50c4e..92148511 100644 --- a/src/assets/icons/AddColumn.tsx +++ b/src/assets/icons/AddColumn.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiTableColumnPlusAfter } from "@mdi/js"; export default function AddColumn(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/AddRow.tsx b/src/assets/icons/AddRow.tsx index e42c807b..35c65e89 100644 --- a/src/assets/icons/AddRow.tsx +++ b/src/assets/icons/AddRow.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function AddRow(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Backburger.tsx b/src/assets/icons/Backburger.tsx index 1c0d472d..7d71de1d 100644 --- a/src/assets/icons/Backburger.tsx +++ b/src/assets/icons/Backburger.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiBackburger } from "@mdi/js"; export default function Backburger(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/CellResize.tsx b/src/assets/icons/CellResize.tsx index 387f1783..f061c186 100644 --- a/src/assets/icons/CellResize.tsx +++ b/src/assets/icons/CellResize.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiArrowSplitVertical } from "@mdi/js"; export default function CellResize(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/CloudLogs.tsx b/src/assets/icons/CloudLogs.tsx index 5bd2412d..75a843ef 100644 --- a/src/assets/icons/CloudLogs.tsx +++ b/src/assets/icons/CloudLogs.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiFileTree } from "@mdi/js"; export default function CloudLogs(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/ColumnPlusAfter.tsx b/src/assets/icons/ColumnPlusAfter.tsx index 7ca33116..6a4a22b5 100644 --- a/src/assets/icons/ColumnPlusAfter.tsx +++ b/src/assets/icons/ColumnPlusAfter.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiTableColumnPlusAfter } from "@mdi/js"; export default function ColumnPlusAfter(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/ColumnPlusBefore.tsx b/src/assets/icons/ColumnPlusBefore.tsx index 06b3f843..eeea588f 100644 --- a/src/assets/icons/ColumnPlusBefore.tsx +++ b/src/assets/icons/ColumnPlusBefore.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiTableColumnPlusBefore } from "@mdi/js"; export default function ColumnPlusBefore(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/ColumnRemove.tsx b/src/assets/icons/ColumnRemove.tsx index ccebf41f..8e854e46 100644 --- a/src/assets/icons/ColumnRemove.tsx +++ b/src/assets/icons/ColumnRemove.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiTableColumnRemove } from "@mdi/js"; export default function ColumnRemove(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/ConnectTable.tsx b/src/assets/icons/ConnectTable.tsx index 96860972..14889b6c 100644 --- a/src/assets/icons/ConnectTable.tsx +++ b/src/assets/icons/ConnectTable.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function ConnectTable(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/CopyCells.tsx b/src/assets/icons/CopyCells.tsx index 66366c94..65d4b13a 100644 --- a/src/assets/icons/CopyCells.tsx +++ b/src/assets/icons/CopyCells.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function CopyCells(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Derivative.tsx b/src/assets/icons/Derivative.tsx index 6ce52995..f771a40d 100644 --- a/src/assets/icons/Derivative.tsx +++ b/src/assets/icons/Derivative.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiFunctionVariant } from "@mdi/js"; export default function Derivative(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Export.tsx b/src/assets/icons/Export.tsx index 86848757..925e3df1 100644 --- a/src/assets/icons/Export.tsx +++ b/src/assets/icons/Export.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function Export(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Extension.tsx b/src/assets/icons/Extension.tsx index 9cecf34f..6468fe71 100644 --- a/src/assets/icons/Extension.tsx +++ b/src/assets/icons/Extension.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiPuzzleOutline } from "@mdi/js"; export default function Extension(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/FileUpload.tsx b/src/assets/icons/FileUpload.tsx index 799e33d7..7a2e21d0 100644 --- a/src/assets/icons/FileUpload.tsx +++ b/src/assets/icons/FileUpload.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiUpload } from "@mdi/js"; export default function FileUpload(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Freeze.tsx b/src/assets/icons/Freeze.tsx index 501b23f0..baf03e3a 100644 --- a/src/assets/icons/Freeze.tsx +++ b/src/assets/icons/Freeze.tsx @@ -1,12 +1,12 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function FileDownload(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Go.tsx b/src/assets/icons/Go.tsx index 1124fc3d..705609e7 100644 --- a/src/assets/icons/Go.tsx +++ b/src/assets/icons/Go.tsx @@ -3,5 +3,5 @@ import { SvgIconProps } from "@material-ui/core/SvgIcon"; /** Right chevron icon with optical alignment */ export default function Go(props: SvgIconProps) { - return ; + return ; } diff --git a/src/assets/icons/Id.tsx b/src/assets/icons/Id.tsx index bfed9c3b..2a7be4ef 100644 --- a/src/assets/icons/Id.tsx +++ b/src/assets/icons/Id.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiIdentifier } from "@mdi/js"; export default function Id(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Image.tsx b/src/assets/icons/Image.tsx index c30a4ba1..694078b1 100644 --- a/src/assets/icons/Image.tsx +++ b/src/assets/icons/Image.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiImageOutline } from "@mdi/js"; export default function Image(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Import.tsx b/src/assets/icons/Import.tsx index fd02aad9..ef9c9139 100644 --- a/src/assets/icons/Import.tsx +++ b/src/assets/icons/Import.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function Export(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Json.tsx b/src/assets/icons/Json.tsx index 9abaa29d..a3bde93e 100644 --- a/src/assets/icons/Json.tsx +++ b/src/assets/icons/Json.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiCodeJson } from "@mdi/js"; export default function Json(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/MultiSelect.tsx b/src/assets/icons/MultiSelect.tsx index 8d011994..1564e3d9 100644 --- a/src/assets/icons/MultiSelect.tsx +++ b/src/assets/icons/MultiSelect.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiFormatListBulletedSquare } from "@mdi/js"; export default function MultiSelect(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Number.tsx b/src/assets/icons/Number.tsx index 82f0f1c1..cd4e0d67 100644 --- a/src/assets/icons/Number.tsx +++ b/src/assets/icons/Number.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiNumeric } from "@mdi/js"; export default function Number(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Percentage.tsx b/src/assets/icons/Percentage.tsx index cced468f..413cb002 100644 --- a/src/assets/icons/Percentage.tsx +++ b/src/assets/icons/Percentage.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiPercent } from "@mdi/js"; export default function Percentage(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/RowHeight.tsx b/src/assets/icons/RowHeight.tsx index 89b8fbab..188bc078 100644 --- a/src/assets/icons/RowHeight.tsx +++ b/src/assets/icons/RowHeight.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiTableRowHeight } from "@mdi/js"; export default function RowHeight(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/SingleSelect.tsx b/src/assets/icons/SingleSelect.tsx index 4506b05e..0af1d051 100644 --- a/src/assets/icons/SingleSelect.tsx +++ b/src/assets/icons/SingleSelect.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function SingleSelect(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Slider.tsx b/src/assets/icons/Slider.tsx index a8768183..87bf8510 100644 --- a/src/assets/icons/Slider.tsx +++ b/src/assets/icons/Slider.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function Slider(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Status.tsx b/src/assets/icons/Status.tsx index 44b8ea76..1e74485b 100644 --- a/src/assets/icons/Status.tsx +++ b/src/assets/icons/Status.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiPulse } from "@mdi/js"; export default function Status(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/SubTable.tsx b/src/assets/icons/SubTable.tsx index 74783994..14b6c3e7 100644 --- a/src/assets/icons/SubTable.tsx +++ b/src/assets/icons/SubTable.tsx @@ -1,9 +1,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function SubTable(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Unfreeze.tsx b/src/assets/icons/Unfreeze.tsx index 61e3fccb..f72a6c4d 100644 --- a/src/assets/icons/Unfreeze.tsx +++ b/src/assets/icons/Unfreeze.tsx @@ -1,12 +1,12 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; export default function FileDownload(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/assets/icons/Upload.tsx b/src/assets/icons/Upload.tsx index 3f544aec..d1f348a1 100644 --- a/src/assets/icons/Upload.tsx +++ b/src/assets/icons/Upload.tsx @@ -2,9 +2,9 @@ import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"; import { mdiUpload } from "@mdi/js"; export default function Upload(props: SvgIconProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/src/components/AppBar.tsx b/src/components/AppBar.tsx index 7b80e012..a2d9aa1c 100644 --- a/src/components/AppBar.tsx +++ b/src/components/AppBar.tsx @@ -2,75 +2,75 @@ import { Link } from "react-router-dom"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - useTheme, - useScrollTrigger, - AppBar as MuiAppBar, - Toolbar, - Grid, - Button, + useTheme, + useScrollTrigger, + AppBar as MuiAppBar, + Toolbar, + Grid, + Button, } from "@material-ui/core"; import Logo from "assets/Logo"; import routes from "constants/routes"; const useStyles = makeStyles((theme) => - createStyles({ - appBar: { - backgroundColor: theme.palette.background.paper, - marginBottom: theme.spacing(6), - }, + createStyles({ + appBar: { + backgroundColor: theme.palette.background.paper, + marginBottom: theme.spacing(6), + }, - logo: { - display: "block", - marginRight: theme.spacing(1), - }, - heading: { - textTransform: "none", - color: theme.palette.primary.main, - cursor: "default", - userSelect: "none", - fontFeatureSettings: '"liga"', - }, + logo: { + display: "block", + marginRight: theme.spacing(1), + }, + heading: { + textTransform: "none", + color: theme.palette.primary.main, + cursor: "default", + userSelect: "none", + fontFeatureSettings: '"liga"', + }, - locationDropdown: { - minWidth: 140, - margin: 0, - }, - }) + locationDropdown: { + minWidth: 140, + margin: 0, + }, + }) ); interface IAppBarProps {} const AppBar: React.FunctionComponent = () => { - const classes = useStyles(); - const theme = useTheme(); - const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 }); + const classes = useStyles(); + const theme = useTheme(); + const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 }); - return ( - - - - - + return ( + + + + + - - - - - - ); + + + + + + ); }; export default AppBar; diff --git a/src/components/Auth/AuthLayout.tsx b/src/components/Auth/AuthLayout.tsx index a7055ea2..01456404 100644 --- a/src/components/Auth/AuthLayout.tsx +++ b/src/components/Auth/AuthLayout.tsx @@ -8,88 +8,88 @@ import bgPattern from "assets/bg-pattern.svg"; import Logo from "assets/Logo"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - backgroundBlendMode: "normal, overlay, normal, normal", - // backgroundImage: ` - // linear-gradient(to bottom, rgba(255,255,255,0), #fff), - // linear-gradient(155deg, #303030 -4%, ${theme.palette.primary.main} 92%), - // url('${bgPattern}'), - // linear-gradient(161deg, #ecf4ff -31%, #fff4f4 160%) - // `, - backgroundImage: ` + createStyles({ + root: { + backgroundBlendMode: "normal, overlay, normal, normal", + // backgroundImage: ` + // linear-gradient(to bottom, rgba(255,255,255,0), #fff), + // linear-gradient(155deg, #303030 -4%, ${theme.palette.primary.main} 92%), + // url('${bgPattern}'), + // linear-gradient(161deg, #ecf4ff -31%, #fff4f4 160%) + // `, + backgroundImage: ` linear-gradient(to bottom, ${alpha( - theme.palette.background.default, - 0 - )}, ${theme.palette.background.default} 75%), + theme.palette.background.default, + 0 + )}, ${theme.palette.background.default} 75%), linear-gradient(155deg, ${theme.palette.primary.main} 10%, ${ - theme.palette.secondary.main - } 90%), + theme.palette.secondary.main + } 90%), url('${bgPattern}'), linear-gradient(161deg, ${alpha( - theme.palette.background.default, - 0.95 - )} -31%, ${alpha(theme.palette.background.default, 0.98)} 160%) + theme.palette.background.default, + 0.95 + )} -31%, ${alpha(theme.palette.background.default, 0.98)} 160%) `, - display: "grid", - placeItems: "center", - padding: theme.spacing(1), + display: "grid", + placeItems: "center", + padding: theme.spacing(1), - cursor: "default", - }, + cursor: "default", + }, - paper: { - position: "relative", - overflow: "hidden", + paper: { + position: "relative", + overflow: "hidden", - maxWidth: 400, - width: "100%", - padding: theme.spacing(4), - backgroundColor: theme.palette.background.paper, + maxWidth: 400, + width: "100%", + padding: theme.spacing(4), + backgroundColor: theme.palette.background.paper, - "--spacing-contents": theme.spacing(4), - "& > * + *": { marginTop: "var(--spacing-contents)" }, + "--spacing-contents": theme.spacing(4), + "& > * + *": { marginTop: "var(--spacing-contents)" }, - textAlign: "center", - }, + textAlign: "center", + }, - projectName: { - display: "block", - marginTop: theme.spacing(1), + projectName: { + display: "block", + marginTop: theme.spacing(1), - color: theme.palette.text.disabled, - }, + color: theme.palette.text.disabled, + }, - progress: { - position: "absolute", - left: 0, - right: 0, - top: 0, - marginTop: 0, - }, - }) + progress: { + position: "absolute", + left: 0, + right: 0, + top: 0, + marginTop: 0, + }, + }) ); export interface IAuthLayoutProps { - children: React.ReactNode; - loading?: boolean; + children: React.ReactNode; + loading?: boolean; } export default function AuthLayout({ children, loading }: IAuthLayoutProps) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - - - {process.env.REACT_APP_FIREBASE_PROJECT_ID} - - {children} + return ( + + + + + {process.env.REACT_APP_FIREBASE_PROJECT_ID} + + {children} - {loading && } - - - ); + {loading && } + + + ); } diff --git a/src/components/Auth/FirebaseUi.tsx b/src/components/Auth/FirebaseUi.tsx index d531caba..7ab4243b 100644 --- a/src/components/Auth/FirebaseUi.tsx +++ b/src/components/Auth/FirebaseUi.tsx @@ -13,234 +13,234 @@ import { auth, db } from "../../firebase"; import { defaultUiConfig, getSignInOptions } from "../../firebase/firebaseui"; const useStyles = makeStyles((theme) => - createStyles({ - "@global": { - ".rowy-firebaseui": { - "& .firebaseui-container": { - backgroundColor: "transparent", - color: theme.palette.text.primary, - fontFamily: theme.typography.fontFamily, - }, - "& .firebaseui-text": { - color: theme.palette.text.secondary, - fontFamily: theme.typography.fontFamily, - }, - "& .firebaseui-tos": { - ...theme.typography.caption, - color: theme.palette.text.disabled, - }, - "& .firebaseui-country-selector": { - color: theme.palette.text.primary, - }, - "& .firebaseui-title": { - ...theme.typography.h5, - color: theme.palette.text.primary, - }, - "& .firebaseui-subtitle": { - ...theme.typography.h6, - color: theme.palette.text.secondary, - }, - "& .firebaseui-error": { - ...theme.typography.caption, - color: theme.palette.error.main, - }, + createStyles({ + "@global": { + ".rowy-firebaseui": { + "& .firebaseui-container": { + backgroundColor: "transparent", + color: theme.palette.text.primary, + fontFamily: theme.typography.fontFamily, + }, + "& .firebaseui-text": { + color: theme.palette.text.secondary, + fontFamily: theme.typography.fontFamily, + }, + "& .firebaseui-tos": { + ...theme.typography.caption, + color: theme.palette.text.disabled, + }, + "& .firebaseui-country-selector": { + color: theme.palette.text.primary, + }, + "& .firebaseui-title": { + ...theme.typography.h5, + color: theme.palette.text.primary, + }, + "& .firebaseui-subtitle": { + ...theme.typography.h6, + color: theme.palette.text.secondary, + }, + "& .firebaseui-error": { + ...theme.typography.caption, + color: theme.palette.error.main, + }, - "& .firebaseui-card-content, & .firebaseui-card-footer": { padding: 0 }, - "& .firebaseui-idp-list, & .firebaseui-tenant-list": { margin: 0 }, - "& .firebaseui-idp-list>.firebaseui-list-item, & .firebaseui-tenant-list>.firebaseui-list-item": { - margin: 0, - }, - "& .firebaseui-list-item + .firebaseui-list-item": { - paddingTop: theme.spacing(2), - }, + "& .firebaseui-card-content, & .firebaseui-card-footer": { padding: 0 }, + "& .firebaseui-idp-list, & .firebaseui-tenant-list": { margin: 0 }, + "& .firebaseui-idp-list>.firebaseui-list-item, & .firebaseui-tenant-list>.firebaseui-list-item": { + margin: 0, + }, + "& .firebaseui-list-item + .firebaseui-list-item": { + paddingTop: theme.spacing(2), + }, - "& .mdl-button": { - borderRadius: 24, - ...theme.typography.button, - }, - "& .mdl-button--raised": { boxShadow: "none" }, - "& .mdl-card": { - boxShadow: "none", - minHeight: 0, - }, - "& .mdl-button--primary.mdl-button--primary": { - color: theme.palette.primary.main, - }, - "& .mdl-button--raised.mdl-button--colored": { - backgroundColor: theme.palette.primary.main, - color: theme.palette.primary.contrastText, + "& .mdl-button": { + borderRadius: 24, + ...theme.typography.button, + }, + "& .mdl-button--raised": { boxShadow: "none" }, + "& .mdl-card": { + boxShadow: "none", + minHeight: 0, + }, + "& .mdl-button--primary.mdl-button--primary": { + color: theme.palette.primary.main, + }, + "& .mdl-button--raised.mdl-button--colored": { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, - "&:active, &:focus:not(:active), &:hover": { - backgroundColor: theme.palette.primary.main, - }, - }, + "&:active, &:focus:not(:active), &:hover": { + backgroundColor: theme.palette.primary.main, + }, + }, - "& .firebaseui-idp-button, & .firebaseui-tenant-button": { - maxWidth: "none", - minHeight: 48, - }, - "& .firebaseui-idp-text": { - ...theme.typography.button, + "& .firebaseui-idp-button, & .firebaseui-tenant-button": { + maxWidth: "none", + minHeight: 48, + }, + "& .firebaseui-idp-text": { + ...theme.typography.button, - paddingLeft: theme.spacing(2), - paddingRight: Number(theme.spacing(2).replace("px", "")) + 18, - marginLeft: -18, - width: "100%", - textAlign: "center", + paddingLeft: theme.spacing(2), + paddingRight: Number(theme.spacing(2).replace("px", "")) + 18, + marginLeft: -18, + width: "100%", + textAlign: "center", - [theme.breakpoints.down("sm")]: { - "&.firebaseui-idp-text-long": { display: "none" }, - "&.firebaseui-idp-text-short": { display: "table-cell" }, - }, - }, + [theme.breakpoints.down("sm")]: { + "&.firebaseui-idp-text-long": { display: "none" }, + "&.firebaseui-idp-text-short": { display: "table-cell" }, + }, + }, - "& .firebaseui-idp-google": { - "& .firebaseui-idp-icon-wrapper::before": { - content: "''", - display: "block", - position: "absolute", - top: 2, - left: 2, - width: 48 - 4, - height: 48 - 4, - zIndex: 0, + "& .firebaseui-idp-google": { + "& .firebaseui-idp-icon-wrapper::before": { + content: "''", + display: "block", + position: "absolute", + top: 2, + left: 2, + width: 48 - 4, + height: 48 - 4, + zIndex: 0, - backgroundColor: "#fff", - borderRadius: "50%", - }, - "& .firebaseui-idp-icon-wrapper img": { - position: "relative", - left: -1, - }, + backgroundColor: "#fff", + borderRadius: "50%", + }, + "& .firebaseui-idp-icon-wrapper img": { + position: "relative", + left: -1, + }, - "&>.firebaseui-idp-text": { - color: "#fff", - }, - }, + "&>.firebaseui-idp-text": { + color: "#fff", + }, + }, - "& .firebaseui-card-header": { padding: 0 }, - "& .firebaseui-card-actions": { padding: 0 }, + "& .firebaseui-card-header": { padding: 0 }, + "& .firebaseui-card-actions": { padding: 0 }, - "& .firebaseui-input, & .firebaseui-input-invalid": { - ...theme.typography.body1, - color: theme.palette.text.primary, - }, - "& .firebaseui-textfield.mdl-textfield .firebaseui-input": { - borderColor: theme.palette.divider, - }, - "& .mdl-textfield.is-invalid .mdl-textfield__input": { - borderColor: theme.palette.error.main, - }, - "& .firebaseui-label": { - ...theme.typography.subtitle2, - color: theme.palette.text.secondary, - }, - "& .mdl-textfield--floating-label.is-dirty .mdl-textfield__label, .mdl-textfield--floating-label.is-focused .mdl-textfield__label": { - color: theme.palette.text.primary, - }, - "& .firebaseui-textfield.mdl-textfield .firebaseui-label:after": { - backgroundColor: theme.palette.primary.main, - }, - "& .mdl-textfield.is-invalid .mdl-textfield__label:after": { - backgroundColor: theme.palette.error.main, - }, + "& .firebaseui-input, & .firebaseui-input-invalid": { + ...theme.typography.body1, + color: theme.palette.text.primary, + }, + "& .firebaseui-textfield.mdl-textfield .firebaseui-input": { + borderColor: theme.palette.divider, + }, + "& .mdl-textfield.is-invalid .mdl-textfield__input": { + borderColor: theme.palette.error.main, + }, + "& .firebaseui-label": { + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + }, + "& .mdl-textfield--floating-label.is-dirty .mdl-textfield__label, .mdl-textfield--floating-label.is-focused .mdl-textfield__label": { + color: theme.palette.text.primary, + }, + "& .firebaseui-textfield.mdl-textfield .firebaseui-label:after": { + backgroundColor: theme.palette.primary.main, + }, + "& .mdl-textfield.is-invalid .mdl-textfield__label:after": { + backgroundColor: theme.palette.error.main, + }, - "& .mdl-progress>.bufferbar": { - background: alpha(theme.palette.primary.main, 0.33), - }, - "& .mdl-progress>.progressbar": { - backgroundColor: theme.palette.primary.main + " !important", - }, - }, - }, + "& .mdl-progress>.bufferbar": { + background: alpha(theme.palette.primary.main, 0.33), + }, + "& .mdl-progress>.progressbar": { + backgroundColor: theme.palette.primary.main + " !important", + }, + }, + }, - signInText: { - display: "none", - [theme.breakpoints.down("sm")]: { display: "block" }, + signInText: { + display: "none", + [theme.breakpoints.down("sm")]: { display: "block" }, - textAlign: "center", - color: theme.palette.text.disabled, - marginBottom: theme.spacing(-1), - }, + textAlign: "center", + color: theme.palette.text.disabled, + marginBottom: theme.spacing(-1), + }, - skeleton: { - marginBottom: "calc(var(--spacing-contents) * -1)", + skeleton: { + marginBottom: "calc(var(--spacing-contents) * -1)", - "& > *": { - width: "100%", - height: 48, - borderRadius: 24, - }, + "& > *": { + width: "100%", + height: 48, + borderRadius: 24, + }, - "& > * + *": { - marginTop: theme.spacing(2), - }, - }, - }) + "& > * + *": { + marginTop: theme.spacing(2), + }, + }, + }) ); export default function FirebaseUi(props: Partial) { - const classes = useStyles(); + const classes = useStyles(); - const [signInOptions, setSignInOptions] = useState< - Parameters[0] | undefined - >(); - useEffect(() => { - db.doc("/_rowy_/publicSettings") - .get() - .then((doc) => { - const options = doc?.get("signInOptions"); - if (!options) { - setSignInOptions(["google"]); - } else { - setSignInOptions(options); - } - }) - .catch(() => setSignInOptions(["google"])); - }, []); + const [signInOptions, setSignInOptions] = useState< + Parameters[0] | undefined + >(); + useEffect(() => { + db.doc("/_rowy_/publicSettings") + .get() + .then((doc) => { + const options = doc?.get("signInOptions"); + if (!options) { + setSignInOptions(["google"]); + } else { + setSignInOptions(options); + } + }) + .catch(() => setSignInOptions(["google"])); + }, []); - if (!signInOptions) - return ( -
- -
- ); + if (!signInOptions) + return ( +
+ +
+ ); - const uiConfig: firebaseui.auth.Config = { - ...defaultUiConfig, - ...props.uiConfig, - callbacks: { - uiShown: () => { - const node = document.getElementById("rowy-firebaseui-skeleton"); - if (node) node.style.display = "none"; - }, - ...props.uiConfig?.callbacks, - }, - signInOptions: getSignInOptions(signInOptions), - }; + const uiConfig: firebaseui.auth.Config = { + ...defaultUiConfig, + ...props.uiConfig, + callbacks: { + uiShown: () => { + const node = document.getElementById("rowy-firebaseui-skeleton"); + if (node) node.style.display = "none"; + }, + ...props.uiConfig?.callbacks, + }, + signInOptions: getSignInOptions(signInOptions), + }; - return ( - <> - - Sign in with - + return ( + <> + + Sign in with + -
- {Object.keys(signInOptions).map((_, i) => ( - - ))} -
+
+ {Object.keys(signInOptions).map((_, i) => ( + + ))} +
- - - ); + + + ); } diff --git a/src/components/BuilderInstaller.tsx b/src/components/BuilderInstaller.tsx index 5781ee38..cf1f7224 100644 --- a/src/components/BuilderInstaller.tsx +++ b/src/components/BuilderInstaller.tsx @@ -6,63 +6,63 @@ import OpenInNewIcon from "@material-ui/icons/OpenInNew"; import CheckCircleIcon from "@material-ui/icons/CheckCircle"; export interface IProjectSettings - extends Pick {} + extends Pick {} export default function BuilderInstaller({ handleClose }: IProjectSettings) { - const [settingsState] = useDoc({ - path: "_rowy_/settings", - }); + const [settingsState] = useDoc({ + path: "_rowy_/settings", + }); - if (settingsState.loading) return null; + if (settingsState.loading) return null; - const complete = - settingsState.doc.buildStatus === "COMPLETE" || - !!settingsState.doc.buildUrl; - const building = settingsState.doc.buildStatus === "BUILDING"; - const waiting = !settingsState.doc.buildStatus && !settingsState.doc.buildUrl; + const complete = + settingsState.doc.buildStatus === "COMPLETE" || + !!settingsState.doc.buildUrl; + const building = settingsState.doc.buildStatus === "BUILDING"; + const waiting = !settingsState.doc.buildStatus && !settingsState.doc.buildUrl; - return ( - - - You will be redirected to Google Cloud Shell to deploy Rowy Function - Builder to Cloud Run. - -
+ return ( + + + You will be redirected to Google Cloud Shell to deploy Rowy Function + Builder to Cloud Run. + +
- - {complete && ( - <> - - Deploy Complete - - )} - {building && ( - <> - - Deploying... - - )} - {waiting && ( - <> - - - Waiting for deploy... - - - )} - - - } - /> - ); + + {complete && ( + <> + + Deploy Complete + + )} + {building && ( + <> + + Deploying... + + )} + {waiting && ( + <> + + + Waiting for deploy... + + + )} + + + } + /> + ); } diff --git a/src/components/ButtonWithStatus.tsx b/src/components/ButtonWithStatus.tsx index eed524f4..87a141bd 100644 --- a/src/components/ButtonWithStatus.tsx +++ b/src/components/ButtonWithStatus.tsx @@ -6,60 +6,60 @@ import { Button, ButtonProps } from "@material-ui/core"; import { alpha } from "@material-ui/core/styles"; export const useStyles = makeStyles((theme) => - createStyles({ - root: { - position: "relative", - zIndex: 1, - }, + createStyles({ + root: { + position: "relative", + zIndex: 1, + }, - active: { - color: - theme.palette.mode === "dark" - ? theme.palette.primary.light - : theme.palette.primary.dark, - backgroundColor: alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity - ), - borderColor: theme.palette.primary.main, + active: { + color: + theme.palette.mode === "dark" + ? theme.palette.primary.light + : theme.palette.primary.dark, + backgroundColor: alpha( + theme.palette.primary.main, + theme.palette.action.selectedOpacity + ), + borderColor: theme.palette.primary.main, - "&:hover": { - color: - theme.palette.mode === "dark" - ? theme.palette.primary.light - : theme.palette.primary.dark, - backgroundColor: alpha( - theme.palette.mode === "dark" - ? theme.palette.primary.light - : theme.palette.primary.dark, - theme.palette.action.selectedOpacity + - theme.palette.action.hoverOpacity - ), - borderColor: "currentColor", - }, - }, - }) + "&:hover": { + color: + theme.palette.mode === "dark" + ? theme.palette.primary.light + : theme.palette.primary.dark, + backgroundColor: alpha( + theme.palette.mode === "dark" + ? theme.palette.primary.light + : theme.palette.primary.dark, + theme.palette.action.selectedOpacity + + theme.palette.action.hoverOpacity + ), + borderColor: "currentColor", + }, + }, + }) ); export interface IButtonWithStatusProps extends ButtonProps { - active?: boolean; + active?: boolean; } export const ButtonWithStatus = React.forwardRef(function ButtonWithStatus_( - { active = false, className, ...props }: IButtonWithStatusProps, - ref: React.Ref + { active = false, className, ...props }: IButtonWithStatusProps, + ref: React.Ref ) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - ); + + + ); } diff --git a/src/components/Confirmation.tsx b/src/components/Confirmation.tsx index bc4b5bc5..08e7134a 100644 --- a/src/components/Confirmation.tsx +++ b/src/components/Confirmation.tsx @@ -10,113 +10,113 @@ import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; const useStyles = makeStyles(() => - createStyles({ - root: { - display: "flex", - flexWrap: "wrap", - }, - dryWrapper: {}, - dryField: {}, - }) + createStyles({ + root: { + display: "flex", + flexWrap: "wrap", + }, + dryWrapper: {}, + dryField: {}, + }) ); export interface IConfirmationProps { - children: JSX.Element; - message?: { - title?: string; - customBody?: string; - body?: string | React.ReactNode; - cancel?: string; - confirm?: string | JSX.Element; - color?: "error"; - }; - confirmationCommand?: string; - functionName?: string; - stopPropagation?: boolean; + children: JSX.Element; + message?: { + title?: string; + customBody?: string; + body?: string | React.ReactNode; + cancel?: string; + confirm?: string | JSX.Element; + color?: "error"; + }; + confirmationCommand?: string; + functionName?: string; + stopPropagation?: boolean; } export default function Confirmation({ - children, - message, - confirmationCommand, - functionName = "onClick", - stopPropagation = false, + children, + message, + confirmationCommand, + functionName = "onClick", + stopPropagation = false, }: IConfirmationProps) { - const classes = useStyles(); - const [showDialog, setShowDialog] = useState(false); - const [dryText, setDryText] = useState(""); + const classes = useStyles(); + const [showDialog, setShowDialog] = useState(false); + const [dryText, setDryText] = useState(""); - const handleClose = () => { - setShowDialog(false); - }; + const handleClose = () => { + setShowDialog(false); + }; - const confirmHandler = children.props[functionName]; - const button = React.cloneElement(children, { - [functionName]: (e) => { - if (stopPropagation && e && e.stopPropagation) e.stopPropagation(); - setShowDialog(true); - }, - }); + const confirmHandler = children.props[functionName]; + const button = React.cloneElement(children, { + [functionName]: (e) => { + if (stopPropagation && e && e.stopPropagation) e.stopPropagation(); + setShowDialog(true); + }, + }); - return ( - <> - {button} + return ( + <> + {button} - - - {(message && message.title) || "Are you sure?"} - - {message && ( - - {message.customBody} - {message.body && - (typeof message.body === "string" ? ( - {message.body} - ) : ( - message.body - ))} - {confirmationCommand && ( -
- - Type {confirmationCommand} below to continue: - - { - setDryText(e.target.value); - }} - className={classes.dryField} - InputProps={{ disableUnderline: true }} - autoFocus - margin="dense" - label={confirmationCommand} - fullWidth - /> -
- )} -
- )} - - - - -
- - ); + + + {(message && message.title) || "Are you sure?"} + + {message && ( + + {message.customBody} + {message.body && + (typeof message.body === "string" ? ( + {message.body} + ) : ( + message.body + ))} + {confirmationCommand && ( +
+ + Type {confirmationCommand} below to continue: + + { + setDryText(e.target.value); + }} + className={classes.dryField} + InputProps={{ disableUnderline: true }} + autoFocus + margin="dense" + label={confirmationCommand} + fullWidth + /> +
+ )} +
+ )} + + + + +
+ + ); } diff --git a/src/components/ConfirmationDialog/Context.ts b/src/components/ConfirmationDialog/Context.ts index 34139c12..d66daa83 100644 --- a/src/components/ConfirmationDialog/Context.ts +++ b/src/components/ConfirmationDialog/Context.ts @@ -1,7 +1,7 @@ import React, { useContext } from "react"; import { IConfirmation, CONFIRMATION_EMPTY_STATE } from "./props"; const ConfirmationContext = React.createContext( - CONFIRMATION_EMPTY_STATE + CONFIRMATION_EMPTY_STATE ); export default ConfirmationContext; diff --git a/src/components/ConfirmationDialog/Dialog.tsx b/src/components/ConfirmationDialog/Dialog.tsx index 2c45259e..7555cc22 100644 --- a/src/components/ConfirmationDialog/Dialog.tsx +++ b/src/components/ConfirmationDialog/Dialog.tsx @@ -10,78 +10,78 @@ import TextField from "@material-ui/core/TextField"; import { makeStyles, createStyles } from "@material-ui/styles"; //import {confirmationProps} from './props' const useStyles = makeStyles(() => - createStyles({ - root: { - display: "flex", - flexWrap: "wrap", - }, - dryWrapper: {}, - dryField: {}, - }) + createStyles({ + root: { + display: "flex", + flexWrap: "wrap", + }, + dryWrapper: {}, + dryField: {}, + }) ); export default function Confirmation({ - title, - customBody, - body, - cancel, - confirm, - confirmationCommand, - handleConfirm, - open, - handleClose, - maxWidth = "xs", + title, + customBody, + body, + cancel, + confirm, + confirmationCommand, + handleConfirm, + open, + handleClose, + maxWidth = "xs", }: any) { - const classes = useStyles(); - const [dryText, setDryText] = useState(""); - return ( - <> - - {title ?? "Are you sure?"} + const classes = useStyles(); + const [dryText, setDryText] = useState(""); + return ( + <> + + {title ?? "Are you sure?"} - - {customBody} - {body && {body}} - {confirmationCommand && ( -
- - Type {confirmationCommand} below to continue: - - { - setDryText(e.target.value); - }} - className={classes.dryField} - InputProps={{ disableUnderline: true }} - autoFocus - margin="dense" - label={confirmationCommand} - fullWidth - /> -
- )} -
+ + {customBody} + {body && {body}} + {confirmationCommand && ( +
+ + Type {confirmationCommand} below to continue: + + { + setDryText(e.target.value); + }} + className={classes.dryField} + InputProps={{ disableUnderline: true }} + autoFocus + margin="dense" + label={confirmationCommand} + fullWidth + /> +
+ )} +
- - - - -
- - ); + + + + +
+ + ); } diff --git a/src/components/ConfirmationDialog/Provider.tsx b/src/components/ConfirmationDialog/Provider.tsx index eb11b007..0d43afd2 100644 --- a/src/components/ConfirmationDialog/Provider.tsx +++ b/src/components/ConfirmationDialog/Provider.tsx @@ -4,36 +4,36 @@ import { confirmationProps } from "./props"; import Dialog from "./Dialog"; import ConfirmationContext from "./Context"; interface IConfirmationProviderProps { - children: React.ReactNode; + children: React.ReactNode; } const ConfirmationProvider: React.FC = ({ - children, + children, }) => { - const [state, setState] = useState(); - const [open, setOpen] = useState(false); - const handleClose = () => { - setState(undefined); - setOpen(false); - }; - const requestConfirmation = (props: confirmationProps) => { - setState(props); - setOpen(true); - }; - return ( - - {children} + const [state, setState] = useState(); + const [open, setOpen] = useState(false); + const handleClose = () => { + setState(undefined); + setOpen(false); + }; + const requestConfirmation = (props: confirmationProps) => { + setState(props); + setOpen(true); + }; + return ( + + {children} - - - ); + + + ); }; export default ConfirmationProvider; diff --git a/src/components/ConfirmationDialog/props.ts b/src/components/ConfirmationDialog/props.ts index f54b5946..b29d1460 100644 --- a/src/components/ConfirmationDialog/props.ts +++ b/src/components/ConfirmationDialog/props.ts @@ -1,24 +1,24 @@ export type confirmationProps = - | { - title?: string; - customBody?: string; - body?: string; - cancel?: string; - confirm?: string | JSX.Element; - confirmationCommand?: string; - handleConfirm: () => void; - open?: Boolean; - } - | undefined; + | { + title?: string; + customBody?: string; + body?: string; + cancel?: string; + confirm?: string | JSX.Element; + confirmationCommand?: string; + handleConfirm: () => void; + open?: Boolean; + } + | undefined; export interface IConfirmation { - dialogProps?: confirmationProps; - handleClose: () => void; - open: boolean; - requestConfirmation: (props: confirmationProps) => void; + dialogProps?: confirmationProps; + handleClose: () => void; + open: boolean; + requestConfirmation: (props: confirmationProps) => void; } export const CONFIRMATION_EMPTY_STATE = { - dialogProps: undefined, - open: false, - handleClose: () => {}, - requestConfirmation: () => {}, + dialogProps: undefined, + open: false, + handleClose: () => {}, + requestConfirmation: () => {}, }; diff --git a/src/components/ConnectServiceSelect/PopupContents.tsx b/src/components/ConnectServiceSelect/PopupContents.tsx index 2d0443e5..632e3f40 100644 --- a/src/components/ConnectServiceSelect/PopupContents.tsx +++ b/src/components/ConnectServiceSelect/PopupContents.tsx @@ -4,18 +4,18 @@ import { useDebouncedCallback } from "use-debounce"; import _get from "lodash/get"; import { - Button, - Checkbox, - Divider, - Grid, - InputAdornment, - List, - ListItemIcon, - ListItemText, - MenuItem, - TextField, - Typography, - Radio, + Button, + Checkbox, + Divider, + Grid, + InputAdornment, + List, + ListItemIcon, + ListItemText, + MenuItem, + TextField, + Typography, + Radio, } from "@material-ui/core"; import SearchIcon from "@material-ui/icons/Search"; @@ -24,181 +24,181 @@ import useStyles from "./styles"; import Loading from "components/Loading"; export interface IPopupContentsProps - extends Omit {} + extends Omit {} // TODO: Implement infinite scroll here export default function PopupContents({ - value = [], - onChange, - config, + value = [], + onChange, + config, - docRef, + docRef, }: IPopupContentsProps) { - const url = config.url; - const titleKey = config.titleKey ?? config.primaryKey; - const subtitleKey = config.subtitleKey; - const resultsKey = config.resultsKey; - const primaryKey = config.primaryKey; - const multiple = Boolean(config.multiple); + const url = config.url; + const titleKey = config.titleKey ?? config.primaryKey; + const subtitleKey = config.subtitleKey; + const resultsKey = config.resultsKey; + const primaryKey = config.primaryKey; + const multiple = Boolean(config.multiple); - const classes = useStyles(); + const classes = useStyles(); - // Webservice search query - const [query, setQuery] = useState(""); - // Webservice response - const [response, setResponse] = useState(null); + // Webservice search query + const [query, setQuery] = useState(""); + // Webservice response + const [response, setResponse] = useState(null); - const [docData, setDocData] = useState(null); - useEffect(() => { - docRef.get().then((d) => setDocData(d.data())); - }, []); + const [docData, setDocData] = useState(null); + useEffect(() => { + docRef.get().then((d) => setDocData(d.data())); + }, []); - const hits: any["hits"] = _get(response, resultsKey) ?? []; - const [search] = useDebouncedCallback( - async (query: string) => { - if (!docData) return; - if (!url) return; - const uri = new URL(url), - params = { q: query }; - Object.keys(params).forEach((key) => - uri.searchParams.append(key, params[key]) - ); + const hits: any["hits"] = _get(response, resultsKey) ?? []; + const [search] = useDebouncedCallback( + async (query: string) => { + if (!docData) return; + if (!url) return; + const uri = new URL(url), + params = { q: query }; + Object.keys(params).forEach((key) => + uri.searchParams.append(key, params[key]) + ); - const resp = await fetch(uri.toString(), { - method: "POST", - body: JSON.stringify(docData), - headers: { "content-type": "application/json" }, - }); + const resp = await fetch(uri.toString(), { + method: "POST", + body: JSON.stringify(docData), + headers: { "content-type": "application/json" }, + }); - const jsonBody = await resp.json(); - setResponse(jsonBody); - }, - 1000, - { leading: true } - ); + const jsonBody = await resp.json(); + setResponse(jsonBody); + }, + 1000, + { leading: true } + ); - useEffect(() => { - search(query); - }, [query, docData]); + useEffect(() => { + search(query); + }, [query, docData]); - if (!response) return ; + if (!response) return ; - const select = (hit: any) => () => { - if (multiple) onChange([...value, hit]); - else onChange([hit]); - }; - const deselect = (hit: any) => () => { - if (multiple) - onChange(value.filter((v) => v[primaryKey] !== hit[primaryKey])); - else onChange([]); - }; + const select = (hit: any) => () => { + if (multiple) onChange([...value, hit]); + else onChange([hit]); + }; + const deselect = (hit: any) => () => { + if (multiple) + onChange(value.filter((v) => v[primaryKey] !== hit[primaryKey])); + else onChange([]); + }; - const selectedValues = value?.map((item) => _get(item, primaryKey)); + const selectedValues = value?.map((item) => _get(item, primaryKey)); - const clearSelection = () => onChange([]); + const clearSelection = () => onChange([]); - return ( - - - setQuery(e.target.value)} - fullWidth - variant="filled" - margin="dense" - label="Search items" - className={classes.noMargins} - InputProps={{ - endAdornment: ( - - - - ), - }} - onClick={(e) => e.stopPropagation()} - onKeyDown={(e) => e.stopPropagation()} - /> - + return ( + + + setQuery(e.target.value)} + fullWidth + variant="filled" + margin="dense" + label="Search items" + className={classes.noMargins} + InputProps={{ + endAdornment: ( + + + + ), + }} + onClick={(e) => e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + /> + - - - {hits.map((hit) => { - const isSelected = - selectedValues.indexOf(_get(hit, primaryKey)) !== -1; - console.log(`Selected Values: ${selectedValues}`); - return ( - - - - {multiple ? ( - - ) : ( - - )} - - - - - - ); - })} - - + + + {hits.map((hit) => { + const isSelected = + selectedValues.indexOf(_get(hit, primaryKey)) !== -1; + console.log(`Selected Values: ${selectedValues}`); + return ( + + + + {multiple ? ( + + ) : ( + + )} + + + + + + ); + })} + + - {multiple && ( - - - - {value?.length} of {hits?.length} - + {multiple && ( + + + + {value?.length} of {hits?.length} + - - - - )} - - ); + + + + )} + + ); } diff --git a/src/components/ConnectServiceSelect/index.tsx b/src/components/ConnectServiceSelect/index.tsx index b9ee0a9b..f4485e03 100644 --- a/src/components/ConnectServiceSelect/index.tsx +++ b/src/components/ConnectServiceSelect/index.tsx @@ -7,67 +7,67 @@ import Loading from "components/Loading"; import ErrorBoundary from "components/ErrorBoundary"; const PopupContents = lazy( - () => import("./PopupContents" /* webpackChunkName: "PopupContents" */) + () => import("./PopupContents" /* webpackChunkName: "PopupContents" */) ); export type ServiceValue = { value: string; [prop: string]: any }; export interface IConnectServiceSelectProps { - value: ServiceValue[]; - onChange: (value: ServiceValue[]) => void; - row: any; - config: { - displayKey: string; - [key: string]: any; - }; - editable?: boolean; - /** Optional style overrides for root MUI `TextField` component */ - className?: string; - /** Override any props of the root MUI `TextField` component */ - TextFieldProps?: Partial; - docRef: firebase.default.firestore.DocumentReference; + value: ServiceValue[]; + onChange: (value: ServiceValue[]) => void; + row: any; + config: { + displayKey: string; + [key: string]: any; + }; + editable?: boolean; + /** Optional style overrides for root MUI `TextField` component */ + className?: string; + /** Override any props of the root MUI `TextField` component */ + TextFieldProps?: Partial; + docRef: firebase.default.firestore.DocumentReference; } export default function ConnectServiceSelect({ - value = [], - className, - TextFieldProps = {}, - ...props + value = [], + className, + TextFieldProps = {}, + ...props }: IConnectServiceSelectProps) { - const classes = useStyles(); + const classes = useStyles(); - const sanitisedValue = Array.isArray(value) ? value : []; + const sanitisedValue = Array.isArray(value) ? value : []; - return ( - `${(value as any[]).length} selected`, - displayEmpty: true, - classes: { root: classes.selectRoot }, - ...TextFieldProps.SelectProps, - // Must have this set to prevent MUI transforming `value` - // prop for this component to a comma-separated string - MenuProps: { - classes: { paper: classes.paper, list: classes.menuChild }, - MenuListProps: { disablePadding: true }, - anchorOrigin: { vertical: "bottom", horizontal: "center" }, - transformOrigin: { vertical: "top", horizontal: "center" }, - ...TextFieldProps.SelectProps?.MenuProps, - }, - }} - > - - }> - - - - - ); + return ( + `${(value as any[]).length} selected`, + displayEmpty: true, + classes: { root: classes.selectRoot }, + ...TextFieldProps.SelectProps, + // Must have this set to prevent MUI transforming `value` + // prop for this component to a comma-separated string + MenuProps: { + classes: { paper: classes.paper, list: classes.menuChild }, + MenuListProps: { disablePadding: true }, + anchorOrigin: { vertical: "bottom", horizontal: "center" }, + transformOrigin: { vertical: "top", horizontal: "center" }, + ...TextFieldProps.SelectProps?.MenuProps, + }, + }} + > + + }> + + + + + ); } diff --git a/src/components/ConnectServiceSelect/styles.ts b/src/components/ConnectServiceSelect/styles.ts index b567a3b3..251f1431 100644 --- a/src/components/ConnectServiceSelect/styles.ts +++ b/src/components/ConnectServiceSelect/styles.ts @@ -1,83 +1,83 @@ import { makeStyles, createStyles } from "@material-ui/styles"; export const useStyles = makeStyles((theme) => - createStyles({ - root: { minWidth: 200 }, - selectRoot: { paddingRight: theme.spacing(4) }, + createStyles({ + root: { minWidth: 200 }, + selectRoot: { paddingRight: theme.spacing(4) }, - paper: { overflow: "hidden", maxHeight: "calc(100% - 48px)" }, - menuChild: { - padding: `0 ${theme.spacing(2)}`, - minWidth: 340, - // Need to set fixed height here so popup is positioned correctly - height: 340, - }, + paper: { overflow: "hidden", maxHeight: "calc(100% - 48px)" }, + menuChild: { + padding: `0 ${theme.spacing(2)}`, + minWidth: 340, + // Need to set fixed height here so popup is positioned correctly + height: 340, + }, - grid: { outline: 0 }, + grid: { outline: 0 }, - noMargins: { margin: 0 }, + noMargins: { margin: 0 }, - searchRow: { marginTop: theme.spacing(2) }, + searchRow: { marginTop: theme.spacing(2) }, - listRow: { - background: `${theme.palette.background.paper} no-repeat`, - position: "relative", - margin: theme.spacing(0, -2), - maxWidth: `calc(100% + ${theme.spacing(4)})`, + listRow: { + background: `${theme.palette.background.paper} no-repeat`, + position: "relative", + margin: theme.spacing(0, -2), + maxWidth: `calc(100% + ${theme.spacing(4)})`, - "&::before, &::after": { - content: '""', - position: "absolute", - top: 0, - left: 0, - right: 0, - zIndex: 9, + "&::before, &::after": { + content: '""', + position: "absolute", + top: 0, + left: 0, + right: 0, + zIndex: 9, - display: "block", - height: 16, + display: "block", + height: 16, - background: `linear-gradient(to bottom, #fff, rgba(255, 255, 255, 0))`, - }, + background: `linear-gradient(to bottom, #fff, rgba(255, 255, 255, 0))`, + }, - "&::after": { - top: "auto", - bottom: 0, - background: `linear-gradient(to top, #fff, rgba(255, 255, 255, 0))`, - }, - }, - list: () => { - let maxHeightDeductions = 0; - maxHeightDeductions -= 64; // search box - maxHeightDeductions -= 48; // multiple - maxHeightDeductions += 8; // footer padding + "&::after": { + top: "auto", + bottom: 0, + background: `linear-gradient(to top, #fff, rgba(255, 255, 255, 0))`, + }, + }, + list: () => { + let maxHeightDeductions = 0; + maxHeightDeductions -= 64; // search box + maxHeightDeductions -= 48; // multiple + maxHeightDeductions += 8; // footer padding - return { - padding: theme.spacing(2, 0), - overflowY: "auto" as "auto", - // height: `calc(340px - ${-maxHeightDeductions}px)`, - height: 340 + maxHeightDeductions, - }; - }, + return { + padding: theme.spacing(2, 0), + overflowY: "auto" as "auto", + // height: `calc(340px - ${-maxHeightDeductions}px)`, + height: 340 + maxHeightDeductions, + }; + }, - checkboxContainer: { minWidth: theme.spacing(36 / 8) }, - checkbox: { - padding: theme.spacing(6 / 8, 9 / 8), - "&:hover": { background: "transparent" }, - }, + checkboxContainer: { minWidth: theme.spacing(36 / 8) }, + checkbox: { + padding: theme.spacing(6 / 8, 9 / 8), + "&:hover": { background: "transparent" }, + }, - divider: { margin: theme.spacing(0, 2, 0, 6.5) }, + divider: { margin: theme.spacing(0, 2, 0, 6.5) }, - footerRow: { marginBottom: theme.spacing(2) }, - selectedRow: { - "$listRow + &": { marginTop: -theme.spacing(1) }, - "$footerRow + &": { marginTop: -theme.spacing(2) }, + footerRow: { marginBottom: theme.spacing(2) }, + selectedRow: { + "$listRow + &": { marginTop: -theme.spacing(1) }, + "$footerRow + &": { marginTop: -theme.spacing(2) }, - marginBottom: 0, - "& > div": { height: 48 }, - }, - selectAllButton: { marginRight: -theme.spacing(1) }, - selectedNum: { fontFeatureSettings: '"tnum"' }, - }) + marginBottom: 0, + "& > div": { height: 48 }, + }, + selectAllButton: { marginRight: -theme.spacing(1) }, + selectedNum: { fontFeatureSettings: '"tnum"' }, + }) ); export default useStyles; diff --git a/src/components/EmptyState.tsx b/src/components/EmptyState.tsx index 03e7c2d7..32e7da8b 100644 --- a/src/components/EmptyState.tsx +++ b/src/components/EmptyState.tsx @@ -3,53 +3,53 @@ import Div100vh from "react-div-100vh"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - Grid, - GridProps, - Stack, - Typography, - SvgIconTypeMap, + Grid, + GridProps, + Stack, + Typography, + SvgIconTypeMap, } from "@material-ui/core"; import { OverridableComponent } from "@material-ui/core/OverridableComponent"; import ErrorIcon from "@material-ui/icons/ErrorOutline"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - height: "100%", - width: "100%", - textAlign: "center", + createStyles({ + root: { + height: "100%", + width: "100%", + textAlign: "center", - ...theme.typography.body2, - }, + ...theme.typography.body2, + }, - content: { - "&&": { maxWidth: "25em" }, - }, + content: { + "&&": { maxWidth: "25em" }, + }, - icon: { - color: theme.palette.action.active, - fontSize: "3rem", - }, + icon: { + color: theme.palette.action.active, + fontSize: "3rem", + }, - message: { - marginTop: theme.spacing(1), - }, + message: { + marginTop: theme.spacing(1), + }, - basicIcon: { display: "block" }, - }) + basicIcon: { display: "block" }, + }) ); export interface IEmptyStateProps extends Partial { - /** Primary message displayed under the icon */ - message?: React.ReactNode; - /** Description text displayed under primary message */ - description?: React.ReactNode; - /** Override icon component */ - Icon?: OverridableComponent; - /** Set height to `100vh`. Default: `false` */ - fullScreen?: boolean; - /** Basic inline presentation without padding. Default: `false` */ - basic?: boolean; + /** Primary message displayed under the icon */ + message?: React.ReactNode; + /** Description text displayed under primary message */ + description?: React.ReactNode; + /** Override icon component */ + Icon?: OverridableComponent; + /** Set height to `100vh`. Default: `false` */ + fullScreen?: boolean; + /** Basic inline presentation without padding. Default: `false` */ + basic?: boolean; } /** @@ -58,59 +58,59 @@ export interface IEmptyStateProps extends Partial { * Override with props that are passed to the root MUI `Grid` component. */ export default function EmptyState({ - message = "Nothing Here", - description, - Icon = ErrorIcon, - fullScreen = false, - basic = false, - ...props + message = "Nothing Here", + description, + Icon = ErrorIcon, + fullScreen = false, + basic = false, + ...props }: IEmptyStateProps) { - const classes = useStyles({}); + const classes = useStyles({}); - if (basic) - return ( - - - - + if (basic) + return ( + + + + - - {message} - {description && ": "} - {description} - - - ); + + {message} + {description && ": "} + {description} + + + ); - return ( - - - + return ( + + + - - {message} - + + {message} + - {description && ( - - {description} - - )} - - - ); + {description && ( + + {description} + + )} + + + ); } diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index b4ca2af0..05633c5e 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -6,60 +6,60 @@ import ReloadIcon from "@material-ui/icons/Refresh"; import OpenInNewIcon from "@material-ui/icons/OpenInNew"; import meta from "../../package.json"; class ErrorBoundary extends React.Component { - state = { hasError: false, errorMessage: "" }; + state = { hasError: false, errorMessage: "" }; - static getDerivedStateFromError(error: Error) { - // Update state so the next render will show the fallback UI. - return { hasError: true, errorMessage: error.message }; - } + static getDerivedStateFromError(error: Error) { + // Update state so the next render will show the fallback UI. + return { hasError: true, errorMessage: error.message }; + } - componentDidCatch(error: Error, errorInfo: object) { - console.log(error, errorInfo); - // You can also log the error to an error reporting service - //logErrorToMyService(error, errorInfo); - } + componentDidCatch(error: Error, errorInfo: object) { + console.log(error, errorInfo); + // You can also log the error to an error reporting service + //logErrorToMyService(error, errorInfo); + } - render() { - if (this.state.hasError) { - // You can render any custom fallback UI - return ( - - {this.state.errorMessage} - {this.state.errorMessage.startsWith("Loading chunk") ? ( - - ) : ( - - )} - - } - fullScreen - {...this.props} - /> - ); - } + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( + + {this.state.errorMessage} + {this.state.errorMessage.startsWith("Loading chunk") ? ( + + ) : ( + + )} + + } + fullScreen + {...this.props} + /> + ); + } - return this.props.children; - } + return this.props.children; + } } export default ErrorBoundary; diff --git a/src/components/FormattedChip.tsx b/src/components/FormattedChip.tsx index 136955ee..d18a8165 100644 --- a/src/components/FormattedChip.tsx +++ b/src/components/FormattedChip.tsx @@ -5,38 +5,38 @@ import { Chip, ChipProps } from "@material-ui/core"; export const VARIANTS = ["yes", "no", "maybe"]; const useStyles = makeStyles( - createStyles({ - yes: { - backgroundColor: "#58bc8a", - color: "hsl(150, 100%, 11%)", - }, - maybe: { - backgroundColor: "#ffd666", - color: "hsl(39, 100%, 14%)", - }, - no: { - backgroundColor: "#e67d73", - color: "hsl(5, 100%, 13%)", - }, - }) + createStyles({ + yes: { + backgroundColor: "#58bc8a", + color: "hsl(150, 100%, 11%)", + }, + maybe: { + backgroundColor: "#ffd666", + color: "hsl(39, 100%, 14%)", + }, + no: { + backgroundColor: "#e67d73", + color: "hsl(5, 100%, 13%)", + }, + }) ); // TODO: Create a more generalised solution for this export default function FormattedChip(props: ChipProps) { - const classes = useStyles(); + const classes = useStyles(); - const label = - typeof props.label === "string" ? props.label.toLowerCase() : ""; + const label = + typeof props.label === "string" ? props.label.toLowerCase() : ""; - if (VARIANTS.includes(label)) { - return ( - - ); - } + if (VARIANTS.includes(label)) { + return ( + + ); + } - return ; + return ; } diff --git a/src/components/Grid/AlgoliaFilters.tsx b/src/components/Grid/AlgoliaFilters.tsx index 163256ef..cb4606f1 100644 --- a/src/components/Grid/AlgoliaFilters.tsx +++ b/src/components/Grid/AlgoliaFilters.tsx @@ -6,35 +6,35 @@ import { useDebouncedCallback } from "use-debounce"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - Grid, - Typography, - Button, - TextField, - InputAdornment, - ListItemSecondaryAction, + Grid, + Typography, + Button, + TextField, + InputAdornment, + ListItemSecondaryAction, } from "@material-ui/core"; import SearchIcon from "@material-ui/icons/Search"; import MultiSelect from "@antlerengineering/multiselect"; const useStyles = makeStyles((theme) => - createStyles({ - resetFilters: { marginRight: -theme.spacing(1) }, + createStyles({ + resetFilters: { marginRight: -theme.spacing(1) }, - filterGrid: { - marginTop: 0, - marginBottom: theme.spacing(3), - }, + filterGrid: { + marginTop: 0, + marginBottom: theme.spacing(3), + }, - listItemText: { whiteSpace: "pre-line" }, - count: { - position: "static", - marginLeft: "auto", - paddingLeft: theme.spacing(1.5), - transform: "none", - color: theme.palette.text.disabled, - }, - }) + listItemText: { whiteSpace: "pre-line" }, + count: { + position: "static", + marginLeft: "auto", + paddingLeft: theme.spacing(1.5), + transform: "none", + color: theme.palette.text.disabled, + }, + }) ); /** @@ -43,197 +43,197 @@ const useStyles = makeStyles((theme) => * @param requiredFilters Filters not selected by the user */ const generateFiltersString = ( - filterValues: Record, - requiredFilters?: string + filterValues: Record, + requiredFilters?: string ) => { - if (Object.keys(filterValues).length === 0) return null; + if (Object.keys(filterValues).length === 0) return null; - let filtersString = Object.entries(filterValues) - .filter(([, values]) => values.length > 0) - .map( - ([facet, values]) => - `(${values - .map((value) => `${facet}:"${value.replace(/"/g, '\\"')}"`) - .join(" OR ")})` - ) - .join(" AND "); + let filtersString = Object.entries(filterValues) + .filter(([, values]) => values.length > 0) + .map( + ([facet, values]) => + `(${values + .map((value) => `${facet}:"${value.replace(/"/g, '\\"')}"`) + .join(" OR ")})` + ) + .join(" AND "); - if (requiredFilters) { - if (filtersString) - filtersString = requiredFilters + " AND " + filtersString; - else filtersString = requiredFilters; - } + if (requiredFilters) { + if (filtersString) + filtersString = requiredFilters + " AND " + filtersString; + else filtersString = requiredFilters; + } - return filtersString; + return filtersString; }; export interface IAlgoliaFiltersProps { - index: SearchIndex; - request: ReturnType[0]["request"]; - requestDispatch: ReturnType[1]; - requiredFilters?: string; - label: string; - filters: { - label: string; - facet: string; - labelTransformer?: (value: string) => string; - }[]; - search?: boolean; + index: SearchIndex; + request: ReturnType[0]["request"]; + requestDispatch: ReturnType[1]; + requiredFilters?: string; + label: string; + filters: { + label: string; + facet: string; + labelTransformer?: (value: string) => string; + }[]; + search?: boolean; } export default function AlgoliaFilters({ - index, - request, - requestDispatch, - requiredFilters, - label, - filters, - search = true, + index, + request, + requestDispatch, + requiredFilters, + label, + filters, + search = true, }: IAlgoliaFiltersProps) { - const classes = useStyles(); + const classes = useStyles(); - // Store filter values - const [filterValues, setFilterValues] = useState>( - {} - ); - // Push filter values to dispatch - useEffect(() => { - const filtersString = generateFiltersString(filterValues, requiredFilters); - if (filtersString === null) return; - requestDispatch({ filters: filtersString }); - }, [filterValues]); + // Store filter values + const [filterValues, setFilterValues] = useState>( + {} + ); + // Push filter values to dispatch + useEffect(() => { + const filtersString = generateFiltersString(filterValues, requiredFilters); + if (filtersString === null) return; + requestDispatch({ filters: filtersString }); + }, [filterValues]); - // Store facet values - const [facetValues, setFacetValues] = useState< - Record - >({}); - // Get facet values - useEffect(() => { - if (!index) return; + // Store facet values + const [facetValues, setFacetValues] = useState< + Record + >({}); + // Get facet values + useEffect(() => { + if (!index) return; - filters.forEach((filter) => { - const params = { ...request, maxFacetHits: 100 }; - // Ignore current user-selected value for these filters so all options - // continue to show up - params.filters = - generateFiltersString( - { ...filterValues, [filter.facet]: [] }, - requiredFilters - ) ?? ""; + filters.forEach((filter) => { + const params = { ...request, maxFacetHits: 100 }; + // Ignore current user-selected value for these filters so all options + // continue to show up + params.filters = + generateFiltersString( + { ...filterValues, [filter.facet]: [] }, + requiredFilters + ) ?? ""; - index - .searchForFacetValues(filter.facet, "", params) - .then(({ facetHits }) => - setFacetValues((other) => ({ ...other, [filter.facet]: facetHits })) - ); - }); - }, [filters, index, filterValues, requiredFilters]); + index + .searchForFacetValues(filter.facet, "", params) + .then(({ facetHits }) => + setFacetValues((other) => ({ ...other, [filter.facet]: facetHits })) + ); + }); + }, [filters, index, filterValues, requiredFilters]); - // Reset filters - const handleResetFilters = () => { - setFilterValues({}); - setQuery(""); - requestDispatch({ filters: requiredFilters ?? "", query: "" }); - }; + // Reset filters + const handleResetFilters = () => { + setFilterValues({}); + setQuery(""); + requestDispatch({ filters: requiredFilters ?? "", query: "" }); + }; - // Store search query - const [query, setQuery] = useState(""); - const [handleQueryChange] = useDebouncedCallback( - (query: string) => requestDispatch({ query }), - 500 - ); + // Store search query + const [query, setQuery] = useState(""); + const [handleQueryChange] = useDebouncedCallback( + (query: string) => requestDispatch({ query }), + 500 + ); - return ( -
- - - - Filter{label ? " " + label : "s"} - - + return ( +
+ + + + Filter{label ? " " + label : "s"} + + - - - - + + + + - - {search && ( - - { - setQuery(e.target.value); - handleQueryChange(e.target.value); - }} - variant="filled" - type="search" - InputProps={{ - startAdornment: ( - - - - ), - }} - aria-label={`Search${label ? " " + label : ""}`} - placeholder={`Search${label ? " " + label : ""}`} - hiddenLabel - fullWidth - /> - - )} + + {search && ( + + { + setQuery(e.target.value); + handleQueryChange(e.target.value); + }} + variant="filled" + type="search" + InputProps={{ + startAdornment: ( + + + + ), + }} + aria-label={`Search${label ? " " + label : ""}`} + placeholder={`Search${label ? " " + label : ""}`} + hiddenLabel + fullWidth + /> + + )} - {filters.map((filter) => ( - - - setFilterValues((other) => ({ - ...other, - [filter.facet]: value, - })) - } - options={ - facetValues[filter.facet]?.map((item) => ({ - value: item.value, - label: filter.labelTransformer - ? filter.labelTransformer(item.value) - : item.value, - count: item.count, - })) ?? [] - } - itemRenderer={(option) => ( - - {option.label} - - - {(option as any).count} - - - - )} - searchable={facetValues[filter.facet]?.length > 10} - /> - - ))} - -
- ); + {filters.map((filter) => ( + + + setFilterValues((other) => ({ + ...other, + [filter.facet]: value, + })) + } + options={ + facetValues[filter.facet]?.map((item) => ({ + value: item.value, + label: filter.labelTransformer + ? filter.labelTransformer(item.value) + : item.value, + count: item.count, + })) ?? [] + } + itemRenderer={(option) => ( + + {option.label} + + + {(option as any).count} + + + + )} + searchable={facetValues[filter.facet]?.length > 10} + /> + + ))} +
+
+ ); } diff --git a/src/components/Grid/Card/index.tsx b/src/components/Grid/Card/index.tsx index 4dacf5f2..ddc90dfc 100644 --- a/src/components/Grid/Card/index.tsx +++ b/src/components/Grid/Card/index.tsx @@ -2,248 +2,248 @@ import React, { useState } from "react"; import clsx from "clsx"; import { - Card, - Grid, - Typography, - Button, - CardActions, - CardContent, - CardMedia, - Divider, - Tabs, - Tab, + Card, + Grid, + Typography, + Button, + CardActions, + CardContent, + CardMedia, + Divider, + Tabs, + Tab, } from "@material-ui/core"; import { ButtonProps } from "@material-ui/core/Button"; import GoIcon from "assets/icons/Go"; import useStyles from "./styles"; export interface ICardProps { - className?: string; - style?: React.CSSProperties; + className?: string; + style?: React.CSSProperties; - overline?: React.ReactNode; - title?: React.ReactNode; - imageSource?: string; - imageShape?: "square" | "circle"; - imageClassName?: string; + overline?: React.ReactNode; + title?: React.ReactNode; + imageSource?: string; + imageShape?: "square" | "circle"; + imageClassName?: string; - tabs?: { - label: string; - content: React.ReactNode; - disabled?: boolean; - }[]; - bodyContent?: React.ReactNode; + tabs?: { + label: string; + content: React.ReactNode; + disabled?: boolean; + }[]; + bodyContent?: React.ReactNode; - primaryButton?: { label: string } & Partial; - primaryLink?: { - href?: string; - target?: string; - rel?: string; - label: string; - } & Partial; - secondaryAction?: React.ReactNode; + primaryButton?: { label: string } & Partial; + primaryLink?: { + href?: string; + target?: string; + rel?: string; + label: string; + } & Partial; + secondaryAction?: React.ReactNode; } const a11yProps = (index: number) => ({ - id: `full-width-tab-${index}`, - "aria-controls": `full-width-tabpanel-${index}`, + id: `full-width-tab-${index}`, + "aria-controls": `full-width-tabpanel-${index}`, }); export default function BasicCard({ - className, - style, + className, + style, - overline, - title, - imageSource, - imageShape = "square", - imageClassName, + overline, + title, + imageSource, + imageShape = "square", + imageClassName, - tabs, - bodyContent, + tabs, + bodyContent, - primaryButton, - primaryLink, - secondaryAction, + primaryButton, + primaryLink, + secondaryAction, }: ICardProps) { - const classes = useStyles(); + const classes = useStyles(); - const [tab, setTab] = useState(0); + const [tab, setTab] = useState(0); - const handleChangeTab = (event: React.ChangeEvent<{}>, newValue: number) => - setTab(newValue); + const handleChangeTab = (event: React.ChangeEvent<{}>, newValue: number) => + setTab(newValue); - return ( - - - - - - {(overline || title || imageSource) && ( - - - - {overline && ( - - {overline} - - )} - {title && ( - - {title} - - )} - + return ( + + + + + + {(overline || title || imageSource) && ( + + + + {overline && ( + + {overline} + + )} + {title && ( + + {title} + + )} + - {imageSource && ( - - - - )} - - - )} + {imageSource && ( + + + + )} + + + )} - {tabs && ( - - - {tabs?.map((tab, index) => ( - - ))} - - - - )} + {tabs && ( + + + {tabs?.map((tab, index) => ( + + ))} + + + + )} - {(tabs || bodyContent) && ( - - {tabs && ( -
- {tabs[tab].content && Array.isArray(tabs[tab].content) ? ( - - {(tabs[tab].content as React.ReactNode[]).map( - (element, index) => ( - - {element} - - ) - )} - - ) : ( - tabs[tab].content - )} -
- )} + {(tabs || bodyContent) && ( + + {tabs && ( +
+ {tabs[tab].content && Array.isArray(tabs[tab].content) ? ( + + {(tabs[tab].content as React.ReactNode[]).map( + (element, index) => ( + + {element} + + ) + )} + + ) : ( + tabs[tab].content + )} +
+ )} - {bodyContent && Array.isArray(bodyContent) ? ( - - {bodyContent.map((element, i) => ( - - {element} - - ))} - - ) : ( - bodyContent - )} -
- )} -
-
-
+ {bodyContent && Array.isArray(bodyContent) ? ( + + {bodyContent.map((element, i) => ( + + {element} + + ))} + + ) : ( + bodyContent + )} +
+ )} + + + - {(primaryButton || primaryLink || secondaryAction) && ( - - - - - {primaryButton && ( - - )} - {primaryLink && ( - - )} - - {secondaryAction && {secondaryAction}} - - - )} - -
- ); + {(primaryButton || primaryLink || secondaryAction) && ( + + + + + {primaryButton && ( + + )} + {primaryLink && ( + + )} + + {secondaryAction && {secondaryAction}} + + + )} + + + ); } diff --git a/src/components/Grid/Card/styles.ts b/src/components/Grid/Card/styles.ts index 58af8f67..87a174a2 100644 --- a/src/components/Grid/Card/styles.ts +++ b/src/components/Grid/Card/styles.ts @@ -1,58 +1,58 @@ import { makeStyles, createStyles } from "@material-ui/styles"; const useStyles = makeStyles((theme) => - createStyles({ - root: { width: "100%", height: "100%" }, - container: { height: "100%" }, + createStyles({ + root: { width: "100%", height: "100%" }, + container: { height: "100%" }, - cardContentContainer: { - "&:last-child": { paddingBottom: theme.spacing(3) }, - }, - cardContent: { - "&:last-child": { paddingBottom: 0 }, - }, + cardContentContainer: { + "&:last-child": { paddingBottom: theme.spacing(3) }, + }, + cardContent: { + "&:last-child": { paddingBottom: 0 }, + }, - headerContainer: {}, - tabsContainer: { "$headerContainer + &": { marginTop: theme.spacing(2) } }, - contentContainer: { - "$headerContainer + &": { marginTop: theme.spacing(2) }, - }, + headerContainer: {}, + tabsContainer: { "$headerContainer + &": { marginTop: theme.spacing(2) } }, + contentContainer: { + "$headerContainer + &": { marginTop: theme.spacing(2) }, + }, - overline: { - marginBottom: theme.spacing(3), - color: theme.palette.text.disabled, - wordBreak: "break-word", - }, - title: { - whiteSpace: "pre-line", - wordBreak: "break-word", - }, - image: { - width: 80, - height: 80, - borderRadius: theme.shape.borderRadius, - }, - imageCircle: { borderRadius: "50%" }, + overline: { + marginBottom: theme.spacing(3), + color: theme.palette.text.disabled, + wordBreak: "break-word", + }, + title: { + whiteSpace: "pre-line", + wordBreak: "break-word", + }, + image: { + width: 80, + height: 80, + borderRadius: theme.shape.borderRadius, + }, + imageCircle: { borderRadius: "50%" }, - tabs: { margin: theme.spacing(0, -2) }, - tab: { minWidth: 0 }, - tabDivider: { marginTop: -1 }, + tabs: { margin: theme.spacing(0, -2) }, + tab: { minWidth: 0 }, + tabDivider: { marginTop: -1 }, - tabSection: { paddingTop: theme.spacing(2), height: "100%" }, - tabContentGrid: { height: `calc(100% + ${theme.spacing(3)})` }, + tabSection: { paddingTop: theme.spacing(2), height: "100%" }, + tabContentGrid: { height: `calc(100% + ${theme.spacing(3)})` }, - divider: { - margin: theme.spacing(2), - marginBottom: 0, - }, - cardActions: { - padding: theme.spacing(0.75, 1), - display: "flex", - justifyContent: "space-between", - }, + divider: { + margin: theme.spacing(2), + marginBottom: 0, + }, + cardActions: { + padding: theme.spacing(0.75, 1), + display: "flex", + justifyContent: "space-between", + }, - primaryLinkLabel: { whiteSpace: "nowrap" }, - }) + primaryLinkLabel: { whiteSpace: "nowrap" }, + }) ); export default useStyles; diff --git a/src/components/Grid/index.tsx b/src/components/Grid/index.tsx index 42a481f2..092b5e8f 100644 --- a/src/components/Grid/index.tsx +++ b/src/components/Grid/index.tsx @@ -4,78 +4,78 @@ import useAlgolia from "use-algolia"; import AlgoliaFilters from "./AlgoliaFilters"; import _get from "lodash/get"; const advisorsFilters = [ - { label: "Type", facet: "type" }, - { label: "Experience (Industry)", facet: "expertise" }, - { label: "Location", facet: "location" }, + { label: "Type", facet: "type" }, + { label: "Experience (Industry)", facet: "expertise" }, + { label: "Location", facet: "location" }, ]; interface IGridProps { - collection: string; - filters: any[]; + collection: string; + filters: any[]; } export const replacer = (data: any) => (m: string, key: string) => { - const objKey = key.split(":")[0]; - const defaultValue = key.split(":")[1] || ""; - return _get(data, objKey, defaultValue); + const objKey = key.split(":")[0]; + const defaultValue = key.split(":")[1] || ""; + return _get(data, objKey, defaultValue); }; const CARD_CONFIG = { - title: `{{firstName}} {{lastName}}`, - image: "{{profilePhoto[0].downloadURL}}", - body: "{{bio}}", + title: `{{firstName}} {{lastName}}`, + image: "{{profilePhoto[0].downloadURL}}", + body: "{{bio}}", }; export default function Grid({ collection }: IGridProps) { - const [algoliaState, requestDispatch, ,] = useAlgolia( - process.env.REACT_APP_ALGOLIA_APP_ID!, - process.env.REACT_APP_ALGOLIA_SEARCH_API_KEY!, - collection, - { hitsPerPage: 100 } - ); + const [algoliaState, requestDispatch, ,] = useAlgolia( + process.env.REACT_APP_ALGOLIA_APP_ID!, + process.env.REACT_APP_ALGOLIA_SEARCH_API_KEY!, + collection, + { hitsPerPage: 100 } + ); - const isLoading = algoliaState.loading || !algoliaState.index; - const noResults = algoliaState.hits.length === 0; - const requiredFilters = ``; - const isEmpty = - noResults && - algoliaState.request?.query === undefined && - algoliaState.request?.filters === requiredFilters; - return ( - <> - {algoliaState.index && !isEmpty && ( - - )} - - {" "} - {algoliaState.hits.map((hit) => { - return ( - - {" "} - {" "} - - ); - })} - - - ); + const isLoading = algoliaState.loading || !algoliaState.index; + const noResults = algoliaState.hits.length === 0; + const requiredFilters = ``; + const isEmpty = + noResults && + algoliaState.request?.query === undefined && + algoliaState.request?.filters === requiredFilters; + return ( + <> + {algoliaState.index && !isEmpty && ( + + )} + + {" "} + {algoliaState.hits.map((hit) => { + return ( + + {" "} + {" "} + + ); + })} + + + ); } diff --git a/src/components/HelperText.tsx b/src/components/HelperText.tsx index d840ffa6..61b295e9 100644 --- a/src/components/HelperText.tsx +++ b/src/components/HelperText.tsx @@ -1,21 +1,21 @@ import { useTheme } from "@material-ui/core"; export interface IHelperTextProps { - children: React.ReactNode; + children: React.ReactNode; } export default function HelperText(props: IHelperTextProps) { - const theme = useTheme(); + const theme = useTheme(); - return ( -
- ); + return ( +
+ ); } diff --git a/src/components/HomeNavigation/NavDrawer.tsx b/src/components/HomeNavigation/NavDrawer.tsx index c4c9ef5c..a6360048 100644 --- a/src/components/HomeNavigation/NavDrawer.tsx +++ b/src/components/HomeNavigation/NavDrawer.tsx @@ -1,15 +1,15 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { - useTheme, - useMediaQuery, - Drawer, - DrawerProps, - Grid, - IconButton, - List, - MenuItem, - ListItemIcon, - ListItemText, + useTheme, + useMediaQuery, + Drawer, + DrawerProps, + Grid, + IconButton, + List, + MenuItem, + ListItemIcon, + ListItemText, } from "@material-ui/core"; import CloseIcon from "assets/icons/Backburger"; import AddIcon from "@material-ui/icons/Add"; @@ -23,109 +23,109 @@ import useRouter from "hooks/useRouter"; export const NAV_DRAWER_WIDTH = 300; const useStyles = makeStyles((theme) => - createStyles({ - paper: { - width: NAV_DRAWER_WIDTH, - overflowX: "hidden", - backgroundColor: theme.palette.background.paper, - }, + createStyles({ + paper: { + width: NAV_DRAWER_WIDTH, + overflowX: "hidden", + backgroundColor: theme.palette.background.paper, + }, - logoRow: { - height: APP_BAR_HEIGHT, - marginTop: 0, - marginBottom: theme.spacing(1), + logoRow: { + height: APP_BAR_HEIGHT, + marginTop: 0, + marginBottom: theme.spacing(1), - padding: theme.spacing(0, 2), - }, - logo: { marginLeft: theme.spacing(1.5) }, + padding: theme.spacing(0, 2), + }, + logo: { marginLeft: theme.spacing(1.5) }, - nav: { height: "100%" }, - list: { - display: "flex", - flexDirection: "column", - flexWrap: "nowrap", + nav: { height: "100%" }, + list: { + display: "flex", + flexDirection: "column", + flexWrap: "nowrap", - height: "100%", - }, + height: "100%", + }, - createTable: { marginTop: "auto" }, - }) + createTable: { marginTop: "auto" }, + }) ); export interface INavDrawerProps extends DrawerProps { - handleCreateTable: () => void; + handleCreateTable: () => void; } export default function NavDrawer({ - handleCreateTable, - ...props + handleCreateTable, + ...props }: INavDrawerProps) { - const classes = useStyles(); - const theme = useTheme(); - const isSm = useMediaQuery(theme.breakpoints.down("md")); + const classes = useStyles(); + const theme = useTheme(); + const isSm = useMediaQuery(theme.breakpoints.down("md")); - const { sections } = useRowyContext(); - const { location } = useRouter(); - const { hash } = location; + const { sections } = useRowyContext(); + const { location } = useRouter(); + const { hash } = location; - return ( - - - - - - - + return ( + + + + + + + - - - - + + + + - + + ); } diff --git a/src/components/HomeNavigation/index.tsx b/src/components/HomeNavigation/index.tsx index 914b6773..8ecb9277 100644 --- a/src/components/HomeNavigation/index.tsx +++ b/src/components/HomeNavigation/index.tsx @@ -2,11 +2,11 @@ import clsx from "clsx"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - useScrollTrigger, - Grid, - AppBar, - Toolbar, - IconButton, + useScrollTrigger, + Grid, + AppBar, + Toolbar, + IconButton, } from "@material-ui/core"; import MenuIcon from "@material-ui/icons/Menu"; @@ -17,157 +17,157 @@ import UserMenu from "components/Navigation/UserMenu"; export const APP_BAR_HEIGHT = 56; const useStyles = makeStyles((theme) => - createStyles({ - open: {}, + createStyles({ + open: {}, - navDrawerContainer: { - [theme.breakpoints.up("md")]: { - width: 0, - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), + navDrawerContainer: { + [theme.breakpoints.up("md")]: { + width: 0, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), - "$open &": { - width: NAV_DRAWER_WIDTH, - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - }, - }, - }, + "$open &": { + width: NAV_DRAWER_WIDTH, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }, + }, + }, - appBar: { - height: APP_BAR_HEIGHT, + appBar: { + height: APP_BAR_HEIGHT, - [theme.breakpoints.down("md")]: { paddingRight: 0 }, + [theme.breakpoints.down("md")]: { paddingRight: 0 }, - [theme.breakpoints.up("md")]: { - transition: - theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }) + - ", " + - theme.transitions.create(["box-shadow", "background-color"]), - "$open &": { - width: `calc(100% - ${NAV_DRAWER_WIDTH}px)`, - transition: - theme.transitions.create("width", { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }) + - ", " + - theme.transitions.create(["box-shadow", "background-color"]), - }, - }, + [theme.breakpoints.up("md")]: { + transition: + theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }) + + ", " + + theme.transitions.create(["box-shadow", "background-color"]), + "$open &": { + width: `calc(100% - ${NAV_DRAWER_WIDTH}px)`, + transition: + theme.transitions.create("width", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }) + + ", " + + theme.transitions.create(["box-shadow", "background-color"]), + }, + }, - // Elevation 8 - backgroundImage: - "linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))", - "&::before": { - content: "''", - display: "block", - position: "absolute", - top: 0, - right: 0, - bottom: 0, - left: 0, + // Elevation 8 + backgroundImage: + "linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))", + "&::before": { + content: "''", + display: "block", + position: "absolute", + top: 0, + right: 0, + bottom: 0, + left: 0, - backgroundColor: theme.palette.background.default, - transition: theme.transitions.create("opacity"), - }, - }, - appBarScrolled: { - "&::before": { - opacity: 0, - }, - }, - toolbar: { - height: APP_BAR_HEIGHT, - minHeight: "auto", - minWidth: 0, - maxWidth: "none", - padding: theme.spacing(0, 2), - }, + backgroundColor: theme.palette.background.default, + transition: theme.transitions.create("opacity"), + }, + }, + appBarScrolled: { + "&::before": { + opacity: 0, + }, + }, + toolbar: { + height: APP_BAR_HEIGHT, + minHeight: "auto", + minWidth: 0, + maxWidth: "none", + padding: theme.spacing(0, 2), + }, - openButton: { - opacity: 1, - transition: theme.transitions.create("opacity"), - "$open &": { opacity: 0 }, - }, - logo: { - flex: 1, - textAlign: "center", + openButton: { + opacity: 1, + transition: theme.transitions.create("opacity"), + "$open &": { opacity: 0 }, + }, + logo: { + flex: 1, + textAlign: "center", - opacity: 1, - transition: theme.transitions.create("opacity"), - "$open &": { opacity: 0 }, - }, - }) + opacity: 1, + transition: theme.transitions.create("opacity"), + "$open &": { opacity: 0 }, + }, + }) ); export interface IHomeNavigationProps { - children: React.ReactNode; + children: React.ReactNode; - open: boolean; - setOpen: React.Dispatch>; + open: boolean; + setOpen: React.Dispatch>; - handleCreateTable: () => void; + handleCreateTable: () => void; } export default function HomeNavigation({ - children, - open, - setOpen, - handleCreateTable, + children, + open, + setOpen, + handleCreateTable, }: IHomeNavigationProps) { - const classes = useStyles(); - const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 }); + const classes = useStyles(); + const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 }); - return ( - - - setOpen(false)} - handleCreateTable={handleCreateTable} - /> - + return ( + + + setOpen(false)} + handleCreateTable={handleCreateTable} + /> + - - - - setOpen(true)} - edge="start" - size="large" - className={classes.openButton} - > - - + + + + setOpen(true)} + edge="start" + size="large" + className={classes.openButton} + > + + -
- -
+
+ +
- -
-
+ +
+
- {children} -
-
- ); + {children} +
+ + ); } diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index f44f1178..9209b03f 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -4,44 +4,44 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { Grid, CircularProgress, Typography } from "@material-ui/core"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - height: "100%", - width: "100%", - textAlign: "center", - }, - progress: { color: theme.palette.action.active }, - content: { maxWidth: "25em" }, - message: { marginTop: theme.spacing(1) }, - }) + createStyles({ + root: { + height: "100%", + width: "100%", + textAlign: "center", + }, + progress: { color: theme.palette.action.active }, + content: { maxWidth: "25em" }, + message: { marginTop: theme.spacing(1) }, + }) ); interface ILoading { - message?: string; - fullScreen?: boolean; + message?: string; + fullScreen?: boolean; } export default function Loading({ - message = "Loading", - fullScreen = false, + message = "Loading", + fullScreen = false, }: ILoading) { - const classes = useStyles({}); + const classes = useStyles({}); - return ( - - - - - {message} - - - - ); + return ( + + + + + {message} + + + + ); } diff --git a/src/components/Modal/SlideTransition.tsx b/src/components/Modal/SlideTransition.tsx index fda269b2..242930d5 100644 --- a/src/components/Modal/SlideTransition.tsx +++ b/src/components/Modal/SlideTransition.tsx @@ -5,73 +5,73 @@ import { TransitionProps } from "react-transition-group/Transition"; import { TransitionProps as MuiTransitionProps } from "@material-ui/core/transitions"; export const SlideTransition: React.ForwardRefExoticComponent< - Pick & React.RefAttributes + Pick & React.RefAttributes > = React.forwardRef( - ({ children, ...props }: TransitionProps, ref: React.Ref) => { - const theme = useTheme(); + ({ children, ...props }: TransitionProps, ref: React.Ref) => { + const theme = useTheme(); - if (!children) return null; + if (!children) return null; - const defaultStyle = { - opacity: 0, - transform: "translateY(16px)", + const defaultStyle = { + opacity: 0, + transform: "translateY(16px)", - transition: theme.transitions.create(["transform", "opacity"], { - duration: "300ms", - easing: "cubic-bezier(0.075, 0.82, 0.165, 1)", - }), - }; + transition: theme.transitions.create(["transform", "opacity"], { + duration: "300ms", + easing: "cubic-bezier(0.075, 0.82, 0.165, 1)", + }), + }; - const transitionStyles = { - entering: { - willChange: "transform, opacity", - }, + const transitionStyles = { + entering: { + willChange: "transform, opacity", + }, - entered: { - opacity: 1, - transform: "none", - }, + entered: { + opacity: 1, + transform: "none", + }, - exiting: { - opacity: 0, - transform: "none", + exiting: { + opacity: 0, + transform: "none", - transition: theme.transitions.create(["opacity"], { - duration: theme.transitions.duration.leavingScreen, - }), - }, + transition: theme.transitions.create(["opacity"], { + duration: theme.transitions.duration.leavingScreen, + }), + }, - exited: { - opacity: 0, - transform: "none", - transition: "none", - }, + exited: { + opacity: 0, + transform: "none", + transition: "none", + }, - unmounted: {}, - }; + unmounted: {}, + }; - return ( - - {(state) => - React.cloneElement(children as any, { - style: { ...defaultStyle, ...transitionStyles[state] }, - ref, - }) - } - - ); - } + return ( + + {(state) => + React.cloneElement(children as any, { + style: { ...defaultStyle, ...transitionStyles[state] }, + ref, + }) + } + + ); + } ); export default SlideTransition; export const SlideTransitionMui = React.forwardRef(function Transition( - props: MuiTransitionProps & { children?: React.ReactElement }, - ref: React.Ref + props: MuiTransitionProps & { children?: React.ReactElement }, + ref: React.Ref ) { - return ; + return ; }); diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index c77a7c5d..2fd9ea7a 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,124 +1,124 @@ import { ReactNode, useState } from "react"; import { - useTheme, - useMediaQuery, - Dialog, - DialogProps, - Stack, - DialogTitle, - IconButton, - DialogContent, - DialogActions, - Button, - ButtonProps, + useTheme, + useMediaQuery, + Dialog, + DialogProps, + Stack, + DialogTitle, + IconButton, + DialogContent, + DialogActions, + Button, + ButtonProps, } from "@material-ui/core"; import CloseIcon from "@material-ui/icons/Close"; import { SlideTransitionMui } from "./SlideTransition"; export interface IModalProps extends Partial> { - onClose: () => void; - disableBackdropClick?: boolean; + onClose: () => void; + disableBackdropClick?: boolean; - title: ReactNode; - header?: ReactNode; - footer?: ReactNode; + title: ReactNode; + header?: ReactNode; + footer?: ReactNode; - children?: ReactNode; - body?: ReactNode; + children?: ReactNode; + body?: ReactNode; - actions?: { - primary?: Partial; - secondary?: Partial; - }; + actions?: { + primary?: Partial; + secondary?: Partial; + }; - hideCloseButton?: boolean; - fullHeight?: boolean; + hideCloseButton?: boolean; + fullHeight?: boolean; } export default function Modal({ - onClose, - disableBackdropClick, - title, - header, - footer, - children, - body, - actions, - hideCloseButton, - fullHeight, - ...props + onClose, + disableBackdropClick, + title, + header, + footer, + children, + body, + actions, + hideCloseButton, + fullHeight, + ...props }: IModalProps) { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); - const [open, setOpen] = useState(true); - const handleClose = (_, reason?: string) => { - if (disableBackdropClick && reason === "backdropClick") return; + const [open, setOpen] = useState(true); + const handleClose = (_, reason?: string) => { + if (disableBackdropClick && reason === "backdropClick") return; - setOpen(false); - setTimeout(() => onClose(), 300); - }; + setOpen(false); + setTimeout(() => onClose(), 300); + }; - return ( - - - - {title} - + return ( + + + + {title} + - {!hideCloseButton && ( - - - - )} - + {!hideCloseButton && ( + + + + )} + - {header} + {header} - {children || body} + {children || body} - {footer} + {footer} - {actions && ( - - {actions.secondary && - ); + {actions.primary && ( + - ) - } - /> - ); + return ( + One click deploy + ) + } + /> + ); } diff --git a/src/components/RenderedHtml.tsx b/src/components/RenderedHtml.tsx index 17808eed..2df7d67e 100644 --- a/src/components/RenderedHtml.tsx +++ b/src/components/RenderedHtml.tsx @@ -4,107 +4,107 @@ import clsx from "clsx"; import { makeStyles, createStyles } from "@material-ui/styles"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - maxWidth: "33em", - ...theme.typography.body2, + createStyles({ + root: { + maxWidth: "33em", + ...theme.typography.body2, - "& * + *": { - marginTop: "1em !important", - }, + "& * + *": { + marginTop: "1em !important", + }, - "& h1, & h2, & h3, & h4, & h5, & h6": { - fontFamily: '"Europa", "Open Sans", sans-serif', - margin: 0, - lineHeight: 1.2, - fontWeight: "bold", - }, - "& p": { - margin: 0, - marginTop: "inherit", - }, + "& h1, & h2, & h3, & h4, & h5, & h6": { + fontFamily: '"Europa", "Open Sans", sans-serif', + margin: 0, + lineHeight: 1.2, + fontWeight: "bold", + }, + "& p": { + margin: 0, + marginTop: "inherit", + }, - "& a": { - fontWeight: "bold", - color: theme.palette.primary.main, - textDecoration: "underline", + "& a": { + fontWeight: "bold", + color: theme.palette.primary.main, + textDecoration: "underline", - "&:hover": { color: theme.palette.primary.dark }, - }, + "&:hover": { color: theme.palette.primary.dark }, + }, - "& ul, & ol": { - margin: 0, - paddingLeft: "1.5em", - }, - "& li + li": { - marginTop: "0.5em", - }, + "& ul, & ol": { + margin: 0, + paddingLeft: "1.5em", + }, + "& li + li": { + marginTop: "0.5em", + }, - "& table": { - borderCollapse: "collapse", - }, + "& table": { + borderCollapse: "collapse", + }, - "& table th, & table td": { - border: `1px solid ${theme.palette.divider}`, - padding: "0.4rem", - }, - "& figure": { - display: "table", - margin: "1rem auto", - }, - "& figure figcaption": { - color: "#999", - display: "block", - marginTop: "0.25rem", - textAlign: "center", - }, - "& hr": { - borderColor: `1px solid ${theme.palette.divider}`, - borderWidth: "1px 0 0 0", - }, - "& code": { - backgroundColor: "#e8e8e8", - borderRadius: theme.shape.borderRadius, - padding: "0.1rem 0.2rem", - fontFamily: theme.typography.fontFamilyMono, - }, - "& pre": { - fontFamily: theme.typography.fontFamilyMono, - }, - '& .mceContent-body:not([dir="rtl"]) blockquote': { - borderLeft: `2px solid ${theme.palette.divider}`, - marginLeft: "1.5rem", - paddingLeft: "1rem", - }, - '& .mceContent-body[dir="rtl"] blockquote': { - borderRight: `2px solid ${theme.palette.divider}`, - marginRight: "1.5rem", - paddingRight: "1rem", - }, - }, - }) + "& table th, & table td": { + border: `1px solid ${theme.palette.divider}`, + padding: "0.4rem", + }, + "& figure": { + display: "table", + margin: "1rem auto", + }, + "& figure figcaption": { + color: "#999", + display: "block", + marginTop: "0.25rem", + textAlign: "center", + }, + "& hr": { + borderColor: `1px solid ${theme.palette.divider}`, + borderWidth: "1px 0 0 0", + }, + "& code": { + backgroundColor: "#e8e8e8", + borderRadius: theme.shape.borderRadius, + padding: "0.1rem 0.2rem", + fontFamily: theme.typography.fontFamilyMono, + }, + "& pre": { + fontFamily: theme.typography.fontFamilyMono, + }, + '& .mceContent-body:not([dir="rtl"]) blockquote': { + borderLeft: `2px solid ${theme.palette.divider}`, + marginLeft: "1.5rem", + paddingLeft: "1rem", + }, + '& .mceContent-body[dir="rtl"] blockquote': { + borderRight: `2px solid ${theme.palette.divider}`, + marginRight: "1.5rem", + paddingRight: "1rem", + }, + }, + }) ); export interface IRenderedHtmlProps - extends React.DetailedHTMLProps< - React.HTMLAttributes, - HTMLDivElement - > { - html: string; + extends React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement + > { + html: string; } export default function RenderedHtml({ - html, - className, - ...props + html, + className, + ...props }: IRenderedHtmlProps) { - const classes = useStyles(); + const classes = useStyles(); - return ( -
- ); + return ( +
+ ); } diff --git a/src/components/RichTextEditor.tsx b/src/components/RichTextEditor.tsx index f8d47376..53ec5e86 100644 --- a/src/components/RichTextEditor.tsx +++ b/src/components/RichTextEditor.tsx @@ -18,128 +18,128 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { useTheme } from "@material-ui/core"; const useStyles = makeStyles((theme) => - createStyles({ - "@global": { - body: { - fontFamily: theme.typography.fontFamily + " !important", - }, - }, + createStyles({ + "@global": { + body: { + fontFamily: theme.typography.fontFamily + " !important", + }, + }, - root: { - "& .tox": { - "&.tox-tinymce": { - borderRadius: theme.shape.borderRadius, - border: "none", - backgroundColor: - theme.palette.mode === "light" - ? "rgba(0, 0, 0, 0.09)" - : "rgba(255, 255, 255, 0.09)", + root: { + "& .tox": { + "&.tox-tinymce": { + borderRadius: theme.shape.borderRadius, + border: "none", + backgroundColor: + theme.palette.mode === "light" + ? "rgba(0, 0, 0, 0.09)" + : "rgba(255, 255, 255, 0.09)", - transition: theme.transitions.create("background-color", { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), + transition: theme.transitions.create("background-color", { + duration: theme.transitions.duration.shorter, + easing: theme.transitions.easing.easeOut, + }), - "&:hover": { - backgroundColor: - theme.palette.mode === "light" - ? "rgba(0, 0, 0, 0.13)" - : "rgba(255, 255, 255, 0.13)", - }, - }, + "&:hover": { + backgroundColor: + theme.palette.mode === "light" + ? "rgba(0, 0, 0, 0.13)" + : "rgba(255, 255, 255, 0.13)", + }, + }, - "& .tox-sidebar-wrap": { - margin: 1, - }, + "& .tox-sidebar-wrap": { + margin: 1, + }, - "& .tox-toolbar-overlord, & .tox-edit-area__iframe, & .tox-toolbar__primary": { - background: "transparent", - borderRadius: (theme.shape.borderRadius as number) - 1, - }, + "& .tox-toolbar-overlord, & .tox-edit-area__iframe, & .tox-toolbar__primary": { + background: "transparent", + borderRadius: (theme.shape.borderRadius as number) - 1, + }, - "& .tox-toolbar__primary": { padding: theme.spacing(0.5, 0) }, - "& .tox-toolbar__group": { - padding: theme.spacing(0, 1), - border: "none !important", - }, + "& .tox-toolbar__primary": { padding: theme.spacing(0.5, 0) }, + "& .tox-toolbar__group": { + padding: theme.spacing(0, 1), + border: "none !important", + }, - "& .tox-tbtn": { - borderRadius: theme.shape.borderRadius, - color: theme.palette.text.secondary, - cursor: "pointer", - margin: 0, + "& .tox-tbtn": { + borderRadius: theme.shape.borderRadius, + color: theme.palette.text.secondary, + cursor: "pointer", + margin: 0, - transition: theme.transitions.create(["color", "background-color"], { - duration: theme.transitions.duration.shortest, - }), + transition: theme.transitions.create(["color", "background-color"], { + duration: theme.transitions.duration.shortest, + }), - "&:hover": { - color: theme.palette.text.primary, - backgroundColor: "transparent", - }, + "&:hover": { + color: theme.palette.text.primary, + backgroundColor: "transparent", + }, - "& svg": { fill: "currentColor" }, - }, + "& svg": { fill: "currentColor" }, + }, - "& .tox-tbtn--enabled, & .tox-tbtn--enabled:hover": { - backgroundColor: theme.palette.action.selected + " !important", - color: theme.palette.text.primary, - }, - }, - }, + "& .tox-tbtn--enabled, & .tox-tbtn--enabled:hover": { + backgroundColor: theme.palette.action.selected + " !important", + color: theme.palette.text.primary, + }, + }, + }, - focus: { - "& .tox.tox-tinymce": { - backgroundColor: - (theme.palette.mode === "light" - ? "rgba(0, 0, 0, 0.09)" - : "rgba(255, 255, 255, 0.09)") + "!important", - }, - }, - }) + focus: { + "& .tox.tox-tinymce": { + backgroundColor: + (theme.palette.mode === "light" + ? "rgba(0, 0, 0, 0.09)" + : "rgba(255, 255, 255, 0.09)") + "!important", + }, + }, + }) ); export interface IRichTextEditorProps { - value?: string; - onChange: (value: string) => void; - disabled?: boolean; + value?: string; + onChange: (value: string) => void; + disabled?: boolean; } export default function RichTextEditor({ - value, - onChange, - disabled, + value, + onChange, + disabled, }: IRichTextEditorProps) { - const classes = useStyles(); - const theme = useTheme(); - const [focus, setFocus] = useState(false); + const classes = useStyles(); + const theme = useTheme(); + const [focus, setFocus] = useState(false); - return ( -
- setFocus(true)} - onBlur={() => setFocus(false)} - /> -
- ); + return ( +
+ setFocus(true)} + onBlur={() => setFocus(false)} + /> +
+ ); } diff --git a/src/components/RichTooltip.tsx b/src/components/RichTooltip.tsx index 20a396c7..5d016eeb 100644 --- a/src/components/RichTooltip.tsx +++ b/src/components/RichTooltip.tsx @@ -5,108 +5,108 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { Tooltip, TooltipProps, Button, ButtonProps } from "@material-ui/core"; const useStyles = makeStyles((theme) => - createStyles({ - tooltip: { - backgroundColor: theme.palette.background.default, - boxShadow: theme.shadows[2], + createStyles({ + tooltip: { + backgroundColor: theme.palette.background.default, + boxShadow: theme.shadows[2], - ...theme.typography.caption, - color: theme.palette.text.primary, - padding: 0, - }, + ...theme.typography.caption, + color: theme.palette.text.primary, + padding: 0, + }, - arrow: { - "&::before": { - backgroundColor: theme.palette.background.default, - boxShadow: theme.shadows[2], - }, - }, + arrow: { + "&::before": { + backgroundColor: theme.palette.background.default, + boxShadow: theme.shadows[2], + }, + }, - grid: { - padding: theme.spacing(2), - cursor: "default", + grid: { + padding: theme.spacing(2), + cursor: "default", - display: "grid", - gridTemplateColumns: "40px auto", - gap: theme.spacing(1, 2), - }, - emoji: { - fontSize: `${40 / 16}rem`, - fontWeight: 400, - fontFamily: - "apple color emoji, segoe ui emoji, noto color emoji, android emoji, emojisymbols, emojione mozilla, twemoji mozilla, segoe ui symbol", - }, - message: { - alignSelf: "center", - }, - dismissButton: { - marginLeft: theme.spacing(-1), - gridColumn: 2, - justifySelf: "flex-start", - }, - }) + display: "grid", + gridTemplateColumns: "40px auto", + gap: theme.spacing(1, 2), + }, + emoji: { + fontSize: `${40 / 16}rem`, + fontWeight: 400, + fontFamily: + "apple color emoji, segoe ui emoji, noto color emoji, android emoji, emojisymbols, emojione mozilla, twemoji mozilla, segoe ui symbol", + }, + message: { + alignSelf: "center", + }, + dismissButton: { + marginLeft: theme.spacing(-1), + gridColumn: 2, + justifySelf: "flex-start", + }, + }) ); export interface IRichTooltipProps extends Partial { - render: (props: { - openTooltip: () => void; - closeTooltip: () => void; - toggleTooltip: () => void; - }) => TooltipProps["children"]; + render: (props: { + openTooltip: () => void; + closeTooltip: () => void; + toggleTooltip: () => void; + }) => TooltipProps["children"]; - emoji?: React.ReactNode; - message: React.ReactNode; - dismissButtonText?: React.ReactNode; - dismissButtonProps?: Partial; + emoji?: React.ReactNode; + message: React.ReactNode; + dismissButtonText?: React.ReactNode; + dismissButtonProps?: Partial; } export default function RichTooltip({ - render, - emoji, - message, - dismissButtonText, - dismissButtonProps, - ...props + render, + emoji, + message, + dismissButtonText, + dismissButtonProps, + ...props }: IRichTooltipProps) { - const classes = useStyles(); - const [open, setOpen] = useState(false); + const classes = useStyles(); + const [open, setOpen] = useState(false); - const openTooltip = () => setOpen(true); - const closeTooltip = () => setOpen(false); - const toggleTooltip = () => setOpen((state) => !state); + const openTooltip = () => setOpen(true); + const closeTooltip = () => setOpen(false); + const toggleTooltip = () => setOpen((state) => !state); - return ( - - {emoji} + return ( + + {emoji} -
{message}
+
{message}
- {dismissButtonText && ( - - )} -
- } - {...props} - > - {render({ openTooltip, closeTooltip, toggleTooltip })} - - ); + {dismissButtonText && ( + + )} +
+ } + {...props} + > + {render({ openTooltip, closeTooltip, toggleTooltip })} + + ); } diff --git a/src/components/SideDrawer/Form/Autosave.tsx b/src/components/SideDrawer/Form/Autosave.tsx index 08fc3080..0030095c 100644 --- a/src/components/SideDrawer/Form/Autosave.tsx +++ b/src/components/SideDrawer/Form/Autosave.tsx @@ -11,65 +11,65 @@ import { useRowyContext } from "contexts/RowyContext"; import { RowyState } from "hooks/useRowy"; export interface IAutosaveProps { - control: Control; - docRef: firebase.default.firestore.DocumentReference; - row: any; - reset: UseFormMethods["reset"]; - dirtyFields: UseFormMethods["formState"]["dirtyFields"]; + control: Control; + docRef: firebase.default.firestore.DocumentReference; + row: any; + reset: UseFormMethods["reset"]; + dirtyFields: UseFormMethods["formState"]["dirtyFields"]; } const getEditables = (values: Values, tableState?: RowyState) => - _pick( - values, - (tableState && - (Array.isArray(tableState?.columns) - ? tableState?.columns - : Object.values(tableState?.columns) - ).map((c) => c.key)) ?? - [] - ); + _pick( + values, + (tableState && + (Array.isArray(tableState?.columns) + ? tableState?.columns + : Object.values(tableState?.columns) + ).map((c) => c.key)) ?? + [] + ); export default function Autosave({ - control, - docRef, - row, - reset, - dirtyFields, + control, + docRef, + row, + reset, + dirtyFields, }: IAutosaveProps) { - const { tableState, updateCell } = useRowyContext(); + const { tableState, updateCell } = useRowyContext(); - const values = useWatch({ control }); - const [debouncedValue] = useDebounce(getEditables(values, tableState), 1000, { - equalityFn: _isEqual, - }); + const values = useWatch({ control }); + const [debouncedValue] = useDebounce(getEditables(values, tableState), 1000, { + equalityFn: _isEqual, + }); - useEffect(() => { - if (!row || !row.ref) return; - if (row.ref.id !== docRef.id) return; - if (!updateCell) return; + useEffect(() => { + if (!row || !row.ref) return; + if (row.ref.id !== docRef.id) return; + if (!updateCell) return; - // Get only fields that have had their value updated by the user - const updatedValues = _pickBy( - _pickBy(debouncedValue, (_, key) => dirtyFields[key]), - (value, key) => !_isEqual(value, row[key]) - ); - console.log(debouncedValue, row); - console.log(updatedValues, dirtyFields); - if (Object.keys(updatedValues).length === 0) return; + // Get only fields that have had their value updated by the user + const updatedValues = _pickBy( + _pickBy(debouncedValue, (_, key) => dirtyFields[key]), + (value, key) => !_isEqual(value, row[key]) + ); + console.log(debouncedValue, row); + console.log(updatedValues, dirtyFields); + if (Object.keys(updatedValues).length === 0) return; - // Update the document - Object.entries(updatedValues).forEach(([key, value]) => - updateCell( - row.ref, - key, - value, - // After the cell is updated, set this field to be not dirty - // so it doesn’t get updated again when a different field in the form - // is updated + make sure the new value is kept after reset - () => reset({ ...values, [key]: value }) - ) - ); - }, [debouncedValue]); + // Update the document + Object.entries(updatedValues).forEach(([key, value]) => + updateCell( + row.ref, + key, + value, + // After the cell is updated, set this field to be not dirty + // so it doesn’t get updated again when a different field in the form + // is updated + make sure the new value is kept after reset + () => reset({ ...values, [key]: value }) + ) + ); + }, [debouncedValue]); - return null; + return null; } diff --git a/src/components/SideDrawer/Form/FieldSkeleton.tsx b/src/components/SideDrawer/Form/FieldSkeleton.tsx index 8f56ed46..2096b0ff 100644 --- a/src/components/SideDrawer/Form/FieldSkeleton.tsx +++ b/src/components/SideDrawer/Form/FieldSkeleton.tsx @@ -3,15 +3,15 @@ import { Skeleton } from "@material-ui/core"; import { SkeletonProps } from "@material-ui/lab"; export default function FieldSkeleton(props: SkeletonProps) { - const theme = useTheme(); - return ( - - ); + const theme = useTheme(); + return ( + + ); } diff --git a/src/components/SideDrawer/Form/FieldWrapper.tsx b/src/components/SideDrawer/Form/FieldWrapper.tsx index c6dafe9d..6b64d396 100644 --- a/src/components/SideDrawer/Form/FieldWrapper.tsx +++ b/src/components/SideDrawer/Form/FieldWrapper.tsx @@ -13,119 +13,119 @@ import { FieldType } from "constants/fields"; import { getFieldProp } from "components/fields"; import { projectId } from "../../../firebase"; const useStyles = makeStyles((theme) => - createStyles({ - header: { - paddingBottom: theme.spacing(1), - color: theme.palette.text.secondary, + createStyles({ + header: { + paddingBottom: theme.spacing(1), + color: theme.palette.text.secondary, - "& svg": { - display: "block", - fontSize: 18, - }, - }, - iconContainer: { - marginRight: theme.spacing(1), - }, + "& svg": { + display: "block", + fontSize: 18, + }, + }, + iconContainer: { + marginRight: theme.spacing(1), + }, - label: { - ...theme.typography.caption, - lineHeight: "18px", - fontWeight: 500, - }, + label: { + ...theme.typography.caption, + lineHeight: "18px", + fontWeight: 500, + }, - disabledText: { - paddingLeft: theme.spacing(18 / 8 + 1), - color: theme.palette.text.disabled, + disabledText: { + paddingLeft: theme.spacing(18 / 8 + 1), + color: theme.palette.text.disabled, - whiteSpace: "normal", - wordBreak: "break-all", - }, - launchButton: { margin: theme.spacing(-3, -1.5, 0, 0) }, - }) + whiteSpace: "normal", + wordBreak: "break-all", + }, + launchButton: { margin: theme.spacing(-3, -1.5, 0, 0) }, + }) ); export interface IFieldWrapperProps { - children?: React.ReactNode; - type: FieldType | "debug"; - name?: string; - label?: React.ReactNode; - debugText?: React.ReactNode; - disabled?: boolean; + children?: React.ReactNode; + type: FieldType | "debug"; + name?: string; + label?: React.ReactNode; + debugText?: React.ReactNode; + disabled?: boolean; } export default function FieldWrapper({ - children, - type, - name, - label, - debugText, - disabled, + children, + type, + name, + label, + debugText, + disabled, }: IFieldWrapperProps) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - - {type === "debug" ? : getFieldProp("icon", type)} - - - {label} - - {disabled && ( - - - - )} - + return ( + + + + {type === "debug" ? : getFieldProp("icon", type)} + + + {label} + + {disabled && ( + + + + )} + - - }> - {children ?? - (!debugText && ( - - This field cannot be edited here - - ))} - - + + }> + {children ?? + (!debugText && ( + + This field cannot be edited here + + ))} + + - {debugText && ( - - - - {debugText} - - + {debugText && ( + + + + {debugText} + + - - - - - - - )} - - ); + + + + + + + )} + + ); } diff --git a/src/components/SideDrawer/Form/Label.tsx b/src/components/SideDrawer/Form/Label.tsx index 2764b75e..6416e849 100644 --- a/src/components/SideDrawer/Form/Label.tsx +++ b/src/components/SideDrawer/Form/Label.tsx @@ -1,45 +1,45 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { - FormLabel, - FormLabelProps, - Tooltip, - IconButton, + FormLabel, + FormLabelProps, + Tooltip, + IconButton, } from "@material-ui/core"; import HelpIcon from "@material-ui/icons/HelpOutline"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - display: "block", - marginBottom: theme.spacing(1), - }, - }) + createStyles({ + root: { + display: "block", + marginBottom: theme.spacing(1), + }, + }) ); export interface ILabelProps extends FormLabelProps { - label?: React.ReactNode; - hint?: React.ReactNode; + label?: React.ReactNode; + hint?: React.ReactNode; } export default function Label({ - label, - children, - hint, - ...props + label, + children, + hint, + ...props }: ILabelProps) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - {label || children} + return ( + + {label || children} - {hint && ( - - - - - - )} - - ); + {hint && ( + + + + + + )} + + ); } diff --git a/src/components/SideDrawer/Form/Reset.tsx b/src/components/SideDrawer/Form/Reset.tsx index 0bd9c7ef..eef61568 100644 --- a/src/components/SideDrawer/Form/Reset.tsx +++ b/src/components/SideDrawer/Form/Reset.tsx @@ -6,45 +6,45 @@ import _isEqual from "lodash/isEqual"; import { Values } from "./utils"; export interface IResetProps { - defaultValues: Values; - dirtyFields: UseFormMethods["formState"]["dirtyFields"]; - reset: UseFormMethods["reset"]; - getValues: UseFormMethods["getValues"]; + defaultValues: Values; + dirtyFields: UseFormMethods["formState"]["dirtyFields"]; + reset: UseFormMethods["reset"]; + getValues: UseFormMethods["getValues"]; } /** * Reset the form’s values and errors when the Firestore doc’s data updates */ export default function Reset({ - defaultValues, - dirtyFields, - reset, - getValues, + defaultValues, + dirtyFields, + reset, + getValues, }: IResetProps) { - useEffect( - () => { - const resetValues = { ...defaultValues }; - const currentValues = getValues(); + useEffect( + () => { + const resetValues = { ...defaultValues }; + const currentValues = getValues(); - // If the field is dirty, (i.e. the user input a value but it hasn’t been) - // saved to the db yet, keep its current value and keep it marked as dirty - for (const [field, isDirty] of Object.entries(dirtyFields)) { - if (isDirty) { - resetValues[field] = currentValues[field]; - } - } + // If the field is dirty, (i.e. the user input a value but it hasn’t been) + // saved to the db yet, keep its current value and keep it marked as dirty + for (const [field, isDirty] of Object.entries(dirtyFields)) { + if (isDirty) { + resetValues[field] = currentValues[field]; + } + } - // Compare currentValues to resetValues - const diff = _pickBy(getValues(), (v, k) => !_isEqual(v, resetValues[k])); - // Reset if needed & keep the current dirty fields - if (Object.keys(diff).length > 0) { - reset(resetValues, { isDirty: true, dirtyFields: true }); - } - }, - // `defaultValues` is the `initialValue` of each field type + - // the current value in the Firestore doc - [JSON.stringify(defaultValues)] - ); + // Compare currentValues to resetValues + const diff = _pickBy(getValues(), (v, k) => !_isEqual(v, resetValues[k])); + // Reset if needed & keep the current dirty fields + if (Object.keys(diff).length > 0) { + reset(resetValues, { isDirty: true, dirtyFields: true }); + } + }, + // `defaultValues` is the `initialValue` of each field type + + // the current value in the Firestore doc + [JSON.stringify(defaultValues)] + ); - return null; + return null; } diff --git a/src/components/SideDrawer/Form/index.tsx b/src/components/SideDrawer/Form/index.tsx index d6f42e6b..200ae30a 100644 --- a/src/components/SideDrawer/Form/index.tsx +++ b/src/components/SideDrawer/Form/index.tsx @@ -16,107 +16,107 @@ import { useAppContext } from "contexts/AppContext"; import { useRowyContext } from "contexts/RowyContext"; export interface IFormProps { - values: Values; + values: Values; } export default function Form({ values }: IFormProps) { - const { tableState } = useRowyContext(); - const { userDoc } = useAppContext(); - const userDocHiddenFields = - userDoc.state.doc?.tables?.[`${tableState!.tablePath}`]?.hiddenFields ?? []; + const { tableState } = useRowyContext(); + const { userDoc } = useAppContext(); + const userDocHiddenFields = + userDoc.state.doc?.tables?.[`${tableState!.tablePath}`]?.hiddenFields ?? []; - const fields = _sortBy(Object.values(tableState!.columns), "index").filter( - (f) => !userDocHiddenFields.includes(f.name) - ); + const fields = _sortBy(Object.values(tableState!.columns), "index").filter( + (f) => !userDocHiddenFields.includes(f.name) + ); - // Get initial values from fields config. This won’t be written to the db - // when the SideDrawer is opened. Only dirty fields will be written - const initialValues = fields.reduce( - (a, { key, type }) => ({ ...a, [key]: getFieldProp("initialValue", type) }), - {} - ); - const { ref: docRef, ...rowValues } = values; - const defaultValues = { ...initialValues, ...rowValues }; + // Get initial values from fields config. This won’t be written to the db + // when the SideDrawer is opened. Only dirty fields will be written + const initialValues = fields.reduce( + (a, { key, type }) => ({ ...a, [key]: getFieldProp("initialValue", type) }), + {} + ); + const { ref: docRef, ...rowValues } = values; + const defaultValues = { ...initialValues, ...rowValues }; - const { control, reset, formState, getValues } = useForm({ - mode: "onBlur", - defaultValues, - }); - const { dirtyFields } = formState; + const { control, reset, formState, getValues } = useForm({ + mode: "onBlur", + defaultValues, + }); + const { dirtyFields } = formState; - // const { sideDrawerRef } = useRowyContext(); - // useEffect(() => { - // const column = sideDrawerRef?.current?.cell?.column; - // if (!column) return; + // const { sideDrawerRef } = useRowyContext(); + // useEffect(() => { + // const column = sideDrawerRef?.current?.cell?.column; + // if (!column) return; - // const elem = document.getElementById(`sidedrawer-label-${column}`) - // ?.parentNode as HTMLElement; + // const elem = document.getElementById(`sidedrawer-label-${column}`) + // ?.parentNode as HTMLElement; - // // Time out for double-clicking on cells, which can open the null editor - // setTimeout(() => elem?.scrollIntoView({ behavior: "smooth" }), 200); - // }, [sideDrawerRef?.current]); + // // Time out for double-clicking on cells, which can open the null editor + // setTimeout(() => elem?.scrollIntoView({ behavior: "smooth" }), 200); + // }, [sideDrawerRef?.current]); - return ( -
- + return ( + + - + - - {fields.map((field, i) => { - // Derivative/aggregate field support - let type = field.type; - if (field.config && field.config.renderFieldType) { - type = field.config.renderFieldType; - } + + {fields.map((field, i) => { + // Derivative/aggregate field support + let type = field.type; + if (field.config && field.config.renderFieldType) { + type = field.config.renderFieldType; + } - const fieldComponent: IFieldConfig["SideDrawerField"] = getFieldProp( - "SideDrawerField", - type - ); + const fieldComponent: IFieldConfig["SideDrawerField"] = getFieldProp( + "SideDrawerField", + type + ); - // Should not reach this state - if (_isEmpty(fieldComponent)) { - // console.error('Could not find SideDrawerField component', field); - return null; - } + // Should not reach this state + if (_isEmpty(fieldComponent)) { + // console.error('Could not find SideDrawerField component', field); + return null; + } - return ( - - {React.createElement(fieldComponent, { - column: field, - control, - docRef, - disabled: field.editable === false, - })} - - ); - })} + return ( + + {React.createElement(fieldComponent, { + column: field, + control, + docRef, + disabled: field.editable === false, + })} + + ); + })} - - - - ); + + + + ); } diff --git a/src/components/SideDrawer/Form/utils.ts b/src/components/SideDrawer/Form/utils.ts index a4897af8..3f96e5ba 100644 --- a/src/components/SideDrawer/Form/utils.ts +++ b/src/components/SideDrawer/Form/utils.ts @@ -4,52 +4,52 @@ import { FieldType } from "constants/fields"; import { colord } from "colord"; export interface IFieldProps { - control: Control; - name: string; - docRef: firebase.default.firestore.DocumentReference; - editable?: boolean; + control: Control; + name: string; + docRef: firebase.default.firestore.DocumentReference; + editable?: boolean; } export type Values = Record; export type Field = { - type?: FieldType; - name: string; - label?: string; - [key: string]: any; + type?: FieldType; + name: string; + label?: string; + [key: string]: any; }; export type Fields = (Field | ((values: Values) => Field))[]; export const useFieldStyles = makeStyles((theme) => - createStyles({ - root: { - borderRadius: theme.shape.borderRadius, - padding: theme.spacing(0.75, 1, 0.75, 1.5), + createStyles({ + root: { + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(0.75, 1, 0.75, 1.5), - backgroundColor: theme.palette.action.input, - boxShadow: `0 0 0 1px ${ - theme.palette.mode === "dark" - ? colord(theme.palette.divider) - .alpha(colord(theme.palette.divider).alpha() / 2) - .toHslString() - : theme.palette.divider - } inset`, + backgroundColor: theme.palette.action.input, + boxShadow: `0 0 0 1px ${ + theme.palette.mode === "dark" + ? colord(theme.palette.divider) + .alpha(colord(theme.palette.divider).alpha() / 2) + .toHslString() + : theme.palette.divider + } inset`, - "&.Mui-disabled": { - backgroundColor: - theme.palette.mode === "dark" - ? "transparent" - : theme.palette.action.disabledBackground, - }, + "&.Mui-disabled": { + backgroundColor: + theme.palette.mode === "dark" + ? "transparent" + : theme.palette.action.disabledBackground, + }, - width: "100%", - minHeight: 32, + width: "100%", + minHeight: 32, - display: "flex", - textAlign: "left", - alignItems: "center", + display: "flex", + textAlign: "left", + alignItems: "center", - ...theme.typography.body2, - color: theme.palette.text.primary, - }, - }) + ...theme.typography.body2, + color: theme.palette.text.primary, + }, + }) ); diff --git a/src/components/SideDrawer/index.tsx b/src/components/SideDrawer/index.tsx index a9d879d7..f75e19ae 100644 --- a/src/components/SideDrawer/index.tsx +++ b/src/components/SideDrawer/index.tsx @@ -21,150 +21,150 @@ export const DRAWER_COLLAPSED_WIDTH = 36; type SelectedCell = { row: number; column: string } | null; export type SideDrawerRef = { - cell: SelectedCell; - setCell: React.Dispatch>; - open: boolean; - setOpen: React.Dispatch>; + cell: SelectedCell; + setCell: React.Dispatch>; + open: boolean; + setOpen: React.Dispatch>; }; export default function SideDrawer() { - const classes = useStyles(); - const { tableState, dataGridRef, sideDrawerRef } = useRowyContext(); + const classes = useStyles(); + const { tableState, dataGridRef, sideDrawerRef } = useRowyContext(); - const [cell, setCell] = useState(null); - const [open, setOpen] = useState(false); - if (sideDrawerRef) sideDrawerRef.current = { cell, setCell, open, setOpen }; + const [cell, setCell] = useState(null); + const [open, setOpen] = useState(false); + if (sideDrawerRef) sideDrawerRef.current = { cell, setCell, open, setOpen }; - const disabled = !open && (!cell || _isNil(cell.row)); - useEffect(() => { - if (disabled && setOpen) setOpen(false); - }, [disabled]); + const disabled = !open && (!cell || _isNil(cell.row)); + useEffect(() => { + if (disabled && setOpen) setOpen(false); + }, [disabled]); - const handleNavigate = (direction: "up" | "down") => () => { - if (!tableState?.rows) return; + const handleNavigate = (direction: "up" | "down") => () => { + if (!tableState?.rows) return; - let row = cell!.row; - if (direction === "up" && row > 0) row -= 1; - if (direction === "down" && row < tableState.rows.length - 1) row += 1; + let row = cell!.row; + if (direction === "up" && row > 0) row -= 1; + if (direction === "down" && row < tableState.rows.length - 1) row += 1; - setCell!((cell) => ({ column: cell!.column, row })); + setCell!((cell) => ({ column: cell!.column, row })); - const idx = tableState?.columns[cell!.column]?.index; - dataGridRef?.current?.selectCell({ rowIdx: row, idx }); - }; + const idx = tableState?.columns[cell!.column]?.index; + dataGridRef?.current?.selectCell({ rowIdx: row, idx }); + }; - const [urlDocState, dispatchUrlDoc] = useDoc({}); - useEffect(() => { - if (urlDocState.doc) setOpen(true); - }, [urlDocState]); + const [urlDocState, dispatchUrlDoc] = useDoc({}); + useEffect(() => { + if (urlDocState.doc) setOpen(true); + }, [urlDocState]); - useEffect(() => { - setOpen(false); - dispatchUrlDoc({ path: "", doc: null }); - }, [window.location.pathname]); + useEffect(() => { + setOpen(false); + dispatchUrlDoc({ path: "", doc: null }); + }, [window.location.pathname]); - useEffect(() => { - const rowRef = queryString.parse(window.location.search).rowRef as string; - if (rowRef) dispatchUrlDoc({ path: decodeURIComponent(rowRef) }); - }, []); + useEffect(() => { + const rowRef = queryString.parse(window.location.search).rowRef as string; + if (rowRef) dispatchUrlDoc({ path: decodeURIComponent(rowRef) }); + }, []); - useEffect(() => { - if (cell && tableState?.rows[cell.row]) { - window.history.pushState( - "", - `${tableState?.tablePath}`, - `${window.location.pathname}?rowRef=${encodeURIComponent( - tableState?.rows[cell.row].ref.path - )}` - ); - // console.log(tableState?.tablePath, tableState?.rows[cell.row].id); - if (urlDocState.doc) { - urlDocState.unsubscribe(); - dispatchUrlDoc({ path: "", doc: null }); - } - } - }, [cell]); + useEffect(() => { + if (cell && tableState?.rows[cell.row]) { + window.history.pushState( + "", + `${tableState?.tablePath}`, + `${window.location.pathname}?rowRef=${encodeURIComponent( + tableState?.rows[cell.row].ref.path + )}` + ); + // console.log(tableState?.tablePath, tableState?.rows[cell.row].id); + if (urlDocState.doc) { + urlDocState.unsubscribe(); + dispatchUrlDoc({ path: "", doc: null }); + } + } + }, [cell]); - return ( -
- - -
- {open && - (urlDocState.doc || cell) && - !_isEmpty(tableState?.columns) && ( -
- )} -
-
+ return ( +
+ + +
+ {open && + (urlDocState.doc || cell) && + !_isEmpty(tableState?.columns) && ( + + )} +
+
- {open && ( -
- - - + {open && ( +
+ + + - = tableState.rows.length - 1 - } - onClick={handleNavigate("down")} - > - - -
- )} + = tableState.rows.length - 1 + } + onClick={handleNavigate("down")} + > + + +
+ )} -
- { - if (setOpen) setOpen((o) => !o); - }} - > - - -
-
-
- ); +
+ { + if (setOpen) setOpen((o) => !o); + }} + > + + +
+
+
+ ); } diff --git a/src/components/SideDrawer/useStyles.ts b/src/components/SideDrawer/useStyles.ts index 1a2b8a64..0f99312d 100644 --- a/src/components/SideDrawer/useStyles.ts +++ b/src/components/SideDrawer/useStyles.ts @@ -4,109 +4,109 @@ import { APP_BAR_HEIGHT } from "components/Navigation"; import { TABLE_HEADER_HEIGHT } from "components/Table/TableHeader"; export const useStyles = makeStyles((theme) => - createStyles({ - open: {}, - disabled: { - "& $paper": { - transform: `translateX(${DRAWER_WIDTH + 32}px)`, - boxShadow: "none", - }, - }, + createStyles({ + open: {}, + disabled: { + "& $paper": { + transform: `translateX(${DRAWER_WIDTH + 32}px)`, + boxShadow: "none", + }, + }, - drawer: { - width: DRAWER_WIDTH, - flexShrink: 0, - whiteSpace: "nowrap", - }, + drawer: { + width: DRAWER_WIDTH, + flexShrink: 0, + whiteSpace: "nowrap", + }, - paper: { - border: "none", - boxShadow: theme.shadows[4].replace(/, 0 (\d+px)/g, ", -$1 0"), - borderTopLeftRadius: `${(theme.shape.borderRadius as number) * 3}px`, - borderBottomLeftRadius: `${(theme.shape.borderRadius as number) * 3}px`, + paper: { + border: "none", + boxShadow: theme.shadows[4].replace(/, 0 (\d+px)/g, ", -$1 0"), + borderTopLeftRadius: `${(theme.shape.borderRadius as number) * 3}px`, + borderBottomLeftRadius: `${(theme.shape.borderRadius as number) * 3}px`, - width: DRAWER_WIDTH, - overflowX: "visible", - overflowY: "visible", + width: DRAWER_WIDTH, + overflowX: "visible", + overflowY: "visible", - top: APP_BAR_HEIGHT + TABLE_HEADER_HEIGHT, - height: `calc(100% - ${APP_BAR_HEIGHT + TABLE_HEADER_HEIGHT}px)`, + top: APP_BAR_HEIGHT + TABLE_HEADER_HEIGHT, + height: `calc(100% - ${APP_BAR_HEIGHT + TABLE_HEADER_HEIGHT}px)`, - transition: theme.transitions.create("transform", { - easing: theme.transitions.easing.easeInOut, - duration: theme.transitions.duration.standard, - }), + transition: theme.transitions.create("transform", { + easing: theme.transitions.easing.easeInOut, + duration: theme.transitions.duration.standard, + }), - zIndex: theme.zIndex.drawer - 1, - }, - paperClose: { - transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, - }, + zIndex: theme.zIndex.drawer - 1, + }, + paperClose: { + transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, + }, - "@keyframes bumpPaper": { - "0%": { - transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, - }, - "50%": { - transform: `translateX(calc(${ - DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH - }px - ${theme.spacing(4)}))`, - }, - "100%": { - transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, - }, - }, - bumpPaper: { - animation: `${theme.transitions.duration.standard}ms ${theme.transitions.easing.easeInOut} $bumpPaper`, - }, + "@keyframes bumpPaper": { + "0%": { + transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, + }, + "50%": { + transform: `translateX(calc(${ + DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH + }px - ${theme.spacing(4)}))`, + }, + "100%": { + transform: `translateX(${DRAWER_WIDTH - DRAWER_COLLAPSED_WIDTH}px)`, + }, + }, + bumpPaper: { + animation: `${theme.transitions.duration.standard}ms ${theme.transitions.easing.easeInOut} $bumpPaper`, + }, - fab: { - display: "flex", + fab: { + display: "flex", - boxShadow: theme.shadows[4], - "&:active": { boxShadow: theme.shadows[4] }, + boxShadow: theme.shadows[4], + "&:active": { boxShadow: theme.shadows[4] }, - "&.Mui-disabled": { boxShadow: theme.shadows[4] }, + "&.Mui-disabled": { boxShadow: theme.shadows[4] }, - "& + &": { marginTop: theme.spacing(4) }, - }, + "& + &": { marginTop: theme.spacing(4) }, + }, - navFabContainer: { - position: "absolute", - top: theme.spacing(6), - left: -18, - zIndex: theme.zIndex.drawer + 1, - }, - "@keyframes navFab": { - from: { - opacity: 0, - transform: "translateY(-48px)", - }, - to: { - opacity: 1, - transform: "translateY(0)", - }, - }, - navFab: { - animation: `${theme.transitions.duration.standard}ms ${theme.transitions.easing.easeInOut} both $navFab`, - }, + navFabContainer: { + position: "absolute", + top: theme.spacing(6), + left: -18, + zIndex: theme.zIndex.drawer + 1, + }, + "@keyframes navFab": { + from: { + opacity: 0, + transform: "translateY(-48px)", + }, + to: { + opacity: 1, + transform: "translateY(0)", + }, + }, + navFab: { + animation: `${theme.transitions.duration.standard}ms ${theme.transitions.easing.easeInOut} both $navFab`, + }, - drawerFabContainer: { - position: "absolute", - top: "50%", - transform: "translateY(-50%)", - left: theme.spacing(-3.5), - zIndex: theme.zIndex.drawer + 1, - }, - drawerFabIcon: { - // width: "2em", - // height: "2em", - "$open &": { transform: "rotate(180deg)" }, - }, + drawerFabContainer: { + position: "absolute", + top: "50%", + transform: "translateY(-50%)", + left: theme.spacing(-3.5), + zIndex: theme.zIndex.drawer + 1, + }, + drawerFabIcon: { + // width: "2em", + // height: "2em", + "$open &": { transform: "rotate(180deg)" }, + }, - drawerContents: { - padding: theme.spacing(5), - overflowY: "auto", - }, - }) + drawerContents: { + padding: theme.spacing(5), + overflowY: "auto", + }, + }) ); diff --git a/src/components/Snack.tsx b/src/components/Snack.tsx index 76b15c7e..ac62f4f1 100644 --- a/src/components/Snack.tsx +++ b/src/components/Snack.tsx @@ -6,96 +6,96 @@ import { useSnackContext } from "contexts/SnackContext"; import antlerPalette from "theme/palette"; const useStyles = makeStyles((theme) => - createStyles({ - progressAction: { marginRight: 0 }, - progressText: { marginLeft: theme.spacing(2) }, - progress: { - color: antlerPalette.green[100], - marginLeft: theme.spacing(2), - }, + createStyles({ + progressAction: { marginRight: 0 }, + progressText: { marginLeft: theme.spacing(2) }, + progress: { + color: antlerPalette.green[100], + marginLeft: theme.spacing(2), + }, - alertIcon: { padding: 0 }, - alertMessage: { padding: theme.spacing(0.75, 2) }, - }) + alertIcon: { padding: 0 }, + alertMessage: { padding: theme.spacing(0.75, 2) }, + }) ); export default function Snack() { - const classes = useStyles(); + const classes = useStyles(); - const { - position, - isOpen, - close, - message, - duration, - action, - variant, - progress, - } = useSnackContext(); + const { + position, + isOpen, + close, + message, + duration, + action, + variant, + progress, + } = useSnackContext(); - if (variant === "progress") - return ( - - - {progress.value} - {progress.target && `/${progress.target}`} - + if (variant === "progress") + return ( + + + {progress.value} + {progress.target && `/${progress.target}`} + - - - } - ContentProps={{ classes: { action: classes.progressAction } }} - // Stop closing when user clicks - ClickAwayListenerProps={{ mouseEvent: false }} - /> - ); + + + } + ContentProps={{ classes: { action: classes.progressAction } }} + // Stop closing when user clicks + ClickAwayListenerProps={{ mouseEvent: false }} + /> + ); - if (!variant) - return ( - - ); + if (!variant) + return ( + + ); - return ( - - - {message} - - - ); + return ( + + + {message} + + + ); } diff --git a/src/components/StyledCard.tsx b/src/components/StyledCard.tsx index f29a6600..4bd89a59 100644 --- a/src/components/StyledCard.tsx +++ b/src/components/StyledCard.tsx @@ -3,179 +3,179 @@ import { Link, LinkProps } from "react-router-dom"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - Card, - Grid, - Typography, - Button, - CardActions, - CardContent, - CardMedia, - Divider, + Card, + Grid, + Typography, + Button, + CardActions, + CardContent, + CardMedia, + Divider, } from "@material-ui/core"; import { ButtonProps } from "@material-ui/core/Button"; import GoIcon from "assets/icons/Go"; const useStyles = makeStyles((theme) => - createStyles({ - root: { width: "100%" }, - container: { height: "100%" }, - cardContent: { "&:last-child": { paddingBottom: 0 } }, + createStyles({ + root: { width: "100%" }, + container: { height: "100%" }, + cardContent: { "&:last-child": { paddingBottom: 0 } }, - headerSection: { marginBottom: theme.spacing(1) }, - overline: { - marginBottom: theme.spacing(2), - color: theme.palette.text.secondary, - }, - title: { whiteSpace: "pre-line" }, - image: { - width: 80, - height: 80, - borderRadius: theme.shape.borderRadius, - }, + headerSection: { marginBottom: theme.spacing(1) }, + overline: { + marginBottom: theme.spacing(2), + color: theme.palette.text.secondary, + }, + title: { whiteSpace: "pre-line" }, + image: { + width: 80, + height: 80, + borderRadius: theme.shape.borderRadius, + }, - cardActions: { - // padding: theme.spacing(1), + cardActions: { + // padding: theme.spacing(1), - display: "flex", - justifyContent: "space-between", - }, + display: "flex", + justifyContent: "space-between", + }, - divider: { - margin: theme.spacing(2), - marginBottom: 0, - }, - }) + divider: { + margin: theme.spacing(2), + marginBottom: 0, + }, + }) ); interface StyledCardProps { - className?: string; + className?: string; - overline?: React.ReactNode; - title?: string; - imageSource?: string; + overline?: React.ReactNode; + title?: string; + imageSource?: string; - bodyContent?: React.ReactNode; + bodyContent?: React.ReactNode; - primaryButton?: Partial; - primaryLink?: { - to: LinkProps["to"]; - children?: React.ReactNode; - label?: string; - }; - secondaryAction?: React.ReactNode; - headerAction?: React.ReactNode; + primaryButton?: Partial; + primaryLink?: { + to: LinkProps["to"]; + children?: React.ReactNode; + label?: string; + }; + secondaryAction?: React.ReactNode; + headerAction?: React.ReactNode; } export default function StyledCard({ - className, - overline, - title, - imageSource, - bodyContent, - primaryButton, - primaryLink, - secondaryAction, - headerAction, + className, + overline, + title, + imageSource, + bodyContent, + primaryButton, + primaryLink, + secondaryAction, + headerAction, }: StyledCardProps) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - - - - - - - {overline && ( - - {overline} - - )} - - {title && ( - - {title} - - )} - {headerAction && headerAction} - - + return ( + + + + + + + + + {overline && ( + + {overline} + + )} + + {title && ( + + {title} + + )} + {headerAction && headerAction} + + - {imageSource && ( - - - - )} - - + {imageSource && ( + + + + )} + + - - {bodyContent && Array.isArray(bodyContent) ? ( - - {bodyContent.map((element) => ( - {element} - ))} - - ) : ( - - {bodyContent} - - )} - - - - + + {bodyContent && Array.isArray(bodyContent) ? ( + + {bodyContent.map((element) => ( + {element} + ))} + + ) : ( + + {bodyContent} + + )} + + + + - - - - {primaryButton && ( - - ), - }); - } - const userTokenInfo = await appContext?.currentUser?.getIdTokenResult(); - const userToken = userTokenInfo?.token; - try { - snackLog.requestSnackLog(); - const response = await fetch(buildUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - configPath: tableState?.config.tableConfig.path, - token: userToken, - }), - }); - const data = await response.json(); - } catch (e) { - console.error(e); - } - }, - }); - } - handleSave(fieldName, { config: newConfig }); - handleClose(); - setShowRebuildPrompt(false); - }, - children: "Update", - }, - secondary: { - onClick: handleClose, - children: "Cancel", - }, - }} - /> - ); + + + } + actions={{ + primary: { + onClick: () => { + if (showRebuildPrompt) { + requestConfirmation({ + title: "Deploy Changes", + body: + "You have made changes that affect the behavior of the cloud function of this table, Would you like to redeploy it now?", + confirm: "Deploy", + cancel: "Later", + handleConfirm: async () => { + const settingsDoc = await db.doc("/_rowy_/settings").get(); + const buildUrl = settingsDoc.get("buildUrl"); + if (!buildUrl) { + snack.open({ + message: `Rowy functions builder is not yet setup`, + variant: "error", + action: ( + + ), + }); + } + const userTokenInfo = await appContext?.currentUser?.getIdTokenResult(); + const userToken = userTokenInfo?.token; + try { + snackLog.requestSnackLog(); + const response = await fetch(buildUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + configPath: tableState?.config.tableConfig.path, + token: userToken, + }), + }); + const data = await response.json(); + } catch (e) { + console.error(e); + } + }, + }); + } + handleSave(fieldName, { config: newConfig }); + handleClose(); + setShowRebuildPrompt(false); + }, + children: "Update", + }, + secondary: { + onClick: handleClose, + children: "Cancel", + }, + }} + /> + ); } diff --git a/src/components/Table/ColumnMenu/FieldsDropdown.tsx b/src/components/Table/ColumnMenu/FieldsDropdown.tsx index 0dec25cc..447557af 100644 --- a/src/components/Table/ColumnMenu/FieldsDropdown.tsx +++ b/src/components/Table/ColumnMenu/FieldsDropdown.tsx @@ -1,9 +1,9 @@ import { makeStyles, createStyles } from "@material-ui/styles"; import { - TextField, - MenuItem, - ListItemIcon, - TextFieldProps, + TextField, + MenuItem, + ListItemIcon, + TextFieldProps, } from "@material-ui/core"; import { FIELDS } from "components/fields"; @@ -11,70 +11,70 @@ import { FieldType } from "constants/fields"; import { getFieldProp } from "components/fields"; const useStyles = makeStyles((theme) => - createStyles({ - helperText: { - ...theme.typography.body2, - marginTop: theme.spacing(1), - }, + createStyles({ + helperText: { + ...theme.typography.body2, + marginTop: theme.spacing(1), + }, - listItemIcon: { - verticalAlign: "text-bottom", - minWidth: theme.spacing(5), - "& svg": { margin: theme.spacing(-0.5, 0) }, - }, - }) + listItemIcon: { + verticalAlign: "text-bottom", + minWidth: theme.spacing(5), + "& svg": { margin: theme.spacing(-0.5, 0) }, + }, + }) ); export interface IFieldsDropdownProps { - value: FieldType; - onChange: TextFieldProps["onChange"]; - className?: string; - hideLabel?: boolean; - options?: FieldType[]; + value: FieldType; + onChange: TextFieldProps["onChange"]; + className?: string; + hideLabel?: boolean; + options?: FieldType[]; } /** * Returns dropdown component of all available types */ export default function FieldsDropdown({ - value, - onChange, - className, - hideLabel = false, - options: optionsProp, + value, + onChange, + className, + hideLabel = false, + options: optionsProp, }: IFieldsDropdownProps) { - const classes = useStyles(); + const classes = useStyles(); - const options = optionsProp - ? FIELDS.filter((fieldConfig) => optionsProp.indexOf(fieldConfig.type) > -1) - : FIELDS; + const options = optionsProp + ? FIELDS.filter((fieldConfig) => optionsProp.indexOf(fieldConfig.type) > -1) + : FIELDS; - return ( - - {options.map((fieldConfig) => ( - - - {fieldConfig.icon} - - {fieldConfig.name} - - ))} - - ); + return ( + + {options.map((fieldConfig) => ( + + + {fieldConfig.icon} + + {fieldConfig.name} + + ))} + + ); } diff --git a/src/components/Table/ColumnMenu/MenuContents.tsx b/src/components/Table/ColumnMenu/MenuContents.tsx index ea68036b..2d3c14c6 100644 --- a/src/components/Table/ColumnMenu/MenuContents.tsx +++ b/src/components/Table/ColumnMenu/MenuContents.tsx @@ -1,82 +1,82 @@ import { - MenuItem, - ListItemIcon, - ListSubheader, - Divider, + MenuItem, + ListItemIcon, + ListSubheader, + Divider, } from "@material-ui/core"; import { alpha } from "@material-ui/core/styles"; export interface IMenuContentsProps { - menuItems: { - type?: string; - label?: string; - activeLabel?: string; - icon?: JSX.Element; - activeIcon?: JSX.Element; - onClick?: () => void; - active?: boolean; - color?: "error"; - disabled?: boolean; - }[]; + menuItems: { + type?: string; + label?: string; + activeLabel?: string; + icon?: JSX.Element; + activeIcon?: JSX.Element; + onClick?: () => void; + active?: boolean; + color?: "error"; + disabled?: boolean; + }[]; } export default function MenuContents({ menuItems }: IMenuContentsProps) { - return ( - <> - {menuItems.map((item, index) => { - if (item.type === "subheader") - return ( - <> - {index !== 0 && } - - {item.label} - - - ); + return ( + <> + {menuItems.map((item, index) => { + if (item.type === "subheader") + return ( + <> + {index !== 0 && } + + {item.label} + + + ); - let icon: JSX.Element = item.icon ?? <>; - if (item.active && !!item.activeIcon) icon = item.activeIcon; + let icon: JSX.Element = item.icon ?? <>; + if (item.active && !!item.activeIcon) icon = item.activeIcon; - return ( - - alpha( - theme.palette.error.main, - theme.palette.action.hoverOpacity - ), - }, - } - : undefined - } - selected={item.active} - disabled={item.disabled} - > - - alpha( - theme.palette.error.main, - theme.palette.action.activeOpacity - ), - } - : undefined - } - > - {icon} - - {item.active ? item.activeLabel : item.label} - - ); - })} - - ); + return ( + + alpha( + theme.palette.error.main, + theme.palette.action.hoverOpacity + ), + }, + } + : undefined + } + selected={item.active} + disabled={item.disabled} + > + + alpha( + theme.palette.error.main, + theme.palette.action.activeOpacity + ), + } + : undefined + } + > + {icon} + + {item.active ? item.activeLabel : item.label} + + ); + })} + + ); } diff --git a/src/components/Table/ColumnMenu/NameChange.tsx b/src/components/Table/ColumnMenu/NameChange.tsx index 355ea736..b656745c 100644 --- a/src/components/Table/ColumnMenu/NameChange.tsx +++ b/src/components/Table/ColumnMenu/NameChange.tsx @@ -6,48 +6,48 @@ import { TextField } from "@material-ui/core"; import Modal from "components/Modal"; export default function NameChange({ - name, - fieldName, - open, - handleClose, - handleSave, + name, + fieldName, + open, + handleClose, + handleSave, }: IMenuModalProps) { - const [newName, setName] = useState(name); + const [newName, setName] = useState(name); - if (!open) return null; + if (!open) return null; - return ( - { - setName(e.target.value); - }} - /> - } - actions={{ - primary: { - onClick: () => { - handleSave(fieldName, { name: newName }); - handleClose(); - }, - children: "Update", - }, - secondary: { - onClick: handleClose, - children: "Cancel", - }, - }} - /> - ); + return ( + { + setName(e.target.value); + }} + /> + } + actions={{ + primary: { + onClick: () => { + handleSave(fieldName, { name: newName }); + handleClose(); + }, + children: "Update", + }, + secondary: { + onClick: handleClose, + children: "Cancel", + }, + }} + /> + ); } diff --git a/src/components/Table/ColumnMenu/NewColumn.tsx b/src/components/Table/ColumnMenu/NewColumn.tsx index eb8976ef..75c8c332 100644 --- a/src/components/Table/ColumnMenu/NewColumn.tsx +++ b/src/components/Table/ColumnMenu/NewColumn.tsx @@ -11,124 +11,124 @@ import FieldsDropdown from "./FieldsDropdown"; import { getFieldProp } from "components/fields"; import { analytics } from "analytics"; const useStyles = makeStyles((theme) => - createStyles({ - helperText: { - ...theme.typography.body2, - marginTop: theme.spacing(1), - }, - }) + createStyles({ + helperText: { + ...theme.typography.body2, + marginTop: theme.spacing(1), + }, + }) ); export interface IFormDialogProps extends IMenuModalProps { - data: Record; - openSettings: (column: any) => void; + data: Record; + openSettings: (column: any) => void; } export default function FormDialog({ - open, - data, - openSettings, - handleClose, - handleSave, + open, + data, + openSettings, + handleClose, + handleSave, }: IFormDialogProps) { - const classes = useStyles(); + const classes = useStyles(); - const [columnLabel, setColumnLabel] = useState(""); - const [fieldKey, setFieldKey] = useState(""); - const [type, setType] = useState(FieldType.shortText); - const requireConfiguration = getFieldProp("requireConfiguration", type); - useEffect(() => { - if (type !== FieldType.id) setFieldKey(_camel(columnLabel)); - }, [columnLabel]); + const [columnLabel, setColumnLabel] = useState(""); + const [fieldKey, setFieldKey] = useState(""); + const [type, setType] = useState(FieldType.shortText); + const requireConfiguration = getFieldProp("requireConfiguration", type); + useEffect(() => { + if (type !== FieldType.id) setFieldKey(_camel(columnLabel)); + }, [columnLabel]); - useEffect(() => { - if (type === FieldType.id) { - setColumnLabel("ID"); - setFieldKey("id"); - } - }, [type]); + useEffect(() => { + if (type === FieldType.id) { + setColumnLabel("ID"); + setFieldKey("id"); + } + }, [type]); - if (!open) return null; + if (!open) return null; - return ( - -
- setColumnLabel(e.target.value)} - helperText="Set the user-facing name for this column." - FormHelperTextProps={{ classes: { root: classes.helperText } }} - /> -
+ return ( + +
+ setColumnLabel(e.target.value)} + helperText="Set the user-facing name for this column." + FormHelperTextProps={{ classes: { root: classes.helperText } }} + /> +
-
- setFieldKey(e.target.value)} - disabled={type === FieldType.id && fieldKey === "id"} - helperText="Set the Firestore field key to link to this column. It will display any existing data for this field key." - FormHelperTextProps={{ classes: { root: classes.helperText } }} - /> -
+
+ setFieldKey(e.target.value)} + disabled={type === FieldType.id && fieldKey === "id"} + helperText="Set the Firestore field key to link to this column. It will display any existing data for this field key." + FormHelperTextProps={{ classes: { root: classes.helperText } }} + /> +
-
- setType(newType.target.value as FieldType)} - /> -
- - } - actions={{ - primary: { - onClick: () => { - handleSave(fieldKey, { - type, - name: columnLabel, - fieldName: fieldKey, - key: fieldKey, - config: {}, - ...data.initializeColumn, - }); - if (requireConfiguration) { - openSettings({ - type, - name: columnLabel, - fieldName: fieldKey, - key: fieldKey, - config: {}, - ...data.initializeColumn, - }); - } else handleClose(); - analytics.logEvent("create_column", { - type, - }); - }, - disabled: !columnLabel || !fieldKey || !type, - children: requireConfiguration ? "Next" : "Add", - }, - secondary: { - onClick: handleClose, - children: "Cancel", - }, - }} - /> - ); +
+ setType(newType.target.value as FieldType)} + /> +
+ + } + actions={{ + primary: { + onClick: () => { + handleSave(fieldKey, { + type, + name: columnLabel, + fieldName: fieldKey, + key: fieldKey, + config: {}, + ...data.initializeColumn, + }); + if (requireConfiguration) { + openSettings({ + type, + name: columnLabel, + fieldName: fieldKey, + key: fieldKey, + config: {}, + ...data.initializeColumn, + }); + } else handleClose(); + analytics.logEvent("create_column", { + type, + }); + }, + disabled: !columnLabel || !fieldKey || !type, + children: requireConfiguration ? "Next" : "Add", + }, + secondary: { + onClick: handleClose, + children: "Cancel", + }, + }} + /> + ); } diff --git a/src/components/Table/ColumnMenu/Subheading.tsx b/src/components/Table/ColumnMenu/Subheading.tsx index 49e41390..bf2043a7 100644 --- a/src/components/Table/ColumnMenu/Subheading.tsx +++ b/src/components/Table/ColumnMenu/Subheading.tsx @@ -1,19 +1,19 @@ import { useTheme, Typography, TypographyProps } from "@material-ui/core"; export default function Subheading(props: TypographyProps<"h2">) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - ); + return ( + + ); } diff --git a/src/components/Table/ColumnMenu/TypeChange.tsx b/src/components/Table/ColumnMenu/TypeChange.tsx index 93688abf..6435279a 100644 --- a/src/components/Table/ColumnMenu/TypeChange.tsx +++ b/src/components/Table/ColumnMenu/TypeChange.tsx @@ -5,46 +5,46 @@ import Modal from "components/Modal"; import FieldsDropdown from "./FieldsDropdown"; import { analytics } from "analytics"; export default function FormDialog({ - fieldName, - type, - open, - handleClose, - handleSave, + fieldName, + type, + open, + handleClose, + handleSave, }: IMenuModalProps) { - const [newType, setType] = useState(type); + const [newType, setType] = useState(type); - if (!open) return null; + if (!open) return null; - return ( - { - setType(newType.target.value); - }} - /> - } - actions={{ - primary: { - onClick: () => { - handleSave(fieldName, { type: newType }); - handleClose(); - analytics.logEvent("change_column_type", { - newType, - prevType: type, - }); - }, - children: "Update", - }, - secondary: { - onClick: handleClose, - children: "Cancel", - }, - }} - maxWidth="xs" - /> - ); + return ( + { + setType(newType.target.value); + }} + /> + } + actions={{ + primary: { + onClick: () => { + handleSave(fieldName, { type: newType }); + handleClose(); + analytics.logEvent("change_column_type", { + newType, + prevType: type, + }); + }, + children: "Update", + }, + secondary: { + onClick: handleClose, + children: "Cancel", + }, + }} + maxWidth="xs" + /> + ); } diff --git a/src/components/Table/ColumnMenu/index.tsx b/src/components/Table/ColumnMenu/index.tsx index de77ca4a..a7e36af2 100644 --- a/src/components/Table/ColumnMenu/index.tsx +++ b/src/components/Table/ColumnMenu/index.tsx @@ -33,285 +33,285 @@ import { PopoverProps } from "@material-ui/core"; const INITIAL_MODAL = { type: "", data: {} }; enum ModalStates { - nameChange = "NAME_CHANGE", - typeChange = "TYPE_CHANGE", - new = "NEW_COLUMN", - settings = "COLUMN_SETTINGS", + nameChange = "NAME_CHANGE", + typeChange = "TYPE_CHANGE", + new = "NEW_COLUMN", + settings = "COLUMN_SETTINGS", } type SelectedColumnHeader = { - column: Column & { [key: string]: any }; - anchorEl: PopoverProps["anchorEl"]; + column: Column & { [key: string]: any }; + anchorEl: PopoverProps["anchorEl"]; }; export type ColumnMenuRef = { - selectedColumnHeader: SelectedColumnHeader | null; - setSelectedColumnHeader: React.Dispatch< - React.SetStateAction - >; + selectedColumnHeader: SelectedColumnHeader | null; + setSelectedColumnHeader: React.Dispatch< + React.SetStateAction + >; }; export interface IMenuModalProps { - name: string; - fieldName: string; - type: FieldType; + name: string; + fieldName: string; + type: FieldType; - open: boolean; - config: Record; + open: boolean; + config: Record; - handleClose: () => void; - handleSave: (fieldName: string, config: Record) => void; + handleClose: () => void; + handleSave: (fieldName: string, config: Record) => void; } export default function ColumnMenu() { - const [modal, setModal] = useState(INITIAL_MODAL); - const { tableState, tableActions, columnMenuRef } = useRowyContext(); + const [modal, setModal] = useState(INITIAL_MODAL); + const { tableState, tableActions, columnMenuRef } = useRowyContext(); - const [selectedColumnHeader, setSelectedColumnHeader] = useState(null); - if (columnMenuRef) - columnMenuRef.current = { - selectedColumnHeader, - setSelectedColumnHeader, - } as any; + const [selectedColumnHeader, setSelectedColumnHeader] = useState(null); + if (columnMenuRef) + columnMenuRef.current = { + selectedColumnHeader, + setSelectedColumnHeader, + } as any; - const { column, anchorEl } = (selectedColumnHeader ?? {}) as any; + const { column, anchorEl } = (selectedColumnHeader ?? {}) as any; - useEffect(() => { - if (column && column.type === FieldType.last) { - setModal({ - type: ModalStates.new, - data: { - initializeColumn: { index: column.index ? column.index + 1 : 0 }, - }, - }); - } - }, [column]); - if (!tableState || !tableActions) return null; - const { orderBy } = tableState; + useEffect(() => { + if (column && column.type === FieldType.last) { + setModal({ + type: ModalStates.new, + data: { + initializeColumn: { index: column.index ? column.index + 1 : 0 }, + }, + }); + } + }, [column]); + if (!tableState || !tableActions) return null; + const { orderBy } = tableState; - const actions = tableActions!.column; + const actions = tableActions!.column; - const handleClose = () => { - if (!setSelectedColumnHeader) return; - setSelectedColumnHeader({ - column: column!, - anchorEl: null, - }); - setTimeout(() => setSelectedColumnHeader(null), 300); - }; + const handleClose = () => { + if (!setSelectedColumnHeader) return; + setSelectedColumnHeader({ + column: column!, + anchorEl: null, + }); + setTimeout(() => setSelectedColumnHeader(null), 300); + }; - const isConfigurable = Boolean( - getFieldProp("settings", column?.type) || - getFieldProp("initializable", column?.type) - ); + const isConfigurable = Boolean( + getFieldProp("settings", column?.type) || + getFieldProp("initializable", column?.type) + ); - if (!column) return null; + if (!column) return null; - const isSorted = orderBy?.[0]?.key === column.key; - const isAsc = isSorted && orderBy?.[0]?.direction === "asc"; + const isSorted = orderBy?.[0]?.key === column.key; + const isAsc = isSorted && orderBy?.[0]?.direction === "asc"; - const clearModal = () => { - setModal(INITIAL_MODAL); - setTimeout(() => handleClose(), 300); - }; + const clearModal = () => { + setModal(INITIAL_MODAL); + setTimeout(() => handleClose(), 300); + }; - const handleModalSave = (key: string, update: Record) => { - actions.update(key, update); - }; - const openSettings = (column) => { - setSelectedColumnHeader({ - column, - }); - setModal({ type: ModalStates.settings, data: { column } }); - }; - const menuItems = [ - { - type: "subheader", - label: column.name, - }, - { - label: "Lock", - activeLabel: "Unlock", - icon: , - activeIcon: , - onClick: () => { - actions.update(column.key, { editable: !column.editable }); - handleClose(); - }, - active: !column.editable, - }, - { - label: "Freeze", - activeLabel: "Unfreeze", - icon: , - activeIcon: , - onClick: () => { - actions.update(column.key, { fixed: !column.fixed }); - handleClose(); - }, - active: column.fixed, - }, - { - label: "Enable resize", - activeLabel: "Disable resize", - icon: , - onClick: () => { - actions.update(column.key, { resizable: !column.resizable }); - handleClose(); - }, - active: column.resizable, - }, - { - label: "Sort: descending", - activeLabel: "Sorted: descending", - icon: , - onClick: () => { - tableActions.table.orderBy( - isSorted && !isAsc ? [] : [{ key: column.key, direction: "desc" }] - ); - handleClose(); - }, - active: isSorted && !isAsc, - disabled: column.type === FieldType.id, - }, - { - label: "Sort: ascending", - activeLabel: "Sorted: ascending", - icon: , - onClick: () => { - tableActions.table.orderBy( - isSorted && isAsc ? [] : [{ key: column.key, direction: "asc" }] - ); - handleClose(); - }, - active: isSorted && isAsc, - disabled: column.type === FieldType.id, - }, - { type: "subheader", label: "Edit" }, - { - label: "Rename…", - icon: , - onClick: () => { - setModal({ type: ModalStates.nameChange, data: {} }); - }, - }, - { - label: `Edit type: ${getFieldProp("name", column.type)}…`, - // This is based off the cell type - icon: getFieldProp("icon", column.type), - onClick: () => { - setModal({ type: ModalStates.typeChange, data: { column } }); - }, - }, - { - label: `Column settings…`, - // This is based off the cell type - icon: , - onClick: () => { - openSettings(column); - }, - disabled: !isConfigurable, - }, - // { - // label: "Re-order", - // icon: , - // onClick: () => alert("REORDER"), - // }, - { - label: "Add new to left…", - icon: , - onClick: () => - setModal({ - type: ModalStates.new, - data: { - initializeColumn: { index: column.index ? column.index - 1 : 0 }, - }, - }), - }, - { - label: "Add new to right…", - icon: , - onClick: () => - setModal({ - type: ModalStates.new, - data: { - initializeColumn: { index: column.index ? column.index + 1 : 0 }, - }, - }), - }, - { - label: "Hide for everyone", - activeLabel: "Show", - icon: , - activeIcon: , - onClick: () => { - actions.update(column.key, { hidden: !column.hidden }); - handleClose(); - }, - active: column.hidden, - color: "error" as "error", - }, - { - label: "Delete column", - icon: , - onClick: () => { - actions.remove(column.key); - handleClose(); - }, - color: "error" as "error", - }, - ]; + const handleModalSave = (key: string, update: Record) => { + actions.update(key, update); + }; + const openSettings = (column) => { + setSelectedColumnHeader({ + column, + }); + setModal({ type: ModalStates.settings, data: { column } }); + }; + const menuItems = [ + { + type: "subheader", + label: column.name, + }, + { + label: "Lock", + activeLabel: "Unlock", + icon: , + activeIcon: , + onClick: () => { + actions.update(column.key, { editable: !column.editable }); + handleClose(); + }, + active: !column.editable, + }, + { + label: "Freeze", + activeLabel: "Unfreeze", + icon: , + activeIcon: , + onClick: () => { + actions.update(column.key, { fixed: !column.fixed }); + handleClose(); + }, + active: column.fixed, + }, + { + label: "Enable resize", + activeLabel: "Disable resize", + icon: , + onClick: () => { + actions.update(column.key, { resizable: !column.resizable }); + handleClose(); + }, + active: column.resizable, + }, + { + label: "Sort: descending", + activeLabel: "Sorted: descending", + icon: , + onClick: () => { + tableActions.table.orderBy( + isSorted && !isAsc ? [] : [{ key: column.key, direction: "desc" }] + ); + handleClose(); + }, + active: isSorted && !isAsc, + disabled: column.type === FieldType.id, + }, + { + label: "Sort: ascending", + activeLabel: "Sorted: ascending", + icon: , + onClick: () => { + tableActions.table.orderBy( + isSorted && isAsc ? [] : [{ key: column.key, direction: "asc" }] + ); + handleClose(); + }, + active: isSorted && isAsc, + disabled: column.type === FieldType.id, + }, + { type: "subheader", label: "Edit" }, + { + label: "Rename…", + icon: , + onClick: () => { + setModal({ type: ModalStates.nameChange, data: {} }); + }, + }, + { + label: `Edit type: ${getFieldProp("name", column.type)}…`, + // This is based off the cell type + icon: getFieldProp("icon", column.type), + onClick: () => { + setModal({ type: ModalStates.typeChange, data: { column } }); + }, + }, + { + label: `Column settings…`, + // This is based off the cell type + icon: , + onClick: () => { + openSettings(column); + }, + disabled: !isConfigurable, + }, + // { + // label: "Re-order", + // icon: , + // onClick: () => alert("REORDER"), + // }, + { + label: "Add new to left…", + icon: , + onClick: () => + setModal({ + type: ModalStates.new, + data: { + initializeColumn: { index: column.index ? column.index - 1 : 0 }, + }, + }), + }, + { + label: "Add new to right…", + icon: , + onClick: () => + setModal({ + type: ModalStates.new, + data: { + initializeColumn: { index: column.index ? column.index + 1 : 0 }, + }, + }), + }, + { + label: "Hide for everyone", + activeLabel: "Show", + icon: , + activeIcon: , + onClick: () => { + actions.update(column.key, { hidden: !column.hidden }); + handleClose(); + }, + active: column.hidden, + color: "error" as "error", + }, + { + label: "Delete column", + icon: , + onClick: () => { + actions.remove(column.key); + handleClose(); + }, + color: "error" as "error", + }, + ]; - const menuModalProps = { - name: column.name, - fieldName: column.key, - type: column.type, + const menuModalProps = { + name: column.name, + fieldName: column.key, + type: column.type, - open: modal.type === ModalStates.typeChange, - config: column.config, + open: modal.type === ModalStates.typeChange, + config: column.config, - handleClose: clearModal, - handleSave: handleModalSave, - }; + handleClose: clearModal, + handleSave: handleModalSave, + }; - return ( - <> - {column.type !== FieldType.last && ( - - - - )} - {column && ( - <> - - - - - - )} - - ); + return ( + <> + {column.type !== FieldType.last && ( + + + + )} + {column && ( + <> + + + + + + )} + + ); } diff --git a/src/components/Table/EmptyTable.tsx b/src/components/Table/EmptyTable.tsx index 2078e137..90017dab 100644 --- a/src/components/Table/EmptyTable.tsx +++ b/src/components/Table/EmptyTable.tsx @@ -11,145 +11,145 @@ import ImportWizard from "components/Wizards/ImportWizard"; import ImportCSV from "./TableHeader/ImportCsv"; const useStyles = makeStyles((theme) => - createStyles({ - root: { - height: `calc(100vh - ${APP_BAR_HEIGHT}px)`, - width: 300, - margin: "0 auto", - textAlign: "center", - userSelect: "none", - }, + createStyles({ + root: { + height: `calc(100vh - ${APP_BAR_HEIGHT}px)`, + width: 300, + margin: "0 auto", + textAlign: "center", + userSelect: "none", + }, - tablePath: { - fontFamily: theme.typography.fontFamilyMono, - textTransform: "none", - }, - }) + tablePath: { + fontFamily: theme.typography.fontFamilyMono, + textTransform: "none", + }, + }) ); export default function EmptyTable() { - const classes = useStyles(); - const { tableState, importWizardRef, columnMenuRef } = useRowyContext(); + const classes = useStyles(); + const { tableState, importWizardRef, columnMenuRef } = useRowyContext(); - if (tableState?.rows && tableState?.rows.length > 0) - return ( - - - - You have existing data in your Firestore collection -
- - “{tableState?.tablePath}” - -
-
+ if (tableState?.rows && tableState?.rows.length > 0) + return ( + + + + You have existing data in your Firestore collection +
+ + “{tableState?.tablePath}” + +
+
- - - You can start by importing this existing data to this table - - + + + You can start by importing this existing data to this table + + - - + + - - - - ); + +
+
+ ); - return ( - - - - You have no data in this table - - + return ( + + + + You have no data in this table + + - - - You can start by importing data from an external CSV file - - + + + You can start by importing data from an external CSV file + + - - ( - - )} - PopoverProps={{ - anchorOrigin: { - vertical: "bottom", - horizontal: "center", - }, - transformOrigin: { - vertical: "top", - horizontal: "center", - }, - }} - /> - + + ( + + )} + PopoverProps={{ + anchorOrigin: { + vertical: "bottom", + horizontal: "center", + }, + transformOrigin: { + vertical: "top", + horizontal: "center", + }, + }} + /> + - + - - - Or you can manually add new columns and rows - - + + + Or you can manually add new columns and rows + + - - + + - - - - ); + + + + ); } diff --git a/src/components/Table/Filters/Row.tsx b/src/components/Table/Filters/Row.tsx index 321f5817..d87212b2 100644 --- a/src/components/Table/Filters/Row.tsx +++ b/src/components/Table/Filters/Row.tsx @@ -1,5 +1,5 @@ const FiltersRow = () => { - return <>; + return <>; }; export default FiltersRow; diff --git a/src/components/Table/Filters/index.tsx b/src/components/Table/Filters/index.tsx index 7a6b180a..1c58c90f 100644 --- a/src/components/Table/Filters/index.tsx +++ b/src/components/Table/Filters/index.tsx @@ -5,15 +5,15 @@ import _isEmpty from "lodash/isEmpty"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - Popover, - Button, - Typography, - IconButton, - Grid, - MenuItem, - TextField, - Switch, - Chip, + Popover, + Button, + Typography, + IconButton, + Grid, + MenuItem, + TextField, + Switch, + Chip, } from "@material-ui/core"; import FilterIcon from "@material-ui/icons/FilterList"; import CloseIcon from "@material-ui/icons/Close"; @@ -28,373 +28,373 @@ import { useRowyContext } from "contexts/RowyContext"; import { useAppContext } from "contexts/AppContext"; import { DocActions } from "hooks/useDoc"; const getType = (column) => - column.type === FieldType.derivative - ? column.config.renderFieldType - : column.type; + column.type === FieldType.derivative + ? column.config.renderFieldType + : column.type; const OPERATORS = [ - { - value: "==", - label: "Equals", - compatibleTypes: [ - FieldType.phone, - FieldType.color, - FieldType.date, - FieldType.dateTime, - FieldType.shortText, - FieldType.singleSelect, - FieldType.url, - FieldType.email, - FieldType.checkbox, - ], - }, - { - value: "in", - label: "matches any of", - compatibleTypes: [FieldType.singleSelect], - }, - // { - // value: "array-contains", - // label: "includes", - // compatibleTypes: [FieldType.connectTable], - // }, - // { - // value: "array-contains", - // label: "Has", - // compatibleTypes: [FieldType.multiSelect], - // }, - { - value: "array-contains-any", - label: "Has any", - compatibleTypes: [FieldType.multiSelect, FieldType.connectTable], - }, - { value: "<", label: "<", compatibleTypes: [FieldType.number] }, - { value: "<=", label: "<=", compatibleTypes: [FieldType.number] }, - { value: "==", label: "==", compatibleTypes: [FieldType.number] }, - { value: ">=", label: ">=", compatibleTypes: [FieldType.number] }, - { value: ">", label: ">", compatibleTypes: [FieldType.number] }, - { - value: "<", - label: "before", - compatibleTypes: [FieldType.date, FieldType.dateTime], - }, - { - value: ">=", - label: "after", - compatibleTypes: [FieldType.date, FieldType.dateTime], - }, + { + value: "==", + label: "Equals", + compatibleTypes: [ + FieldType.phone, + FieldType.color, + FieldType.date, + FieldType.dateTime, + FieldType.shortText, + FieldType.singleSelect, + FieldType.url, + FieldType.email, + FieldType.checkbox, + ], + }, + { + value: "in", + label: "matches any of", + compatibleTypes: [FieldType.singleSelect], + }, + // { + // value: "array-contains", + // label: "includes", + // compatibleTypes: [FieldType.connectTable], + // }, + // { + // value: "array-contains", + // label: "Has", + // compatibleTypes: [FieldType.multiSelect], + // }, + { + value: "array-contains-any", + label: "Has any", + compatibleTypes: [FieldType.multiSelect, FieldType.connectTable], + }, + { value: "<", label: "<", compatibleTypes: [FieldType.number] }, + { value: "<=", label: "<=", compatibleTypes: [FieldType.number] }, + { value: "==", label: "==", compatibleTypes: [FieldType.number] }, + { value: ">=", label: ">=", compatibleTypes: [FieldType.number] }, + { value: ">", label: ">", compatibleTypes: [FieldType.number] }, + { + value: "<", + label: "before", + compatibleTypes: [FieldType.date, FieldType.dateTime], + }, + { + value: ">=", + label: "after", + compatibleTypes: [FieldType.date, FieldType.dateTime], + }, ]; const useStyles = makeStyles((theme) => - createStyles({ - paper: { width: 640 }, + createStyles({ + paper: { width: 640 }, - closeButton: { - position: "absolute", - top: theme.spacing(0.5), - right: theme.spacing(0.5), - }, + closeButton: { + position: "absolute", + top: theme.spacing(0.5), + right: theme.spacing(0.5), + }, - content: { padding: theme.spacing(4) }, + content: { padding: theme.spacing(4) }, - topRow: { marginBottom: theme.spacing(3.5) }, - bottomButtons: { marginTop: theme.spacing(4.5) }, + topRow: { marginBottom: theme.spacing(3.5) }, + bottomButtons: { marginTop: theme.spacing(4.5) }, - activeButton: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - position: "relative", - zIndex: 1, - }, + activeButton: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + position: "relative", + zIndex: 1, + }, - filterChip: { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderTopRightRadius: theme.shape.borderRadius, - borderBottomRightRadius: theme.shape.borderRadius, - borderLeft: "none", - backgroundColor: theme.palette.background.paper, - height: 32, - // paddingLeft: theme.shape.borderRadius, - // marginLeft: -theme.shape.borderRadius, - paddingRight: theme.spacing(0.5) + " !important", - }, - filterChipLabel: { - padding: theme.spacing(0, 1.5), - }, - }) + filterChip: { + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + borderTopRightRadius: theme.shape.borderRadius, + borderBottomRightRadius: theme.shape.borderRadius, + borderLeft: "none", + backgroundColor: theme.palette.background.paper, + height: 32, + // paddingLeft: theme.shape.borderRadius, + // marginLeft: -theme.shape.borderRadius, + paddingRight: theme.spacing(0.5) + " !important", + }, + filterChipLabel: { + padding: theme.spacing(0, 1.5), + }, + }) ); const UNFILTERABLES = [ - FieldType.image, - FieldType.file, - FieldType.action, - FieldType.subTable, - FieldType.last, - FieldType.longText, + FieldType.image, + FieldType.file, + FieldType.action, + FieldType.subTable, + FieldType.last, + FieldType.longText, ]; const Filters = () => { - const { tableState, tableActions } = useRowyContext(); - const { userDoc } = useAppContext(); + const { tableState, tableActions } = useRowyContext(); + const { userDoc } = useAppContext(); - useEffect(() => { - if (userDoc.state.doc && tableState?.tablePath) { - if (userDoc.state.doc.tables?.[tableState?.tablePath]?.filters) { - tableActions?.table.filter( - userDoc.state.doc.tables[tableState?.tablePath].filters - ); - tableActions?.table.orderBy(); - } - } - }, [userDoc.state, tableState?.tablePath]); - const filterColumns = _sortBy(Object.values(tableState!.columns), "index") - .filter((c) => !UNFILTERABLES.includes(c.type)) - .map((c) => ({ - key: c.key, - label: c.name, - type: c.type, - options: c.options, - ...c, - })); - const classes = useStyles(); - const filters = []; + useEffect(() => { + if (userDoc.state.doc && tableState?.tablePath) { + if (userDoc.state.doc.tables?.[tableState?.tablePath]?.filters) { + tableActions?.table.filter( + userDoc.state.doc.tables[tableState?.tablePath].filters + ); + tableActions?.table.orderBy(); + } + } + }, [userDoc.state, tableState?.tablePath]); + const filterColumns = _sortBy(Object.values(tableState!.columns), "index") + .filter((c) => !UNFILTERABLES.includes(c.type)) + .map((c) => ({ + key: c.key, + label: c.name, + type: c.type, + options: c.options, + ...c, + })); + const classes = useStyles(); + const filters = []; - const [selectedColumn, setSelectedColumn] = useState(); + const [selectedColumn, setSelectedColumn] = useState(); - const [query, setQuery] = useState({ - key: "", - operator: "", - value: "", - }); + const [query, setQuery] = useState({ + key: "", + operator: "", + value: "", + }); - useEffect(() => { - if (selectedColumn) { - let updatedQuery: RowyFilter = { - key: selectedColumn.key, - operator: "", - value: "", - }; - const type = getType(selectedColumn); - if ( - [ - FieldType.phone, - FieldType.shortText, - FieldType.url, - FieldType.email, - FieldType.checkbox, - ].includes(type) - ) { - updatedQuery = { ...updatedQuery, operator: "==" }; - } - if (type === FieldType.checkbox) { - updatedQuery = { ...updatedQuery, value: false }; - } - if (type === FieldType.connectTable) { - updatedQuery = { - key: `${selectedColumn.key}ID`, - operator: "array-contains-any", - value: [], - }; - } - if (type === FieldType.multiSelect) { - updatedQuery = { - ...updatedQuery, - operator: "array-contains-any", - value: [], - }; - } - setQuery(updatedQuery); - } - }, [selectedColumn]); + useEffect(() => { + if (selectedColumn) { + let updatedQuery: RowyFilter = { + key: selectedColumn.key, + operator: "", + value: "", + }; + const type = getType(selectedColumn); + if ( + [ + FieldType.phone, + FieldType.shortText, + FieldType.url, + FieldType.email, + FieldType.checkbox, + ].includes(type) + ) { + updatedQuery = { ...updatedQuery, operator: "==" }; + } + if (type === FieldType.checkbox) { + updatedQuery = { ...updatedQuery, value: false }; + } + if (type === FieldType.connectTable) { + updatedQuery = { + key: `${selectedColumn.key}ID`, + operator: "array-contains-any", + value: [], + }; + } + if (type === FieldType.multiSelect) { + updatedQuery = { + ...updatedQuery, + operator: "array-contains-any", + value: [], + }; + } + setQuery(updatedQuery); + } + }, [selectedColumn]); - const operators = selectedColumn - ? OPERATORS.filter((operator) => - operator.compatibleTypes.includes(getType(selectedColumn)) - ) - : []; + const operators = selectedColumn + ? OPERATORS.filter((operator) => + operator.compatibleTypes.includes(getType(selectedColumn)) + ) + : []; - const [anchorEl, setAnchorEl] = useState(null); - const handleClose = () => setAnchorEl(null); + const [anchorEl, setAnchorEl] = useState(null); + const handleClose = () => setAnchorEl(null); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(anchorEl ? null : event.currentTarget); - }; + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(anchorEl ? null : event.currentTarget); + }; - const handleChangeColumn = (e) => { - const column = _find(filterColumns, (c) => c.key === e.target.value); - setSelectedColumn(column); - }; - const open = Boolean(anchorEl); + const handleChangeColumn = (e) => { + const column = _find(filterColumns, (c) => c.key === e.target.value); + setSelectedColumn(column); + }; + const open = Boolean(anchorEl); - const id = open ? "simple-popper" : undefined; + const id = open ? "simple-popper" : undefined; - const renderInputField = (selectedColumn, operator) => { - const type = getType(selectedColumn); - switch (type) { - case FieldType.checkbox: - return ( - { - setQuery((query) => ({ ...query, value: e.target.checked })); - }} - /> - ); - case FieldType.email: - case FieldType.phone: - case FieldType.shortText: - case FieldType.longText: - case FieldType.url: - return ( - { - const value = e.target.value; - if (value) setQuery((query) => ({ ...query, value: value })); - }} - variant="filled" - hiddenLabel - placeholder="Text value" - /> - ); - case FieldType.number: - return ( - { - const value = e.target.value; - if (query.value || value) - setQuery((query) => ({ - ...query, - value: value !== "" ? parseFloat(value) : "", - })); - }} - value={typeof query.value === "number" ? query.value : ""} - variant="filled" - hiddenLabel - type="number" - placeholder="number value" - /> - ); + const renderInputField = (selectedColumn, operator) => { + const type = getType(selectedColumn); + switch (type) { + case FieldType.checkbox: + return ( + { + setQuery((query) => ({ ...query, value: e.target.checked })); + }} + /> + ); + case FieldType.email: + case FieldType.phone: + case FieldType.shortText: + case FieldType.longText: + case FieldType.url: + return ( + { + const value = e.target.value; + if (value) setQuery((query) => ({ ...query, value: value })); + }} + variant="filled" + hiddenLabel + placeholder="Text value" + /> + ); + case FieldType.number: + return ( + { + const value = e.target.value; + if (query.value || value) + setQuery((query) => ({ + ...query, + value: value !== "" ? parseFloat(value) : "", + })); + }} + value={typeof query.value === "number" ? query.value : ""} + variant="filled" + hiddenLabel + type="number" + placeholder="number value" + /> + ); - case FieldType.singleSelect: - if (operator === "in") - return ( - setQuery((query) => ({ ...query, value }))} - options={ - selectedColumn.config.options - ? selectedColumn.config.options.sort() - : [] - } - label="" - value={Array.isArray(query?.value) ? query.value : []} - TextFieldProps={{ hiddenLabel: true }} - /> - ); + case FieldType.singleSelect: + if (operator === "in") + return ( + setQuery((query) => ({ ...query, value }))} + options={ + selectedColumn.config.options + ? selectedColumn.config.options.sort() + : [] + } + label="" + value={Array.isArray(query?.value) ? query.value : []} + TextFieldProps={{ hiddenLabel: true }} + /> + ); - return ( - { - if (value !== null) setQuery((query) => ({ ...query, value })); - }} - options={ - selectedColumn.config.options - ? selectedColumn.config.options.sort() - : [] - } - label="" - value={typeof query?.value === "string" ? query.value : null} - TextFieldProps={{ hiddenLabel: true }} - /> - ); + return ( + { + if (value !== null) setQuery((query) => ({ ...query, value })); + }} + options={ + selectedColumn.config.options + ? selectedColumn.config.options.sort() + : [] + } + label="" + value={typeof query?.value === "string" ? query.value : null} + TextFieldProps={{ hiddenLabel: true }} + /> + ); - case FieldType.multiSelect: - return ( - setQuery((query) => ({ ...query, value }))} - value={query.value as string[]} - max={10} - options={ - selectedColumn.config.options - ? selectedColumn.config.options.sort() - : [] - } - label={""} - searchable={false} - freeText={true} - /> - ); + case FieldType.multiSelect: + return ( + setQuery((query) => ({ ...query, value }))} + value={query.value as string[]} + max={10} + options={ + selectedColumn.config.options + ? selectedColumn.config.options.sort() + : [] + } + label={""} + searchable={false} + freeText={true} + /> + ); - case FieldType.date: - case FieldType.dateTime: - return <>//TODO:Date/Time picker; - default: - return <>Not available; - // return ; - break; - } - }; + case FieldType.date: + case FieldType.dateTime: + return <>//TODO:Date/Time picker; + default: + return <>Not available; + // return ; + break; + } + }; - const handleUpdateFilters = (filters: RowyFilter[]) => { - userDoc.dispatch({ - action: DocActions.update, - data: { - tables: { [`${tableState?.tablePath}`]: { filters } }, - }, - }); - }; - return ( - <> - - } - active={tableState?.filters && tableState?.filters.length > 0} - className={ - tableState?.filters && tableState?.filters.length > 0 - ? classes.activeButton - : "" - } - > - {tableState?.filters && tableState?.filters.length > 0 - ? "Filtered" - : "Filter"} - + const handleUpdateFilters = (filters: RowyFilter[]) => { + userDoc.dispatch({ + action: DocActions.update, + data: { + tables: { [`${tableState?.tablePath}`]: { filters } }, + }, + }); + }; + return ( + <> + + } + active={tableState?.filters && tableState?.filters.length > 0} + className={ + tableState?.filters && tableState?.filters.length > 0 + ? classes.activeButton + : "" + } + > + {tableState?.filters && tableState?.filters.length > 0 + ? "Filtered" + : "Filter"} + - {(tableState?.filters ?? []).map((filter) => ( - handleUpdateFilters([])} - classes={{ - root: classes.filterChip, - label: classes.filterChipLabel, - }} - variant="outlined" - /> - ))} - - - - - + {(tableState?.filters ?? []).map((filter) => ( + handleUpdateFilters([])} + classes={{ + root: classes.filterChip, + label: classes.filterChipLabel, + }} + variant="outlined" + /> + ))} + + + + + -
- {/* + {/* { */} - - - Column - - - Condition - - - Value - - + + + Column + + + Condition + + + Value + + - - - - - Select Column - - {filterColumns.map((c) => ( - - {c.label} - - ))} - - + + + + + Select Column + + {filterColumns.map((c) => ( + + {c.label} + + ))} + + - - { - setQuery((query) => ({ - ...query, - operator: e.target.value as string, - })); - }} - SelectProps={{ displayEmpty: true }} - > - - Select Condition - - {operators.map((operator) => ( - - {operator.label} - - ))} - - + + { + setQuery((query) => ({ + ...query, + operator: e.target.value as string, + })); + }} + SelectProps={{ displayEmpty: true }} + > + + Select Condition + + {operators.map((operator) => ( + + {operator.label} + + ))} + + - - {query.operator && - renderInputField(selectedColumn, query.operator)} - - + + {query.operator && + renderInputField(selectedColumn, query.operator)} + + - - {/* */} - - - -
-
- - ); + + {/* */} + + + +
+ + + ); }; export default Filters; diff --git a/src/components/Table/FinalColumnHeader.tsx b/src/components/Table/FinalColumnHeader.tsx index baf29af8..70457a99 100644 --- a/src/components/Table/FinalColumnHeader.tsx +++ b/src/components/Table/FinalColumnHeader.tsx @@ -7,71 +7,71 @@ import AddColumnIcon from "assets/icons/AddColumn"; import { useRowyContext } from "contexts/RowyContext"; const useStyles = makeStyles((theme) => - createStyles({ - "@global": { - ".rdg-header-row .rdg-cell.final-column-header": { - border: "none", - ".rdg.rdg &": { padding: theme.spacing(0, 0.75) }, + createStyles({ + "@global": { + ".rdg-header-row .rdg-cell.final-column-header": { + border: "none", + ".rdg.rdg &": { padding: theme.spacing(0, 0.75) }, - "&::before": { - content: "''", - display: "block", - width: 46, - height: "100%", + "&::before": { + content: "''", + display: "block", + width: 46, + height: "100%", - position: "absolute", - top: 0, - left: 0, + position: "absolute", + top: 0, + left: 0, - border: "1px solid var(--border-color)", - borderLeftWidth: 0, - borderTopRightRadius: theme.shape.borderRadius, - borderBottomRightRadius: theme.shape.borderRadius, - }, - }, - }, + border: "1px solid var(--border-color)", + borderLeftWidth: 0, + borderTopRightRadius: theme.shape.borderRadius, + borderBottomRightRadius: theme.shape.borderRadius, + }, + }, + }, - root: { - height: "100%", - width: "auto", - }, + root: { + height: "100%", + width: "auto", + }, - button: { zIndex: 1 }, - }) + button: { zIndex: 1 }, + }) ); const FinalColumnHeader: Column["headerRenderer"] = ({ column }) => { - const classes = useStyles(); + const classes = useStyles(); - const { columnMenuRef } = useRowyContext(); - if (!columnMenuRef) return null; + const { columnMenuRef } = useRowyContext(); + if (!columnMenuRef) return null; - const handleClick = ( - event: React.MouseEvent - ) => - columnMenuRef?.current?.setSelectedColumnHeader({ - column, - anchorEl: event.currentTarget, - }); + const handleClick = ( + event: React.MouseEvent + ) => + columnMenuRef?.current?.setSelectedColumnHeader({ + column, + anchorEl: event.currentTarget, + }); - return ( - - - - ); + return ( + + + + ); }; export default FinalColumnHeader; diff --git a/src/components/Table/HiddenFields.tsx b/src/components/Table/HiddenFields.tsx index 742d68e5..9a04b74e 100644 --- a/src/components/Table/HiddenFields.tsx +++ b/src/components/Table/HiddenFields.tsx @@ -14,135 +14,135 @@ import { useAppContext } from "contexts/AppContext"; import { DocActions } from "hooks/useDoc"; import { formatSubTableName } from "../../utils/fns"; const useStyles = makeStyles((theme) => - createStyles({ - listbox: {}, - option: { - "$listbox &": { - padding: theme.spacing(0, 2), - marginBottom: -1, + createStyles({ + listbox: {}, + option: { + "$listbox &": { + padding: theme.spacing(0, 2), + marginBottom: -1, - "&::after": { content: "none" }, + "&::after": { content: "none" }, - "&:hover, &.Mui-focused, &.Mui-focusVisible": { - backgroundColor: "transparent", + "&:hover, &.Mui-focused, &.Mui-focusVisible": { + backgroundColor: "transparent", - position: "relative", - zIndex: 2, + position: "relative", + zIndex: 2, - "& > div": { - color: theme.palette.text.primary, - borderColor: "currentColor", - boxShadow: `0 0 0 1px ${theme.palette.text.primary} inset`, - }, - "& $hiddenIcon": { opacity: 0.5 }, - }, + "& > div": { + color: theme.palette.text.primary, + borderColor: "currentColor", + boxShadow: `0 0 0 1px ${theme.palette.text.primary} inset`, + }, + "& $hiddenIcon": { opacity: 0.5 }, + }, - '&[aria-selected="true"], &[aria-selected="true"].Mui-focused, &[aria-selected="true"].Mui-focusVisible': { - backgroundColor: "transparent", + '&[aria-selected="true"], &[aria-selected="true"].Mui-focused, &[aria-selected="true"].Mui-focusVisible': { + backgroundColor: "transparent", - position: "relative", - zIndex: 1, + position: "relative", + zIndex: 1, - "& $hiddenIcon": { opacity: 1 }, - }, - }, - }, + "& $hiddenIcon": { opacity: 1 }, + }, + }, + }, - hiddenIcon: { opacity: 0 }, - }) + hiddenIcon: { opacity: 0 }, + }) ); export default function HiddenFields() { - const classes = useStyles(); - const buttonRef = useRef(null); + const classes = useStyles(); + const buttonRef = useRef(null); - const { tableState } = useRowyContext(); - const { userDoc } = useAppContext(); + const { tableState } = useRowyContext(); + const { userDoc } = useAppContext(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); - // Store local selection here - const [hiddenFields, setHiddenFields] = useState([]); + // Store local selection here + const [hiddenFields, setHiddenFields] = useState([]); - // Initialise hiddenFields from user doc - const userDocHiddenFields = - userDoc.state.doc?.tables?.[formatSubTableName(tableState?.tablePath!)] - ?.hiddenFields; - useEffect(() => { - if (userDocHiddenFields) setHiddenFields(userDocHiddenFields); - else setHiddenFields([]); - }, [userDocHiddenFields]); + // Initialise hiddenFields from user doc + const userDocHiddenFields = + userDoc.state.doc?.tables?.[formatSubTableName(tableState?.tablePath!)] + ?.hiddenFields; + useEffect(() => { + if (userDocHiddenFields) setHiddenFields(userDocHiddenFields); + else setHiddenFields([]); + }, [userDocHiddenFields]); - if (!tableState || !userDoc) return null; + if (!tableState || !userDoc) return null; - const tableColumns = _sortBy( - Object.entries(tableState.columns).filter(([key]) => key !== "undefined"), - (column) => column[1].index - ).map(([key]) => ({ - value: key, - label: tableState.columns[key].name, - })); + const tableColumns = _sortBy( + Object.entries(tableState.columns).filter(([key]) => key !== "undefined"), + (column) => column[1].index + ).map(([key]) => ({ + value: key, + label: tableState.columns[key].name, + })); - // Save when MultiSelect closes - const handleSave = () => { - // Only update if there were any changes because it’s slow to update - if (!_isEqual(hiddenFields, userDocHiddenFields)) - userDoc.dispatch({ - action: DocActions.update, - data: { - tables: { - [formatSubTableName(tableState?.tablePath)]: { hiddenFields }, - }, - }, - }); + // Save when MultiSelect closes + const handleSave = () => { + // Only update if there were any changes because it’s slow to update + if (!_isEqual(hiddenFields, userDocHiddenFields)) + userDoc.dispatch({ + action: DocActions.update, + data: { + tables: { + [formatSubTableName(tableState?.tablePath)]: { hiddenFields }, + }, + }, + }); - setOpen(false); - }; - const renderOption = (props, option, { selected }) => ( -
  • - } - active={selected} - /> -
  • - ); - return ( - <> - } - onClick={() => setOpen((o) => !o)} - active={hiddenFields.length > 0} - ref={buttonRef} - > - {hiddenFields.length > 0 ? `${hiddenFields.length} Hidden` : "Hide"} - - - - ); + setOpen(false); + }; + const renderOption = (props, option, { selected }) => ( +
  • + } + active={selected} + /> +
  • + ); + return ( + <> + } + onClick={() => setOpen((o) => !o)} + active={hiddenFields.length > 0} + ref={buttonRef} + > + {hiddenFields.length > 0 ? `${hiddenFields.length} Hidden` : "Hide"} + + + + ); } diff --git a/src/components/Table/HotKeys.tsx b/src/components/Table/HotKeys.tsx index fa984711..7ecaa7e8 100644 --- a/src/components/Table/HotKeys.tsx +++ b/src/components/Table/HotKeys.tsx @@ -9,95 +9,95 @@ const onSubmit: any = () => () => {}; * Listens Hot Keys combination keys to trigger keyboard shortcuts */ const Hotkeys = (props: any) => { - const { selectedCell } = props; - const { currentUser } = useAppContext(); + const { selectedCell } = props; + const { currentUser } = useAppContext(); - useHotkeys( - "cmd+c", - () => { - handleCopy(); - }, - [selectedCell] - ); - useHotkeys( - "ctrl+c", - () => { - handleCopy(); - }, - [selectedCell] - ); - useHotkeys( - "cmd+v", - () => { - handlePaste(); - }, - [selectedCell] - ); - useHotkeys( - "ctrl+v", - () => { - handlePaste(); - }, - [selectedCell] - ); - useHotkeys( - "ctrl+x", - () => { - handleCut(); - }, - [selectedCell] - ); - useHotkeys( - "cmd+x", - () => { - handleCut(); - }, - [selectedCell] - ); - const stringFields = [ - FieldType.email, - FieldType.shortText, - FieldType.phone, - FieldType.singleSelect, - FieldType.longText, - FieldType.url, - ]; - const numberFields = [FieldType.number, FieldType.rating]; - /** - * populate cell from clipboard - */ - const handlePaste = async () => { - const { row, column } = selectedCell; - const newValue = await navigator.clipboard.readText(); - if (stringFields.includes(column.type)) - onSubmit(column.key, row, currentUser?.uid)(newValue); - else if (numberFields.includes(column.type)) { - const numberValue = parseInt(newValue, 10); - if (`${numberValue}` !== "NaN") { - onSubmit(column.key, row, currentUser?.uid)(numberValue); - } - } - }; - const supportedFields = [...stringFields, ...numberFields]; - /** - * copy cell content to clipboard works only on supported fields - */ - const handleCopy = () => { - const { row, column } = selectedCell; - if (supportedFields.includes(column.type)) { - navigator.clipboard.writeText(row[column.key]); - } - }; - /** - * copy cell content to clipboard and clears cell(only on supported fields) - */ - const handleCut = () => { - const { row, column } = selectedCell; - if (supportedFields.includes(column.type)) { - navigator.clipboard.writeText(row[column.key]); - onSubmit(column.key, row)(null); - } - }; - return <>; + useHotkeys( + "cmd+c", + () => { + handleCopy(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+c", + () => { + handleCopy(); + }, + [selectedCell] + ); + useHotkeys( + "cmd+v", + () => { + handlePaste(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+v", + () => { + handlePaste(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+x", + () => { + handleCut(); + }, + [selectedCell] + ); + useHotkeys( + "cmd+x", + () => { + handleCut(); + }, + [selectedCell] + ); + const stringFields = [ + FieldType.email, + FieldType.shortText, + FieldType.phone, + FieldType.singleSelect, + FieldType.longText, + FieldType.url, + ]; + const numberFields = [FieldType.number, FieldType.rating]; + /** + * populate cell from clipboard + */ + const handlePaste = async () => { + const { row, column } = selectedCell; + const newValue = await navigator.clipboard.readText(); + if (stringFields.includes(column.type)) + onSubmit(column.key, row, currentUser?.uid)(newValue); + else if (numberFields.includes(column.type)) { + const numberValue = parseInt(newValue, 10); + if (`${numberValue}` !== "NaN") { + onSubmit(column.key, row, currentUser?.uid)(numberValue); + } + } + }; + const supportedFields = [...stringFields, ...numberFields]; + /** + * copy cell content to clipboard works only on supported fields + */ + const handleCopy = () => { + const { row, column } = selectedCell; + if (supportedFields.includes(column.type)) { + navigator.clipboard.writeText(row[column.key]); + } + }; + /** + * copy cell content to clipboard and clears cell(only on supported fields) + */ + const handleCut = () => { + const { row, column } = selectedCell; + if (supportedFields.includes(column.type)) { + navigator.clipboard.writeText(row[column.key]); + onSubmit(column.key, row)(null); + } + }; + return <>; }; export default Hotkeys; diff --git a/src/components/Table/Settings/Menu.tsx b/src/components/Table/Settings/Menu.tsx index 02c6f456..38786fa1 100644 --- a/src/components/Table/Settings/Menu.tsx +++ b/src/components/Table/Settings/Menu.tsx @@ -9,53 +9,53 @@ const options = ["Webhooks", "Rules", "Algolia", "CollectionSync"]; const ITEM_HEIGHT = 48; export default function SettingsMenu({ modal, setModal }) { - const [anchorEl, setAnchorEl] = React.useState(null); - const open = Boolean(anchorEl); + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; - const handleClose = (option: string) => () => { - setModal(option); - setAnchorEl(null); - }; + const handleClose = (option: string) => () => { + setModal(option); + setAnchorEl(null); + }; - return ( -
    - - - - - {options.map((option) => ( - - {option} - - ))} - -
    - ); + return ( +
    + + + + + {options.map((option) => ( + + {option} + + ))} + +
    + ); } diff --git a/src/components/Table/Settings/Webhooks.tsx b/src/components/Table/Settings/Webhooks.tsx index 71abb23f..be81edae 100644 --- a/src/components/Table/Settings/Webhooks.tsx +++ b/src/components/Table/Settings/Webhooks.tsx @@ -21,166 +21,166 @@ import { WEBHOOK_URL } from "../../../firebase"; import { makeId } from "../../../utils/fns"; const useStyles = makeStyles((theme) => - createStyles({ - form: { - display: "flex", - flexDirection: "column", - margin: "auto", - width: "fit-content", - }, - formControl: { - marginTop: theme.spacing(2), - minWidth: 120, - }, - formControlLabel: { - marginTop: theme.spacing(1), - }, - }) + createStyles({ + form: { + display: "flex", + flexDirection: "column", + margin: "auto", + width: "fit-content", + }, + formControl: { + marginTop: theme.spacing(2), + minWidth: 120, + }, + formControlLabel: { + marginTop: theme.spacing(1), + }, + }) ); enum WebhookTypes { - custom = "CUSTOM", - typeForm = "TYPE_FORM", + custom = "CUSTOM", + typeForm = "TYPE_FORM", } const EmptyState = { - enabled: false, - type: WebhookTypes.custom, - secret: "", - customParser: "", + enabled: false, + type: WebhookTypes.custom, + secret: "", + customParser: "", }; export default function WebhooksDialog({ open, handleClose }) { - const classes = useStyles(); + const classes = useStyles(); - const { tableState, tableActions } = useRowyContext(); + const { tableState, tableActions } = useRowyContext(); - const [state, setState] = useState<{ - enabled: boolean; - type: WebhookTypes; - secret: string; - customParser: string; - }>(EmptyState); - const tableFields = Object.keys(tableState?.columns as any); - const fullWidth = true; - const maxWidth: DialogProps["maxWidth"] = "xl"; - const handleChange = (key: string) => (value: any) => { - setState((s) => ({ ...s, [key]: value })); - }; - const initializeWebhooksConfig = () => { - const secret = makeId(32); - handleChange("secret")(secret); - setState({ ...EmptyState, secret }); - tableActions?.table.updateConfig("webhooks", { - enabled: false, - type: WebhookTypes.custom, - secret, - customParser: "", // TODO: add a boilerplate/example - }); - }; - useEffect(() => { - if ( - tableState && - !tableState.config.tableConfig.loading && - !tableState?.config.webhooks && - !state.secret - ) { - initializeWebhooksConfig(); - } else if (tableState?.config.webhooks) { - setState({ ...tableState?.config.webhooks }); - } - }, [tableState?.config]); + const [state, setState] = useState<{ + enabled: boolean; + type: WebhookTypes; + secret: string; + customParser: string; + }>(EmptyState); + const tableFields = Object.keys(tableState?.columns as any); + const fullWidth = true; + const maxWidth: DialogProps["maxWidth"] = "xl"; + const handleChange = (key: string) => (value: any) => { + setState((s) => ({ ...s, [key]: value })); + }; + const initializeWebhooksConfig = () => { + const secret = makeId(32); + handleChange("secret")(secret); + setState({ ...EmptyState, secret }); + tableActions?.table.updateConfig("webhooks", { + enabled: false, + type: WebhookTypes.custom, + secret, + customParser: "", // TODO: add a boilerplate/example + }); + }; + useEffect(() => { + if ( + tableState && + !tableState.config.tableConfig.loading && + !tableState?.config.webhooks && + !state.secret + ) { + initializeWebhooksConfig(); + } else if (tableState?.config.webhooks) { + setState({ ...tableState?.config.webhooks }); + } + }, [tableState?.config]); - const handleWebhookTypeChange = ( - event: React.ChangeEvent<{ value: unknown }> - ) => { - handleChange("type")(event.target.value as WebhookTypes); - }; + const handleWebhookTypeChange = ( + event: React.ChangeEvent<{ value: unknown }> + ) => { + handleChange("type")(event.target.value as WebhookTypes); + }; - const handleSave = async () => { - handleClose(); - await tableActions?.table.updateConfig("webhooks", { - ...state, - }); - }; - const handleCancel = () => { - handleClose(); - setState({ ...tableState?.config.webhooks }); - }; - return ( - - - Webhooks - - - } - label={"Enable webhooks for this table"} - labelPlacement="end" - checked={state.enabled} - onChange={ - () => { - handleChange("enabled")(!state.enabled); - } - // handleChange("isCollectionGroup", !formState.isCollectionGroup) - } - // classes={{ root: classes.formControlLabel, label: classes.label }} - /> - Webhook Type - - - {state.type === WebhookTypes.custom && ( - - )} -
    - {state.type === WebhookTypes.typeForm && ( - <> - Web hook url: - - {WEBHOOK_URL}?tablePath={tableState?.tablePath} - &type=TYPE_FORM&secret={state.secret} - - instructions: - - please set the question reference in typeform to the following - field keys :{" "} - {tableFields.map((key) => ( - <> - {" "} - {key}, - - ))} - - - )} -
    - - - - -
    -
    - ); + const handleSave = async () => { + handleClose(); + await tableActions?.table.updateConfig("webhooks", { + ...state, + }); + }; + const handleCancel = () => { + handleClose(); + setState({ ...tableState?.config.webhooks }); + }; + return ( + + + Webhooks + + + } + label={"Enable webhooks for this table"} + labelPlacement="end" + checked={state.enabled} + onChange={ + () => { + handleChange("enabled")(!state.enabled); + } + // handleChange("isCollectionGroup", !formState.isCollectionGroup) + } + // classes={{ root: classes.formControlLabel, label: classes.label }} + /> + Webhook Type + + + {state.type === WebhookTypes.custom && ( + + )} +
    + {state.type === WebhookTypes.typeForm && ( + <> + Web hook url: + + {WEBHOOK_URL}?tablePath={tableState?.tablePath} + &type=TYPE_FORM&secret={state.secret} + + instructions: + + please set the question reference in typeform to the following + field keys :{" "} + {tableFields.map((key) => ( + <> + {" "} + {key}, + + ))} + + + )} +
    + + + + +
    +
    + ); } diff --git a/src/components/Table/Settings/index.tsx b/src/components/Table/Settings/index.tsx index 3fd3c873..fedfbda9 100644 --- a/src/components/Table/Settings/index.tsx +++ b/src/components/Table/Settings/index.tsx @@ -2,16 +2,16 @@ import { useState } from "react"; import SettingsMenu from "./Menu"; //import Webhooks from "./Webhooks"; export default function Settings() { - const [modal, setModal] = useState(""); - return ( - <> - - {/* + + {/* { setModal(""); }} /> */} - - ); + + ); } diff --git a/src/components/Table/Skeleton/HeaderRowSkeleton.tsx b/src/components/Table/Skeleton/HeaderRowSkeleton.tsx index ab205020..b1d14b2f 100644 --- a/src/components/Table/Skeleton/HeaderRowSkeleton.tsx +++ b/src/components/Table/Skeleton/HeaderRowSkeleton.tsx @@ -4,31 +4,31 @@ import AddColumnIcon from "assets/icons/AddColumn"; const NUM_CELLS = 5; export default function HeaderRowSkeleton() { - return ( - - {new Array(NUM_CELLS + 1).fill(undefined).map((_, i) => ( - - ))} + return ( + + {new Array(NUM_CELLS + 1).fill(undefined).map((_, i) => ( + + ))} - - - - - ); + + + + + ); } diff --git a/src/components/Table/Skeleton/TableHeaderSkeleton.tsx b/src/components/Table/Skeleton/TableHeaderSkeleton.tsx index f0a8ef29..0ea3946c 100644 --- a/src/components/Table/Skeleton/TableHeaderSkeleton.tsx +++ b/src/components/Table/Skeleton/TableHeaderSkeleton.tsx @@ -5,44 +5,44 @@ import AddRowIcon from "assets/icons/AddRow"; import { TABLE_HEADER_HEIGHT } from "components/Table/TableHeader"; const ButtonSkeleton = (props) => ( - + ); export default function TableHeaderSkeleton() { - return ( - - - - + return ( + + + + -
    +
    - - - - - - + + + + + + -
    +
    - -
    - - -
    - - - ); + +
    + + +
    + + + ); } diff --git a/src/components/Table/TableHeader/Export/Download.tsx b/src/components/Table/TableHeader/Export/Download.tsx index 2bf0e0c2..cb6894b9 100644 --- a/src/components/Table/TableHeader/Export/Download.tsx +++ b/src/components/Table/TableHeader/Export/Download.tsx @@ -9,11 +9,11 @@ import MultiSelect from "@antlerengineering/multiselect"; import JSZip from "jszip"; import { - DialogActions, - Button, - TextField, - FormControlLabel, - Checkbox, + DialogActions, + Button, + TextField, + FormControlLabel, + Checkbox, } from "@material-ui/core"; import { SnackContext } from "contexts/SnackContext"; @@ -28,178 +28,178 @@ const LABEL_COLUMNS = hasDataTypes(["string", "number"]); const download = (url) => fetch(url).then((resp) => resp.blob()); const selectedColumnsFilesReducer = (doc: any, labelColumns: any[]) => ( - accumulator: any, - currentColumn: any + accumulator: any, + currentColumn: any ) => { - const files = _get(doc, currentColumn.key); - if (!files || files.length === 0) return accumulator; - return [ - ...accumulator, - ...files.map((file, index) => ({ - ...file, - fieldKey: currentColumn.key, - name: - labelColumns.length === 0 - ? file.name - : `${currentColumn.key}/${labelColumns - .map((labelColumn) => { - const value = _get(doc, labelColumn.key); - return value && typeof value === "string" - ? value.replace(/[^a-zA-Z ]/g, "") - : ""; - }) - .join("_")}${ - files.length === 1 ? "" : `_${index}` - }.${file.name.split(".").pop()}`, - })), - ]; + const files = _get(doc, currentColumn.key); + if (!files || files.length === 0) return accumulator; + return [ + ...accumulator, + ...files.map((file, index) => ({ + ...file, + fieldKey: currentColumn.key, + name: + labelColumns.length === 0 + ? file.name + : `${currentColumn.key}/${labelColumns + .map((labelColumn) => { + const value = _get(doc, labelColumn.key); + return value && typeof value === "string" + ? value.replace(/[^a-zA-Z ]/g, "") + : ""; + }) + .join("_")}${ + files.length === 1 ? "" : `_${index}` + }.${file.name.split(".").pop()}`, + })), + ]; }; export default function Export({ query, closeModal }) { - const { tableState } = useRowyContext(); - const snackContext = useContext(SnackContext); + const { tableState } = useRowyContext(); + const snackContext = useContext(SnackContext); - const [columns, setColumns] = useState([]); - const [labelColumnsEnabled, setLabelColumnsEnabled] = useState(false); - const [labelColumns, setLabelColumns] = useState([]); - const [packageName, setPackageName] = useState(tableState?.tablePath); + const [columns, setColumns] = useState([]); + const [labelColumnsEnabled, setLabelColumnsEnabled] = useState(false); + const [labelColumns, setLabelColumns] = useState([]); + const [packageName, setPackageName] = useState(tableState?.tablePath); - const handleClose = () => { - closeModal(); - setColumns([]); - }; + const handleClose = () => { + closeModal(); + setColumns([]); + }; - const handleChange = (setState) => (keys: string[]) => - setState( - keys - .map((key) => _find(tableState!.columns, ["key", key])) - .filter((x) => !!x) - ); + const handleChange = (setState) => (keys: string[]) => + setState( + keys + .map((key) => _find(tableState!.columns, ["key", key])) + .filter((x) => !!x) + ); - const handleDownload = async () => { - handleClose(); - snackContext.open({ - variant: "progress", - message: "Preparing file. Download will start shortly", - }); - let querySnapshot = await query.get(); - let docs = querySnapshot.docs.map((doc) => doc.data()); - const files = docs - .map((doc: any) => - columns.reduce(selectedColumnsFilesReducer(doc, labelColumns), []) - ) - .reduce((acc, row) => [...acc, ...row], []); - var zip = new JSZip(); - let completedCount = 0; - const downloads = files.map((file) => - download(file.downloadURL).then((blob: any) => { - zip.file(file.name, blob, { base64: true }); - completedCount++; - snackContext.open({ - variant: "progress", - message: "Downloading", - }); - snackContext.setProgress({ - value: completedCount, - target: files.length, - }); - }) - ); + const handleDownload = async () => { + handleClose(); + snackContext.open({ + variant: "progress", + message: "Preparing file. Download will start shortly", + }); + let querySnapshot = await query.get(); + let docs = querySnapshot.docs.map((doc) => doc.data()); + const files = docs + .map((doc: any) => + columns.reduce(selectedColumnsFilesReducer(doc, labelColumns), []) + ) + .reduce((acc, row) => [...acc, ...row], []); + var zip = new JSZip(); + let completedCount = 0; + const downloads = files.map((file) => + download(file.downloadURL).then((blob: any) => { + zip.file(file.name, blob, { base64: true }); + completedCount++; + snackContext.open({ + variant: "progress", + message: "Downloading", + }); + snackContext.setProgress({ + value: completedCount, + target: files.length, + }); + }) + ); - await Promise.all(downloads); - zip - .generateAsync({ type: "blob" }) - .then((content) => saveAs(content, `${packageName}.zip`)); - snackContext.open({ - variant: "success", - message: "Download completed successfully", - duration: 2000, - }); - }; - return ( - <> - x.key)} - onChange={handleChange(setColumns)} - options={(typeof tableState!.columns === "object" && - !Array.isArray(tableState!.columns) - ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( - (column: any) => - isString(column?.name) && - isString(column?.key) && - DOWNLOADABLE_COLUMNS.includes(column.type) - ) - : [] - ).map((column: any) => ({ label: column.name, value: column.key }))} - label="Columns to Export" - labelPlural="columns" - TextFieldProps={{ - helperText: "Only File and Image columns are downloadable", - }} - multiple - selectAll - /> + await Promise.all(downloads); + zip + .generateAsync({ type: "blob" }) + .then((content) => saveAs(content, `${packageName}.zip`)); + snackContext.open({ + variant: "success", + message: "Download completed successfully", + duration: 2000, + }); + }; + return ( + <> + x.key)} + onChange={handleChange(setColumns)} + options={(typeof tableState!.columns === "object" && + !Array.isArray(tableState!.columns) + ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( + (column: any) => + isString(column?.name) && + isString(column?.key) && + DOWNLOADABLE_COLUMNS.includes(column.type) + ) + : [] + ).map((column: any) => ({ label: column.name, value: column.key }))} + label="Columns to Export" + labelPlural="columns" + TextFieldProps={{ + helperText: "Only File and Image columns are downloadable", + }} + multiple + selectAll + /> - setPackageName(e.target.value)} - /> + setPackageName(e.target.value)} + /> - setLabelColumnsEnabled(e.target.checked)} - name="labelColumnsEnabled" - /> - } - label="Replace file names with table values" - /> + setLabelColumnsEnabled(e.target.checked)} + name="labelColumnsEnabled" + /> + } + label="Replace file names with table values" + /> - x.key)} - onChange={handleChange(setLabelColumns)} - options={(typeof tableState!.columns === "object" && - !Array.isArray(tableState!.columns) - ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( - (column: any) => - isString(column?.name) && - isString(column?.key) && - LABEL_COLUMNS.includes(column.type) - ) - : [] - ).map((column: any) => ({ label: column.name, value: column.key }))} - label="Column Values to Include in File Names" - labelPlural="columns" - TextFieldProps={{ - autoFocus: true, - helperText: - labelColumns.length === 0 - ? `Use original file name` - : `eg. column/${labelColumns.map((c) => c.key).join("_")}.jpeg`, - }} - multiple - selectAll - /> + x.key)} + onChange={handleChange(setLabelColumns)} + options={(typeof tableState!.columns === "object" && + !Array.isArray(tableState!.columns) + ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( + (column: any) => + isString(column?.name) && + isString(column?.key) && + LABEL_COLUMNS.includes(column.type) + ) + : [] + ).map((column: any) => ({ label: column.name, value: column.key }))} + label="Column Values to Include in File Names" + labelPlural="columns" + TextFieldProps={{ + autoFocus: true, + helperText: + labelColumns.length === 0 + ? `Use original file name` + : `eg. column/${labelColumns.map((c) => c.key).join("_")}.jpeg`, + }} + multiple + selectAll + /> -
    +
    - - + + - - - - ); + + + + ); } diff --git a/src/components/Table/TableHeader/Export/Export.tsx b/src/components/Table/TableHeader/Export/Export.tsx index eaf4e418..78d478fc 100644 --- a/src/components/Table/TableHeader/Export/Export.tsx +++ b/src/components/Table/TableHeader/Export/Export.tsx @@ -17,197 +17,197 @@ import { FieldType } from "constants/fields"; import { getFieldProp } from "components/fields"; const selectedColumnsJsonReducer = (doc: any) => ( - accumulator: any, - currentColumn: any + accumulator: any, + currentColumn: any ) => { - const value = _get(doc, currentColumn.key); - return { - ...accumulator, - [currentColumn.key]: value, - }; + const value = _get(doc, currentColumn.key); + return { + ...accumulator, + [currentColumn.key]: value, + }; }; const selectedColumnsCsvReducer = (doc: any) => ( - accumulator: any, - currentColumn: any + accumulator: any, + currentColumn: any ) => { - const value = _get(doc, currentColumn.key); - const formatter = getFieldProp("csvExportFormatter", currentColumn.type); - if (formatter) { - return { - ...accumulator, - [currentColumn.name]: value ? formatter(value, currentColumn.config) : "", - }; - } - // TODO: move to field csvExportFormatter - switch (currentColumn.type) { - case FieldType.multiSelect: - return { - ...accumulator, - [currentColumn.name]: value ? value.join() : "", - }; - case FieldType.file: - case FieldType.image: - return { - ...accumulator, - [currentColumn.name]: value - ? value - .map((item: { downloadURL: string }) => item.downloadURL) - .join() - : "", - }; - case FieldType.connectTable: - return { - ...accumulator, - [currentColumn.name]: - value && Array.isArray(value) - ? value - .map((item: any) => - currentColumn.config.primaryKeys.reduce( - (labelAccumulator: string, currentKey: any) => - `${labelAccumulator} ${item.snapshot[currentKey]}`, - "" - ) - ) - .join() - : "", - }; - case FieldType.checkbox: - return { - ...accumulator, - [currentColumn.name]: - typeof value === "boolean" ? (value ? "YES" : "NO") : "", - }; - case FieldType.dateTime: - case FieldType.date: - return { - ...accumulator, - [currentColumn.name]: value && value["toDate"] ? value.toDate() : "", - }; - case FieldType.action: - return { - ...accumulator, - [currentColumn.name]: value && value.status ? value.status : "", - }; - default: - return { - ...accumulator, - [currentColumn.name]: value ? value : "", - }; - } + const value = _get(doc, currentColumn.key); + const formatter = getFieldProp("csvExportFormatter", currentColumn.type); + if (formatter) { + return { + ...accumulator, + [currentColumn.name]: value ? formatter(value, currentColumn.config) : "", + }; + } + // TODO: move to field csvExportFormatter + switch (currentColumn.type) { + case FieldType.multiSelect: + return { + ...accumulator, + [currentColumn.name]: value ? value.join() : "", + }; + case FieldType.file: + case FieldType.image: + return { + ...accumulator, + [currentColumn.name]: value + ? value + .map((item: { downloadURL: string }) => item.downloadURL) + .join() + : "", + }; + case FieldType.connectTable: + return { + ...accumulator, + [currentColumn.name]: + value && Array.isArray(value) + ? value + .map((item: any) => + currentColumn.config.primaryKeys.reduce( + (labelAccumulator: string, currentKey: any) => + `${labelAccumulator} ${item.snapshot[currentKey]}`, + "" + ) + ) + .join() + : "", + }; + case FieldType.checkbox: + return { + ...accumulator, + [currentColumn.name]: + typeof value === "boolean" ? (value ? "YES" : "NO") : "", + }; + case FieldType.dateTime: + case FieldType.date: + return { + ...accumulator, + [currentColumn.name]: value && value["toDate"] ? value.toDate() : "", + }; + case FieldType.action: + return { + ...accumulator, + [currentColumn.name]: value && value.status ? value.status : "", + }; + default: + return { + ...accumulator, + [currentColumn.name]: value ? value : "", + }; + } }; export default function Export({ query, closeModal }) { - const { tableState } = useRowyContext(); - const snackContext = useContext(SnackContext); + const { tableState } = useRowyContext(); + const snackContext = useContext(SnackContext); - const [columns, setColumns] = useState([]); - const [exportType, setExportType] = useState<"csv" | "json">("csv"); + const [columns, setColumns] = useState([]); + const [exportType, setExportType] = useState<"csv" | "json">("csv"); - const handleClose = () => { - closeModal(); - setColumns([]); - }; + const handleClose = () => { + closeModal(); + setColumns([]); + }; - const handleChange = (keys: string[]) => - setColumns( - keys - .map((key) => _find(tableState!.columns, ["key", key])) - .filter((x) => !!x) - ); + const handleChange = (keys: string[]) => + setColumns( + keys + .map((key) => _find(tableState!.columns, ["key", key])) + .filter((x) => !!x) + ); - const handleExport = async () => { - handleClose(); - snackContext.open({ - variant: "info", - message: "Preparing file. Download will start shortly", - duration: 5000, - }); - let querySnapshot = await query.get(); - let docs = querySnapshot.docs.map((doc) => ({ - id: doc.ref.id, - ...doc.data(), - })); + const handleExport = async () => { + handleClose(); + snackContext.open({ + variant: "info", + message: "Preparing file. Download will start shortly", + duration: 5000, + }); + let querySnapshot = await query.get(); + let docs = querySnapshot.docs.map((doc) => ({ + id: doc.ref.id, + ...doc.data(), + })); - const fileName = `${tableState?.tablePath!}-${new Date().toISOString()}.${exportType}`; - switch (exportType) { - case "csv": - const csvData = docs.map((doc: any) => - columns.reduce(selectedColumnsCsvReducer(doc), {}) - ); - const csv = json2csv(csvData); - const csvBlob = new Blob([csv], { - type: `text/${exportType};charset=utf-8`, - }); - saveAs(csvBlob, fileName); - break; - case "json": - const jsonData = docs.map((doc: any) => - columns.reduce(selectedColumnsJsonReducer(doc), { id: doc.id }) - ); - const jsonBlob = new Blob([JSON.stringify(jsonData)], { - type: `text/${exportType};charset=utf-8`, - }); - saveAs(jsonBlob, fileName); - break; - default: - break; - } - }; - return ( - <> - x.key)} - onChange={handleChange} - options={(typeof tableState!.columns === "object" && - !Array.isArray(tableState!.columns) - ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( - (column: any) => isString(column?.name) && isString(column?.key) - ) - : [] - ).map((column: any) => ({ label: column.name, value: column.key }))} - label="Columns to Export" - labelPlural="columns" - TextFieldProps={{ - autoFocus: true, - helperText: "Files and images will be added as URLs", - }} - multiple - selectAll - /> + const fileName = `${tableState?.tablePath!}-${new Date().toISOString()}.${exportType}`; + switch (exportType) { + case "csv": + const csvData = docs.map((doc: any) => + columns.reduce(selectedColumnsCsvReducer(doc), {}) + ); + const csv = json2csv(csvData); + const csvBlob = new Blob([csv], { + type: `text/${exportType};charset=utf-8`, + }); + saveAs(csvBlob, fileName); + break; + case "json": + const jsonData = docs.map((doc: any) => + columns.reduce(selectedColumnsJsonReducer(doc), { id: doc.id }) + ); + const jsonBlob = new Blob([JSON.stringify(jsonData)], { + type: `text/${exportType};charset=utf-8`, + }); + saveAs(jsonBlob, fileName); + break; + default: + break; + } + }; + return ( + <> + x.key)} + onChange={handleChange} + options={(typeof tableState!.columns === "object" && + !Array.isArray(tableState!.columns) + ? _sortBy(Object.values(tableState!.columns), ["index"]).filter( + (column: any) => isString(column?.name) && isString(column?.key) + ) + : [] + ).map((column: any) => ({ label: column.name, value: column.key }))} + label="Columns to Export" + labelPlural="columns" + TextFieldProps={{ + autoFocus: true, + helperText: "Files and images will be added as URLs", + }} + multiple + selectAll + /> - { - if (v) { - setExportType(v as "csv" | "json"); - } - }} - multiple={false} - searchable={false} - clearable={false} - TextFieldProps={{ helperText: "Encoding: UTF-8" }} - /> + { + if (v) { + setExportType(v as "csv" | "json"); + } + }} + multiple={false} + searchable={false} + clearable={false} + TextFieldProps={{ helperText: "Encoding: UTF-8" }} + /> -
    +
    - - + + - - - - ); + + + + ); } diff --git a/src/components/Table/TableHeader/Export/index.tsx b/src/components/Table/TableHeader/Export/index.tsx index baea11b6..12c8cdef 100644 --- a/src/components/Table/TableHeader/Export/index.tsx +++ b/src/components/Table/TableHeader/Export/index.tsx @@ -18,128 +18,128 @@ import { db } from "../../../../firebase"; import { isCollectionGroup } from "utils/fns"; const useStyles = makeStyles((theme) => - createStyles({ - paper: { - [theme.breakpoints.up("sm")]: { - maxWidth: 440, - height: 610, - }, - }, + createStyles({ + paper: { + [theme.breakpoints.up("sm")]: { + maxWidth: 440, + height: 610, + }, + }, - tabs: { - marginLeft: "calc(var(--dialog-spacing) * -1)", - marginRight: "calc(var(--dialog-spacing) * -1)", - marginTop: theme.spacing(1), - }, - tab: { minWidth: 0 }, - divider: { - margin: "-1px calc(var(--dialog-spacing) * -1) 0", - }, + tabs: { + marginLeft: "calc(var(--dialog-spacing) * -1)", + marginRight: "calc(var(--dialog-spacing) * -1)", + marginTop: theme.spacing(1), + }, + tab: { minWidth: 0 }, + divider: { + margin: "-1px calc(var(--dialog-spacing) * -1) 0", + }, - tabPanel: { - marginTop: "var(--dialog-contents-spacing)", - marginBottom: "calc(var(--dialog-spacing) * -1)", - padding: 0, + tabPanel: { + marginTop: "var(--dialog-contents-spacing)", + marginBottom: "calc(var(--dialog-spacing) * -1)", + padding: 0, - flexGrow: 1, - display: "flex", - flexDirection: "column", - height: - "calc(100% - var(--dialog-contents-spacing) + var(--dialog-spacing))", + flexGrow: 1, + display: "flex", + flexDirection: "column", + height: + "calc(100% - var(--dialog-contents-spacing) + var(--dialog-spacing))", - "& > * + *": { marginTop: "var(--dialog-contents-spacing)" }, - "&[hidden]": { display: "none" }, - }, - }) + "& > * + *": { marginTop: "var(--dialog-contents-spacing)" }, + "&[hidden]": { display: "none" }, + }, + }) ); export default function Export() { - const classes = useStyles(); - const [open, setOpen] = useState(false); - const [mode, setMode] = useState<"Export" | "Download">("Export"); - const { tableState } = useRowyContext(); + const classes = useStyles(); + const [open, setOpen] = useState(false); + const [mode, setMode] = useState<"Export" | "Download">("Export"); + const { tableState } = useRowyContext(); - const query: any = useMemo(() => { - let _query = isCollectionGroup() - ? db.collectionGroup(tableState?.tablePath!) - : db.collection(tableState?.tablePath!); - // add filters - tableState?.filters.forEach((filter) => { - _query = _query.where( - filter.key, - filter.operator as firebase.default.firestore.WhereFilterOp, - filter.value - ); - }); - // optional order results - if (tableState?.orderBy) { - tableState?.orderBy?.forEach((orderBy) => { - _query = _query.orderBy(orderBy.key, orderBy.direction); - }); - } - return _query.limit(10000); - }, [tableState?.tablePath, tableState?.orderBy, tableState?.filters]); + const query: any = useMemo(() => { + let _query = isCollectionGroup() + ? db.collectionGroup(tableState?.tablePath!) + : db.collection(tableState?.tablePath!); + // add filters + tableState?.filters.forEach((filter) => { + _query = _query.where( + filter.key, + filter.operator as firebase.default.firestore.WhereFilterOp, + filter.value + ); + }); + // optional order results + if (tableState?.orderBy) { + tableState?.orderBy?.forEach((orderBy) => { + _query = _query.orderBy(orderBy.key, orderBy.direction); + }); + } + return _query.limit(10000); + }, [tableState?.tablePath, tableState?.orderBy, tableState?.filters]); - const handleClose = () => { - setOpen(false); - setMode("Export"); - }; + const handleClose = () => { + setOpen(false); + setMode("Export"); + }; - return ( - <> - setOpen(true)} - icon={} - /> + return ( + <> + setOpen(true)} + icon={} + /> - {open && ( - - - - {(tableState?.filters && tableState?.filters.length !== 0) || - (tableState?.orderBy && tableState?.orderBy.length !== 0) - ? "The filters and sorting applied to the table will be used in the export." - : "No filters or sorting will be applied on the exported data."} - + {open && ( + + + + {(tableState?.filters && tableState?.filters.length !== 0) || + (tableState?.orderBy && tableState?.orderBy.length !== 0) + ? "The filters and sorting applied to the table will be used in the export." + : "No filters or sorting will be applied on the exported data."} + - setMode(v)} - indicatorColor="primary" - textColor="primary" - variant="fullWidth" - aria-label="Modal tabs" - action={(actions) => - setTimeout(() => actions?.updateIndicator(), 200) - } - > - - - - - - } - > - - - + setMode(v)} + indicatorColor="primary" + textColor="primary" + variant="fullWidth" + aria-label="Modal tabs" + action={(actions) => + setTimeout(() => actions?.updateIndicator(), 200) + } + > + + + + + + } + > + + + - - - - - - )} - - ); + + + + + + )} + + ); } diff --git a/src/components/Table/TableHeader/Extensions/ExtensionList.tsx b/src/components/Table/TableHeader/Extensions/ExtensionList.tsx index b2f0d6af..08c52d37 100644 --- a/src/components/Table/TableHeader/Extensions/ExtensionList.tsx +++ b/src/components/Table/TableHeader/Extensions/ExtensionList.tsx @@ -2,19 +2,19 @@ import { useState, useRef } from "react"; import { format, formatRelative } from "date-fns"; import { - Stack, - ButtonBase, - List, - ListItem, - ListItemText, - Avatar, - Button, - IconButton, - Menu, - MenuItem, - Switch, - Tooltip, - Typography, + Stack, + ButtonBase, + List, + ListItem, + ListItemText, + Avatar, + Button, + IconButton, + Menu, + MenuItem, + Switch, + Tooltip, + Typography, } from "@material-ui/core"; import AddIcon from "@material-ui/icons/Add"; import ExtensionIcon from "assets/icons/Extension"; @@ -26,195 +26,195 @@ import EmptyState from "components/EmptyState"; import { extensionTypes, IExtension, IExtensionType } from "./utils"; export interface IExtensionListProps { - extensions: IExtension[]; - handleAddExtension: (type: IExtensionType) => void; - handleUpdateActive: (index: number, active: boolean) => void; - handleDuplicate: (index: number) => void; - handleEdit: (index: number) => void; - handleDelete: (index: number) => void; + extensions: IExtension[]; + handleAddExtension: (type: IExtensionType) => void; + handleUpdateActive: (index: number, active: boolean) => void; + handleDuplicate: (index: number) => void; + handleEdit: (index: number) => void; + handleDelete: (index: number) => void; } export default function ExtensionList({ - extensions, - handleAddExtension, - handleUpdateActive, - handleDuplicate, - handleEdit, - handleDelete, + extensions, + handleAddExtension, + handleUpdateActive, + handleDuplicate, + handleEdit, + handleDelete, }: IExtensionListProps) { - const [anchorEl, setAnchorEl] = useState(null); - const addButtonRef = useRef(null); + const [anchorEl, setAnchorEl] = useState(null); + const addButtonRef = useRef(null); - const activeExtensionCount = extensions.filter( - (extension) => extension.active - ).length; + const activeExtensionCount = extensions.filter( + (extension) => extension.active + ).length; - const handleAddButton = () => { - setAnchorEl(addButtonRef.current); - }; + const handleAddButton = () => { + setAnchorEl(addButtonRef.current); + }; - const handleChooseAddType = (type: IExtensionType) => { - handleClose(); - handleAddExtension(type); - }; + const handleChooseAddType = (type: IExtensionType) => { + handleClose(); + handleAddExtension(type); + }; - const handleClose = () => { - setAnchorEl(null); - }; + const handleClose = () => { + setAnchorEl(null); + }; - return ( - <> - - - Extensions ({activeExtensionCount} / {extensions.length}) - + return ( + <> + + + Extensions ({activeExtensionCount} / {extensions.length}) + - - - {extensionTypes.map((type) => ( - { - handleChooseAddType(type); - }} - > - {type} - - ))} - - + + + {extensionTypes.map((type) => ( + { + handleChooseAddType(type); + }} + > + {type} + + ))} + + - {extensions.length === 0 ? ( - - - - ) : ( - - {extensions.map((extensionObject, index) => ( - - } - secondaryAction={ - - - - - handleUpdateActive(index, !extensionObject.active) - } - inputProps={{ "aria-label": "Activate" }} - sx={{ mr: 1 }} - /> - + {extensions.length === 0 ? ( + + + + ) : ( + + {extensions.map((extensionObject, index) => ( + + } + secondaryAction={ + + + + + handleUpdateActive(index, !extensionObject.active) + } + inputProps={{ "aria-label": "Activate" }} + sx={{ mr: 1 }} + /> + - - handleDuplicate(index)} - > - - - - - handleEdit(index)} - > - - - - - handleDelete(index)} - sx={{ "&&": { mr: -1.5 } }} - > - - - - + + handleDuplicate(index)} + > + + + + + handleEdit(index)} + > + + + + + handleDelete(index)} + sx={{ "&&": { mr: -1.5 } }} + > + + + + - - Last updated by {extensionObject.lastEditor.displayName} -
    - on{" "} - {format(extensionObject.lastEditor.lastUpdate, "PPPP")} -
    - at{" "} - {format(extensionObject.lastEditor.lastUpdate, "pppp")} - - } - > - - - {formatRelative( - extensionObject.lastEditor.lastUpdate, - new Date() - )} - - - -
    -
    - } - /> - ))} -
    - )} - - ); + + Last updated by {extensionObject.lastEditor.displayName} +
    + on{" "} + {format(extensionObject.lastEditor.lastUpdate, "PPPP")} +
    + at{" "} + {format(extensionObject.lastEditor.lastUpdate, "pppp")} + + } + > + + + {formatRelative( + extensionObject.lastEditor.lastUpdate, + new Date() + )} + + + +
    + + } + /> + ))} + + )} + + ); } diff --git a/src/components/Table/TableHeader/Extensions/ExtensionMigration.tsx b/src/components/Table/TableHeader/Extensions/ExtensionMigration.tsx index b6d87132..777f2790 100644 --- a/src/components/Table/TableHeader/Extensions/ExtensionMigration.tsx +++ b/src/components/Table/TableHeader/Extensions/ExtensionMigration.tsx @@ -14,143 +14,143 @@ import { sparkToExtensionObjects } from "./utils"; import WIKI_LINKS from "constants/wikiLinks"; export interface IExtensionMigrationProps { - handleClose: () => void; - handleUpgradeComplete: () => void; + handleClose: () => void; + handleUpgradeComplete: () => void; } export default function ExtensionMigration({ - handleClose, - handleUpgradeComplete, + handleClose, + handleUpgradeComplete, }: IExtensionMigrationProps) { - const appContext = useAppContext(); - const { tableState, tableActions } = useRowyContext(); + const appContext = useAppContext(); + const { tableState, tableActions } = useRowyContext(); - const [isSaved, setIsSaved] = useState(false); - const [isUpgrading, setIsUpgrading] = useState(false); + const [isSaved, setIsSaved] = useState(false); + const [isUpgrading, setIsUpgrading] = useState(false); - const currentEditor = () => ({ - displayName: appContext?.currentUser?.displayName ?? "Unknown user", - photoURL: appContext?.currentUser?.photoURL ?? "", - lastUpdate: Date.now(), - }); + const currentEditor = () => ({ + displayName: appContext?.currentUser?.displayName ?? "Unknown user", + photoURL: appContext?.currentUser?.photoURL ?? "", + lastUpdate: Date.now(), + }); - const downloadSparkFile = () => { - const tablePathTokens = - tableState?.tablePath?.split("/").filter(function (_, i) { - // replace IDs with dash that appears at even indexes - return i % 2 === 0; - }) ?? []; - const tablePath = tablePathTokens.join("-"); + const downloadSparkFile = () => { + const tablePathTokens = + tableState?.tablePath?.split("/").filter(function (_, i) { + // replace IDs with dash that appears at even indexes + return i % 2 === 0; + }) ?? []; + const tablePath = tablePathTokens.join("-"); - // https://medium.com/front-end-weekly/text-file-download-in-react-a8b28a580c0d - const element = document.createElement("a"); - const file = new Blob([tableState?.config.sparks ?? ""], { - type: "text/plain;charset=utf-8", - }); - element.href = URL.createObjectURL(file); - element.download = `sparks-${tablePath}.ts`; - document.body.appendChild(element); - element.click(); - setIsSaved(true); - }; + // https://medium.com/front-end-weekly/text-file-download-in-react-a8b28a580c0d + const element = document.createElement("a"); + const file = new Blob([tableState?.config.sparks ?? ""], { + type: "text/plain;charset=utf-8", + }); + element.href = URL.createObjectURL(file); + element.download = `sparks-${tablePath}.ts`; + document.body.appendChild(element); + element.click(); + setIsSaved(true); + }; - const upgradeToExtensions = () => { - setIsUpgrading(true); - const extensionObjects = sparkToExtensionObjects( - tableState?.config.sparks ?? "[]", - currentEditor() - ); - console.log(extensionObjects); - tableActions?.table.updateConfig("extensionObjects", extensionObjects); - tableActions?.table.updateConfig( - "sparks", - firebase.firestore.FieldValue.delete() - ); - setTimeout(handleUpgradeComplete, 500); - }; + const upgradeToExtensions = () => { + setIsUpgrading(true); + const extensionObjects = sparkToExtensionObjects( + tableState?.config.sparks ?? "[]", + currentEditor() + ); + console.log(extensionObjects); + tableActions?.table.updateConfig("extensionObjects", extensionObjects); + tableActions?.table.updateConfig( + "sparks", + firebase.firestore.FieldValue.delete() + ); + setTimeout(handleUpgradeComplete, 500); + }; - return ( - -
    - - It looks like you have Sparks configured for this table. - - - Sparks have been revamped to Extensions, with a brand new UI. Your - existing Sparks are not compatible with this change, but you can - migrate your Sparks to Extensions. - -
    + return ( + +
    + + It looks like you have Sparks configured for this table. + + + Sparks have been revamped to Extensions, with a brand new UI. Your + existing Sparks are not compatible with this change, but you can + migrate your Sparks to Extensions. + +
    -
    - - 1. Back Up Existing Sparks - - - Back up your existing Sparks to a .ts file. - - -
    +
    + + 1. Back Up Existing Sparks + + + Back up your existing Sparks to a .ts file. + + +
    -
    - - 2. Migrate Sparks to Extensions - +
    + + 2. Migrate Sparks to Extensions + - - After the upgrade, Sparks will be removed from this table. You may - need to make manual changes to your Extensions code. - + + After the upgrade, Sparks will be removed from this table. You may + need to make manual changes to your Extensions code. + - - Read the Extensions documentation - - + + Read the Extensions documentation + + - } - style={{ width: "100%" }} - > - Migrate to Extensions - -
    - - } - /> - ); + } + style={{ width: "100%" }} + > + Migrate to Extensions + +
    + + } + /> + ); } diff --git a/src/components/Table/TableHeader/Extensions/ExtensionModal.tsx b/src/components/Table/TableHeader/Extensions/ExtensionModal.tsx index 263511a4..c9da2b9a 100644 --- a/src/components/Table/TableHeader/Extensions/ExtensionModal.tsx +++ b/src/components/Table/TableHeader/Extensions/ExtensionModal.tsx @@ -3,21 +3,21 @@ import _isEqual from "lodash/isEqual"; import useStateRef from "react-usestateref"; import { - styled, - Button, - Checkbox, - Divider, - FormControl, - FormControlLabel, - FormGroup, - FormLabel, - Grid, - IconButton, - Switch, - Stack, - Tab, - TextField, - Typography, + styled, + Button, + Checkbox, + Divider, + FormControl, + FormControlLabel, + FormGroup, + FormLabel, + Grid, + IconButton, + Switch, + Stack, + Tab, + TextField, + Typography, } from "@material-ui/core"; import TabContext from "@material-ui/lab/TabContext"; import TabList from "@material-ui/lab/TabList"; @@ -36,504 +36,504 @@ import { IExtension, triggerTypes } from "./utils"; import WIKI_LINKS from "constants/wikiLinks"; const additionalVariables = [ - { - key: "change", - description: - "you can pass in field name to change.before.get() or change.after.get() to get changes", - }, - { - key: "triggerType", - description: "triggerType indicates the type of the extension invocation", - }, - { - key: "fieldTypes", - description: - "fieldTypes is a map of all fields and its corresponding Rowy column type", - }, - { - key: "extensionConfig", - description: "the configuration object of this extension", - }, + { + key: "change", + description: + "you can pass in field name to change.before.get() or change.after.get() to get changes", + }, + { + key: "triggerType", + description: "triggerType indicates the type of the extension invocation", + }, + { + key: "fieldTypes", + description: + "fieldTypes is a map of all fields and its corresponding Rowy column type", + }, + { + key: "extensionConfig", + description: "the configuration object of this extension", + }, ]; const StyledTabPanel = styled(TabPanel)({ - flexGrow: 1, + flexGrow: 1, - overflowY: "auto", - margin: "0 calc(var(--dialog-spacing) * -1) 0 !important", - padding: "var(--dialog-spacing) var(--dialog-spacing) 0", + overflowY: "auto", + margin: "0 calc(var(--dialog-spacing) * -1) 0 !important", + padding: "var(--dialog-spacing) var(--dialog-spacing) 0", - "&[hidden]": { display: "none" }, + "&[hidden]": { display: "none" }, - display: "flex", - flexDirection: "column", + display: "flex", + flexDirection: "column", }); export interface IExtensionModalProps { - handleClose: IModalProps["onClose"]; - handleAdd: (extensionObject: IExtension) => void; - handleUpdate: (extensionObject: IExtension) => void; - mode: "add" | "update"; - extensionObject: IExtension; + handleClose: IModalProps["onClose"]; + handleAdd: (extensionObject: IExtension) => void; + handleUpdate: (extensionObject: IExtension) => void; + mode: "add" | "update"; + extensionObject: IExtension; } export default function ExtensionModal({ - handleClose, - handleAdd, - handleUpdate, - mode, - extensionObject: initialObject, + handleClose, + handleAdd, + handleUpdate, + mode, + extensionObject: initialObject, }: IExtensionModalProps) { - const { requestConfirmation } = useConfirmation(); - const [extensionObject, setExtensionObject] = useState( - initialObject - ); - const [tab, setTab] = useState("triggersRequirements"); - const [validation, setValidation, validationRef] = useStateRef({ - condition: true, - extensionBody: true, - }); - const [, setConditionEditorActive, conditionEditorActiveRef] = useStateRef( - false - ); - const [, setBodyEditorActive, bodyEditorActiveRef] = useStateRef(false); - const { tableState } = useRowyContext(); - const columns = Object.keys(tableState?.columns ?? {}); - const edited = !_isEqual(initialObject, extensionObject); + const { requestConfirmation } = useConfirmation(); + const [extensionObject, setExtensionObject] = useState( + initialObject + ); + const [tab, setTab] = useState("triggersRequirements"); + const [validation, setValidation, validationRef] = useStateRef({ + condition: true, + extensionBody: true, + }); + const [, setConditionEditorActive, conditionEditorActiveRef] = useStateRef( + false + ); + const [, setBodyEditorActive, bodyEditorActiveRef] = useStateRef(false); + const { tableState } = useRowyContext(); + const columns = Object.keys(tableState?.columns ?? {}); + const edited = !_isEqual(initialObject, extensionObject); - const handleAddOrUpdate = () => { - switch (mode) { - case "add": - handleAdd(extensionObject); - return; - case "update": - handleUpdate(extensionObject); - return; - } - }; + const handleAddOrUpdate = () => { + switch (mode) { + case "add": + handleAdd(extensionObject); + return; + case "update": + handleUpdate(extensionObject); + return; + } + }; - return ( - - - - { - setExtensionObject({ - ...extensionObject, - name: event.target.value, - }); - }} - /> - + return ( + + + + { + setExtensionObject({ + ...extensionObject, + name: event.target.value, + }); + }} + /> + - - - setExtensionObject({ - ...extensionObject, - active: e.target.checked, - }) - } - size="medium" - /> - } - label={`Extension is ${ - !extensionObject.active ? "de" : "" - }activated`} - /> - + + + setExtensionObject({ + ...extensionObject, + active: e.target.checked, + }) + } + size="medium" + /> + } + label={`Extension is ${ + !extensionObject.active ? "de" : "" + }activated`} + /> + - - - - + + + + - - setTab(val)} - variant="fullWidth" - centered - style={{ - marginTop: 0, - marginLeft: "calc(var(--dialog-spacing) * -1)", - marginRight: "calc(var(--dialog-spacing) * -1)", - }} - > - - - - + + setTab(val)} + variant="fullWidth" + centered + style={{ + marginTop: 0, + marginLeft: "calc(var(--dialog-spacing) * -1)", + marginRight: "calc(var(--dialog-spacing) * -1)", + }} + > + + + + - - - - - - Triggers - - - Select a trigger that runs your extension code. Selected - actions on any cells will trigger the extension. - + + + + + + Triggers + + + Select a trigger that runs your extension code. Selected + actions on any cells will trigger the extension. + - - {triggerTypes.map((trigger) => ( - { - if ( - extensionObject.triggers.includes(trigger) - ) { - setExtensionObject({ - ...extensionObject, - triggers: extensionObject.triggers.filter( - (t) => t !== trigger - ), - }); - } else { - setExtensionObject({ - ...extensionObject, - triggers: [ - ...extensionObject.triggers, - trigger, - ], - }); - } - }} - /> - } - /> - ))} - - - + + {triggerTypes.map((trigger) => ( + { + if ( + extensionObject.triggers.includes(trigger) + ) { + setExtensionObject({ + ...extensionObject, + triggers: extensionObject.triggers.filter( + (t) => t !== trigger + ), + }); + } else { + setExtensionObject({ + ...extensionObject, + triggers: [ + ...extensionObject.triggers, + trigger, + ], + }); + } + }} + /> + } + /> + ))} + + + - - - - Required Fields (optional) - - - Optionally, select the fields that are required for the - extension to be triggered for a row. - + + + + Required Fields (optional) + + + Optionally, select the fields that are required for the + extension to be triggered for a row. + - *": { flexShrink: 0 }, - }} - > - {columns.sort().map((field) => ( - { - if ( - extensionObject.requiredFields.includes(field) - ) { - setExtensionObject({ - ...extensionObject, - requiredFields: extensionObject.requiredFields.filter( - (t) => t !== field - ), - }); - } else { - setExtensionObject({ - ...extensionObject, - requiredFields: [ - ...extensionObject.requiredFields, - field, - ], - }); - } - }} - /> - } - /> - ))} + *": { flexShrink: 0 }, + }} + > + {columns.sort().map((field) => ( + { + if ( + extensionObject.requiredFields.includes(field) + ) { + setExtensionObject({ + ...extensionObject, + requiredFields: extensionObject.requiredFields.filter( + (t) => t !== field + ), + }); + } else { + setExtensionObject({ + ...extensionObject, + requiredFields: [ + ...extensionObject.requiredFields, + field, + ], + }); + } + }} + /> + } + /> + ))} - {extensionObject.requiredFields.map((trigger, index) => { - const isRowyColumn = columns.includes(trigger); - if (isRowyColumn) { - return null; - } + {extensionObject.requiredFields.map((trigger, index) => { + const isRowyColumn = columns.includes(trigger); + if (isRowyColumn) { + return null; + } - return ( - - { - setExtensionObject({ - ...extensionObject, - requiredFields: extensionObject.requiredFields.filter( - (t) => t !== trigger - ), - }); - }} - > - - - { - setExtensionObject({ - ...extensionObject, - requiredFields: extensionObject.requiredFields.map( - (value, i) => - i === index ? event.target.value : value - ), - }); - }} - /> - - ); - })} + return ( + + { + setExtensionObject({ + ...extensionObject, + requiredFields: extensionObject.requiredFields.filter( + (t) => t !== trigger + ), + }); + }} + > + + + { + setExtensionObject({ + ...extensionObject, + requiredFields: extensionObject.requiredFields.map( + (value, i) => + i === index ? event.target.value : value + ), + }); + }} + /> + + ); + })} - - - - - - - + + + + + + + -
    - - Conditions - +
    + + Conditions + - { - setExtensionObject({ - ...extensionObject, - conditions: newValue, - }); - }} - onValideStatusUpdate={({ isValid }) => { - if (!conditionEditorActiveRef.current) { - return; - } - setValidation({ - ...validationRef.current, - condition: isValid, - }); - console.log(validationRef.current); - }} - diagnosticsOptions={{ - noSemanticValidation: false, - noSyntaxValidation: false, - noSuggestionDiagnostics: true, - }} - onMount={() => { - setConditionEditorActive(true); - }} - onUnmount={() => { - setConditionEditorActive(false); - }} - /> -
    - - + { + setExtensionObject({ + ...extensionObject, + conditions: newValue, + }); + }} + onValideStatusUpdate={({ isValid }) => { + if (!conditionEditorActiveRef.current) { + return; + } + setValidation({ + ...validationRef.current, + condition: isValid, + }); + console.log(validationRef.current); + }} + diagnosticsOptions={{ + noSemanticValidation: false, + noSyntaxValidation: false, + noSuggestionDiagnostics: true, + }} + onMount={() => { + setConditionEditorActive(true); + }} + onUnmount={() => { + setConditionEditorActive(false); + }} + /> +
    + +
    - -
    - - Extension Body - + +
    + + Extension Body + - { - setExtensionObject({ - ...extensionObject, - extensionBody: newValue, - }); - }} - onValidStatusUpdate={({ isValid }) => { - if (!bodyEditorActiveRef.current) { - return; - } - setValidation({ - ...validationRef.current, - extensionBody: isValid, - }); - console.log(validationRef.current); - }} - diagnosticsOptions={{ - noSemanticValidation: false, - noSyntaxValidation: false, - noSuggestionDiagnostics: true, - }} - onMount={() => { - setBodyEditorActive(true); - }} - onUnmount={() => { - setBodyEditorActive(false); - }} - /> -
    - -
    - - - } - actions={{ - primary: { - children: mode === "add" ? "Add" : "Update", - disabled: !edited || !extensionObject.name.length, - onClick: () => { - let warningMessage; - if (!validation.condition && !validation.extensionBody) { - warningMessage = "Condition and extension body are not valid"; - } else if (!validation.condition) { - warningMessage = "Condition is not valid"; - } else if (!validation.extensionBody) { - warningMessage = "Extension body is not valid"; - } + { + setExtensionObject({ + ...extensionObject, + extensionBody: newValue, + }); + }} + onValidStatusUpdate={({ isValid }) => { + if (!bodyEditorActiveRef.current) { + return; + } + setValidation({ + ...validationRef.current, + extensionBody: isValid, + }); + console.log(validationRef.current); + }} + diagnosticsOptions={{ + noSemanticValidation: false, + noSyntaxValidation: false, + noSuggestionDiagnostics: true, + }} + onMount={() => { + setBodyEditorActive(true); + }} + onUnmount={() => { + setBodyEditorActive(false); + }} + /> +
    + +
    +
    + + } + actions={{ + primary: { + children: mode === "add" ? "Add" : "Update", + disabled: !edited || !extensionObject.name.length, + onClick: () => { + let warningMessage; + if (!validation.condition && !validation.extensionBody) { + warningMessage = "Condition and extension body are not valid"; + } else if (!validation.condition) { + warningMessage = "Condition is not valid"; + } else if (!validation.extensionBody) { + warningMessage = "Extension body is not valid"; + } - if (warningMessage) { - requestConfirmation({ - title: "Validation Failed", - body: `${warningMessage}. Continue?`, - confirm: "Yes, I know what I’m doing", - cancel: "No, I’ll fix the errors", - handleConfirm: handleAddOrUpdate, - }); - } else { - handleAddOrUpdate(); - } - }, - }, - }} - /> - ); + if (warningMessage) { + requestConfirmation({ + title: "Validation Failed", + body: `${warningMessage}. Continue?`, + confirm: "Yes, I know what I’m doing", + cancel: "No, I’ll fix the errors", + handleConfirm: handleAddOrUpdate, + }); + } else { + handleAddOrUpdate(); + } + }, + }, + }} + /> + ); } diff --git a/src/components/Table/TableHeader/Extensions/index.tsx b/src/components/Table/TableHeader/Extensions/index.tsx index 30e7c7dc..55c8d27e 100644 --- a/src/components/Table/TableHeader/Extensions/index.tsx +++ b/src/components/Table/TableHeader/Extensions/index.tsx @@ -18,274 +18,274 @@ import { useSnackContext } from "contexts/SnackContext"; import { useSnackLogContext } from "contexts/SnackLogContext"; import { - serialiseExtension, - emptyExtensionObject, - IExtension, - IExtensionType, + serialiseExtension, + emptyExtensionObject, + IExtension, + IExtensionType, } from "./utils"; import WIKI_LINKS from "constants/wikiLinks"; export default function ExtensionsEditor() { - const snack = useSnackContext(); - const { tableState, tableActions } = useRowyContext(); - const appContext = useAppContext(); - const { requestConfirmation } = useConfirmation(); - const currentextensionObjects = (tableState?.config.extensionObjects ?? - []) as IExtension[]; - const [localExtensionsObjects, setLocalExtensionsObjects] = useState( - currentextensionObjects - ); - const [openExtensionList, setOpenExtensionList] = useState(false); - const [openMigrationGuide, setOpenMigrationGuide] = useState(false); - const [extensionModal, setExtensionModal] = useState<{ - mode: "add" | "update"; - extensionObject: IExtension; - index?: number; - } | null>(null); - const snackLogContext = useSnackLogContext(); - const edited = !_isEqual(currentextensionObjects, localExtensionsObjects); + const snack = useSnackContext(); + const { tableState, tableActions } = useRowyContext(); + const appContext = useAppContext(); + const { requestConfirmation } = useConfirmation(); + const currentextensionObjects = (tableState?.config.extensionObjects ?? + []) as IExtension[]; + const [localExtensionsObjects, setLocalExtensionsObjects] = useState( + currentextensionObjects + ); + const [openExtensionList, setOpenExtensionList] = useState(false); + const [openMigrationGuide, setOpenMigrationGuide] = useState(false); + const [extensionModal, setExtensionModal] = useState<{ + mode: "add" | "update"; + extensionObject: IExtension; + index?: number; + } | null>(null); + const snackLogContext = useSnackLogContext(); + const edited = !_isEqual(currentextensionObjects, localExtensionsObjects); - const tablePathTokens = - tableState?.tablePath?.split("/").filter(function (_, i) { - // replace IDs with dash that appears at even indexes - return i % 2 === 0; - }) ?? []; + const tablePathTokens = + tableState?.tablePath?.split("/").filter(function (_, i) { + // replace IDs with dash that appears at even indexes + return i % 2 === 0; + }) ?? []; - const handleOpen = () => { - if (tableState?.config.sparks) { - // migration is required - console.log("Extension migration required."); - setOpenMigrationGuide(true); - } else { - setOpenExtensionList(true); - } - }; + const handleOpen = () => { + if (tableState?.config.sparks) { + // migration is required + console.log("Extension migration required."); + setOpenMigrationGuide(true); + } else { + setOpenExtensionList(true); + } + }; - const handleClose = () => { - if (edited) { - requestConfirmation({ - title: "Discard Changes", - body: "You will lose changes you have made to extensions", - confirm: "Discard", - handleConfirm: () => { - setLocalExtensionsObjects(currentextensionObjects); - setOpenExtensionList(false); - }, - }); - } else { - setOpenExtensionList(false); - } - }; + const handleClose = () => { + if (edited) { + requestConfirmation({ + title: "Discard Changes", + body: "You will lose changes you have made to extensions", + confirm: "Discard", + handleConfirm: () => { + setLocalExtensionsObjects(currentextensionObjects); + setOpenExtensionList(false); + }, + }); + } else { + setOpenExtensionList(false); + } + }; - const handleSaveExtensions = () => { - tableActions?.table.updateConfig( - "extensionObjects", - localExtensionsObjects - ); - setOpenExtensionList(false); - }; + const handleSaveExtensions = () => { + tableActions?.table.updateConfig( + "extensionObjects", + localExtensionsObjects + ); + setOpenExtensionList(false); + }; - const handleSaveDeploy = async () => { - handleSaveExtensions(); + const handleSaveDeploy = async () => { + handleSaveExtensions(); - // compile extension objects into ft-build readable extension string - const serialisedExtension = serialiseExtension(localExtensionsObjects); - tableActions?.table.updateConfig("extensions", serialisedExtension); + // compile extension objects into ft-build readable extension string + const serialisedExtension = serialiseExtension(localExtensionsObjects); + tableActions?.table.updateConfig("extensions", serialisedExtension); - const settingsDoc = await db.doc("/_rowy_/settings").get(); - const buildUrl = settingsDoc.get("buildUrl"); - if (!buildUrl) { - snack.open({ - message: "Cloud Run Trigger URL not set.", - variant: "error", - action: ( - - ), - }); - } + const settingsDoc = await db.doc("/_rowy_/settings").get(); + const buildUrl = settingsDoc.get("buildUrl"); + if (!buildUrl) { + snack.open({ + message: "Cloud Run Trigger URL not set.", + variant: "error", + action: ( + + ), + }); + } - // request GCP build - const userTokenInfo = await appContext?.currentUser?.getIdTokenResult(); - const userToken = userTokenInfo?.token; - try { - snackLogContext.requestSnackLog(); - const response = await fetch(buildUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - configPath: tableState?.config.tableConfig.path, - token: userToken, - }), - }); - const data = await response.json(); - } catch (e) { - console.error(e); - } - }; + // request GCP build + const userTokenInfo = await appContext?.currentUser?.getIdTokenResult(); + const userToken = userTokenInfo?.token; + try { + snackLogContext.requestSnackLog(); + const response = await fetch(buildUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + configPath: tableState?.config.tableConfig.path, + token: userToken, + }), + }); + const data = await response.json(); + } catch (e) { + console.error(e); + } + }; - const handleAddExtension = (extensionObject: IExtension) => { - setLocalExtensionsObjects([...localExtensionsObjects, extensionObject]); - setExtensionModal(null); - }; + const handleAddExtension = (extensionObject: IExtension) => { + setLocalExtensionsObjects([...localExtensionsObjects, extensionObject]); + setExtensionModal(null); + }; - const handleUpdateExtension = (extensionObject: IExtension) => { - setLocalExtensionsObjects( - localExtensionsObjects.map((extension, index) => { - if (index === extensionModal?.index) { - return { - ...extensionObject, - lastEditor: currentEditor(), - }; - } else { - return extension; - } - }) - ); - setExtensionModal(null); - }; + const handleUpdateExtension = (extensionObject: IExtension) => { + setLocalExtensionsObjects( + localExtensionsObjects.map((extension, index) => { + if (index === extensionModal?.index) { + return { + ...extensionObject, + lastEditor: currentEditor(), + }; + } else { + return extension; + } + }) + ); + setExtensionModal(null); + }; - const handleUpdateActive = (index: number, active: boolean) => { - setLocalExtensionsObjects( - localExtensionsObjects.map((extensionObject, i) => { - if (i === index) { - return { - ...extensionObject, - active, - lastEditor: currentEditor(), - }; - } else { - return extensionObject; - } - }) - ); - }; + const handleUpdateActive = (index: number, active: boolean) => { + setLocalExtensionsObjects( + localExtensionsObjects.map((extensionObject, i) => { + if (i === index) { + return { + ...extensionObject, + active, + lastEditor: currentEditor(), + }; + } else { + return extensionObject; + } + }) + ); + }; - const handleDuplicate = (index: number) => { - setLocalExtensionsObjects([ - ...localExtensionsObjects, - { - ...localExtensionsObjects[index], - name: `${localExtensionsObjects[index].name} (duplicate)`, - active: false, - lastEditor: currentEditor(), - }, - ]); - }; + const handleDuplicate = (index: number) => { + setLocalExtensionsObjects([ + ...localExtensionsObjects, + { + ...localExtensionsObjects[index], + name: `${localExtensionsObjects[index].name} (duplicate)`, + active: false, + lastEditor: currentEditor(), + }, + ]); + }; - const handleEdit = (index: number) => { - setExtensionModal({ - mode: "update", - extensionObject: localExtensionsObjects[index], - index, - }); - }; + const handleEdit = (index: number) => { + setExtensionModal({ + mode: "update", + extensionObject: localExtensionsObjects[index], + index, + }); + }; - const handleDelete = (index: number) => { - requestConfirmation({ - title: `Delete ${localExtensionsObjects[index].name}?`, - body: "This extension will be permanently deleted.", - confirm: "Confirm", - handleConfirm: () => { - setLocalExtensionsObjects( - localExtensionsObjects.filter((_, i) => i !== index) - ); - }, - }); - }; + const handleDelete = (index: number) => { + requestConfirmation({ + title: `Delete ${localExtensionsObjects[index].name}?`, + body: "This extension will be permanently deleted.", + confirm: "Confirm", + handleConfirm: () => { + setLocalExtensionsObjects( + localExtensionsObjects.filter((_, i) => i !== index) + ); + }, + }); + }; - const currentEditor = () => ({ - displayName: appContext?.currentUser?.displayName ?? "Unknown user", - photoURL: appContext?.currentUser?.photoURL ?? "", - lastUpdate: Date.now(), - }); + const currentEditor = () => ({ + displayName: appContext?.currentUser?.displayName ?? "Unknown user", + photoURL: appContext?.currentUser?.photoURL ?? "", + lastUpdate: Date.now(), + }); - return ( - <> - } - /> + return ( + <> + } + /> - {openExtensionList && !!tableState && ( - - - {tablePathTokens.map((pathToken) => { - return {pathToken}; - })} - - { - setExtensionModal({ - mode: "add", - extensionObject: emptyExtensionObject( - type, - currentEditor() - ), - }); - }} - handleUpdateActive={handleUpdateActive} - handleEdit={handleEdit} - handleDuplicate={handleDuplicate} - handleDelete={handleDelete} - /> - - } - actions={{ - primary: { - children: "Save & Deploy", - onClick: handleSaveDeploy, - disabled: !edited, - }, - secondary: { - children: "Save", - onClick: handleSaveExtensions, - disabled: !edited, - }, - }} - /> - )} + {openExtensionList && !!tableState && ( + + + {tablePathTokens.map((pathToken) => { + return {pathToken}; + })} + + { + setExtensionModal({ + mode: "add", + extensionObject: emptyExtensionObject( + type, + currentEditor() + ), + }); + }} + handleUpdateActive={handleUpdateActive} + handleEdit={handleEdit} + handleDuplicate={handleDuplicate} + handleDelete={handleDelete} + /> + + } + actions={{ + primary: { + children: "Save & Deploy", + onClick: handleSaveDeploy, + disabled: !edited, + }, + secondary: { + children: "Save", + onClick: handleSaveExtensions, + disabled: !edited, + }, + }} + /> + )} - {extensionModal && ( - { - setExtensionModal(null); - }} - handleAdd={handleAddExtension} - handleUpdate={handleUpdateExtension} - mode={extensionModal.mode} - extensionObject={extensionModal.extensionObject} - /> - )} + {extensionModal && ( + { + setExtensionModal(null); + }} + handleAdd={handleAddExtension} + handleUpdate={handleUpdateExtension} + mode={extensionModal.mode} + extensionObject={extensionModal.extensionObject} + /> + )} - {openMigrationGuide && ( - { - setOpenMigrationGuide(false); - }} - handleUpgradeComplete={() => { - setOpenMigrationGuide(false); - setOpenExtensionList(true); - }} - /> - )} - - ); + {openMigrationGuide && ( + { + setOpenMigrationGuide(false); + }} + handleUpgradeComplete={() => { + setOpenMigrationGuide(false); + setOpenExtensionList(true); + }} + /> + )} + + ); } diff --git a/src/components/Table/TableHeader/Extensions/utils.ts b/src/components/Table/TableHeader/Extensions/utils.ts index 768b1877..d0f9b412 100644 --- a/src/components/Table/TableHeader/Extensions/utils.ts +++ b/src/components/Table/TableHeader/Extensions/utils.ts @@ -1,54 +1,54 @@ type IExtensionType = - | "task" - | "docSync" - | "historySnapshot" - | "algoliaIndex" - | "meiliIndex" - | "bigqueryIndex" - | "slackMessage" - | "sendgridEmail" - | "apiCall" - | "twilioMessage"; + | "task" + | "docSync" + | "historySnapshot" + | "algoliaIndex" + | "meiliIndex" + | "bigqueryIndex" + | "slackMessage" + | "sendgridEmail" + | "apiCall" + | "twilioMessage"; type IExtensionTrigger = "create" | "update" | "delete"; interface IExtensionEditor { - displayName: string; - photoURL: string; - lastUpdate: number; + displayName: string; + photoURL: string; + lastUpdate: number; } interface IExtension { - // rowy meta fields - name: string; - active: boolean; - lastEditor: IExtensionEditor; + // rowy meta fields + name: string; + active: boolean; + lastEditor: IExtensionEditor; - // ft build fields - triggers: IExtensionTrigger[]; - type: IExtensionType; - requiredFields: string[]; - extensionBody: string; - conditions: string; + // ft build fields + triggers: IExtensionTrigger[]; + type: IExtensionType; + requiredFields: string[]; + extensionBody: string; + conditions: string; } const triggerTypes: IExtensionTrigger[] = ["create", "update", "delete"]; const extensionTypes: IExtensionType[] = [ - "task", - "docSync", - "historySnapshot", - "algoliaIndex", - "meiliIndex", - "bigqueryIndex", - "slackMessage", - "sendgridEmail", - "apiCall", - "twilioMessage", + "task", + "docSync", + "historySnapshot", + "algoliaIndex", + "meiliIndex", + "bigqueryIndex", + "slackMessage", + "sendgridEmail", + "apiCall", + "twilioMessage", ]; const extensionBodyTemplate = { - task: `const extensionBody: TaskBody = async({row, db, change, ref}) => { + task: `const extensionBody: TaskBody = async({row, db, change, ref}) => { // task extensions are very flexible you can do anything from updating other documents in your database, to making an api request to 3rd party service. // eg: @@ -65,7 +65,7 @@ const extensionBodyTemplate = { console.log("Task Extension completed.") }`, - docSync: `const extensionBody: DocSyncBody = async({row, db, change, ref}) => { + docSync: `const extensionBody: DocSyncBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -74,14 +74,14 @@ const extensionBodyTemplate = { targetPath: "", // fill in the path here }) }`, - historySnapshot: `const extensionBody: HistorySnapshotBody = async({row, db, change, ref}) => { + historySnapshot: `const extensionBody: HistorySnapshotBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ trackedFields: [], // a list of string of column names }) }`, - algoliaIndex: `const extensionBody: AlgoliaIndexBody = async({row, db, change, ref}) => { + algoliaIndex: `const extensionBody: AlgoliaIndexBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -91,7 +91,7 @@ const extensionBodyTemplate = { objectID: ref.id, // algolia object ID, ref.id is one possible choice }) }`, - meiliIndex: `const extensionBody: MeiliIndexBody = async({row, db, change, ref}) => { + meiliIndex: `const extensionBody: MeiliIndexBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return({ @@ -101,7 +101,7 @@ const extensionBodyTemplate = { objectID: ref.id, // algolia object ID, ref.id is one possible choice }) }`, - bigqueryIndex: `const extensionBody: BigqueryIndexBody = async({row, db, change, ref}) => { + bigqueryIndex: `const extensionBody: BigqueryIndexBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -111,7 +111,7 @@ const extensionBodyTemplate = { objectID: ref.id, // algolia object ID, ref.id is one possible choice }) }`, - slackMessage: `const extensionBody: SlackMessageBody = async({row, db, change, ref}) => { + slackMessage: `const extensionBody: SlackMessageBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -121,7 +121,7 @@ const extensionBodyTemplate = { attachments: [], // the attachments parameter to pass in to slack api }) }`, - sendgridEmail: `const extensionBody: SendgridEmailBody = async({row, db, change, ref}) => { + sendgridEmail: `const extensionBody: SendgridEmailBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -137,7 +137,7 @@ const extensionBodyTemplate = { categories: [], // helper info to categorise sendgrid emails }) }`, - apiCall: `const extensionBody: ApiCallBody = async({row, db, change, ref}) => { + apiCall: `const extensionBody: ApiCallBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -147,7 +147,7 @@ const extensionBodyTemplate = { callback: ()=>{}, }) }`, - twilioMessage: `const extensionBody: TwilioMessageBody = async({row, db, change, ref}) => { + twilioMessage: `const extensionBody: TwilioMessageBody = async({row, db, change, ref}) => { // feel free to add your own code logic here return ({ @@ -159,120 +159,120 @@ const extensionBodyTemplate = { }; function emptyExtensionObject( - type: IExtensionType, - user: IExtensionEditor + type: IExtensionType, + user: IExtensionEditor ): IExtension { - return { - name: "Untitled extension", - active: false, - triggers: [], - type, - extensionBody: extensionBodyTemplate[type] ?? extensionBodyTemplate["task"], - requiredFields: [], - conditions: `const condition: Condition = async({row, change}) => { + return { + name: "Untitled extension", + active: false, + triggers: [], + type, + extensionBody: extensionBodyTemplate[type] ?? extensionBodyTemplate["task"], + requiredFields: [], + conditions: `const condition: Condition = async({row, change}) => { // feel free to add your own code logic here return true; }`, - lastEditor: user, - }; + lastEditor: user, + }; } /* Convert extension objects into a single ft-build readable string */ function serialiseExtension(extensions: IExtension[]): string { - const serialisedExtension = - "[" + - extensions - .filter((extension) => extension.active) - .map( - (extension) => `{ + const serialisedExtension = + "[" + + extensions + .filter((extension) => extension.active) + .map( + (extension) => `{ name: "${extension.name}", type: "${extension.type}", triggers: [${extension.triggers - .map((trigger) => `"${trigger}"`) - .join(", ")}], + .map((trigger) => `"${trigger}"`) + .join(", ")}], conditions: ${extension.conditions - .replace(/^.*:\s*Condition\s*=/, "") - .replace(/\s*;\s*$/, "")}, + .replace(/^.*:\s*Condition\s*=/, "") + .replace(/\s*;\s*$/, "")}, requiredFields: [${extension.requiredFields - .map((field) => `"${field}"`) - .join(", ")}], + .map((field) => `"${field}"`) + .join(", ")}], extensionBody: ${extension.extensionBody - .replace(/^.*:\s*\w*Body\s*=/, "") - .replace(/\s*;\s*$/, "")} + .replace(/^.*:\s*\w*Body\s*=/, "") + .replace(/\s*;\s*$/, "")} }` - ) - .join(",") + - "]"; - console.log("serialisedExtension", serialisedExtension); - return serialisedExtension; + ) + .join(",") + + "]"; + console.log("serialisedExtension", serialisedExtension); + return serialisedExtension; } function sparkToExtensionObjects( - sparkConfig: string, - user: IExtensionEditor + sparkConfig: string, + user: IExtensionEditor ): IExtension[] { - const parseString2Array = (str: string): string[] => { - return str - .trim() - .replace(/\[|\]/g, "") - .split(",") - .map((x) => x.trim().replace(/'/g, "")); - }; - const oldSparks = sparkConfig.replace(/"/g, "'"); - const sparkTypes = oldSparks - .match(/(?<=type:).*(?=,)/g) - ?.map((x) => x.trim().replace(/'/g, "")); - const triggers = oldSparks - .match(/(?<=triggers:).*(?=,)/g) - ?.map((x) => parseString2Array(x)); - const shouldRun = oldSparks - .match(/(?<=shouldRun:).*(?=,)/g) - ?.map((x) => x.trim()); - const requiredFields = oldSparks - .match(/(?<=requiredFields:).*(?=,)/g) - ?.map((x) => parseString2Array(x)); - const splitSparks = oldSparks.split(`type:`); - const sparks = sparkTypes?.map((x, index) => { - const sparkBody = splitSparks[index + 1] - ?.split("sparkBody:")[1] - ?.trim() - .slice(0, -1); - const _triggers = triggers?.[index]; - const _shouldRun = shouldRun?.[index]; - const _requiredFields = requiredFields?.[index]; - return { - type: x, - triggers: _triggers, - shouldRun: _shouldRun, - requiredFields: _requiredFields, - sparkBody, - }; - }); - const extensionObjects = sparks?.map( - (spark, index): IExtension => { - return { - // rowy meta fields - name: `Migrated spark ${index}`, - active: true, - lastEditor: user, + const parseString2Array = (str: string): string[] => { + return str + .trim() + .replace(/\[|\]/g, "") + .split(",") + .map((x) => x.trim().replace(/'/g, "")); + }; + const oldSparks = sparkConfig.replace(/"/g, "'"); + const sparkTypes = oldSparks + .match(/(?<=type:).*(?=,)/g) + ?.map((x) => x.trim().replace(/'/g, "")); + const triggers = oldSparks + .match(/(?<=triggers:).*(?=,)/g) + ?.map((x) => parseString2Array(x)); + const shouldRun = oldSparks + .match(/(?<=shouldRun:).*(?=,)/g) + ?.map((x) => x.trim()); + const requiredFields = oldSparks + .match(/(?<=requiredFields:).*(?=,)/g) + ?.map((x) => parseString2Array(x)); + const splitSparks = oldSparks.split(`type:`); + const sparks = sparkTypes?.map((x, index) => { + const sparkBody = splitSparks[index + 1] + ?.split("sparkBody:")[1] + ?.trim() + .slice(0, -1); + const _triggers = triggers?.[index]; + const _shouldRun = shouldRun?.[index]; + const _requiredFields = requiredFields?.[index]; + return { + type: x, + triggers: _triggers, + shouldRun: _shouldRun, + requiredFields: _requiredFields, + sparkBody, + }; + }); + const extensionObjects = sparks?.map( + (spark, index): IExtension => { + return { + // rowy meta fields + name: `Migrated spark ${index}`, + active: true, + lastEditor: user, - // ft build fields - triggers: (spark.triggers ?? []) as IExtensionTrigger[], - type: spark.type as IExtensionType, - requiredFields: spark.requiredFields ?? [], - extensionBody: spark.sparkBody, - conditions: spark.shouldRun ?? "", - }; - } - ); - return extensionObjects ?? []; + // ft build fields + triggers: (spark.triggers ?? []) as IExtensionTrigger[], + type: spark.type as IExtensionType, + requiredFields: spark.requiredFields ?? [], + extensionBody: spark.sparkBody, + conditions: spark.shouldRun ?? "", + }; + } + ); + return extensionObjects ?? []; } export { - serialiseExtension, - extensionTypes, - triggerTypes, - emptyExtensionObject, - sparkToExtensionObjects, + serialiseExtension, + extensionTypes, + triggerTypes, + emptyExtensionObject, + sparkToExtensionObjects, }; export type { IExtension, IExtensionType, IExtensionEditor }; diff --git a/src/components/Table/TableHeader/ImportCsv.tsx b/src/components/Table/TableHeader/ImportCsv.tsx index 1c8548af..6d841582 100644 --- a/src/components/Table/TableHeader/ImportCsv.tsx +++ b/src/components/Table/TableHeader/ImportCsv.tsx @@ -6,13 +6,13 @@ import { useDebouncedCallback } from "use-debounce"; import { makeStyles, createStyles } from "@material-ui/styles"; import { - Button, - Popover, - PopoverProps as MuiPopoverProps, - Grid, - Typography, - TextField, - FormHelperText, + Button, + Popover, + PopoverProps as MuiPopoverProps, + Grid, + Typography, + TextField, + FormHelperText, } from "@material-ui/core"; import Tab from "@material-ui/core/Tab"; @@ -27,279 +27,279 @@ import FileUploadIcon from "assets/icons/FileUpload"; import CheckIcon from "@material-ui/icons/CheckCircle"; import ImportCsvWizard, { - IImportCsvWizardProps, + IImportCsvWizardProps, } from "components/Wizards/ImportCsvWizard"; const useStyles = makeStyles((theme) => - createStyles({ - tabPanel: { - padding: theme.spacing(2, 3), - width: 400, - height: 200, - }, - continueButton: { - margin: theme.spacing(-4, "auto", 2), - display: "flex", - minWidth: 100, - }, + createStyles({ + tabPanel: { + padding: theme.spacing(2, 3), + width: 400, + height: 200, + }, + continueButton: { + margin: theme.spacing(-4, "auto", 2), + display: "flex", + minWidth: 100, + }, - dropzone: { - height: 137, - borderRadius: theme.shape.borderRadius, - border: `dashed 2px ${theme.palette.divider}`, - backgroundColor: theme.palette.action.input, - cursor: "pointer", + dropzone: { + height: 137, + borderRadius: theme.shape.borderRadius, + border: `dashed 2px ${theme.palette.divider}`, + backgroundColor: theme.palette.action.input, + cursor: "pointer", - "& svg": { opacity: theme.palette.action.activeOpacity }, + "& svg": { opacity: theme.palette.action.activeOpacity }, - "&:focus": { - borderColor: theme.palette.primary.main, - color: theme.palette.primary.main, - outline: "none", - }, - }, - error: { - "$dropzone&": { - borderColor: theme.palette.error.main, - color: theme.palette.error.main, - }, - }, - dropzoneError: { margin: theme.spacing(0.5, 1.5) }, + "&:focus": { + borderColor: theme.palette.primary.main, + color: theme.palette.primary.main, + outline: "none", + }, + }, + error: { + "$dropzone&": { + borderColor: theme.palette.error.main, + color: theme.palette.error.main, + }, + }, + dropzoneError: { margin: theme.spacing(0.5, 1.5) }, - pasteField: { - ...theme.typography.body2, - fontFamily: theme.typography.fontFamilyMono, - }, - pasteInput: { - whiteSpace: "nowrap", - overflow: "auto", - }, - }) + pasteField: { + ...theme.typography.body2, + fontFamily: theme.typography.fontFamilyMono, + }, + pasteInput: { + whiteSpace: "nowrap", + overflow: "auto", + }, + }) ); export interface IImportCsvProps { - render?: ( - onClick: (event: React.MouseEvent) => void - ) => React.ReactNode; - PopoverProps?: Partial; + render?: ( + onClick: (event: React.MouseEvent) => void + ) => React.ReactNode; + PopoverProps?: Partial; } export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) { - const classes = useStyles(); + const classes = useStyles(); - const [open, setOpen] = useState(null); - const [tab, setTab] = useState("upload"); - const [csvData, setCsvData] = useState( - null - ); - const [error, setError] = useState(""); - const validCsv = - csvData !== null && csvData?.columns.length > 0 && csvData?.rows.length > 0; + const [open, setOpen] = useState(null); + const [tab, setTab] = useState("upload"); + const [csvData, setCsvData] = useState( + null + ); + const [error, setError] = useState(""); + const validCsv = + csvData !== null && csvData?.columns.length > 0 && csvData?.rows.length > 0; - const handleOpen = (event: React.MouseEvent) => - setOpen(event.currentTarget); - const handleClose = () => { - setOpen(null); - setCsvData(null); - setTab("upload"); - setError(""); - }; - const popoverId = open ? "csv-popover" : undefined; + const handleOpen = (event: React.MouseEvent) => + setOpen(event.currentTarget); + const handleClose = () => { + setOpen(null); + setCsvData(null); + setTab("upload"); + setError(""); + }; + const popoverId = open ? "csv-popover" : undefined; - const parseCsv = (csvString: string) => - parse(csvString, {}, (err, rows) => { - if (err) { - setError(err.message); - } else { - const columns = rows.shift() ?? []; - if (columns.length === 0) { - setError("No columns detected"); - } else { - const mappedRows = rows.map((row) => - row.reduce((a, c, i) => ({ ...a, [columns[i]]: c }), {}) - ); - setCsvData({ columns, rows: mappedRows }); - setError(""); - } - } - }); + const parseCsv = (csvString: string) => + parse(csvString, {}, (err, rows) => { + if (err) { + setError(err.message); + } else { + const columns = rows.shift() ?? []; + if (columns.length === 0) { + setError("No columns detected"); + } else { + const mappedRows = rows.map((row) => + row.reduce((a, c, i) => ({ ...a, [columns[i]]: c }), {}) + ); + setCsvData({ columns, rows: mappedRows }); + setError(""); + } + } + }); - const onDrop = useCallback(async (acceptedFiles) => { - const file = acceptedFiles[0]; - const reader = new FileReader(); - reader.onload = (event: any) => parseCsv(event.target.result); - reader.readAsText(file); - }, []); - const { getRootProps, getInputProps, isDragActive } = useDropzone({ - onDrop, - multiple: false, - accept: "text/csv", - }); + const onDrop = useCallback(async (acceptedFiles) => { + const file = acceptedFiles[0]; + const reader = new FileReader(); + reader.onload = (event: any) => parseCsv(event.target.result); + reader.readAsText(file); + }, []); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop, + multiple: false, + accept: "text/csv", + }); - const [handlePaste] = useDebouncedCallback( - (value: string) => parseCsv(value), - 1000 - ); + const [handlePaste] = useDebouncedCallback( + (value: string) => parseCsv(value), + 1000 + ); - const [loading, setLoading] = useState(false); - const [handleUrl] = useDebouncedCallback((value: string) => { - setLoading(true); - setError(""); - fetch(value, { mode: "no-cors" }) - .then((res) => res.text()) - .then((data) => { - parseCsv(data); - setLoading(false); - }) - .catch((e) => { - setError(e.message); - setLoading(false); - }); - }, 1000); + const [loading, setLoading] = useState(false); + const [handleUrl] = useDebouncedCallback((value: string) => { + setLoading(true); + setError(""); + fetch(value, { mode: "no-cors" }) + .then((res) => res.text()) + .then((data) => { + parseCsv(data); + setLoading(false); + }) + .catch((e) => { + setError(e.message); + setLoading(false); + }); + }, 1000); - const [openWizard, setOpenWizard] = useState(false); + const [openWizard, setOpenWizard] = useState(false); - return ( - <> - {render ? ( - render(handleOpen) - ) : ( - } - /> - )} + return ( + <> + {render ? ( + render(handleOpen) + ) : ( + } + /> + )} - - - { - setTab(v); - setCsvData(null); - setError(""); - }} - aria-label="Import CSV method tabs" - action={(actions) => - setTimeout(() => actions?.updateIndicator(), 200) - } - variant="fullWidth" - > - - - - + + + { + setTab(v); + setCsvData(null); + setError(""); + }} + aria-label="Import CSV method tabs" + action={(actions) => + setTimeout(() => actions?.updateIndicator(), 200) + } + variant="fullWidth" + > + + + + - - - - {isDragActive ? ( - - Drop CSV file here… - - ) : ( - <> - - {validCsv ? : } - - - - {validCsv - ? "Valid CSV" - : "Click to upload or drop CSV file here"} - - - - )} - + + + + {isDragActive ? ( + + Drop CSV file here… + + ) : ( + <> + + {validCsv ? : } + + + + {validCsv + ? "Valid CSV" + : "Click to upload or drop CSV file here"} + + + + )} + - {error && ( - - {error} - - )} - + {error && ( + + {error} + + )} + - - { - if (csvData !== null) setCsvData(null); - handlePaste(e.target.value); - }} - InputProps={{ - classes: { - root: classes.pasteField, - input: classes.pasteInput, - }, - }} - helperText={error} - error={!!error} - /> - + + { + if (csvData !== null) setCsvData(null); + handlePaste(e.target.value); + }} + InputProps={{ + classes: { + root: classes.pasteField, + input: classes.pasteInput, + }, + }} + helperText={error} + error={!!error} + /> + - - { - if (csvData !== null) setCsvData(null); - handleUrl(e.target.value); - }} - helperText={loading ? "Fetching CSV…" : error} - error={!!error} - /> - - + + { + if (csvData !== null) setCsvData(null); + handleUrl(e.target.value); + }} + helperText={loading ? "Fetching CSV…" : error} + error={!!error} + /> + + - - + + - {openWizard && csvData && ( - setOpenWizard(false)} - csvData={csvData} - /> - )} - - ); + {openWizard && csvData && ( + setOpenWizard(false)} + csvData={csvData} + /> + )} + + ); } diff --git a/src/components/Table/TableHeader/ReExecute.tsx b/src/components/Table/TableHeader/ReExecute.tsx index c403c80a..911b0be5 100644 --- a/src/components/Table/TableHeader/ReExecute.tsx +++ b/src/components/Table/TableHeader/ReExecute.tsx @@ -13,66 +13,66 @@ import { DialogContentText } from "@material-ui/core"; import Modal from "components/Modal"; export default function ReExecute() { - const [open, setOpen] = useState(false); - const [updating, setUpdating] = useState(false); - const handleClose = () => { - setOpen(false); - }; + const [open, setOpen] = useState(false); + const [updating, setUpdating] = useState(false); + const handleClose = () => { + setOpen(false); + }; - const { tableState } = useRowyContext(); - const query: any = isCollectionGroup() - ? db.collectionGroup(tableState?.tablePath!) - : db.collection(tableState?.tablePath!); + const { tableState } = useRowyContext(); + const query: any = isCollectionGroup() + ? db.collectionGroup(tableState?.tablePath!) + : db.collection(tableState?.tablePath!); - const handleConfirm = async () => { - setUpdating(true); - const _forcedUpdateAt = new Date(); - const querySnapshot = await query.get(); - const docs = [...querySnapshot.docs]; - while (docs.length) { - const batch = db.batch(); - const temp = docs.splice(0, 499); - temp.forEach((doc) => { - batch.update(doc.ref, { _forcedUpdateAt }); - }); - await batch.commit(); - } - setTimeout(() => { - setUpdating(false); - setOpen(false); - }, 3000); // give time to for ft function to run - }; + const handleConfirm = async () => { + setUpdating(true); + const _forcedUpdateAt = new Date(); + const querySnapshot = await query.get(); + const docs = [...querySnapshot.docs]; + while (docs.length) { + const batch = db.batch(); + const temp = docs.splice(0, 499); + temp.forEach((doc) => { + batch.update(doc.ref, { _forcedUpdateAt }); + }); + await batch.commit(); + } + setTimeout(() => { + setUpdating(false); + setOpen(false); + }, 3000); // give time to for ft function to run + }; - return ( - <> - setOpen(true)} - icon={} - /> + return ( + <> + setOpen(true)} + icon={} + /> - {open && ( - , - disabled: updating, - }, - secondary: { - children: "Cancel", - onClick: handleClose, - }, - }} - /> - )} - - ); + {open && ( + , + disabled: updating, + }, + secondary: { + children: "Cancel", + onClick: handleClose, + }, + }} + /> + )} + + ); } diff --git a/src/components/Table/TableHeader/RowHeight.tsx b/src/components/Table/TableHeader/RowHeight.tsx index a620a468..9227b26d 100644 --- a/src/components/Table/TableHeader/RowHeight.tsx +++ b/src/components/Table/TableHeader/RowHeight.tsx @@ -1,10 +1,10 @@ import { useRef, useState } from "react"; import { - useTheme, - TextField, - ListSubheader, - MenuItem, + useTheme, + TextField, + ListSubheader, + MenuItem, } from "@material-ui/core"; import RowHeightIcon from "assets/icons/RowHeight"; @@ -12,59 +12,59 @@ import TableHeaderButton from "./TableHeaderButton"; import { useRowyContext } from "contexts/RowyContext"; export default function RowHeight() { - const theme = useTheme(); + const theme = useTheme(); - const buttonRef = useRef(null); + const buttonRef = useRef(null); - const [open, setOpen] = useState(false); - const handleOpen = () => setOpen(true); - const handleClose = () => setOpen(false); + const [open, setOpen] = useState(false); + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); - const { tableActions, tableState } = useRowyContext(); + const { tableActions, tableState } = useRowyContext(); - const rowHeight = tableState?.config.rowHeight; - const updateConfig = tableActions?.table.updateConfig; + const rowHeight = tableState?.config.rowHeight; + const updateConfig = tableActions?.table.updateConfig; - console.log(buttonRef); + console.log(buttonRef); - return ( - <> - } - onClick={handleOpen} - ref={buttonRef} - /> + return ( + <> + } + onClick={handleOpen} + ref={buttonRef} + /> - { - if (updateConfig) updateConfig("rowHeight", event.target.value); - }} - style={{ display: "none" }} - SelectProps={{ - open: open, - onOpen: handleOpen, - onClose: handleClose, - MenuProps: { - anchorEl: buttonRef.current, - anchorOrigin: { vertical: "bottom", horizontal: "right" }, - transformOrigin: { vertical: "top", horizontal: "right" }, - style: { zIndex: theme.zIndex.tooltip }, - }, - }} - label="Row Height" - id="row-height-select" - > - Row Height - Short - Tall - Grande - Venti - Trenta - - - ); + { + if (updateConfig) updateConfig("rowHeight", event.target.value); + }} + style={{ display: "none" }} + SelectProps={{ + open: open, + onOpen: handleOpen, + onClose: handleClose, + MenuProps: { + anchorEl: buttonRef.current, + anchorOrigin: { vertical: "bottom", horizontal: "right" }, + transformOrigin: { vertical: "top", horizontal: "right" }, + style: { zIndex: theme.zIndex.tooltip }, + }, + }} + label="Row Height" + id="row-height-select" + > + Row Height + Short + Tall + Grande + Venti + Trenta + + + ); } diff --git a/src/components/Table/TableHeader/TableHeaderButton.tsx b/src/components/Table/TableHeader/TableHeaderButton.tsx index ed575dbc..4bf28341 100644 --- a/src/components/Table/TableHeader/TableHeaderButton.tsx +++ b/src/components/Table/TableHeader/TableHeaderButton.tsx @@ -2,29 +2,29 @@ import { forwardRef } from "react"; import { Tooltip, Button, ButtonProps } from "@material-ui/core"; export interface ITableHeaderButtonProps extends Partial { - title: string; - icon: React.ReactNode; + title: string; + icon: React.ReactNode; } export const TableHeaderButton = forwardRef(function TableHeaderButton_( - { title, icon, ...props }: ITableHeaderButtonProps, - ref: React.Ref + { title, icon, ...props }: ITableHeaderButtonProps, + ref: React.Ref ) { - return ( - - - - ); + return ( + + + + ); }); export default TableHeaderButton; diff --git a/src/components/Table/TableHeader/TableLogs.tsx b/src/components/Table/TableHeader/TableLogs.tsx index 90fb7552..cdb6da9b 100644 --- a/src/components/Table/TableHeader/TableLogs.tsx +++ b/src/components/Table/TableHeader/TableLogs.tsx @@ -12,15 +12,15 @@ import { format } from "date-fns"; import moment from "moment"; import { - Chip, - Stack, - CircularProgress, - Typography, - Box, - Tabs, - Tab, - IconButton, - Button, + Chip, + Stack, + CircularProgress, + Typography, + Box, + Tabs, + Tab, + IconButton, + Button, } from "@material-ui/core"; import Modal from "components/Modal"; import { makeStyles, createStyles } from "@material-ui/styles"; @@ -40,542 +40,542 @@ import routes from "constants/routes"; import { DATE_TIME_FORMAT } from "constants/dates"; function a11yProps(index) { - return { - id: `vertical-tab-${index}`, - "aria-controls": `vertical-tabpanel-${index}`, - }; + return { + id: `vertical-tab-${index}`, + "aria-controls": `vertical-tabpanel-${index}`, + }; } const isTargetInsideBox = (target, box) => { - const targetRect = target.getBoundingClientRect(); - const boxRect = box.getBoundingClientRect(); - return targetRect.y < boxRect.y + boxRect.height; + const targetRect = target.getBoundingClientRect(); + const boxRect = box.getBoundingClientRect(); + return targetRect.y < boxRect.y + boxRect.height; }; const useStyles = makeStyles((theme) => - createStyles({ - toolbarStatusIcon: { - fontSize: 12, + createStyles({ + toolbarStatusIcon: { + fontSize: 12, - position: "absolute", - bottom: 2, - right: 5, + position: "absolute", + bottom: 2, + right: 5, - backgroundColor: theme.palette.background.paper, - boxShadow: `0 0 0 1px ${theme.palette.background.paper}`, - borderRadius: "50%", - }, + backgroundColor: theme.palette.background.paper, + boxShadow: `0 0 0 1px ${theme.palette.background.paper}`, + borderRadius: "50%", + }, - root: { - display: "flex", - height: "100%", - }, + root: { + display: "flex", + height: "100%", + }, - logPanel: { - width: "100%", - backgroundColor: "#1E1E1E", - }, - logPanelProgress: { - marginLeft: "2em", - marginTop: "1em", - }, - logEntryWrapper: { - overflowY: "scroll", - maxHeight: "100%", - }, - logNumber: { - float: "left", - width: "2em", - textAlign: "right", - paddingRight: "1em", - }, - logEntry: { - lineBreak: "anywhere", - paddingLeft: "2em", - whiteSpace: "break-spaces", - userSelect: "text", - }, - logTypeInfo: { - color: "green", - }, - logTypeError: { - color: "red", - }, - logFont: { - ...theme.typography.body2, - fontFamily: theme.typography.fontFamilyMono, - // TODO: - color: "#CCC", + logPanel: { + width: "100%", + backgroundColor: "#1E1E1E", + }, + logPanelProgress: { + marginLeft: "2em", + marginTop: "1em", + }, + logEntryWrapper: { + overflowY: "scroll", + maxHeight: "100%", + }, + logNumber: { + float: "left", + width: "2em", + textAlign: "right", + paddingRight: "1em", + }, + logEntry: { + lineBreak: "anywhere", + paddingLeft: "2em", + whiteSpace: "break-spaces", + userSelect: "text", + }, + logTypeInfo: { + color: "green", + }, + logTypeError: { + color: "red", + }, + logFont: { + ...theme.typography.body2, + fontFamily: theme.typography.fontFamilyMono, + // TODO: + color: "#CCC", - "& code": { - fontFamily: theme.typography.fontFamilyMono, - }, - }, + "& code": { + fontFamily: theme.typography.fontFamilyMono, + }, + }, - snackLog: { - position: "absolute", - left: 40, - bottom: 40, - backgroundColor: "#282829", - width: "min(40vw, 640px)", - padding: theme.spacing(1, 2, 2, 2), - borderRadius: 4, - zIndex: 1, - height: 300, - transition: "height 300ms ease-out", - }, - snackLogExpanded: { - height: "calc(100% - 300px)", - }, + snackLog: { + position: "absolute", + left: 40, + bottom: 40, + backgroundColor: "#282829", + width: "min(40vw, 640px)", + padding: theme.spacing(1, 2, 2, 2), + borderRadius: 4, + zIndex: 1, + height: 300, + transition: "height 300ms ease-out", + }, + snackLogExpanded: { + height: "calc(100% - 300px)", + }, - whiteText: { - color: "white", - }, - }) + whiteText: { + color: "white", + }, + }) ); LogPanel.propTypes = { - logs: PropTypes.array, - status: PropTypes.string, - index: PropTypes.any.isRequired, - value: PropTypes.any.isRequired, + logs: PropTypes.array, + status: PropTypes.string, + index: PropTypes.any.isRequired, + value: PropTypes.any.isRequired, }; function LogRow({ logRecord, index }) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - {index} - - - - {moment(logRecord.timestamp).format("LTS")} - - {" "} - - {logRecord.log - .replaceAll("\\n", "\n") - .replaceAll("\\t", "\t") - .replaceAll("\\", "")} - - - - ); + return ( + + + {index} + + + + {moment(logRecord.timestamp).format("LTS")} + + {" "} + + {logRecord.log + .replaceAll("\\n", "\n") + .replaceAll("\\t", "\t") + .replaceAll("\\", "")} + + + + ); } function LogPanel(props) { - const { logs, status, value, index, ...other } = props; - const classes = useStyles(); + const { logs, status, value, index, ...other } = props; + const classes = useStyles(); - // useStateRef is necessary to resolve the state syncing issue - // https://stackoverflow.com/a/63039797/12208834 - const [liveStreaming, setLiveStreaming, liveStreamingStateRef] = useStateRef( - true - ); - const liveStreamingRef = useRef(); - const isActive = value === index; + // useStateRef is necessary to resolve the state syncing issue + // https://stackoverflow.com/a/63039797/12208834 + const [liveStreaming, setLiveStreaming, liveStreamingStateRef] = useStateRef( + true + ); + const liveStreamingRef = useRef(); + const isActive = value === index; - const handleScroll = _throttle(() => { - const target = document.querySelector("#live-stream-target"); - const scrollBox = document.querySelector("#live-stream-scroll-box"); - const liveStreamTargetVisible = isTargetInsideBox(target, scrollBox); - if (liveStreamTargetVisible !== liveStreamingStateRef.current) { - setLiveStreaming(liveStreamTargetVisible); - } - }, 500); + const handleScroll = _throttle(() => { + const target = document.querySelector("#live-stream-target"); + const scrollBox = document.querySelector("#live-stream-scroll-box"); + const liveStreamTargetVisible = isTargetInsideBox(target, scrollBox); + if (liveStreamTargetVisible !== liveStreamingStateRef.current) { + setLiveStreaming(liveStreamTargetVisible); + } + }, 500); - const scrollToLive = () => { - const liveStreamTarget = document.querySelector("#live-stream-target"); - liveStreamTarget?.scrollIntoView?.({ - behavior: "smooth", - }); - }; + const scrollToLive = () => { + const liveStreamTarget = document.querySelector("#live-stream-target"); + liveStreamTarget?.scrollIntoView?.({ + behavior: "smooth", + }); + }; - useEffect(() => { - if (liveStreaming && isActive && status === "BUILDING") { - if (!liveStreamingRef.current) { - scrollToLive(); - } else { - setTimeout(scrollToLive, 100); - } - } - }, [logs, value]); + useEffect(() => { + if (liveStreaming && isActive && status === "BUILDING") { + if (!liveStreamingRef.current) { + scrollToLive(); + } else { + setTimeout(scrollToLive, 100); + } + } + }, [logs, value]); - useEffect(() => { - if (isActive) { - const liveStreamScrollBox = document.querySelector( - "#live-stream-scroll-box" - ); - liveStreamScrollBox!.addEventListener("scroll", () => { - handleScroll(); - }); - } - }, [value]); + useEffect(() => { + if (isActive) { + const liveStreamScrollBox = document.querySelector( + "#live-stream-scroll-box" + ); + liveStreamScrollBox!.addEventListener("scroll", () => { + handleScroll(); + }); + } + }, [value]); - return ( -