@material-ui/lab#AlertTitle TypeScript Examples

The following examples show how to use @material-ui/lab#AlertTitle. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: GeneralErrorAlert.tsx    From abacus with GNU General Public License v2.0 6 votes vote down vote up
export default function GeneralErrorAlert({ error }: { error?: Error }): JSX.Element | null {
  const classes = useStyles()

  if (error instanceof HttpResponseError) {
    return (
      <Alert severity='error' className={classes.root}>
        <AlertTitle>
          Error Response: {error.status} {error.response.statusText}
        </AlertTitle>
        {error.json && typeof error.json === 'object' && (error?.json as Record<string | number, unknown>).message}
      </Alert>
    )
  } else if (error instanceof Error) {
    return (
      <Alert severity='error' className={classes.root}>
        <AlertTitle>{error.name}:</AlertTitle>
        {error.message}
      </Alert>
    )
  }

  return null
}
Example #2
Source File: App.tsx    From clarity with Apache License 2.0 6 votes vote down vote up
Alerts = observer((props: AppProps) => {
  if (props.errors.lastError == null) return null;
  // Not using the `data-dismiss="alert"` to dismiss via Bootstrap JS
  // becuase then it doesn't re-render when there's a new error.
  return (
    <div id="alert-message">
      <Alert severity="error" onClose={() => props.errors.dismissLast()}>
        <AlertTitle>Error!</AlertTitle>
        {props.errors.lastError}
      </Alert>
    </div>
  );
})
Example #3
Source File: PromoteRc.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
PromoteRc = ({ latestRelease, onSuccess }: PromoteRcProps) => {
  function Body() {
    if (latestRelease === null) {
      return <NoLatestRelease />;
    }

    if (!latestRelease.prerelease) {
      return (
        <Box marginBottom={2}>
          <Alert
            data-testid={TEST_IDS.promoteRc.notRcWarning}
            severity="warning"
          >
            <AlertTitle>
              Latest Git release is not a Release Candidate
            </AlertTitle>
            One can only promote Release Candidates to Release Versions
          </Alert>
        </Box>
      );
    }

    return <PromoteRcBody rcRelease={latestRelease} onSuccess={onSuccess} />;
  }

  return (
    <InfoCardPlus>
      <Box marginBottom={2}>
        <Typography variant="h4">Promote Release Candidate</Typography>
      </Box>

      <Body />
    </InfoCardPlus>
  );
}
Example #4
Source File: snack.tsx    From bitpay-browser-extension with MIT License 6 votes vote down vote up
Snack: React.FC<{ message: string; onClose: () => void }> = ({ message, onClose }) => (
  <div className="snack">
    <Snackbar style={position} open={!!message} autoHideDuration={6000} anchorOrigin={anchorOrigin} onClose={onClose}>
      <Alert
        style={menu}
        severity="error"
        action={
          <IconButton aria-label="close" color="inherit" size="small" style={icon} onClick={onClose}>
            <img src="/assets/icons/close.svg" alt="close" />
          </IconButton>
        }
      >
        <AlertTitle style={title}>Please Try Again!</AlertTitle>
        {message}
      </Alert>
    </Snackbar>
  </div>
)
Example #5
Source File: App.tsx    From signer with Apache License 2.0 6 votes vote down vote up
Alerts = observer((props: AppProps) => {
  if (props.errors.lastError == null) return null;
  // Not using the `data-dismiss="alert"` to dismiss via Bootstrap JS
  // becuase then it doesn't re-render when there's a new error.
  return (
    <div id="alert-message">
      <Alert severity="error" onClose={() => props.errors.dismissLast()}>
        <AlertTitle>Error!</AlertTitle>
        {props.errors.lastError}
      </Alert>
    </div>
  );
})
Example #6
Source File: AlertUploadSize.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
export default function UploadSizeAlert(props: Props): ReactElement | null {
  const classes = useStyles()

  const totalSize = props.files.reduce((previous, current) => previous + current.size, 0)

  const aboveLimit = totalSize >= LIMIT

  return (
    <Collapse in={aboveLimit}>
      <div className={classes.root}>
        <Alert severity="warning">
          <AlertTitle>Warning</AlertTitle>
          The files you are trying to upload are above the recommended size. The chunks may not be synchronised properly
          over the network.
        </Alert>
      </div>
    </Collapse>
  )
}
Example #7
Source File: Patch.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
function BodyWrapper({
  latestRelease,
  releaseBranch,
  onSuccess,
  ctaMessage,
}: PatchProps & { ctaMessage: string }) {
  const { project } = useProjectContext();

  if (latestRelease === null) {
    return <NoLatestRelease />;
  }

  if (releaseBranch === null) {
    return <NoLatestRelease />;
  }

  const bumpedTag = getBumpedTag({
    project,
    tag: latestRelease.tagName,
    bumpLevel: 'patch',
  });

  if (bumpedTag.error !== undefined) {
    return (
      <Alert severity="error">
        {bumpedTag.error.title && (
          <AlertTitle>{bumpedTag.error.title}</AlertTitle>
        )}

        {bumpedTag.error.subtitle}
      </Alert>
    );
  }

  return (
    <PatchBody
      bumpedTag={bumpedTag.bumpedTag}
      latestRelease={latestRelease}
      releaseBranch={releaseBranch}
      onSuccess={onSuccess}
      tagParts={bumpedTag.tagParts}
      ctaMessage={ctaMessage}
    />
  );
}
Example #8
Source File: Footer.tsx    From knboard with MIT License 5 votes vote down vote up
Footer = () => {
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
    null
  );

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);
  const id = open ? "about-popover" : undefined;

  return (
    <Container>
      <List>
        <Item>
          <Button
            aria-describedby={id}
            onClick={handleClick}
            css={css`
              padding: 0;
              text-transform: initial;
              color: #888;
              font-weight: 400;
              &:hover {
                background-color: initial;
              }
            `}
          >
            About
          </Button>
        </Item>
      </List>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
      >
        <Alert
          severity="info"
          css={css`
            padding: 1rem 1rem 2rem 1rem;
            max-width: 400px;
          `}
        >
          <AlertTitle>About</AlertTitle>
          <p>
            <b>Knboard</b> is an app that helps visualize your work using kanban
            boards, maximizing efficiency.
          </p>
          It is an <b>open-source</b> project built using Django & React,
          available on{" "}
          <Link
            href="https://github.com/rrebase/knboard"
            target="_blank"
            rel="noopener"
          >
            GitHub
          </Link>
          .
        </Alert>
      </Popover>
    </Container>
  );
}
Example #9
Source File: CreateReleaseCandidate.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
CreateReleaseCandidate = ({
  defaultBranch,
  latestRelease,
  releaseBranch,
  onSuccess,
}: CreateReleaseCandidateProps) => {
  const { project } = useProjectContext();

  const [semverBumpLevel, setSemverBumpLevel] = useState<'major' | 'minor'>(
    SEMVER_PARTS.minor,
  );
  const [releaseCandidateGitInfo, setReleaseCandidateGitInfo] = useState(
    getReleaseCandidateGitInfo({ latestRelease, project, semverBumpLevel }),
  );

  useEffect(() => {
    setReleaseCandidateGitInfo(
      getReleaseCandidateGitInfo({ latestRelease, project, semverBumpLevel }),
    );
  }, [semverBumpLevel, setReleaseCandidateGitInfo, latestRelease, project]);

  const { progress, responseSteps, run, runInvoked } =
    useCreateReleaseCandidate({
      defaultBranch,
      latestRelease,
      releaseCandidateGitInfo,
      project,
      onSuccess,
    });
  if (responseSteps.length > 0) {
    return (
      <ResponseStepDialog
        progress={progress}
        responseSteps={responseSteps}
        title="Create Release Candidate"
      />
    );
  }

  if (releaseCandidateGitInfo.error !== undefined) {
    return (
      <InfoCardPlusWrapper>
        <Alert severity="error">
          {releaseCandidateGitInfo.error.title && (
            <AlertTitle>{releaseCandidateGitInfo.error.title}</AlertTitle>
          )}

          {releaseCandidateGitInfo.error.subtitle}
        </Alert>
      </InfoCardPlusWrapper>
    );
  }

  const tagAlreadyExists =
    latestRelease !== null &&
    latestRelease.tagName === releaseCandidateGitInfo.rcReleaseTag;
  const conflictingPreRelease =
    latestRelease !== null && latestRelease.prerelease;

  return (
    <InfoCardPlusWrapper>
      {project.versioningStrategy === 'semver' &&
        latestRelease &&
        !conflictingPreRelease && (
          <Box marginBottom={2} data-testid={TEST_IDS.createRc.semverSelect}>
            <FormControl style={{ margin: 5, minWidth: 250 }}>
              <InputLabel>Select bump severity</InputLabel>

              <Select
                value={semverBumpLevel}
                onChange={({ target: { value: semverSeverity } }: any) => {
                  setSemverBumpLevel(semverSeverity);
                }}
              >
                <MenuItem value={SEMVER_PARTS.minor}>
                  {SEMVER_PARTS.minor}
                </MenuItem>
                <MenuItem value={SEMVER_PARTS.major}>
                  {SEMVER_PARTS.major}
                </MenuItem>
              </Select>
            </FormControl>
          </Box>
        )}

      {conflictingPreRelease || tagAlreadyExists ? (
        <>
          {conflictingPreRelease && (
            <Box marginBottom={2}>
              <Alert severity="warning">
                The most recent release is already a Release Candidate
              </Alert>
            </Box>
          )}

          {tagAlreadyExists && (
            <Box marginBottom={2}>
              <Alert severity="warning">
                There's already a tag named{' '}
                <strong>{releaseCandidateGitInfo.rcReleaseTag}</strong>
              </Alert>
            </Box>
          )}
        </>
      ) : (
        <Box marginBottom={2}>
          <Typography>
            <Differ
              icon="branch"
              current={releaseBranch?.name}
              next={releaseCandidateGitInfo.rcBranch}
            />
          </Typography>

          <Typography>
            <Differ
              icon="tag"
              current={latestRelease?.tagName}
              next={releaseCandidateGitInfo.rcReleaseTag}
            />
          </Typography>
        </Box>
      )}

      <Button
        data-testid={TEST_IDS.createRc.cta}
        disabled={conflictingPreRelease || tagAlreadyExists || runInvoked}
        variant="contained"
        color="primary"
        onClick={() => run()}
      >
        Create Release Candidate
      </Button>
    </InfoCardPlusWrapper>
  );
}
Example #10
Source File: Features.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
export function Features({
  features,
}: {
  features: ComponentProps<typeof GitReleaseManager>['features'];
}) {
  const pluginApiClient = useApi(gitReleaseManagerApiRef);
  const { project } = useProjectContext();
  const { gitBatchInfo, fetchGitBatchInfo } = useGetGitBatchInfo({
    pluginApiClient,
    project,
  });

  const { versioningStrategyMatches } = useVersioningStrategyMatchesRepoTags({
    latestReleaseTagName: gitBatchInfo.value?.latestRelease?.tagName,
    project,
    repositoryName: gitBatchInfo.value?.repository.name,
  });

  if (gitBatchInfo.error) {
    return (
      <Alert severity="error">
        Error occurred while fetching information for "{project.owner}/
        {project.repo}" ({gitBatchInfo.error.message})
      </Alert>
    );
  }

  if (gitBatchInfo.loading) {
    return <Progress />;
  }

  if (gitBatchInfo.value === undefined) {
    return <Alert severity="error">Failed to fetch latest Git release</Alert>;
  }

  if (!gitBatchInfo.value.repository.pushPermissions) {
    return (
      <Alert severity="error">
        You lack push permissions for repository "{project.owner}/{project.repo}
        "
      </Alert>
    );
  }

  const { tagNameError } = validateTagName({
    project,
    tagName: gitBatchInfo.value.latestRelease?.tagName,
  });
  if (tagNameError) {
    return (
      <Alert severity="error">
        {tagNameError.title && <AlertTitle>{tagNameError.title}</AlertTitle>}
        {tagNameError.subtitle}
      </Alert>
    );
  }

  let CustomFeatures =
    features?.custom?.factory({
      latestRelease: gitBatchInfo.value.latestRelease,
      project,
      releaseBranch: gitBatchInfo.value.releaseBranch,
      repository: gitBatchInfo.value.repository,
    }) ?? null;
  if (Array.isArray(CustomFeatures)) {
    CustomFeatures = CustomFeatures.map((CustomFeature, index) => (
      <React.Fragment key={`grm--custom-feature--${index}`}>
        {CustomFeature}
      </React.Fragment>
    ));
  }

  return (
    <RefetchContext.Provider value={{ fetchGitBatchInfo }}>
      <ErrorBoundary>
        {gitBatchInfo.value.latestRelease && !versioningStrategyMatches && (
          <Alert severity="warning" style={{ marginBottom: 20 }}>
            Versioning mismatch, expected {project.versioningStrategy} version,
            got "{gitBatchInfo.value.latestRelease.tagName}"
          </Alert>
        )}

        {!gitBatchInfo.value.latestRelease && (
          <Alert severity="info" style={{ marginBottom: 20 }}>
            This repository doesn't have any releases yet
          </Alert>
        )}

        {!gitBatchInfo.value.releaseBranch && (
          <Alert severity="info" style={{ marginBottom: 20 }}>
            This repository doesn't have any release branches
          </Alert>
        )}

        {!features?.info?.omit && (
          <Info
            latestRelease={gitBatchInfo.value.latestRelease}
            releaseBranch={gitBatchInfo.value.releaseBranch}
            statsEnabled={features?.stats?.omit !== true}
          />
        )}

        {!features?.createRc?.omit && (
          <CreateReleaseCandidate
            latestRelease={gitBatchInfo.value.latestRelease}
            releaseBranch={gitBatchInfo.value.releaseBranch}
            defaultBranch={gitBatchInfo.value.repository.defaultBranch}
            onSuccess={features?.createRc?.onSuccess}
          />
        )}

        {!features?.promoteRc?.omit && (
          <PromoteRc
            latestRelease={gitBatchInfo.value.latestRelease}
            onSuccess={features?.promoteRc?.onSuccess}
          />
        )}

        {!features?.patch?.omit && (
          <Patch
            latestRelease={gitBatchInfo.value.latestRelease}
            releaseBranch={gitBatchInfo.value.releaseBranch}
            onSuccess={features?.patch?.onSuccess}
          />
        )}

        {CustomFeatures}
      </ErrorBoundary>
    </RefetchContext.Provider>
  );
}
Example #11
Source File: PatchBody.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
PatchBody = ({
  bumpedTag,
  latestRelease,
  releaseBranch,
  onSuccess,
  tagParts,
  ctaMessage,
}: PatchBodyProps) => {
  const pluginApiClient = useApi(gitReleaseManagerApiRef);
  const { project } = useProjectContext();
  const [checkedCommitIndex, setCheckedCommitIndex] = useState(-1);

  const gitDataResponse = useAsync(async () => {
    const [
      { recentCommits: recentCommitsOnDefaultBranch },
      { recentCommits: recentCommitsOnReleaseBranch },
    ] = await Promise.all([
      pluginApiClient.getRecentCommits({
        owner: project.owner,
        repo: project.repo,
      }),
      pluginApiClient.getRecentCommits({
        owner: project.owner,
        repo: project.repo,
        releaseBranchName: releaseBranch.name,
      }),
    ]);

    return {
      recentCommitsOnDefaultBranch,
      recentCommitsOnReleaseBranch,
    };
  });

  const { progress, responseSteps, run, runInvoked } = usePatch({
    bumpedTag,
    latestRelease,
    project,
    tagParts,
    onSuccess,
  });

  if (responseSteps.length > 0) {
    return (
      <ResponseStepDialog
        progress={progress}
        responseSteps={responseSteps}
        title={ctaMessage}
      />
    );
  }

  if (gitDataResponse.error) {
    return (
      <Alert data-testid={TEST_IDS.patch.error} severity="error">
        Unexpected error: {gitDataResponse.error.message}
      </Alert>
    );
  }

  if (gitDataResponse.loading) {
    return (
      <Box data-testid={TEST_IDS.patch.loading}>
        <Progress />
      </Box>
    );
  }

  function Description() {
    return (
      <>
        {!latestRelease.prerelease && (
          <Box marginBottom={2}>
            <Alert data-testid={TEST_IDS.patch.notPrerelease} severity="info">
              <AlertTitle>
                The current Git release is a <b>Release Version</b>
              </AlertTitle>
              It's still possible to patch it, but be extra mindful of changes
            </Alert>
          </Box>
        )}

        <Box marginBottom={2}>
          <Typography>
            Patches the release branch, creates a new tag and updates the Git
            release. A dry run on a temporary branch will run prior to patching
            the release branch to ensure there's no merge conflicts. Manual
            patching is recommended should the dry run fail.
          </Typography>
        </Box>

        <Box marginBottom={2}>
          <Typography>
            <Differ
              icon="tag"
              current={latestRelease.tagName}
              next={bumpedTag}
            />
          </Typography>
        </Box>
      </>
    );
  }

  function CommitList() {
    if (!gitDataResponse.value?.recentCommitsOnDefaultBranch) {
      return null;
    }

    return (
      <List>
        {gitDataResponse.value.recentCommitsOnDefaultBranch.map(
          (commit, index) => {
            // FIXME: Performance improvement opportunity: Convert to object lookup
            const commitExistsOnReleaseBranch =
              !!gitDataResponse.value?.recentCommitsOnReleaseBranch.find(
                releaseBranchCommit =>
                  releaseBranchCommit.sha === commit.sha ||
                  // The selected patch commit's sha is included in the commit message,
                  // which means it's part of a previous patch
                  releaseBranchCommit.commit.message.includes(
                    getPatchCommitSuffix({ commitSha: commit.sha }),
                  ),
              );
            const hasNoParent = !commit.firstParentSha;

            return (
              <div style={{ position: 'relative' }} key={`commit-${index}`}>
                {commitExistsOnReleaseBranch && (
                  <Paper
                    elevation={3}
                    style={{
                      position: 'absolute',
                      top: '50%',
                      left: '50%',
                      transform: 'translate3d(-50%,-50%,0)',
                      zIndex: 10,
                      color: 'green',
                      padding: 6,
                      background: 'rgba(244,244,244,1)',
                      borderRadius: 8,
                    }}
                  >
                    <FileCopyIcon
                      fontSize="small"
                      style={{ verticalAlign: 'middle' }}
                    />{' '}
                    Already exists on <b>{releaseBranch?.name}</b>
                  </Paper>
                )}

                <ListItem
                  disabled={
                    runInvoked || commitExistsOnReleaseBranch || hasNoParent
                  }
                  role={undefined}
                  dense
                  button
                  onClick={() => {
                    if (index === checkedCommitIndex) {
                      setCheckedCommitIndex(-1);
                    } else {
                      setCheckedCommitIndex(index);
                    }
                  }}
                >
                  <ListItemIcon>
                    <Checkbox
                      edge="start"
                      checked={checkedCommitIndex === index}
                      tabIndex={-1}
                    />
                  </ListItemIcon>

                  <ListItemText
                    style={{ marginRight: 15 }}
                    id={commit.sha}
                    primary={commit.commit.message}
                    secondary={
                      <>
                        <Link
                          color="primary"
                          to={commit.htmlUrl}
                          target="_blank"
                        >
                          {commit.sha}
                        </Link>{' '}
                        {commit.author.htmlUrl && (
                          <Link
                            color="primary"
                            to={commit.author.htmlUrl}
                            target="_blank"
                          >
                            @{commit.author.login}
                          </Link>
                        )}
                      </>
                    }
                  />

                  <ListItemSecondaryAction>
                    <IconButton
                      aria-label="commit"
                      disabled={
                        runInvoked ||
                        commitExistsOnReleaseBranch ||
                        !releaseBranch
                      }
                      onClick={() => {
                        const repoPath = pluginApiClient.getRepoPath({
                          owner: project.owner,
                          repo: project.repo,
                        });
                        const host = pluginApiClient.getHost();

                        const newTab = window.open(
                          `https://${host}/${repoPath}/compare/${releaseBranch?.name}...${commit.sha}`,
                          '_blank',
                        );
                        newTab?.focus();
                      }}
                    >
                      <OpenInNewIcon />
                    </IconButton>
                  </ListItemSecondaryAction>
                </ListItem>
              </div>
            );
          },
        )}
      </List>
    );
  }

  return (
    <Box data-testid={TEST_IDS.patch.body}>
      <Description />

      <Box style={{ maxHeight: 450, overflowY: 'auto' }}>
        <CommitList />
      </Box>

      <Button
        disabled={checkedCommitIndex === -1 || progress > 0}
        variant="contained"
        color="primary"
        onClick={() => {
          const selectedPatchCommit =
            gitDataResponse.value?.recentCommitsOnDefaultBranch[
              checkedCommitIndex
            ];

          if (!selectedPatchCommit) {
            throw new GitReleaseManagerError(
              'Could not find selected patch commit',
            );
          }

          run(selectedPatchCommit);
        }}
      >
        {ctaMessage}
      </Button>
    </Box>
  );
}
Example #12
Source File: OverviewTrends.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
OverviewTrends = () => {
  const [days, setDays] = useState(14);
  const theme = useTheme<BackstageTheme>();
  const classes = useStyles();
  const client = useApi(xcmetricsApiRef);
  const buildCountsResult = useAsync(
    async () => client.getBuildCounts(days),
    [days],
  );
  const buildTimesResult = useAsync(
    async () => client.getBuildTimes(days),
    [days],
  );

  if (buildCountsResult.loading && buildTimesResult.loading) {
    return <Progress />;
  }

  const sumBuilds = sumField(b => b.builds, buildCountsResult.value);
  const sumErrors = sumField(b => b.errors, buildCountsResult.value);
  const errorRate = sumBuilds && sumErrors ? sumErrors / sumBuilds : undefined;

  const averageBuildDurationP50 = getAverageDuration(
    buildTimesResult.value,
    b => b.durationP50,
  );
  const averageBuildDurationP95 = getAverageDuration(
    buildTimesResult.value,
    b => b.durationP95,
  );
  const totalBuildTime = sumField(t => t.totalDuration, buildTimesResult.value);

  return (
    <>
      <Select
        selected={days}
        items={DAYS_SELECT_ITEMS}
        label="Trends for"
        onChange={selection => setDays(selection as number)}
      />
      {buildCountsResult.error && (
        <Alert severity="error" className={classes.spacingVertical}>
          <AlertTitle>Failed to fetch build counts</AlertTitle>
          {buildCountsResult?.error?.message}
        </Alert>
      )}
      {buildTimesResult.error && (
        <Alert severity="error" className={classes.spacingVertical}>
          <AlertTitle>Failed to fetch build times</AlertTitle>
          {buildTimesResult?.error?.message}
        </Alert>
      )}
      {(!buildCountsResult.error || !buildTimesResult.error) && (
        <div className={classes.spacingVertical}>
          <Trend
            title="Build Time"
            color={theme.palette.secondary.main}
            data={getValues(e => e.durationP50, buildTimesResult.value)}
          />
          <Trend
            title="Error Rate"
            color={theme.palette.status.warning}
            data={getErrorRatios(buildCountsResult.value)}
          />
          <Trend
            title="Build Count"
            color={theme.palette.primary.main}
            data={getValues(e => e.builds, buildCountsResult.value)}
          />
          <Grid
            container
            spacing={3}
            direction="row"
            className={classes.spacingTop}
          >
            <DataValueGridItem field="Build Count" value={sumBuilds} />
            <DataValueGridItem field="Error Count" value={sumErrors} />
            <DataValueGridItem
              field="Error Rate"
              value={errorRate && formatPercentage(errorRate)}
            />
            <DataValueGridItem
              field="Avg. Build Time (P50)"
              value={averageBuildDurationP50}
            />
            <DataValueGridItem
              field="Avg. Build Time (P95)"
              value={averageBuildDurationP95}
            />
            <DataValueGridItem
              field="Total Build Time"
              value={totalBuildTime && formatDuration(totalBuildTime)}
            />
          </Grid>
        </div>
      )}
    </>
  );
}
Example #13
Source File: AccountEnterPage.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
renderCoupon(isLoggedIn: boolean) {
    const couponId = this.state.couponId !== undefined ? this.state.couponId : this.props.couponId || '';
    return (
      <EnterTemplate
        title={this.props.t('redeem-coupon')}
        renderContent={submitButton => (
          <>
            <TextField
              variant='outlined'
              fullWidth
              margin='normal'
              label={this.props.t('coupon-code')}
              placeholder='XXXXXXXX'
              value={couponId}
              onChange={e => this.setState({ couponId: e.target.value })}
              disabled={this.state.isSubmitting || !!this.state.couponPlan || !!this.state.couponRedeemedByYou}
            />
            <Collapse in={!!this.state.couponRedeemedByYou}>
              <Alert className={this.props.classes.alert} severity='success'>
                <AlertTitle>Success!</AlertTitle>
                This coupon has already been applied to your account.
              </Alert>
            </Collapse>
            <Collapse in={!!this.state.couponPlan && !this.state.couponRedeemedByYou}>
              {!!this.state.couponPlan && (
                <PricingPlan plan={this.state.couponPlan} />
              )}
            </Collapse>
            <Collapse in={!this.state.couponRedeemedByYou && !!this.state.couponPlan}>
              <Alert className={this.props.classes.alert} severity='info'>
                {!isLoggedIn ? 'This plan will be applied upon sign-up or login' : 'This plan will replace your current plan on your account.'}
              </Alert>
            </Collapse>
            {submitButton}
            <Collapse in={!!this.state.couponError}>
              <Alert className={this.props.classes.alert} severity='warning'>
                {this.state.couponError}
              </Alert>
            </Collapse>
          </>
        )}
        submitTitle={!!this.state.couponRedeemedByYou
          ? this.props.t('continue')
          : (!this.state.couponPlan
            ? this.props.t('check')
            : (!isLoggedIn
              ? this.props.t('continue')
              : this.props.t('redeem')))}
        submitDisabled={!couponId}
        isSubmitting={this.state.isSubmitting}
        onSubmit={async () => {
          if (!couponId) return;
          if (!!this.state.couponRedeemedByYou) {
            // Already redeemed by you
            this.props.history.push('/dashboard');
          } else if (!this.state.couponPlan) {
            // Need to check couponn
            await this.onCouponCheck(couponId);
          } else if (!isLoggedIn) {
            // Redirect to signup for a valid code
            this.props.history.push('/signup/', {
              [ADMIN_ENTER_COUPON_ID]: couponId,
              [ADMIN_LOGIN_REDIRECT_TO]: `/coupon/${couponId}`,
            })
          } else {
            // Accept code on own account
            this.setState({ isSubmitting: true });
            try {
              await (await ServerAdmin.get().dispatchAdmin()).accountAcceptCouponAdmin({
                couponId,
              });
              this.setState({
                couponRedeemedByYou: true,
                couponError: undefined,
              });
            } finally {
              this.setState({ isSubmitting: false });
            }
          }
        }}
        footer={(!this.state.couponRedeemedByYou && !!this.state.couponPlan && !isLoggedIn) ? {
          text: this.props.t('have-an-account'),
          actionText: this.props.t('log-in-here'),
          linkTo: {
            pathname: '/login',
            state: {
              [ADMIN_ENTER_COUPON_ID]: couponId,
              [ADMIN_LOGIN_REDIRECT_TO]: `/coupon/${couponId}`,
            },
          },
        } : undefined}
        layout={this.props.type}
      />
    );
  }
