KEYCLOAK-11201: Use snowpack instead of SystemJs.

Co-authored-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Stan Silvert 2020-05-07 20:54:52 -04:00 committed by Stian Thorgersen
parent bf8316eefa
commit a827d20a90
43 changed files with 8291 additions and 4180 deletions

View file

@ -196,7 +196,7 @@ public class DeviceActivityTest extends BaseAccountPageTest {
@Test
public void timesTests() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy h:mm a", Locale.ENGLISH);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy, h:mm a", Locale.ENGLISH);
LocalDateTime now = LocalDateTime.now();
LocalDateTime nowPlus1 = now.plusMinutes(1);
String nowStr = now.format(formatter);

View file

@ -46,13 +46,13 @@ public class MyResourcesPage extends AbstractLoggedInPage {
public void createShare(String userName) {
driver.findElement(By.id("username")).sendKeys(userName);
driver.findElement(By.id("add")).click();
driver.findElement(By.id("remove_pf-random-id-0")).click();
driver.findElement(By.id("remove_pf-random-id-1")).click();
driver.findElement(By.id("done")).click();
}
public void removeAllPermissions() {
driver.findElement(By.id("remove_pf-random-id-0")).click();
driver.findElement(By.id("remove_pf-random-id-1")).click();
driver.findElement(By.id("remove_pf-random-id-2")).click();
driver.findElement(By.id("done")).click();
}
}

View file

@ -32,7 +32,7 @@ import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class SigningInPage extends AbstractLoggedInPage {
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMMM d, yyyy h:mm a", Locale.ENGLISH);
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMMM d, yyyy, h:mm a", Locale.ENGLISH);
private static final String CATEG_TITLE = "-categ-title";

View file

@ -112,275 +112,9 @@
<exclude>**/.*</exclude>
<!-- Remove once rcue stops shipping this file -->
<exclude>**/git-Logo.svg</exclude>
<!-- Remove once account2 manual filter list is removed -->
<exclude>**/keycloak-preview/account/resources/node_modules/**</exclude>
</excludes>
</resource>
<!-- account2 manual filter list -->
<!--
To update, use network log in browser, navigate around account2, export
log as HAR json, then run this command to get the new list of includes:
jq -r '.log.entries | .[] | .request.url' /tmp/localhost.har | sed -r 's|^.*/keycloak-preview/(node_modules/.*)$|<include>**/\1</include>|;tx;d;:x' | sort -Vu | xclip -selection clipboard
-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/node_modules/axios/dist/axios.min.js</include>
<include>**/node_modules/camel-case/camel-case.js</include>
<include>**/node_modules/emotion/dist/emotion.umd.min.js</include>
<include>**/node_modules/exenv/index.js</include>
<include>**/node_modules/focus-trap-react/dist/focus-trap-react.js</include>
<include>**/node_modules/focus-trap/dist/focus-trap.min.js</include>
<include>**/node_modules/lower-case/lower-case.js</include>
<include>**/node_modules/moment/min/moment-with-locales.min.js</include>
<include>**/node_modules/no-case/no-case.js</include>
<include>**/node_modules/no-case/vendor/camel-case-regexp.js</include>
<include>**/node_modules/no-case/vendor/camel-case-upper-regexp.js</include>
<include>**/node_modules/no-case/vendor/non-word-regexp.js</include>
<include>**/node_modules/prop-types/prop-types.min.js</include>
<include>**/node_modules/react-dom/umd/react-dom.production.min.js</include>
<include>**/node_modules/react-router-dom/umd/react-router-dom.min.js</include>
<include>**/node_modules/react/umd/react.production.min.js</include>
<include>**/node_modules/systemjs/dist/system.src.js</include>
<include>**/node_modules/tippy.js/dist/tippy.min.js</include>
<include>**/node_modules/upper-case/upper-case.js</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-bold.ttf</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-bold.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-bold.woff</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-light-italic.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-light.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-regular.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-semibold.ttf</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-semibold.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/overpass-webfont/overpass-semibold.woff</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/webfonts/fa-solid-900.ttf</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/webfonts/fa-solid-900.woff</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/webfonts/fa-solid-900.ttf</include>
<include>**/node_modules/@patternfly/patternfly/assets/fonts/webfonts/fa-solid-900.woff2</include>
<include>**/node_modules/@patternfly/patternfly/assets/images/img_avatar.svg</include>
<include>**/node_modules/@patternfly/patternfly/assets/pficon/pficon.woff2</include>
<include>**/node_modules/@patternfly/patternfly/patternfly.min.css</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Alert/AlertActionCloseButton.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Alert/AlertActionLink.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Alert/AlertIcon.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Alert/Alert.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Alert/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Avatar/Avatar.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Avatar/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Backdrop/Backdrop.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Backdrop/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/BackgroundImage/BackgroundImage.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/BackgroundImage/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Badge/Badge.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Badge/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Brand/Brand.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Brand/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Button/Button.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Button/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Card/Card.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Card/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/ChipGroup/ChipButton.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/ChipGroup/ChipGroupToolbarItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/ChipGroup/ChipGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/ChipGroup/Chip.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/ChipGroup/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListAction.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListCell.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListCheck.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListContent.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListItemCells.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListItemRow.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataListToggle.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/DataList.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/DataList/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/DropdownGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/DropdownItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/DropdownMenu.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/DropdownToggleCheckbox.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/DropdownToggle.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/Dropdown.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/Item.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/KebabToggle.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/Separator.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/Toggle.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/dropdownConstants.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Dropdown/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/EmptyState/EmptyStateBody.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/EmptyState/EmptyStateIcon.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/EmptyState/EmptyStateSecondaryActions.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/EmptyState/EmptyState.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/EmptyState/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/ActionGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/FormContext.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/FormGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/FormHelperText.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/Form.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Form/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/FormSelect/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/FormSelect/FormSelect.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/FormSelect/FormSelectOption.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/FormSelect/FormSelectOptionGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalBoxBody.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalBoxCloseButton.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalBoxFooter.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalBoxHeader.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalBox.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/ModalContent.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/Modal.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Modal/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavExpandable.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavItemSeparator.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavList.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/NavVariants.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/Nav.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Nav/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Page/PageHeader.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Page/PageSection.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Page/PageSidebar.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Page/Page.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Page/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Switch/Switch.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Switch/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tabs/TabContent.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tabs/Tabs.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tabs/Tab.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tabs/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/TextInput/TextInput.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/TextInput/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Text/TextContent.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Text/TextListItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Text/TextList.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Text/Text.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Text/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Title/Title.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Title/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tooltip/TooltipArrow.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tooltip/TooltipContent.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tooltip/Tooltip.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tooltip/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/Tooltip/styles.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/components/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/GenerateId/GenerateId.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/componentShape.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/constants.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/htmlConstants.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/typeUtils.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/helpers/util.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Bullseye/Bullseye.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Bullseye/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Gallery/GalleryItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Gallery/Gallery.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Gallery/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Grid/GridItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Grid/Grid.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Grid/gridUtils.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Grid/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Level/LevelItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Level/Level.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Level/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Stack/StackItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Stack/Stack.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Stack/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Toolbar/ToolbarGroup.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Toolbar/ToolbarItem.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Toolbar/ToolbarSection.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Toolbar/Toolbar.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/Toolbar/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/layouts/index.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/styles/gutters.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/styles/sizes.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Alert/alert.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/AppLauncher/app-launcher.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Avatar/avatar.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Backdrop/backdrop.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/BackgroundImage/background-image.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Badge/badge.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Button/button.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Card/card.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/ChipGroup/chip-group.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Chip/chip.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Content/content.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/DataList/data-list.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Dropdown/dropdown.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/EmptyState/empty-state.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/FormControl/form-control.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Form/form.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/ModalBox/modal-box.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Nav/nav.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Page/page.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Switch/switch.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Tabs/tabs.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Title/title.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/components/Tooltip/tooltip.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/layouts/Bullseye/bullseye.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/layouts/Gallery/gallery.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/layouts/Grid/grid.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/layouts/Level/level.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/layouts/Stack/stack.css.js</include>
<include>**/node_modules/@patternfly/react-core/dist/umd/@patternfly/patternfly/utilities/Accessibility/accessibility.css.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/common.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/createIcon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/amazon-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/angle-down-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/angle-left-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/angle-right-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/arrow-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/bars-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/bitbucket-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/builder-image-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/caret-down-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/check-circle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/check-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/chrome-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/cube-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/edge-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/edit-alt-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/ellipsis-v-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/exclamation-circle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/exclamation-triangle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/facebook-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/firefox-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/github-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/gitlab-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/globe-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/google-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/info-alt-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/info-circle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/instagram-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/internet-explorer-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/linkedin-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/link-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/microsoft-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/openshift-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/opera-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/passport-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/paypal-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/plus-circle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/redo-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/remove2-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/safari-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/share-alt-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/stack-overflow-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/times-circle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/times-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/twitter-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/unlink-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/user-check-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/warning-triangle-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/icons/yandex-international-icon.js</include>
<include>**/node_modules/@patternfly/react-icons/dist/umd/index.js</include>
<include>**/node_modules/@patternfly/react-styles/dist/umd/StyleSheet.js</include>
<include>**/node_modules/@patternfly/react-styles/dist/umd/index.js</include>
<include>**/node_modules/@patternfly/react-styles/dist/umd/utils.js</include>
<include>**/node_modules/@patternfly/react-tokens/dist/umd/index.js</include>
<include>**/node_modules/@tippy.js/react/dist/Tippy.min.js</include>
</includes>
</resource>
</resources>
</build>

View file

@ -105,6 +105,8 @@
</#list>
</#if>
<link rel="stylesheet" type="text/css" href="${resourceUrl}/public/base.css"/>
<link rel="stylesheet" type="text/css" href="${resourceUrl}/public/app.css"/>
<link href="${resourceUrl}/public/layout.css" rel="stylesheet"/>
</head>
@ -128,13 +130,8 @@
document.getElementById("landingLoggedInUser").innerHTML = loggedInUserName('${msg("unknownUser")}', '${msg("fullName")}');
}
loadjs("/node_modules/systemjs/dist/system.src.js", function() {
loadjs("/systemjs.config.js", function() {
System.import('${resourceUrl}/Main.js').catch(function (err) {
console.error(err);
});
});
});
loadjs("/app/Main.js");
}).error(function() {
alert('failed to initialize keycloak');
});

View file

@ -0,0 +1,19 @@
{
"plugins": [
[
"snowpack/assets/babel-plugin.js",
{
"webModulesUrl": "./web_modules",
"moduleResolution": "node"
}
],
[
"@babel/plugin-proposal-class-properties",
{}
]
],
"presets": [
"@babel/preset-react",
"@babel/preset-typescript"
]
}

View file

@ -13,8 +13,12 @@ node_modules
# Don't ignore these
!keycloak.js
!systemjs.config.extras.js
!systemjs.config.js
!snowpack.config.js
!.eslintrc.js
!WelcomePageScripts.js
!content.js
public/app.css
public/base.css
public/assets/
web_modules/

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -29,13 +29,11 @@ var toggleReact = function () {
if (reactScreen) reactScreen.style.display = 'block';
if (reactScreen) reactScreen.style.height = '100%';
} else if (!isWelcomePage() && isReactLoading) {
loadPatternFly();
if (welcomeScreen) welcomeScreen.style.display = 'none';
if (reactScreen) reactScreen.style.display = 'none';
if (spinnerScreen) spinnerScreen.style.display = 'block';
if (spinnerScreen) spinnerScreen.style.height = '100%';
} else {
loadPatternFly();
if (reactScreen) reactScreen.style.display = 'none';
if (spinnerScreen) spinnerScreen.style.display = 'none';
if (welcomeScreen) welcomeScreen.style.display = 'block';
@ -43,16 +41,6 @@ var toggleReact = function () {
}
};
var patternFlyHasLoaded = false;
var loadPatternFly = function () {
if (patternFlyHasLoaded) return;
const link = document.createElement("link");
link.rel="stylesheet";
link.href=resourceUrl + "/node_modules/@patternfly/patternfly/patternfly.min.css";
document.head.appendChild(link);
patternFlyHasLoaded = true;
}
function loggedInUserName() {
let userName = l18nMsg['unknownUser'];
if (keycloak.tokenParsed) {
@ -73,9 +61,8 @@ function loggedInUserName() {
var loadjs = function (url, loadListener) {
const script = document.createElement("script");
script.src = resourceUrl + url;
script.type = "module";
if (loadListener)
script.addEventListener("load", loadListener);
document.head.appendChild(script);
};

View file

@ -16,8 +16,6 @@
import * as React from 'react';
import * as moment from 'moment';
import {KeycloakService} from './keycloak-service/keycloak.service';
import {PageNav} from './PageNav';
@ -42,7 +40,6 @@ declare const resourceUrl: string;
declare const brandImg: string;
declare const brandUrl: string;
export interface AppProps {};
export class App extends React.Component<AppProps> {
private kcSvc: KeycloakService = KeycloakService.Instance;
@ -60,9 +57,6 @@ export class App extends React.Component<AppProps> {
this.kcSvc.login();
}
// globally set up locale for date formatting
moment.locale(locale);
const username = (
<span style={{marginLeft: '10px'}} id="loggedInUser">{loggedInUserName()}</span>
);

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -70,7 +70,7 @@ function isChildOf(parent: Expansion, child: PageDef): boolean {
if (isExpansion(item) && isChildOf(item, child)) return true;
if (parent.groupId === child.groupId) return true;
}
return false;
}
@ -91,10 +91,10 @@ function createNavItems(activePage: PageDef, contentParam: ContentItem[], groupN
const page: PageDef = item as PageDef;
return <NavItem id={navLinkId}
groupId={item.groupId}
itemId={item.itemId}
key={item.itemId}
to={'#/app/' + page.path}
isActive={activePage.itemId === item.itemId}
itemId={item.itemId}
key={item.itemId}
to={'#/app/' + page.path}
isActive={activePage.itemId === item.itemId}
type="button">
{Msg.localize(page.label, page.labelParams)}
</NavItem>
@ -112,7 +112,7 @@ export function makeNavItems(activePage: PageDef): React.ReactNode {
function setIds(contentParam: ContentItem[], groupNum: number): number {
if (typeof contentParam === 'undefined') return groupNum;
let expansionGroupNum = groupNum;
for (let i = 0; i < contentParam.length; i++) {
const item: ContentItem = contentParam[i];
if (isExpansion(item)) {
@ -126,7 +126,7 @@ function setIds(contentParam: ContentItem[], groupNum: number): number {
item.itemId = itemId(groupNum, i);
}
};
return expansionGroupNum;
}
@ -134,7 +134,7 @@ export function initGroupAndItemIds(): void {
setIds(content, 0);
console.log({content});
}
// get rid of Expansions and put all PageDef items into a single array
export function flattenContent(pageDefs: ContentItem[]): PageDef[] {
const flat: PageDef[] = [];
@ -153,7 +153,7 @@ export function flattenContent(pageDefs: ContentItem[]): PageDef[] {
export function makeRoutes(): React.ReactNode {
if (typeof content === 'undefined') return (<span/>);
const pageDefs: PageDef[] = flattenContent(content);
const pageDefs: PageDef[] = flattenContent(content);
const routes: React.ReactElement<Route>[] = pageDefs.map((page: PageDef) => {
if (isModulePageDef(page)) {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,19 +16,18 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {System} from 'systemjs';
import {HashRouter} from 'react-router-dom';
import {App} from './app/App';
import {ContentItem, ModulePageDef, flattenContent, initGroupAndItemIds, isExpansion, isModulePageDef} from './app/ContentPages';
import {App} from './App';
import {ContentItem, ModulePageDef, flattenContent, initGroupAndItemIds, isExpansion, isModulePageDef} from './ContentPages';
declare let isReactLoading: boolean;
declare function toggleReact(): void;
export interface MainProps {}
export class Main extends React.Component<MainProps> {
public constructor(props: MainProps) {
super(props);
}
@ -56,7 +55,7 @@ function removeHidden(items: ContentItem[]): ContentItem[] {
for (let item of items) {
if (item.hidden) continue;
if (isExpansion(item)) {
visible.push(item);
item.content = removeHidden(item.content);
@ -76,7 +75,8 @@ initGroupAndItemIds();
function loadModule(modulePage: ModulePageDef): Promise<ModulePageDef> {
return new Promise ((resolve, reject) => {
System.import(resourceUrl + modulePage.modulePath).then( (module: React.Component) => {
console.log('loading: ' + resourceUrl + modulePage.modulePath);
import(resourceUrl + modulePage.modulePath).then( (module: React.Component) => {
modulePage.module = module;
resolve(modulePage);
}).catch((error: Error) => {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,19 +19,19 @@ import {withRouter, RouteComponentProps} from 'react-router-dom';
import {Nav, NavList} from '@patternfly/react-core';
import {makeNavItems, flattenContent, ContentItem, PageDef} from './ContentPages';
declare const content: ContentItem[];
export interface PageNavProps extends RouteComponentProps {}
export interface PageNavState {}
class PageNavigation extends React.Component<PageNavProps, PageNavState> {
public constructor(props: PageNavProps) {
super(props);
}
private findActiveItem(): PageDef {
const currentPath: string = this.props.location.pathname;
const items: PageDef[] = flattenContent(content);
@ -45,7 +45,7 @@ class PageNavigation extends React.Component<PageNavProps, PageNavState> {
return firstItem;
}
public render(): React.ReactNode {
const activeItem: PageDef = this.findActiveItem();
return (

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,7 +15,7 @@
*/
import * as React from 'react';
import {Dropdown, KebabToggle, Toolbar, ToolbarGroup, ToolbarItem} from '@patternfly/react-core';
import {ReferrerDropdownItem} from './widgets/ReferrerDropdownItem';
@ -33,18 +33,18 @@ export class PageToolbar extends React.Component<PageToolbarProps, PageToolbarSt
public constructor(props: PageToolbarProps) {
super(props);
this.state = {
isKebabDropdownOpen: false,
};
}
private onKebabDropdownToggle = (isKebabDropdownOpen: boolean) => {
this.setState({
isKebabDropdownOpen
});
};
public render(): React.ReactNode {
const kebabDropdownItems = [];
if (this.hasReferrer) {
@ -52,9 +52,9 @@ export class PageToolbar extends React.Component<PageToolbarProps, PageToolbarSt
<ReferrerDropdownItem key='referrerDropdownItem'/>
)
}
kebabDropdownItems.push(<LogoutDropdownItem key='LogoutDropdownItem'/>);
return (
<Toolbar>
{this.hasReferrer &&
@ -64,12 +64,12 @@ export class PageToolbar extends React.Component<PageToolbarProps, PageToolbarSt
</ToolbarItem>
</ToolbarGroup>
}
<ToolbarGroup key='secondGroup'>
<ToolbarItem className="pf-m-icons" key='logout'>
<LogoutButton/>
</ToolbarItem>
<ToolbarItem key='kebab' className="pf-m-mobile">
<Dropdown
isPlain

View file

@ -15,111 +15,127 @@
* the License.
*/
//import {KeycloakNotificationService} from '../notification/keycloak-notification.service';
import {KeycloakService} from '../keycloak-service/keycloak.service';
import Axios, {AxiosRequestConfig, AxiosResponse, AxiosError} from 'axios';
import {ContentAlert} from '../content/ContentAlert';
//import {NotificationType} from 'patternfly-ng/notification';*/
type ConfigResolve = (config: RequestInit) => void;
type AxiosResolve = (response: AxiosResponse) => void;
type ConfigResolve = (config: AxiosRequestConfig) => void;
type ErrorReject = (error: Error) => void;
export interface HttpResponse<T = {}> extends Response {
data?: T;
}
/**
export interface RequestInitWithParams extends RequestInit {
params?: {[name: string]: string | number};
}
export class AccountServiceError extends Error {
constructor(public response: HttpResponse) {
super(response.statusText);
}
}
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2018 Red Hat Inc.
*/
export class AccountServiceClient {
private static instance: AccountServiceClient = new AccountServiceClient();
class AccountServiceClient {
private kcSvc: KeycloakService = KeycloakService.Instance;
private accountUrl: string = this.kcSvc.authServerUrl() + 'realms/' + this.kcSvc.realm() + '/account';
private constructor() {}
constructor() {}
public static get Instance(): AccountServiceClient {
return AccountServiceClient.instance;
}
public doGet(endpoint: string,
config?: AxiosRequestConfig): Promise<AxiosResponse> {
public async doGet<T>(endpoint: string,
config?: RequestInitWithParams): Promise<HttpResponse<T>> {
return this.doRequest(endpoint, {...config, method: 'get'});
}
public doDelete(endpoint: string,
config?: AxiosRequestConfig): Promise<AxiosResponse> {
public async doDelete<T>(endpoint: string,
config?: RequestInitWithParams): Promise<HttpResponse<T>> {
return this.doRequest(endpoint, {...config, method: 'delete'});
}
public doPut(endpoint: string,
config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.doRequest(endpoint, {...config,
method: 'put',
headers: {'Content-Type': 'application/json'}
}
);
public async doPost<T>(endpoint: string,
body: string | {},
config?: RequestInitWithParams): Promise<HttpResponse<T>> {
return this.doRequest(endpoint, {...config, body: JSON.stringify(body), method: 'post'});
}
public doPost(endpoint: string,
config?: AxiosRequestConfig): Promise<AxiosResponse> {
return this.doRequest(endpoint, {...config, method: 'post'});
public async doPut<T>(endpoint: string,
body: string | {},
config?: RequestInitWithParams): Promise<HttpResponse<T>> {
return this.doRequest(endpoint, {...config, body: JSON.stringify(body), method: 'put'});
}
public doRequest(endpoint: string,
config?: AxiosRequestConfig): Promise<AxiosResponse> {
public async doRequest<T>(endpoint: string,
config?: RequestInitWithParams): Promise<HttpResponse<T>> {
return new Promise((resolve: AxiosResolve, reject: ErrorReject) => {
this.makeConfig(endpoint, config)
.then((config: AxiosRequestConfig) => {
this.axiosRequest(config, resolve, reject);
}).catch( (error: AxiosError) => {
this.handleError(error);
reject(error);
});
});
const response: HttpResponse<T> = await fetch(this.makeUrl(endpoint, config).toString(),
await this.makeConfig(config));
try {
response.data = await response.json();
} catch (e) {} // ignore. Might be empty
if (!response.ok) {
this.handleError(response);
throw new AccountServiceError(response);
}
return response;
}
private axiosRequest(config: AxiosRequestConfig,
resolve: AxiosResolve,
reject: ErrorReject): void {
Axios.request(config)
.then((response: AxiosResponse) => {
resolve(response);
})
.catch((error: AxiosError) => {
this.handleError(error);
reject(error);
});
}
private handleError(error: AxiosError): void {
if (error != null && error.response != null && error.response.status === 401) {
private handleError(response: HttpResponse): void {
if (response != null && response.status === 401) {
// session timed out?
this.kcSvc.login();
}
console.log(error);
if (error != null && error.response != null && error.response.data != null && error.response.data.errorMessage) {
ContentAlert.danger(error.response.data.errorMessage);
if (response != null && response.data != null && response.data.hasOwnProperty('errorMessage')) {
ContentAlert.danger(response.data['errorMessage']);
} else {
ContentAlert.danger(error.name + ': ' + error.message);
ContentAlert.danger(response.statusText);
}
}
private makeConfig(endpoint: string, config: AxiosRequestConfig = {}): Promise<AxiosRequestConfig> {
private makeUrl(endpoint: string, config?: RequestInitWithParams): URL {
if (endpoint.startsWith('http')) return new URL(endpoint);
const url = new URL(this.accountUrl + endpoint);
// add request params
if (config && config.hasOwnProperty('params')) {
const params: {[name: string]: string} = config.params as {} || {};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
}
return url;
}
private makeConfig(config: RequestInit = {}): Promise<RequestInit> {
return new Promise( (resolve: ConfigResolve) => {
this.kcSvc.getToken()
.then( (token: string) => {
resolve( {
...config,
baseURL: this.accountUrl,
url: endpoint,
headers: {...config.headers, Authorization: 'Bearer ' + token}
headers: {'Content-Type': 'application/json',
...config.headers,
Authorization: 'Bearer ' + token}
});
}).catch(() => {
this.kcSvc.login();
});
});
}
}
const AccountService: AccountServiceClient = new AccountServiceClient();
export default AccountService;
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
event.promise.catch(error => {
if (error instanceof AccountServiceError) {
// We already handled the error. Ignore unhandled rejection.
event.preventDefault();
}
});
});

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,7 +17,7 @@
import * as React from 'react';
import {Alert, AlertActionCloseButton} from '@patternfly/react-core';
import {Msg} from '../widgets/Msg';
interface ContentAlertProps {}
type AlertVariant = 'success' | 'danger' | 'warning' | 'info';
@ -31,53 +31,53 @@ export class ContentAlert extends React.Component<ContentAlertProps, ContentAler
private constructor(props: ContentAlertProps) {
super(props);
this.state = {isVisible: false, message: '', variant: 'success'};
ContentAlert.instance = this;
}
/**
* @param message A literal text message or localization key.
*/
public static success(message: string, params?: string[]): void {
ContentAlert.instance.postAlert('success', message, params);
}
/**
* @param message A literal text message or localization key.
*/
public static danger(message: string, params?: string[]): void {
ContentAlert.instance.postAlert('danger', message, params);
}
/**
* @param message A literal text message or localization key.
*/
public static warning(message: string, params?: string[]): void {
ContentAlert.instance.postAlert('warning', message, params);
}
/**
* @param message A literal text message or localization key.
*/
public static info(message: string, params?: string[]): void {
ContentAlert.instance.postAlert('info', message, params);
}
private hideAlert = () => {
this.setState({isVisible: false});
}
private postAlert = (variant: AlertVariant, message: string, params?: string[]) => {
this.setState({isVisible: true,
message: Msg.localize(message, params),
this.setState({isVisible: true,
message: Msg.localize(message, params),
variant});
if (variant !== 'danger') {
setTimeout(() => this.setState({isVisible: false}), 5000);
}
}
public render(): React.ReactNode {
return (
<React.Fragment>

View file

@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import { AxiosResponse } from 'axios';
import { ActionGroup, Button, Form, FormGroup, TextInput } from '@patternfly/react-core';
import { AccountServiceClient } from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import { Features } from '../../widgets/features';
import { Msg } from '../../widgets/Msg';
import { ContentPage } from '../ContentPage';
@ -74,15 +72,15 @@ export class AccountPage extends React.Component<AccountPageProps, AccountPageSt
}
private fetchPersonalInfo(): void {
AccountServiceClient.Instance.doGet("/")
.then((response: AxiosResponse<FormFields>) => {
AccountService.doGet<FormFields>("/")
.then((response: HttpResponse<FormFields>) => {
this.setState(this.DEFAULT_STATE);
const formFields = response.data;
if (!formFields.attributes || !formFields.attributes.locale) {
formFields.attributes = { locale: [locale] };
if (!formFields!.attributes || !formFields!.attributes.locale) {
formFields!.attributes = { locale: [locale] };
}
this.setState({...{ formFields: formFields }});
this.setState({...{ formFields: formFields as FormFields }});
});
}
@ -106,8 +104,8 @@ export class AccountPage extends React.Component<AccountPageProps, AccountPageSt
const isValid = form.checkValidity();
if (isValid) {
const reqData: FormFields = { ...this.state.formFields };
AccountServiceClient.Instance.doPost("/", { data: reqData })
.then(() => { // to use response, say ((response: AxiosResponse<FormFields>) => {
AccountService.doPost<void>("/", reqData)
.then(() => {
ContentAlert.success('accountUpdatedMessage');
if (locale !== this.state.formFields.attributes!.locale![0]) {
window.location.reload();
@ -202,7 +200,7 @@ export class AccountPage extends React.Component<AccountPageProps, AccountPageSt
>
</TextInput>
</FormGroup>
{features.isInternationalizationEnabled && <FormGroup
{features.isInternationalizationEnabled && <FormGroup
label={Msg.localize('selectLocale')}
isRequired
fieldId="locale"

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,7 +15,6 @@
*/
import * as React from 'react';
import { AxiosResponse } from 'axios';
import {
DataList,
@ -32,7 +31,7 @@ import {
import { InfoAltIcon, CheckIcon, BuilderImageIcon } from '@patternfly/react-icons';
import { ContentPage } from '../ContentPage';
import { ContinueCancelModal } from '../../widgets/ContinueCancelModal';
import { AccountServiceClient } from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import { Msg } from '../../widgets/Msg';
declare const locale: string;
@ -82,7 +81,7 @@ export class ApplicationsPage extends React.Component<ApplicationsPageProps, App
}
private removeConsent = (clientId: string) => {
AccountServiceClient.Instance.doDelete("/applications/" + clientId + "/consent")
AccountService.doDelete("/applications/" + clientId + "/consent")
.then(() => {
this.fetchApplications();
});
@ -95,9 +94,9 @@ export class ApplicationsPage extends React.Component<ApplicationsPageProps, App
};
private fetchApplications(): void {
AccountServiceClient.Instance.doGet("/applications")
.then((response: AxiosResponse<Application[]>) => {
const applications = response.data;
AccountService.doGet<Application[]>("/applications")
.then((response: HttpResponse<Application[]>) => {
const applications = response.data || [];
this.setState({
isRowOpen: new Array(applications.length).fill(false),
applications: applications

View file

@ -15,10 +15,9 @@
*/
import * as React from 'react';
import * as moment from 'moment';
import {AxiosResponse} from 'axios';
import {AccountServiceClient} from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import TimeUtil from '../../util/TimeUtil';
import {
Bullseye,
@ -104,26 +103,26 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
}
private signOutAll = () => {
AccountServiceClient.Instance.doDelete("/sessions")
AccountService.doDelete("/sessions")
.then( () => {
KeycloakService.Instance.logout(baseUrl);
});
}
private signOutSession = (device: Device, session: Session) => {
AccountServiceClient.Instance.doDelete("/sessions/" + session.id)
AccountService.doDelete("/sessions/" + session.id)
.then (() => {
this.fetchDevices();
ContentAlert.success(Msg.localize('signedOutSession', [session.browser, device.os]));
ContentAlert.success('signedOutSession', [session.browser, device.os]);
});
}
private fetchDevices(): void {
AccountServiceClient.Instance.doGet("/sessions/devices")
.then((response: AxiosResponse<Device[]>) => {
AccountService.doGet<Device[]>("/sessions/devices")
.then((response: HttpResponse<Device[]>) => {
console.log({response});
let devices: Device[] = this.moveCurrentToTop(response.data);
let devices: Device[] = this.moveCurrentToTop(response.data as Device[]);
this.setState({
devices: devices
@ -155,7 +154,7 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
}
private time(time: number): string {
return moment(time * 1000).format('LLLL');
return TimeUtil.format(time * 1000);
}
private elementId(item: string, session: Session): string {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,19 +16,18 @@
import * as React from 'react';
import {withRouter, RouteComponentProps} from 'react-router-dom';
import {AxiosResponse} from 'axios';
import {
Badge,
Button,
DataList,
DataList,
DataListAction,
DataListItemCells,
DataListItemCells,
DataListCell,
DataListItemRow,
DataListItemRow,
Stack,
StackItem,
Title,
Title,
TitleLevel,
DataListItem,
} from '@patternfly/react-core';
@ -37,7 +36,7 @@ import {
BitbucketIcon,
CubeIcon,
FacebookIcon,
GithubIcon,
GithubIcon,
GitlabIcon,
GoogleIcon,
InstagramIcon,
@ -51,7 +50,7 @@ import {
UnlinkIcon
} from '@patternfly/react-icons';
import {AccountServiceClient} from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import {Msg} from '../../widgets/Msg';
import {ContentPage} from '../ContentPage';
import {createRedirect} from '../../util/RedirectUri';
@ -77,7 +76,7 @@ interface LinkedAccountsPageState {
* @author Stan Silvert
*/
class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, LinkedAccountsPageState> {
public constructor(props: LinkedAccountsPageProps) {
super(props);
this.state = {
@ -89,11 +88,11 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
}
private getLinkedAccounts(): void {
AccountServiceClient.Instance.doGet("/linked-accounts")
.then((response: AxiosResponse<LinkedAccount[]>) => {
AccountService.doGet<LinkedAccount[]>("/linked-accounts")
.then((response: HttpResponse<LinkedAccount[]>) => {
console.log({response});
const linkedAccounts = response.data.filter((account) => account.connected);
const unLinkedAccounts = response.data.filter((account) => !account.connected);
const linkedAccounts = response.data!.filter((account) => account.connected);
const unLinkedAccounts = response.data!.filter((account) => !account.connected);
this.setState({linkedAccounts: linkedAccounts, unLinkedAccounts: unLinkedAccounts});
});
}
@ -101,8 +100,8 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
private unLinkAccount(account: LinkedAccount): void {
const url = '/linked-accounts/' + account.providerName;
AccountServiceClient.Instance.doDelete(url)
.then((response: AxiosResponse) => {
AccountService.doDelete<void>(url)
.then((response: HttpResponse<void>) => {
console.log({response});
this.getLinkedAccounts();
});
@ -113,10 +112,10 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
const redirectUri: string = createRedirect(this.props.location.pathname);
AccountServiceClient.Instance.doGet(url, { params: {providerId: account.providerName, redirectUri}})
.then((response: AxiosResponse<{accountLinkUri: string}>) => {
AccountService.doGet<{accountLinkUri: string}>(url, { params: {providerId: account.providerName, redirectUri}})
.then((response: HttpResponse<{accountLinkUri: string}>) => {
console.log({response});
window.location.href = response.data.accountLinkUri;
window.location.href = response.data!.accountLinkUri;
});
}
@ -141,7 +140,7 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
<DataList id="unlinked-idps" aria-label='foo'>
{this.makeRows(this.state.unLinkedAccounts, false)}
</DataList>
</StackItem>
</StackItem>
</Stack>
</ContentPage>
);
@ -173,7 +172,7 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
return (
<> {
accounts.map( (account: LinkedAccount) => (
<DataListItem id={`${account.providerAlias}-idp`} key={account.providerName} aria-labelledby="simple-item1">
<DataListItemRow key={account.providerName}>
@ -190,9 +189,9 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
</DataListItemRow>
</DataListItem>
))
} </>
)
}
@ -200,7 +199,7 @@ class LinkedAccountsPage extends React.Component<LinkedAccountsPageProps, Linked
if (account.social) {
return (<Badge><Msg msgKey='socialLogin'/></Badge>);
}
return (<Badge style={{backgroundColor: "green"}} ><Msg msgKey='systemDefined'/></Badge>);
}

View file

@ -33,7 +33,7 @@ import { EditAltIcon } from '@patternfly/react-icons';
import { Resource, Permission, Scope } from './MyResourcesPage';
import { Msg } from '../../widgets/Msg';
import { AccountServiceClient } from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import { ContentAlert } from '../ContentAlert';
interface EditTheResourceProps {
@ -74,7 +74,7 @@ export class EditTheResource extends React.Component<EditTheResourceProps, EditT
async deletePermission(permission: Permission, scope: Scope): Promise<void> {
permission.scopes.splice(permission.scopes.indexOf(scope), 1);
await AccountServiceClient.Instance.doPut(`/resources/${this.props.resource._id}/permissions`, {data: [permission]});
await AccountService.doPut(`/resources/${this.props.resource._id}/permissions`, [permission]);
ContentAlert.success(Msg.localize('shareSuccess'));
this.props.onClose(this.props.resource, this.props.row);
}
@ -120,7 +120,7 @@ export class EditTheResource extends React.Component<EditTheResourceProps, EditT
{p.username}
</DataListCell>,
<DataListCell key={'permission-' + row} width={5}>
<ChipGroup>
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='permissions' categoryName={Msg.localize('permissions')}>
{
p.scopes.length > 0 && p.scopes.map(scope => (

View file

@ -15,13 +15,12 @@
*/
import * as React from 'react';
import {AxiosResponse} from 'axios';
import parse from '../../util/ParseLink';
import { Button, Level, LevelItem, Stack, StackItem, Tab, Tabs, TextInput } from '@patternfly/react-core';
import {AccountServiceClient} from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import {ResourcesTable} from './ResourcesTable';
import {ContentPage} from '../ContentPage';
@ -102,7 +101,7 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
private isSharedWithMeTab(): boolean {
return this.state.activeTabKey === SHARED_WITH_ME_TAB;
}
}
private hasNext(): boolean {
if (this.isSharedWithMeTab()) {
@ -137,9 +136,9 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
}
private fetchResources(url: string, extraParams?: Record<string, string|number>): void {
AccountServiceClient.Instance.doGet(url, {params: extraParams})
.then((response: AxiosResponse<Resource[]>) => {
const resources: Resource[] = response.data;
AccountService.doGet<Resource[]>(url, {params: extraParams})
.then((response: HttpResponse<Resource[]>) => {
const resources: Resource[] = response.data || [];
resources.forEach((resource: Resource) => resource.shareRequests = []);
// serialize the Scope objects from JSON so that toString() will work.
@ -164,9 +163,9 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
}
private fetchShareRequests(resource: Resource): void {
AccountServiceClient.Instance.doGet('/resources/' + resource._id + '/permissions/requests')
.then((response: AxiosResponse<Permission[]>) => {
resource.shareRequests = response.data;
AccountService.doGet('/resources/' + resource._id + '/permissions/requests')
.then((response: HttpResponse<Permission[]>) => {
resource.shareRequests = response.data || [];
if (resource.shareRequests.length > 0) {
this.forceUpdate();
}
@ -174,8 +173,9 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
}
private fetchPending = async () => {
const response = await AccountServiceClient.Instance.doGet(`/resources/pending-requests`);
response.data.forEach((pendingRequest: Resource) => {
const response: HttpResponse<Resource[]> = await AccountService.doGet(`/resources/pending-requests`);
const resources: Resource[] = response.data || [];
resources.forEach((pendingRequest: Resource) => {
this.state.sharedWithMe.data.forEach(resource => {
if (resource._id === pendingRequest._id) {
resource.shareRequests = [{username: 'me', scopes: pendingRequest.scopes}]
@ -185,8 +185,8 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
});
}
private parseResourceResponse(response: AxiosResponse<Resource[]>): PaginatedResources {
const links: string = response.headers.link;
private parseResourceResponse(response: HttpResponse<Resource[]>): PaginatedResources {
const links: string | undefined = response.headers.get('link') || undefined;
const parsed = parse(links);
let next = '';
@ -197,7 +197,7 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
if (parsed.prev) prev = parsed.prev;
}
const resources = response.data;
const resources: Resource[] = response.data || [];
return {nextUrl: next, prevUrl: prev, data: resources};
}
@ -235,7 +235,7 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
<LevelItem>
{this.hasPrevious() && <Button onClick={this.handlePreviousClick}>&lt;<Msg msgKey='previousPage'/></Button>}
</LevelItem>
<LevelItem>
{this.hasPrevious() && <Button onClick={this.handleFirstPageClick}><Msg msgKey='firstPage'/></Button>}
</LevelItem>
@ -252,7 +252,7 @@ export class MyResourcesPage extends React.Component<MyResourcesPageProps, MyRes
this.setState({nameFilter: value});
this.fetchFilteredResources({name: value});
}
private clearNextPrev(): void {
const newMyResources: PaginatedResources = this.state.myResources;
newMyResources.nextUrl = '';

View file

@ -17,7 +17,7 @@ import * as React from 'react';
import { Button, Modal, Text, Badge, DataListItem, DataList, TextVariants, DataListItemRow, DataListItemCells, DataListCell, Chip } from '@patternfly/react-core';
import { UserCheckIcon } from '@patternfly/react-icons';
import { AccountServiceClient } from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import { Msg } from '../../widgets/Msg';
import { ContentAlert } from '../ContentAlert';
import { Resource, Scope, Permission } from './MyResourcesPage';
@ -55,13 +55,16 @@ export class PermissionRequest extends React.Component<PermissionRequestProps, P
const id = this.props.resource._id
this.handleToggleDialog();
const permissionsRequest = await AccountServiceClient.Instance.doGet(`/resources/${id}/permissions`);
const userScopes = permissionsRequest.data.find((p: Permission) => p.username === username).scopes;
const permissionsRequest: HttpResponse<Permission[]> = await AccountService.doGet(`/resources/${id}/permissions`);
const permissions: Permission[] = permissionsRequest.data || [];
// Erik - I had to add the exclamation point. Can we be sure that the 'find' will not return undefined?
const userScopes = permissions.find((p: Permission) => p.username === username)!.scopes;
if (approve) {
userScopes.push(...scopes);
}
try {
await AccountServiceClient.Instance.doPut(`/resources/${id}/permissions`, { data: [{ username: username, scopes: userScopes }] })
await AccountService.doPut(`/resources/${id}/permissions`, [{ username: username, scopes: userScopes }] )
ContentAlert.success(Msg.localize('shareSuccess'));
this.props.onClose();
} catch (e) {

View file

@ -15,7 +15,6 @@
*/
import * as React from 'react';
import {AxiosResponse} from 'axios';
import {
DataList,
@ -34,7 +33,7 @@ import {
import { Remove2Icon } from '@patternfly/react-icons';
import {AccountServiceClient} from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import {PermissionRequest} from "./PermissionRequest";
import {ShareTheResource} from "./ShareTheResource";
import {Permission, Resource} from "./MyResourcesPage";
@ -65,10 +64,10 @@ export class ResourcesTable extends AbstractResourcesTable<CollapsibleResourcesT
};
private fetchPermissions(resource: Resource, row: number): void {
AccountServiceClient.Instance.doGet('resources/' + resource._id + '/permissions')
.then((response: AxiosResponse<Permission[]>) => {
AccountService.doGet(`/resources/${resource._id}/permissions`)
.then((response: HttpResponse<Permission[]>) => {
const newPermissions: Map<number, Permission[]> = new Map(this.state.permissions);
newPermissions.set(row, response.data);
newPermissions.set(row, response.data || []);
this.setState({permissions: newPermissions});
}
);
@ -76,7 +75,7 @@ export class ResourcesTable extends AbstractResourcesTable<CollapsibleResourcesT
private removeShare(resource: Resource, row: number): void {
const permissions = this.state.permissions.get(row)!.map(a => ({ username: a.username, scopes: [] }));
AccountServiceClient.Instance.doPut(`/resources/${resource._id}/permissions`, { data: permissions })
AccountService.doPut(`/resources/${resource._id}/permissions`, permissions)
.then(() => {
ContentAlert.success(Msg.localize('shareSuccess'));
this.onToggle(row);
@ -159,8 +158,8 @@ export class ResourcesTable extends AbstractResourcesTable<CollapsibleResourcesT
<Level gutter='md'>
<LevelItem><span/></LevelItem>
<LevelItem>
<ShareTheResource resource={resource}
permissions={this.state.permissions.get(row)!}
<ShareTheResource resource={resource}
permissions={this.state.permissions.get(row)!}
sharedWithUsersMsg={this.sharedWithUsersMessage(row)}
onClose={this.fetchPermissions.bind(this)}
row={row}/>

View file

@ -16,16 +16,16 @@
import * as React from 'react';
import {
Button,
import {
Button,
Chip,
ChipGroup,
ChipGroupToolbarItem,
Form,
FormGroup,
Form,
FormGroup,
Gallery,
GalleryItem,
Modal,
Modal,
Stack,
StackItem,
TextInput
@ -33,7 +33,7 @@ import {
import { ShareAltIcon } from '@patternfly/react-icons';
import {AccountServiceClient} from '../../account-service/account.service';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import { Resource, Permission, Scope } from './MyResourcesPage';
import { Msg } from '../../widgets/Msg';
import {ContentAlert} from '../ContentAlert';
@ -63,7 +63,7 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
public constructor(props: ShareTheResourceProps) {
super(props);
this.state = {
this.state = {
isOpen: false,
permissionsSelected: [],
permissionsUnSelected: this.props.resource.scopes,
@ -83,23 +83,23 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
private handleAddPermission = () => {
const rscId: string = this.props.resource._id;
const newPermissions: string[] = [];
const newPermissions: string[] = [];
for (const permission of this.state.permissionsSelected) {
newPermissions.push(permission.name);
}
const permissions = [];
for (const username of this.state.usernames) {
permissions.push({username: username, scopes: newPermissions});
}
this.handleToggleDialog();
AccountServiceClient.Instance.doPut('/resources/' + rscId + '/permissions', {data: permissions})
AccountService.doPut(`/resources/${rscId}/permissions`, permissions)
.then(() => {
ContentAlert.success(Msg.localize('shareSuccess'));
ContentAlert.success('shareSuccess');
this.props.onClose(this.props.resource, this.props.row);
})
};
@ -217,9 +217,9 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
<Msg msgKey="add"/>
</Button>
</GalleryItem>
</Gallery>
<ChipGroup>
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='users-selected' categoryName='Share with '>
{this.state.usernames.map((currentChip: string) => (
<Chip key={currentChip} onClick={() => this.handleDeleteUsername(currentChip)}>
@ -234,7 +234,7 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
fieldId="permissions-selected"
>
{this.state.permissionsSelected.length < 1 && <strong>Select permissions below:</strong>}
<ChipGroup>
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='permissions-selected' categoryName='Grant Permissions '>
{this.state.permissionsSelected.map((currentChip: Scope) => (
<Chip key={currentChip.toString()} onClick={() => this.handleSelectPermission(currentChip)}>
@ -248,7 +248,7 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
label=""
fieldId="permissions-not-selected"
>
<ChipGroup>
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='permissions-unselected' categoryName='Not Selected '>
{this.state.permissionsUnSelected.map((currentChip: Scope) => (
<Chip key={currentChip.toString()} onClick={() => this.handleSelectPermission(currentChip)}>
@ -264,7 +264,7 @@ export class ShareTheResource extends React.Component<ShareTheResourceProps, Sha
<StackItem isFilled>
{this.props.sharedWithUsersMsg}
</StackItem>
</Stack>
</Modal>
</React.Fragment>

View file

@ -78,20 +78,22 @@ export class SharedResourcesTable extends AbstractResourcesTable<ResourcesTableS
<a href={resource.client.baseUrl}>{this.getClientName(resource.client)}</a>
</DataListCell>,
<DataListCell key={'permissions-' + row} width={2}>
<ChipGroup>
{ resource.scopes.length > 0 &&
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='permissions' categoryName={Msg.localize('permissions')}>
{
resource.scopes.length > 0 && resource.scopes.map(scope => (
resource.scopes.map(scope => (
<Chip key={scope.name} isReadOnly>
{scope.displayName || scope.name}
</Chip>
))
}
</ChipGroupToolbarItem>
</ChipGroup>
</ChipGroup>}
</DataListCell>,
<DataListCell key={'pending-' + row} width={2}>
{resource.shareRequests.length > 0 &&
<ChipGroup withToolbar>
<ChipGroupToolbarItem key='permissions' categoryName={Msg.localize('pending')}>
{
resource.shareRequests[0].scopes.map(scope => (
@ -101,6 +103,7 @@ export class SharedResourcesTable extends AbstractResourcesTable<ResourcesTableS
))
}
</ChipGroupToolbarItem>
</ChipGroup>
}
</DataListCell>
]}

View file

@ -1,4 +1,4 @@
/*
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
@ -10,11 +10,11 @@ import {EmptyState, EmptyStateBody, EmptyStateIcon, Title, TitleLevel} from '@pa
import { WarningTriangleIcon } from '@patternfly/react-icons';
import {withRouter, RouteComponentProps} from 'react-router-dom';
import {Msg} from '../../widgets/Msg';
export interface PageNotFoundProps extends RouteComponentProps {}
class PgNotFound extends React.Component<PageNotFoundProps> {
public constructor(props: PageNotFoundProps) {
super(props);
}

View file

@ -1,158 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* 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
*
* 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.
*/
import * as React from 'react';
import * as moment from 'moment';
import {AxiosResponse} from 'axios';
import {AccountServiceClient} from '../../account-service/account.service';
import {Msg} from '../../widgets/Msg';
export interface PasswordPageProps {
}
interface FormFields {
readonly currentPassword?: string;
readonly newPassword?: string;
readonly confirmation?: string;
}
interface PasswordPageState {
readonly canSubmit: boolean;
readonly registered: boolean;
readonly lastUpdate: number;
readonly formFields: FormFields;
}
export class PasswordPage extends React.Component<PasswordPageProps, PasswordPageState> {
public state: PasswordPageState = {
canSubmit: false,
registered: false,
lastUpdate: -1,
formFields: {currentPassword: '',
newPassword: '',
confirmation: ''}
}
public constructor(props: PasswordPageProps) {
super(props);
AccountServiceClient.Instance.doGet("/credentials/password")
.then((response: AxiosResponse<PasswordPageState>) => {
this.setState({...response.data});
console.log({response});
});
}
private handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const target: HTMLInputElement = event.target;
const value: string = target.value;
const name: string = target.name;
this.setState({
canSubmit: this.requiredFieldsHaveData(name, value),
registered: this.state.registered,
lastUpdate: this.state.lastUpdate,
formFields: {...this.state.formFields, [name]: value}
});
}
private handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
const reqData: FormFields = {...this.state.formFields};
AccountServiceClient.Instance.doPost("/credentials/password", {data: reqData})
.then((response: AxiosResponse<FormFields>) => {
this.setState({canSubmit: false});
alert('Data posted:' + response.statusText);
});
}
private requiredFieldsHaveData(fieldName: string, newValue: string): boolean {
const fields: FormFields = {...this.state.formFields};
fields[fieldName] = newValue;
for (const field of Object.keys(fields)) {
if (!fields[field]) return false;
}
return true;
}
public render(): React.ReactNode {
const displayNone = {display: 'none'};
return (
<div>
<div className="page-header">
<h1 id="pageTitle"><Msg msgKey="changePasswordHtmlTitle"/></h1>
</div>
<div className="col-sm-12 card-pf">
<div className="card-pf-body p-b" id="passwordLastUpdate">
<span className="i pficon pficon-info"></span>
<Msg msgKey="passwordLastUpdateMessage" /> <strong>{moment(this.state.lastUpdate).format('LLLL')}</strong>
</div>
</div>
<div className="col-sm-12 card-pf">
<div className="card-pf-body row">
<div className="col-sm-4 col-md-4">
<div className="card-pf-subtitle" id="updatePasswordSubTitle">
<Msg msgKey="updatePasswordTitle"/>
</div>
<div className="introMessage" id="updatePasswordSubMessage">
<strong><Msg msgKey="updatePasswordMessageTitle"/></strong>
<p><Msg msgKey="updatePasswordMessage"/></p>
</div>
<div className="subtitle"><span className="required">*</span> <Msg msgKey="requiredFields"/></div>
</div>
<div className="col-sm-6 col-md-6">
<form onSubmit={this.handleSubmit} className="form-horizontal">
<input readOnly value="this is not a login form" style={displayNone} type="text"/>
<input readOnly value="this is not a login form" style={displayNone} type="password"/>
<div className="form-group">
<label htmlFor="password" className="control-label"><Msg msgKey="currentPassword"/></label><span className="required">*</span>
<input onChange={this.handleChange} className="form-control" name="currentPassword" autoFocus autoComplete="off" type="password"/>
</div>
<div className="form-group">
<label htmlFor="password-new" className="control-label"><Msg msgKey="passwordNew"/></label><span className="required">*</span>
<input onChange={this.handleChange} className="form-control" id="newPassword" name="newPassword" autoComplete="off" type="password"/>
</div>
<div className="form-group">
<label htmlFor="password-confirm" className="control-label"><Msg msgKey="passwordConfirm"/></label><span className="required">*</span>
<input onChange={this.handleChange} className="form-control" id="confirmation" name="confirmation" autoComplete="off" type="password"/>
</div>
<div className="form-group">
<div id="kc-form-buttons" className="submit">
<div className="">
<button disabled={!this.state.canSubmit}
type="submit"
className="btn btn-primary btn-lg"
name="submitAction"><Msg msgKey="doSave"/></button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
};

View file

@ -15,8 +15,6 @@
*/
import * as React from 'react';
import * as moment from 'moment';
import {AxiosResponse} from 'axios';
import {withRouter, RouteComponentProps} from 'react-router-dom';
import {
@ -34,7 +32,8 @@ import {
} from '@patternfly/react-core';
import {AIACommand} from '../../util/AIACommand';
import {AccountServiceClient} from '../../account-service/account.service';
import TimeUtil from '../../util/TimeUtil';
import AccountService, {HttpResponse} from '../../account-service/account.service';
import {ContinueCancelModal} from '../../widgets/ContinueCancelModal';
import {Features} from '../../widgets/features';
import {Msg} from '../../widgets/Msg';
@ -96,11 +95,12 @@ class SigningInPage extends React.Component<SigningInPageProps, SigningInPageSta
}
private getCredentialContainers(): void {
AccountServiceClient.Instance.doGet("/credentials")
.then((response: AxiosResponse<CredentialContainer[]>) => {
AccountService.doGet("/credentials")
.then((response: HttpResponse<CredentialContainer[]>) => {
const allContainers: CredContainerMap = new Map();
response.data.forEach(container => {
const containers: CredentialContainer[] = response.data || [];
containers.forEach(container => {
let categoryMap = allContainers.get(container.category);
if (!categoryMap) {
categoryMap = new Map();
@ -115,7 +115,7 @@ class SigningInPage extends React.Component<SigningInPageProps, SigningInPageSta
}
private handleRemove = (credentialId: string, userLabel: string) => {
AccountServiceClient.Instance.doDelete("/credentials/" + credentialId)
AccountService.doDelete("/credentials/" + credentialId)
.then(() => {
this.getCredentialContainers();
ContentAlert.success('successRemovedMessage', [userLabel]);
@ -200,7 +200,9 @@ class SigningInPage extends React.Component<SigningInPageProps, SigningInPageSta
userCredentials.forEach(credential => {
if (!credential.userLabel) credential.userLabel = Msg.localize(credential.type);
if (credential.hasOwnProperty('createdDate') && credential.createdDate! > 0) credential.strCreatedDate = moment(credential.createdDate).format('LLL');
if (credential.hasOwnProperty('createdDate') && credential.createdDate && credential.createdDate! > 0) {
credential.strCreatedDate = TimeUtil.format(credential.createdDate as number);
}
});
let updateAIA: AIACommand;

View file

@ -29,13 +29,13 @@ export class KeycloakService {
private static instance: KeycloakService = new KeycloakService();
private constructor() {
}
public static get Instance(): KeycloakService {
return this.instance;
}
/**
* Configure and initialize the Keycloak adapter.
*
@ -58,7 +58,7 @@ export class KeycloakService {
});
});
}
public authenticated(): boolean {
return KeycloakService.keycloakAuth.authenticated ? KeycloakService.keycloakAuth.authenticated : false;
}
@ -74,12 +74,12 @@ export class KeycloakService {
public account(): void {
KeycloakService.keycloakAuth.accountManagement();
}
public authServerUrl(): string | undefined {
const authServerUrl = KeycloakService.keycloakAuth.authServerUrl;
return authServerUrl!.charAt(authServerUrl!.length - 1) === '/' ? authServerUrl : authServerUrl + '/';
}
public realm(): string | undefined {
return KeycloakService.keycloakAuth.realm;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates.
*
* 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
*
* 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.
*/
declare const locale: string;
/**
* @author Stan Silvert
*/
class TimeUtil {
private options = { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
private formatter: Intl.DateTimeFormat;
constructor() {
try {
this.formatter = new Intl.DateTimeFormat(locale, this.options);
} catch(e) {
// unknown locale falling back to English
this.formatter = new Intl.DateTimeFormat('en', this.options);
}
}
format(time: number): string {
return this.formatter.format(time);
}
}
const TimeUtilInstance: TimeUtil = new TimeUtil();
export default TimeUtilInstance as TimeUtil;

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,7 +17,7 @@
import * as React from 'react';
import { Modal, Button, ButtonProps } from '@patternfly/react-core';
import {Msg} from './Msg';
/**
* For any of these properties that are strings, you can
* pass in a localization key instead of a static string.
@ -41,7 +41,7 @@ interface ContinueCancelModalState {
/**
* This class renders a button that provides a continue/cancel modal dialog when clicked. If the user selects 'Continue'
* then the onContinue function is executed.
*
*
* @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc.
*/
export class ContinueCancelModal extends React.Component<ContinueCancelModalProps, ContinueCancelModalState> {
@ -51,7 +51,7 @@ export class ContinueCancelModal extends React.Component<ContinueCancelModalProp
modalCancelButtonLabel: 'doCancel',
isDisabled: false
};
public constructor(props: ContinueCancelModalProps) {
super(props);
this.state = {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,15 +23,15 @@ import {
import { Msg } from './Msg';
interface AvailableLocale {
locale: string;
locale: string;
label: string;
};
declare const availableLocales: [AvailableLocale];
interface LocaleSelectorProps extends FormSelectProps { }
interface LocaleSelectorProps extends Omit<FormSelectProps, 'children'> { }
interface LocaleSelectorState { }
export class LocaleSelector extends React.Component<LocaleSelectorProps, LocaleSelectorState> {
constructor(props: LocaleSelectorProps) {
super(props);
}
@ -44,7 +44,7 @@ export class LocaleSelector extends React.Component<LocaleSelectorProps, LocaleS
onChange={(value, event) => { if (this.props.onChange) this.props.onChange(value, event) }}
aria-label={Msg.localize('selectLocale')}
>
{availableLocales.map((locale, index) =>
{availableLocales.map((locale, index) =>
<FormSelectOption
key={index}
value={locale.locale}

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,13 +20,13 @@ import {Msg} from './Msg';
import {KeycloakService} from '../keycloak-service/keycloak.service';
import {Button, DropdownItem} from '@patternfly/react-core';
declare const baseUrl: string;
function handleLogout(): void {
KeycloakService.Instance.logout(baseUrl);
}
interface LogoutProps {}
export class LogoutButton extends React.Component<LogoutProps> {
public render(): React.ReactNode {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,20 +15,20 @@
*/
import * as React from 'react';
declare const l18nMsg: {[key: string]: string};
export interface MsgProps {
readonly msgKey: string;
readonly params?: string[];
}
export class Msg extends React.Component<MsgProps> {
public constructor(props: MsgProps) {
super(props);
}
public render(): React.ReactNode {
if (this.props.children) {
return Msg.localizeWithChildren(this.props.msgKey, this.props.children);
@ -46,11 +46,11 @@ export class Msg extends React.Component<MsgProps> {
[parts[i], child, count === i + 1 ? parts[count] : '']
);
}
public static localize(msgKey: string, params?: string[]): string {
let message: string = l18nMsg[this.processKey(msgKey)];
if (message === undefined) message = msgKey;
if ((params !== undefined) && (params.length > 0)) {
params.forEach((value: string, index: number) => {
value = this.processParam(value);
@ -68,17 +68,17 @@ export class Msg extends React.Component<MsgProps> {
// remove Freemarker syntax
return msgKey.substring(2, msgKey.length - 1);
}
// if the param has Freemarker syntax, try to look up its value
private static processParam(param: string): string {
if (!(param.startsWith('${') && param.endsWith('}'))) return param;
// remove Freemarker syntax
const key: string = param.substring(2, param.length - 1);
let value: string = l18nMsg[key];
if (value === undefined) return param;
return value;
}
}

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,7 +20,7 @@ import {Msg} from '../widgets/Msg';
import {DropdownItem} from '@patternfly/react-core';
import {ArrowIcon} from '@patternfly/react-icons';
declare const referrerName: string;
declare const referrerUri: string;
@ -31,13 +31,13 @@ export interface ReferrerDropdownItemProps {
* @author Stan Silvert ssilvert@redhat.com (C) 2018 Red Hat Inc.
*/
export class ReferrerDropdownItem extends React.Component<ReferrerDropdownItemProps> {
public constructor(props: ReferrerDropdownItemProps) {
super(props);
}
public render(): React.ReactNode {
return (
<DropdownItem id="referrerMobileLink" href={referrerUri}>
<ArrowIcon /> {Msg.localize('backTo', [referrerName])}

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,7 @@
import * as React from 'react';
import {Msg} from '../widgets/Msg';
import {Msg} from './Msg';
import {ArrowIcon} from '@patternfly/react-icons';
@ -30,7 +30,7 @@ export interface ReferrerLinkProps {
* @author Stan Silvert ssilvert@redhat.com (C) 2018 Red Hat Inc.
*/
export class ReferrerLink extends React.Component<ReferrerLinkProps> {
public constructor(props: ReferrerLinkProps) {
super(props);
}

View file

@ -4,7 +4,7 @@ var content = [
id: 'personal-info',
path: 'personal-info',
label: 'personalInfoHtmlTitle',
modulePath: '/app/content/account-page/AccountPage',
modulePath: '/app/content/account-page/AccountPage.js',
componentName: 'AccountPage'
},
{
@ -15,35 +15,21 @@ var content = [
id: 'signingin',
path: 'security/signingin',
label: 'signingIn',
modulePath: '/app/content/signingin-page/SigningInPage',
modulePath: '/app/content/signingin-page/SigningInPage.js',
componentName: 'SigningInPage',
},
/* {
path: 'security/password',
label: 'password',
modulePath: '/app/content/aia-page/AppInitiatedActionPage',
componentName: 'AppInitiatedActionPage',
kcAction: 'UPDATE_PASSWORD'
},
{
path: 'security/authenticator',
label: 'authenticator',
modulePath: '/app/content/aia-page/AppInitiatedActionPage',
componentName: 'AppInitiatedActionPage',
kcAction: 'CONFIGURE_TOTP'
}, */
{
id: 'device-activity',
path: 'security/device-activity',
label: 'device-activity',
modulePath: '/app/content/device-activity-page/DeviceActivityPage',
modulePath: '/app/content/device-activity-page/DeviceActivityPage.js',
componentName: 'DeviceActivityPage'
},
{
id: 'linked-accounts',
path: 'security/linked-accounts',
label: 'linkedAccountsHtmlTitle',
modulePath: '/app/content/linked-accounts-page/LinkedAccountsPage',
modulePath: '/app/content/linked-accounts-page/LinkedAccountsPage.js',
componentName: 'LinkedAccountsPage',
hidden: !features.isLinkedAccountsEnabled
}
@ -53,14 +39,14 @@ var content = [
id: 'applications',
path: 'applications',
label: 'applications',
modulePath: '/app/content/applications-page/ApplicationsPage',
modulePath: '/app/content/applications-page/ApplicationsPage.js',
componentName: 'ApplicationsPage'
},
{
id: 'resources',
path: 'resources',
label: 'resources',
modulePath: '/app/content/my-resources-page/MyResourcesPage',
modulePath: '/app/content/my-resources-page/MyResourcesPage.js',
componentName: 'MyResourcesPage',
hidden: !features.isMyResourcesEnabled
}

View file

@ -3,31 +3,34 @@
"version": "1.0.0",
"description": "keycloak-preview account management written in React",
"scripts": {
"build": "tsc --noImplicitAny --strictNullChecks --jsx react -p ./ && npm run lint",
"build:watch": "tsc --noImplicitAny --strictNullChecks --jsx react -p ./ -w",
"lint": "eslint --ignore-pattern *.d.ts ./**/*.ts*"
"build": "snowpack && npm run check-types && npm run babel",
"babel": "babel --source-maps --extensions \".js,.ts,.tsx\" app/ --out-dir app/",
"babel:watch": "npm run babel -- --watch",
"check-types": "tsc --noImplicitAny --strictNullChecks --jsx react -p ./",
"check-types:watch": "npm run check-types -- -w",
"lint": "eslint ./app/**/*.ts*"
},
"keywords": [],
"author": "Stan Silvert",
"license": "Apache-2.0",
"dependencies": {
"@patternfly/patternfly": "^2.26.5",
"@patternfly/react-core": "^3.35.0",
"@types/node": "^12.7.2",
"axios": "^0.19.0",
"moment": "^2.22.2",
"querystring": "^0.2.0",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react-router-dom": "^4.3.1",
"systemjs": "^0.20.17",
"systemjs-plugin-babel": "0.0.25"
"@patternfly/react-core": "^3.153.3",
"@patternfly/react-icons": "^3.15.16",
"react": "npm:@pika/react@^16.13.1",
"react-dom": "npm:@pika/react-dom@^16.13.1",
"react-router-dom": "^4.3.1"
},
"devDependencies": {
"@types/react": "^16.8.8",
"@types/react-dom": "^16.8.3",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.7",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.8.7",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@types/node": "^13.9.8",
"@types/react": "^16.9.23",
"@types/react-dom": "^16.9.5",
"@types/react-router-dom": "^4.3.1",
"@types/systemjs": "^0.20.6",
"@typescript-eslint/eslint-plugin": "^1.4.2",
"@typescript-eslint/parser": "^1.4.2",
"babel-eslint": "^9.0.0",
@ -37,7 +40,10 @@
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"typescript": "^3.3.3333"
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-postcss": "^2.5.0",
"snowpack": "^1.7.0",
"typescript": "^3.8.3"
},
"repository": {}
}

View file

@ -0,0 +1,19 @@
const copy = require('rollup-plugin-copy');
const postcss = require('rollup-plugin-postcss');
module.exports = {
rollup: {
plugins: [
postcss({
extract: 'public/app.css'
}),
copy({
targets: [
{ src: 'node_modules/@patternfly/react-core/dist/styles/base.css', dest: 'public/' },
{ src: 'node_modules/@patternfly/react-core/dist/styles/assets/fonts/overpass-webfont/overpass*.woff2', dest: 'public/assets/fonts/overpass-webfont/'},
{ src: 'node_modules/@patternfly/react-core/dist/styles/assets/pficon/pficon.woff2', dest: 'public/assets/pficon/'},
],
})
]
}
};

View file

@ -1,11 +0,0 @@
/**
* Add barrels and stuff
* Adjust as necessary for your application needs.
*/
// (function (global) {
// System.config({
// packages: {
// // add packages here
// }
// });
// })(this);

View file

@ -2,6 +2,7 @@
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noEmit": true,
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
@ -13,7 +14,7 @@
"suppressImplicitAnyIndexErrors": true
},
"include": [
"./**/*.ts?"
"./app/**/*.ts?"
],
"files": [
"../../../../../../../../adapters/oidc/js/src/main/resources/keycloak.d.ts"