Example #14
Source File: Profile.tsx    From knboard with MIT License 4 votes vote down vote up
Profile = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const userDetail = useSelector(
    (state: RootState) => state.profile.userDetail
  );
  const apiErrors = useSelector((state: RootState) => state.profile.apiErrors);
  const loading = useSelector((state: RootState) => state.profile.loading);
  const { register, errors, handleSubmit, setError } = useForm<FormData>({
    mode: "onChange",
  });

  React.useEffect(() => {
    dispatch(fetchUserDetail());
    dispatch(fetchAvatarList());
  }, [userDetail?.id]);

  React.useEffect(() => {
    if (apiErrors) {
      for (const errorKey in apiErrors) {
        // @ts-ignore
        setError(errorKey, "api_error", apiErrors[errorKey]);
      }
    }
  }, [apiErrors]);

  if (!userDetail) {
    return null;
  }

  const onSubmit = async (data: FormData) => {
    dispatch(updateUser({ ...userDetail, ...data }));
  };

  return (
    <Container maxWidth="sm">
      <SEO title="Profile" />
      {userDetail.is_guest && (
        <Alert
          severity="warning"
          variant="outlined"
          css={css`
            margin: 1rem 0;
          `}
        >
          <AlertTitle>Warning</AlertTitle>
          Guest accounts are deleted 24 hours after creation!
        </Alert>
      )}
      <Title>About</Title>
      <Divider />
      <FormContainer theme={theme}>
        <UserAvatar />
        <UserForm onSubmit={handleSubmit(onSubmit)}>
          <Fields>
            <Row>
              <TextField
                id="username"
                name="username"
                inputRef={register({ required: "This field is required" })}
                defaultValue={userDetail.username}
                helperText={errors.username?.message}
                error={Boolean(errors.username)}
                label="Username"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="first_name"
                name="first_name"
                inputRef={register()}
                defaultValue={userDetail.first_name}
                helperText={errors.first_name?.message}
                error={Boolean(errors.first_name)}
                label="First name"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="last_name"
                name="last_name"
                inputRef={register()}
                defaultValue={userDetail.last_name}
                helperText={errors.last_name?.message}
                error={Boolean(errors.last_name)}
                label="Last name"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="email"
                name="email"
                inputRef={register({
                  pattern: {
                    value: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
                    message: "Invalid email",
                  },
                })}
                defaultValue={userDetail.email}
                helperText={errors.email?.message}
                error={Boolean(errors.email)}
                label="Email"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Button
              variant="contained"
              color="primary"
              type="submit"
              disabled={loading}
              data-testid="profile-save"
              css={css`
                margin: 1rem 0;
                text-align: right;
              `}
            >
              Save
            </Button>
          </Fields>
        </UserForm>
      </FormContainer>
    </Container>
  );
}