react-router-dom#match TypeScript Examples

The following examples show how to use react-router-dom#match. 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: routing.ts    From metaflow-ui with Apache License 2.0 6 votes vote down vote up
/**
 * Returns parameters from given path (if matching to our defined routes)
 * @param pathname location.pathname
 */
export function getRouteMatch(pathname: string): match<KnownURLParams> | null {
  return matchPath<KnownURLParams>(
    pathname,
    Object.keys(SHORT_PATHS).map((key) => SHORT_PATHS[key as keyof PathDefinition]),
  );
}
Example #2
Source File: root.component.test.tsx    From openmrs-esm-patient-registration with MIT License 6 votes vote down vote up
describe('root component', () => {
  it('renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(
      <Root
        savePatientForm={jest.fn()}
        match={sampleMatchProp}
        addressTemplate={{ results: [] }}
        currentSession={{} as any}
        patientIdentifiers={[]}
        relationshipTypes={{ results: [] }}
      />,
      div,
    );
  });
});
Example #3
Source File: patient-registration.test.tsx    From openmrs-esm-patient-registration with MIT License 6 votes vote down vote up
describe('patient registration sections', () => {
  const testSectionExists = (labelText: string) => {
    it(labelText + ' exists', async () => {
      render(
        <ResourcesContext.Provider value={mockResourcesContextValue}>
          <PatientRegistration match={sampleMatchProp} savePatientForm={jest.fn()} />
        </ResourcesContext.Provider>,
      );
      await wait();
      expect(screen.getByLabelText(labelText)).not.toBeNull();
    });
  };

  beforeAll(() => {
    spyOn(mockOpenmrsFramework, 'useConfig').and.returnValue(mockOpenmrsConfig);
  });

  testSectionExists('Demographics Section');
  testSectionExists('Contact Info Section');
});
Example #4
Source File: patient-registration.test.tsx    From openmrs-esm-patient-registration with MIT License 6 votes vote down vote up
describe('patient registration', () => {
  it('renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(
      <ResourcesContext.Provider value={mockResourcesContextValue}>
        <PatientRegistration match={sampleMatchProp} savePatientForm={jest.fn()} />
      </ResourcesContext.Provider>,
      div,
    );
  });
});
Example #5
Source File: Team.tsx    From js-ts-monorepos with BSD 2-Clause "Simplified" License 6 votes vote down vote up
Team: React.FunctionComponent<{ team: ITeam }> = ({ team }) => {
  console.log(
    `%c TEAM render: ${team.name}`,
    "background-color: blue; color: white"
  );
  const { channels } = team;
  return (
    <div className="flex-1 flex">
      <TeamSidebar team={team} />
      <Switch>
        <Route exact path={`/team/${team.id}`}>
          <h3>Please select a channel</h3>
        </Route>
        <Route
          exact
          path={`/team/${team.id}/channel/:channelId`}
          children={({ match }: { match: match<{ channelId: string }> }) => (
            <SelectedChannel match={match} channels={channels} />
          )}
        />
      </Switch>
    </div>
  );
}
Example #6
Source File: App.tsx    From js-ts-monorepos with BSD 2-Clause "Simplified" License 6 votes vote down vote up
App: React.FunctionComponent = () => {
  const [teams, setTeams] = useState<ITeam[]>();

  useAsyncDataEffect(() => getAllTeams(), {
    setter: setTeams,
    stateName: "teams",
  });
  if (!teams) return <Loading message="Loading teams" />;
  return (
    <Router>
      <div className="flex flex-col sm:flex-row w-full h-full">
        <TeamSelector teams={teams} />
        <Switch>
          <Route exact path="/">
            <section className="m-12 text-xl">
              <h3>Please select a team</h3>
            </section>
          </Route>
          <Route exact path="/team">
            <section className="m-12 text-xl">
              <h3>Please select a team</h3>
            </section>
          </Route>
          <Route
            path="/team/:teamId"
            children={({ match }: { match: match<{ teamId: string }> }) => (
              <SelectedTeam match={match} teams={teams} />
            )}
          />
        </Switch>
      </div>
    </Router>
  );
}
Example #7
Source File: Commits.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
function Entities({ match }: Props) {
  const { application } = match.params;

  useNavigationTabs(application, NAVIGATION_KEY, match.url, "Commits");

  return (
    <Switch>
      <RouteWithAnalytics
        exact
        path="/:application/commits/"
        component={CommitList}
      />
      <RouteWithAnalytics
        path="/:application/commits/:commitId"
        component={CommitPage}
      />
    </Switch>
  );
}
Example #8
Source File: RolesPage.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
RolesPage = ({ match }: Props) => {
  const { application } = match.params;

  useNavigationTabs(application, NAVIGATION_KEY, match.url, "Roles");

  const roleMatch = useRouteMatch<{ roleId: string }>(
    "/:application/roles/:roleId"
  );

  let roleId = null;
  if (roleMatch) {
    roleId = roleMatch.params.roleId;
  }

  return (
    <PageContent
      className="roles"
      sideContent={
        <RoleList applicationId={application} selectFirst={null === roleId} />
      }
    >
      {!isEmpty(roleId) && <Role />}
    </PageContent>
  );
}
Example #9
Source File: Entities.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
function Entities({ match }: Props) {
  return (
    <Switch>
      <RouteWithAnalytics
        exact
        path="/:application/entities/"
        component={EntityList}
      />
      <Route path="/:application/entities/:entityId" component={Entity} />
    </Switch>
  );
}
Example #10
Source File: SyncWithGithubPage.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
function SyncWithGithubPage({ match }: Props) {
  const { application } = match.params;

  const { data, error, refetch } = useQuery<{ app: AppWithGitRepository }>(
    GET_APP_GIT_REPOSITORY,
    {
      variables: {
        appId: application,
      },
    }
  );
  useNavigationTabs(application, NAVIGATION_KEY, match.url, `GitHub`);
  const errorMessage = formatError(error);

  return (
    <PageContent>
      <div className={CLASS_NAME}>
        <div className={`${CLASS_NAME}__header`}>
          <Icon icon="github" size="xlarge" />
          <h1>Sync with GitHub</h1>
        </div>
        <div className={`${CLASS_NAME}__message`}>
          Enable sync with GitHub to automatically push the generated code of
          your application and create a Pull Request in your GitHub repository
          every time you commit your changes.
        </div>
        {data?.app && <AuthAppWithGit app={data.app} onDone={refetch} />}

        <Snackbar open={Boolean(error)} message={errorMessage} />
      </div>
    </PageContent>
  );
}
Example #11
Source File: WorkspaceLayout.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
function WorkspaceLayout({ match }: Props) {
  if (isMobileOnly) {
    return <MobileMessage />;
  }

  return (
    <MainLayout className={CLASS_NAME}>
      <MainLayout.Menu>
        <MenuItemWithFixedPanel
          tooltip=""
          icon={false}
          isOpen
          panelKey={"panelKey"}
          onClick={() => {}}
        >
          <WorkspaceSelector />
          <div className={`${CLASS_NAME}__tabs`}>
            <InnerTabLink to={`/`} icon="grid">
              Apps
            </InnerTabLink>
            <InnerTabLink to={`/workspace/settings`} icon="settings">
              Workspace Settings
            </InnerTabLink>
            <InnerTabLink to={`/workspace/members`} icon="users">
              Workspace Members
            </InnerTabLink>
            {/* <InnerTabLink to={`/workspace/plans`} icon="file_text">
              Workspace Plan
            </InnerTabLink> */}
          </div>
        </MenuItemWithFixedPanel>
      </MainLayout.Menu>
      <MainLayout.Content>
        <CompleteInvitation />
        <div className={`${CLASS_NAME}__app-container`}>
          <PageContent className={CLASS_NAME}>
            <Switch>
              <RouteWithAnalytics exact path="/workspace/settings">
                <WorkspaceForm />
              </RouteWithAnalytics>
            </Switch>
            <Switch>
              <RouteWithAnalytics
                exact
                path="/workspace/members"
                component={MemberList}
              />
            </Switch>
            {/* <Switch>
              <RouteWithAnalytics exact path="/workspace/plans" component={Subscription} />
            </Switch> */}
			<Switch>
              <RouteWithAnalytics exact path="/user/profile">
                <ProfilePage />
              </RouteWithAnalytics>
            </Switch>
            <Switch>
              <RouteWithAnalytics exact path="/">
                <ApplicationList />
              </RouteWithAnalytics>
            </Switch>
          </PageContent>
        </div>
      </MainLayout.Content>
      <ScreenResolutionMessage />
    </MainLayout>
  );
}
Example #12
Source File: index.tsx    From metaflow-ui with Apache License 2.0 5 votes vote down vote up
/**
 * Find need for various buttons in breadcrumb. This is now very attached to fixed urls we have now
 * so we might need to make this more generic later.
 * @param routeMatch
 * @param location
 */
export function findAdditionalButtons(routeMatch: match<KnownURLParams> | null, location: string): BreadcrumbButtons[] {
  if (routeMatch === null) return [];
  const queryParams = new URLSearchParams(location);
  const buttons = [];
  const params = routeMatch.params;

  const flowValue = queryParams.get('flow_id') || params.flowId;
  if (flowValue && flowValue.split(',').length === 1) {
    buttons.push({
      label: `${flowValue}`,
      path: getPath.home() + '?flow_id=' + flowValue,
    });
  }

  if (params.flowId && params.runNumber) {
    buttons.push({
      label: `${params.runNumber}`,
      path: getPath.timeline(params.flowId, params.runNumber),
    });
  }

  // Special case since step name might be found from route params or query params.
  const stepValue = queryParams.get('steps') || params.stepName;
  if (params.flowId && params.runNumber && stepValue) {
    buttons.push({
      label: stepValue,
      path: getPath.step(params.flowId, params.runNumber, stepValue || 'undefined'),
    });
  }

  if (params.flowId && params.runNumber && params.stepName && params.taskId) {
    buttons.push({
      label: params.taskId,
      path: getPath.task(params.flowId, params.runNumber, params.stepName, params.taskId),
    });
  }

  return buttons;
}
Example #13
Source File: root.component.test.tsx    From openmrs-esm-patient-registration with MIT License 5 votes vote down vote up
sampleMatchProp: match<{ patientUuid: string }> = {
  isExact: false,
  path,
  url: path.replace(':patientUuid', '1'),
  params: { patientUuid: '1' },
}
Example #14
Source File: patient-registration.test.tsx    From openmrs-esm-patient-registration with MIT License 5 votes vote down vote up
sampleMatchProp: match<{ patientUuid: string }> = {
  isExact: false,
  path,
  url: path.replace(':patientUuid', '1'),
  params: { patientUuid: '1' },
}
Example #15
Source File: PendingChangesPage.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
PendingChangesPage = ({ match }: Props) => {
  const { application } = match.params;
  const [splitView, setSplitView] = useState<boolean>(false);

  useNavigationTabs(application, NAVIGATION_KEY, match.url, "Pending Changes");

  const handleChangeType = useCallback(
    (type: string) => {
      setSplitView(type === SPLIT);
    },
    [setSplitView]
  );
  const { data, error } = useQuery<TData>(GET_PENDING_CHANGES, {
    variables: {
      applicationId: application,
    },
  });

  const errorMessage = formatError(error);

  return (
    <>
      <PageContent className={CLASS_NAME}>
        {!data ? (
          "loading..."
        ) : (
          <div className={`${CLASS_NAME}__header`}>
            <h1>Pending Changes</h1>
            <MultiStateToggle
              label=""
              name="compareMode"
              options={OPTIONS}
              onChange={handleChangeType}
              selectedValue={splitView ? SPLIT : UNIFIED}
            />
          </div>
        )}
        <div className={`${CLASS_NAME}__changes`}>
          {data?.pendingChanges.map((change) => (
            <PendingChangeWithCompare
              key={change.resourceId}
              change={change}
              compareType={EnumCompareType.Pending}
              splitView={splitView}
            />
          ))}
        </div>
      </PageContent>
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </>
  );
}
Example #16
Source File: CommitList.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
CommitList = ({ match }: Props) => {
  const { application } = match.params;

  const [searchPhrase, setSearchPhrase] = useState<string>("");

  const handleSearchChange = useCallback(
    (value) => {
      setSearchPhrase(value);
    },
    [setSearchPhrase]
  );

  const { data, loading, error, refetch, stopPolling, startPolling } = useQuery<
    TData
  >(GET_COMMITS, {
    variables: {
      appId: application,
      orderBy: {
        [CREATED_AT_FIELD]: models.SortOrder.Desc,
      },
      whereMessage:
        searchPhrase !== ""
          ? { contains: searchPhrase, mode: models.QueryMode.Insensitive }
          : undefined,
    },
  });

  //start polling with cleanup
  useEffect(() => {
    refetch().catch(console.error);
    startPolling(POLL_INTERVAL);
    return () => {
      stopPolling();
    };
  }, [refetch, stopPolling, startPolling]);

  const errorMessage = formatError(error);

  return (
    <PageContent className={CLASS_NAME}>
      <div className={`${CLASS_NAME}__header`}>
        <SearchField
          label="search"
          placeholder="search"
          onChange={handleSearchChange}
        />
      </div>
      <div className={`${CLASS_NAME}__title`}>
        {data?.commits?.length} Commits
      </div>
      {loading && <CircularProgress />}
      {data?.commits.map((commit) => (
        <CommitListItem
          key={commit.id}
          commit={commit}
          applicationId={application}
        />
      ))}
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </PageContent>
  );
}
Example #17
Source File: ApplicationLayout.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
enhance = track((props) => {
  return { applicationId: props.match.params.application };
})
Example #18
Source File: Entity.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
enhance = track((props) => {
  return { entityId: props.match.params.entityId };
})
Example #19
Source File: BuildPage.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
BuildPage = ({ match }: Props) => {
  const { application, buildId } = match.params;

  const truncatedId = useMemo(() => {
    return truncateId(buildId);
  }, [buildId]);

  useNavigationTabs(
    application,
    `${NAVIGATION_KEY}_${buildId}`,
    match.url,
    `Build ${truncatedId}`
  );

  const [error, setError] = useState<Error>();

  const [getCommit, { data: commitData }] = useLazyQuery<{
    commit: models.Commit;
  }>(GET_COMMIT);

  const { data, error: errorLoading } = useQuery<{
    build: models.Build;
  }>(GET_BUILD, {
    variables: {
      buildId: buildId,
    },
    onCompleted: (data) => {
      getCommit({ variables: { commitId: data.build.commitId } });
    },
  });

  const actionLog = useMemo<LogData | null>(() => {
    if (!data?.build) return null;

    if (!data.build.action) return null;

    return {
      action: data.build.action,
      title: "Build log",
      versionNumber: data.build.version,
    };
  }, [data]);

  const errorMessage =
    formatError(errorLoading) || (error && formatError(error));

  return (
    <>
      <PageContent className={CLASS_NAME}>
        {!data ? (
          "loading..."
        ) : (
          <>
            <div className={`${CLASS_NAME}__header`}>
              <h2>
                Build <TruncatedId id={data.build.id} />
              </h2>
              {commitData && (
                <ClickableId
                  label="Commit"
                  to={`/${application}/commits/${commitData.commit.id}`}
                  id={commitData.commit.id}
                  eventData={{
                    eventName: "commitHeaderIdClick",
                  }}
                />
              )}
            </div>
            <div className={`${CLASS_NAME}__build-details`}>
              <BuildSteps build={data.build} onError={setError} />
              <aside className="log-container">
                <ActionLog
                  action={actionLog?.action}
                  title={actionLog?.title || ""}
                  versionNumber={actionLog?.versionNumber || ""}
                />
              </aside>
            </div>
          </>
        )}
      </PageContent>
      <Snackbar open={Boolean(error || errorLoading)} message={errorMessage} />
    </>
  );
}
Example #20
Source File: CommitPage.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
CommitPage = ({ match }: Props) => {
  const { application, commitId } = match.params;
  const [splitView, setSplitView] = useState<boolean>(false);

  const handleChangeType = useCallback(
    (type: string) => {
      setSplitView(type === SPLIT);
    },
    [setSplitView]
  );

  const truncatedId = useMemo(() => {
    return truncateId(commitId);
  }, [commitId]);

  useNavigationTabs(
    application,
    `${NAVIGATION_KEY}_${commitId}`,
    match.url,
    `Commit ${truncatedId}`
  );

  const { data, error } = useQuery<{
    commit: models.Commit;
  }>(GET_COMMIT, {
    variables: {
      commitId: commitId,
    },
  });
  const build =
    (data?.commit?.builds &&
      data?.commit?.builds.length &&
      data.commit.builds[0]) ||
    null;

  const errorMessage = formatError(error);
  return (
    <>
      <PageContent className={CLASS_NAME}>
        {!data ? (
          "loading..."
        ) : (
          <>
            <div className={`${CLASS_NAME}__header`}>
              <h2>
                Commit <TruncatedId id={data.commit.id} />
              </h2>
              <UserAndTime
                account={data.commit.user?.account}
                time={data.commit.createdAt}
              />
              <span className="spacer" />
              {build && (
                <ClickableId
                  label="Build"
                  to={`/${application}/builds/${build.id}`}
                  id={build.id}
                  eventData={{
                    eventName: "buildHeaderIdClick",
                  }}
                />
              )}
            </div>
            <div className={`${CLASS_NAME}__commit-message`}>
              {data.commit.message}
            </div>
          </>
        )}

        {data?.commit?.changes && (
          <div className={`${CLASS_NAME}__changes`}>
            <div className={`${CLASS_NAME}__changes__title`}>
              <h3 className={`${CLASS_NAME}__changes__count`}>
                {data.commit.changes.length}
                {data.commit.changes.length > 1 ? " changes" : " change"}
              </h3>
              <MultiStateToggle
                label=""
                name="compareMode"
                options={OPTIONS}
                onChange={handleChangeType}
                selectedValue={splitView ? SPLIT : UNIFIED}
              />
            </div>
            {data.commit.changes.map((change) => (
              <PendingChangeWithCompare
                key={change.resourceId}
                change={change}
                compareType={EnumCompareType.Previous}
                splitView={splitView}
              />
            ))}
          </div>
        )}
      </PageContent>
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </>
  );
}
Example #21
Source File: ApplicationLayout.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
function ApplicationLayout({ match }: Props) {
  const { application } = match.params;

  const [pendingChanges, setPendingChanges] = useState<PendingChangeItem[]>([]);
  const [commitRunning, setCommitRunning] = useState<boolean>(false);
  const [isError, setIsError] = useState<boolean>(false);

  const { data: pendingChangesData, refetch } = useQuery<
    PendingChangeStatusData
  >(GET_PENDING_CHANGES_STATUS, {
    variables: {
      applicationId: application,
    },
  });

  const { data: applicationData } = useQuery<ApplicationData>(GET_APPLICATION, {
    variables: {
      id: match.params.application,
    },
  });

  useEffect(() => {
    setPendingChanges(
      pendingChangesData ? pendingChangesData.pendingChanges : []
    );
  }, [pendingChangesData, setPendingChanges]);

  const addChange = useCallback(
    (
      resourceId: string,
      resourceType: models.EnumPendingChangeResourceType
    ) => {
      const existingChange = pendingChanges.find(
        (changeItem) =>
          changeItem.resourceId === resourceId &&
          changeItem.resourceType === resourceType
      );
      if (existingChange) {
        //reassign pending changes to trigger refresh
        setPendingChanges([...pendingChanges]);
      } else {
        setPendingChanges(
          pendingChanges.concat([
            {
              resourceId,
              resourceType,
            },
          ])
        );
      }
    },
    [pendingChanges, setPendingChanges]
  );

  const addEntity = useCallback(
    (entityId: string) => {
      addChange(entityId, models.EnumPendingChangeResourceType.Entity);
    },
    [addChange]
  );

  const addBlock = useCallback(
    (blockId: string) => {
      addChange(blockId, models.EnumPendingChangeResourceType.Block);
    },
    [addChange]
  );

  const resetPendingChanges = useCallback(() => {
    setPendingChanges([]);
    refetch();
  }, [refetch]);

  const setCommitRunningCallback = useCallback(
    (isRunning: boolean) => {
      setCommitRunning(isRunning);
    },
    [setCommitRunning]
  );
  const setIsErrorCallback = useCallback(
    (onError: boolean) => {
      setIsError(onError);
    },
    [setIsError]
  );

  const pendingChangesContextValue = useMemo(
    () => ({
      pendingChanges,
      commitRunning,
      isError,
      setIsError: setIsErrorCallback,
      setCommitRunning: setCommitRunningCallback,
      addEntity,
      addBlock,
      addChange,
      reset: resetPendingChanges,
    }),
    [
      pendingChanges,
      commitRunning,
      isError,
      addEntity,
      addBlock,
      addChange,
      resetPendingChanges,
      setCommitRunningCallback,
      setIsErrorCallback,
    ]
  );

  return (
    <PendingChangesContext.Provider value={pendingChangesContextValue}>
      <MainLayout
        className={CLASS_NAME}
        footer={<LastCommit applicationId={application} />}
      >
        <MainLayout.Menu>
          <MenuItem
            className={`${CLASS_NAME}__app-icon`}
            title="Dashboard"
            to={`/${application}`}
          >
            <CircleBadge
              name={applicationData?.app.name || ""}
              color={applicationData?.app.color}
            />
          </MenuItem>

          <MenuItem
            title="Entities"
            to={`/${application}/entities`}
            icon="entity_outline"
          />
          <MenuItem
            title="Roles"
            to={`/${application}/roles`}
            icon="roles_outline"
          />
          <MenuItem
            title="Commits"
            to={`/${application}/commits`}
            icon="history_commit_outline"
          />
          <MenuItem
            title="Connect to GitHub"
            to={`/${application}/github`}
            icon="github"
          />
        </MainLayout.Menu>
        <MainLayout.Content>
          <div className={`${CLASS_NAME}__app-container`}>
            <NavigationTabs defaultTabUrl={`/${application}/`} />

            <Switch>
              <RouteWithAnalytics
                path="/:application/pending-changes"
                component={PendingChangesPage}
              />

              <Route path="/:application/entities/" component={Entities} />

              <RouteWithAnalytics
                path="/:application/builds/:buildId"
                component={BuildPage}
              />

              <RouteWithAnalytics
                path="/:application/roles"
                component={RolesPage}
              />
              <Route path="/:application/commits" component={Commits} />
              <RouteWithAnalytics
                path="/:application/fix-related-entities"
                component={RelatedFieldsMigrationFix}
              />
              <RouteWithAnalytics
                path="/:application/github"
                component={SyncWithGithubPage}
              />
              <Route path="/:application/" component={ApplicationHome} />
            </Switch>
          </div>
        </MainLayout.Content>
        <MainLayout.Aside>
          <AsidePanel.Target className="main-layout__aside__expandable" />
        </MainLayout.Aside>

        <ScreenResolutionMessage />
      </MainLayout>
    </PendingChangesContext.Provider>
  );
}
Example #22
Source File: RelatedFieldsMigrationFix.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
RelatedFieldsMigrationFix = ({ match }: Props) => {
  const applicationId = match.params.application;
  const pendingChangesContext = useContext(PendingChangesContext);

  useNavigationTabs(
    applicationId,
    NAVIGATION_KEY,
    match.url,
    "Fix Entity Relations"
  );

  const { data, loading, error, refetch } = useQuery<TData>(GET_LOOKUP_FIELDS, {
    variables: {
      appId: applicationId,
    },
  });

  const [createDefaultRelatedEntity, { error: createError }] = useMutation<{
    createDefaultRelatedField: models.EntityField;
  }>(CREATE_DEFAULT_RELATED_ENTITY, {
    onCompleted: (createData) => {
      refetch();
      pendingChangesContext.addEntity(
        createData.createDefaultRelatedField.properties.relatedEntityId
      );

      const entity = data?.app.entities.find((entity) =>
        entity.fields?.some(
          (field) => field.id === createData.createDefaultRelatedField.id
        )
      );
      if (entity) {
        pendingChangesContext.addEntity(entity.id);
      }
    },
  });

  const handleRelatedFieldFormSubmit = useCallback(
    (relatedFieldValues: FormValues) => {
      createDefaultRelatedEntity({
        variables: {
          fieldId: relatedFieldValues.fieldId,
          relatedFieldDisplayName: relatedFieldValues.relatedFieldDisplayName,
          relatedFieldName: camelCase(
            relatedFieldValues.relatedFieldDisplayName
          ),
        },
      }).catch(console.error);
    },
    [createDefaultRelatedEntity]
  );

  const entityDictionary = useMemo(() => {
    return keyBy(data?.app.entities, (entity) => entity.id);
  }, [data]);

  const fieldDictionary = useMemo(() => {
    const allFields =
      data?.app.entities.flatMap((entity) => entity.fields || []) || [];

    const d = keyBy(allFields, (field) => field.permanentId);
    console.log(d);
    return d;
  }, [data]);

  const errorMessage =
    (error && formatError(error)) || (createError && formatError(createError));

  return (
    <PageContent className={CLASS_NAME}>
      <h2>New Release Updates</h2>
      <div className={`${CLASS_NAME}__message`}>
        Version 0.3.2 includes big improvements in how we manage related
        entities. The changes require your attention. <br />
        Following is a list of all the entities in your app, please provide the
        missing names for each of your existing relation fields.
        <span className={`${CLASS_NAME}__highlight`}>
          {" "}
          It will only take you a minute!
        </span>
      </div>
      {loading && <CircularProgress />}
      {data?.app.entities?.map((entity) => (
        <Panel className={`${CLASS_NAME}__entity`} key={entity.id}>
          <PanelHeader>{entity.displayName}</PanelHeader>
          <div className={`${CLASS_NAME}__entity__fields`}>
            {entity.fields && entity.fields.length
              ? entity.fields?.map((field) => (
                  <EntityRelationFieldsChart
                    key={field.id}
                    fixInPlace
                    applicationId={applicationId}
                    entityId={entity.id}
                    entityName={entity.displayName}
                    field={field}
                    relatedField={
                      fieldDictionary[field.properties.relatedFieldId]
                    }
                    relatedEntityName={
                      entityDictionary[field.properties.relatedEntityId]
                        .displayName
                    }
                    onSubmit={handleRelatedFieldFormSubmit}
                  />
                ))
              : "No relation fields"}
          </div>
        </Panel>
      ))}
      <Snackbar
        open={Boolean(error) || Boolean(createError)}
        message={errorMessage}
      />
    </PageContent>
  );
}
Example #23
Source File: ApplicationHome.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
function ApplicationHome({ match }: Props) {
  const applicationId = match.params.application;
  const location = useLocation();

  const { data, error } = useQuery<{
    app: models.App;
  }>(GET_APPLICATION, {
    variables: {
      id: applicationId,
    },
  });
  useNavigationTabs(
    applicationId,
    NAVIGATION_KEY,
    location.pathname,
    data?.app.name
  );

  const errorMessage = formatError(error);

  return (
    <PageContent
      className={CLASS_NAME}
      sideContent={
        <div>
          <div>
            <InnerTabLink to={`/${applicationId}/`} icon="home">
              Overview
            </InnerTabLink>
          </div>
          <div>
            <InnerTabLink to={`/${applicationId}/update`} icon="settings">
              App Settings
            </InnerTabLink>
          </div>
          <div>
            <InnerTabLink to={`/${applicationId}/db/update`} icon="settings">
              DB Settings
            </InnerTabLink>
          </div>
          <div>
            <InnerTabLink to={`/${applicationId}/auth/update`} icon="settings">
              Auth Settings
            </InnerTabLink>
          </div>
          <div>
            <InnerTabLink to={`/${applicationId}/api-tokens`} icon="id">
              API Tokens
            </InnerTabLink>
          </div>
        </div>
      }
    >
      <Switch>
        <RouteWithAnalytics
          path="/:application/api-tokens"
          component={ApiTokenList}
        />

        <Route
          path="/:application/"
          render={() => (
            <>
              <div
                className={classNames(
                  `${CLASS_NAME}__header`,
                  `theme-${data && COLOR_TO_NAME[data.app.color]}`
                )}
              >
                {data?.app.name}
                <CircleBadge
                  name={data?.app.name || ""}
                  color={data?.app.color || "transparent"}
                />
              </div>
              <Switch>
                <RouteWithAnalytics
                  exact
                  path="/:application/"
                  component={() => (
                    <div className={`${CLASS_NAME}__tiles`}>
                      <NewVersionTile applicationId={applicationId} />
                      <EntitiesTile applicationId={applicationId} />
                      <RolesTile applicationId={applicationId} />
                      <SyncWithGithubTile applicationId={applicationId} />
                    </div>
                  )}
                />
                <RouteWithAnalytics
                  path="/:application/update"
                  component={ApplicationForm}
                />
                <RouteWithAnalytics
                  path="/:application/db/update"
                  component={ApplicationDatabaseSettingsForms}
                />
                <RouteWithAnalytics
                  path="/:application/auth/update"
                  component={ApplicationAuthSettingForm}
                />
              </Switch>
            </>
          )}
        />
      </Switch>
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </PageContent>
  );
}
Example #24
Source File: ApplicationForm.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
function ApplicationForm({ match }: Props) {
  const applicationId = match.params.application;

  const { data, error } = useQuery<{
    app: models.App;
  }>(GET_APPLICATION, {
    variables: {
      id: applicationId,
    },
  });

  const { trackEvent } = useTracking();

  const [updateApp, { error: updateError }] = useMutation<TData>(UPDATE_APP);

  const handleSubmit = useCallback(
    (data) => {
      const { name, description, color } = data;
      trackEvent({
        eventName: "updateAppInfo",
      });
      updateApp({
        variables: {
          data: {
            name,
            description,
            color,
          },
          appId: applicationId,
        },
      }).catch(console.error);
    },
    [updateApp, applicationId, trackEvent]
  );

  const handleColorChange = useCallback(
    (color: string) => {
      trackEvent({
        eventName: "updateAppColor",
      });
      updateApp({
        variables: {
          data: {
            color,
          },
          appId: applicationId,
        },
      }).catch(console.error);
    },
    [updateApp, applicationId, trackEvent]
  );

  const errorMessage = formatError(error || updateError);
  return (
    <div className={CLASS_NAME}>
      {data?.app && (
        <>
          <Formik
            initialValues={data.app}
            validate={(values: models.App) => validate(values, FORM_SCHEMA)}
            enableReinitialize
            onSubmit={handleSubmit}
          >
            {(formik) => {
              return (
                <Form>
                  <h3>App Settings</h3>
                  <FormikAutoSave debounceMS={1000} />
                  <TextField name="name" label="Name" />
                  <TextField
                    autoComplete="off"
                    textarea
                    rows={3}
                    name="description"
                    label="Description"
                  />
                </Form>
              );
            }}
          </Formik>

          <div>
            <hr />
            <h3>
              <Icon icon="color" />
              App Color
            </h3>
            {COLORS.map((color) => (
              <ColorSelectButton
                color={color}
                key={color.value}
                onColorSelected={handleColorChange}
              />
            ))}
          </div>
        </>
      )}
      <Snackbar open={Boolean(error?.message || updateError?.message)} message={errorMessage} />
    </div>
  );
}
Example #25
Source File: Character.tsx    From client with MIT License 4 votes vote down vote up
Character: React.FC<Props> = ({
  match: {
    params: { name }
  }
}) => {
  const [inv, setInv] = useState<any>();

  const { loading, char } = useSelector(
    (state: AppState) => state.rankings.character
  );
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getCharacter(name));

    return () => {
      dispatch(clearCharacter());
    };
  }, [dispatch, name]);

  useEffect(() => {
    char && setInv(inventory.decode(char.Inventory));
  }, [char]);

  return (
    <div className='Character'>
      {loading ? (
        <Loader />
      ) : !char ? (
        `This character doesn't exist`
      ) : (
        <>
          <table className='char-info'>
            <tbody>
              <tr>
                <td colSpan={2}>
                  <Name char={char} style={{ display: 'inline-block' }} />
                </td>
              </tr>
              <tr>
                <td className='image'>
                  <img
                    src={`/images/classes/${cclass.shortClass(char.Class)}.jpg`}
                    alt='char'
                  />
                </td>
                <td className='info'>
                  <table>
                    <tbody>
                      <tr>
                        <td style={{ width: '50%' }}>Class</td>
                        <td>{cclass.charClass(char.Class)}</td>
                      </tr>
                      <tr>
                        <td>Level</td>
                        <td>
                          {char.cLevel}
                          <sup>{char.Resets}</sup>
                        </td>
                      </tr>
                      <tr>
                        <td>Total Points</td>
                        <td>{char.totalPoints.toLocaleString()}</td>
                      </tr>
                      <tr>
                        <td>Status</td>
                        <td
                          dangerouslySetInnerHTML={{
                            __html: rankings.pkStatus(char.PkCount)
                          }}
                        ></td>
                      </tr>
                      <tr>
                        <td>HOF Wins</td>
                        <td>{char.HOFWins}</td>
                      </tr>
                      <tr>
                        <td>Location</td>
                        <td>
                          <span
                            dangerouslySetInnerHTML={{
                              __html: rankings.location(char.MapNumber)
                            }}
                          />{' '}
                          ( {char.MapPosX}, {char.MapPosY} )
                        </td>
                      </tr>
                      <tr>
                        <td>Zen</td>
                        <td>{char.Money.toLocaleString()}</td>
                      </tr>
                    </tbody>
                  </table>
                </td>
              </tr>
            </tbody>
          </table>
          <div className='equipment'>
            <div className='title'>Equipment</div>
            <div className='container'>
              <div
                className='content'
                style={{
                  backgroundImage: `url('/images/classes/${cclass.shortClass(
                    char.Class
                  )}_inv.png')`
                }}
              >
                {inv &&
                  Object.keys(inv).map((key, i) => (
                    <div key={i} className={`item ${key}`}>
                      {inv[key] && (
                        <Item
                          hex={inv[key]}
                          style={{ maxWidth: '100%', maxHeight: '100%' }}
                        />
                      )}
                    </div>
                  ))}
              </div>
            </div>
          </div>
        </>
      )}
    </div>
  );
}
Example #26
Source File: Guild.tsx    From client with MIT License 4 votes vote down vote up
Guild: React.FC<Props> = ({
  match: {
    params: { name }
  }
}) => {
  const [master, setMaster] = useState<Character>();
  const [resetPoints, setResetPoints] = useState(0);
  const [alliance, setAlliance] = useState<IGuild[]>();

  const { loading, data: guild } = useSelector(
    (state: AppState) => state.rankings.guild
  );
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getGuild(name));

    return () => {
      dispatch(clearGuild());
    };
  }, [dispatch, name]);

  useEffect(() => {
    if (guild) {
      let rPoints = 0;
      guild.members.forEach(m => {
        rPoints += m.character.Resets;
      });

      const allys = guild.alliance.filter(
        g => g.G_Name !== guild.G_Name && g.G_Union !== 0
      );

      setMaster(guild.members.find(m => m.Name === guild.G_Master)?.character);
      setResetPoints(rPoints);
      setAlliance(allys);
    }
  }, [guild]);

  return (
    <div className='Guild'>
      {loading ? (
        <Loader />
      ) : !guild ? (
        `This guild doesn't exist`
      ) : (
        <>
          <table className='guild-info'>
            <tbody>
              <tr>
                <td colSpan={2} style={{ color: '#eeeeee' }}>
                  {guild.G_Name}
                </td>
              </tr>
              <tr>
                <td className='image'>
                  <Mark mark={guild.G_Mark} size={100} />
                </td>
                <td className='info'>
                  <table>
                    <tbody>
                      <tr>
                        <td style={{ width: '50%' }}>Guild Master</td>
                        <td>
                          {master && <Name char={master} guild={false} />}
                        </td>
                      </tr>
                      <tr>
                        <td>Alliance</td>
                        <td>
                          {alliance && alliance.length
                            ? alliance.map((g, i) => {
                                return (
                                  <span key={i}>
                                    <Link to={`/guild/${g.G_Name}`}>
                                      {g.G_Name}
                                    </Link>
                                    {i + 1 !== alliance.length ? ', ' : ''}
                                  </span>
                                );
                              })
                            : '-'}
                        </td>
                      </tr>
                      <tr>
                        <td>Reset Points</td>
                        <td>{resetPoints}</td>
                      </tr>
                      <tr>
                        <td>Guild Score</td>
                        <td>{guild.G_Score}</td>
                      </tr>
                    </tbody>
                  </table>
                </td>
              </tr>
            </tbody>
          </table>
          <div className='equipment'>
            <div className='title'>Members ( {guild.members.length} )</div>
            <div className='container'>
              <table className='Table'>
                <thead>
                  <tr>
                    <th>#</th>
                    <th>name</th>
                    <th>
                      level <sup>res</sup>
                    </th>
                    <th>class</th>
                    <th>position</th>
                  </tr>
                </thead>
                <tbody>
                  {guild.members
                    .sort((a, b) => b.character.Resets - a.character.Resets)
                    .sort((a, b) => b.G_Status - a.G_Status)
                    .map((m, i) => (
                      <tr key={i}>
                        <td>{i + 1}</td>
                        <td>
                          <Name char={m.character} guild={false} />
                        </td>
                        <td>
                          {m.character.cLevel} <sup>{m.character.Resets}</sup>
                        </td>
                        <td>{cclass.charClass(m.character.Class)}</td>
                        <td
                          dangerouslySetInnerHTML={{
                            __html: rankings.guildPosition(m.G_Status)
                          }}
                        ></td>
                      </tr>
                    ))}
                </tbody>
              </table>
            </div>
          </div>
        </>
      )}
    </div>
  );
}
Example #27
Source File: Logs.tsx    From client with MIT License 4 votes vote down vote up
Logs: React.FC<Props> = ({
  match: {
    params: { category }
  }
}) => {
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(20);

  const {
    loading,
    logs: { list, count }
  } = useSelector((state: AppState) => state.user.account);
  const chars = useSelector((state: AppState) => state.user.character.list);
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getLogs(page, perPage, category));
    dispatch(getChars());
  }, [dispatch, page, perPage, category]);

  return (
    <>
      <div className='Logs'>
        {loading ? (
          <Loader />
        ) : list ? (
          <table className='Table'>
            <thead>
              <tr>
                <th>#</th>
                <th>module</th>
                <th>log</th>
                <th>time</th>
                <th>ip</th>
              </tr>
            </thead>
            <tbody>
              {list.map((log, i) => {
                // Items
                let message = reactStringReplace(
                  log.message,
                  /{item:([^}]{32})}/gim,
                  match => (
                    <Item
                      key={uuid()}
                      hex={match}
                      image={false}
                      style={{ display: 'inline-block' }}
                    />
                  )
                );

                // Highlight of text
                message = reactStringReplace(
                  message,
                  /{highlight:([^}]+)}/gm,
                  match => (
                    <span key={uuid()} className='highlight'>
                      {match}
                    </span>
                  )
                );

                // Character Names
                message = reactStringReplace(
                  message,
                  /{char:([^}]+)}/gm,
                  match => {
                    const find = chars && chars.find(c => c.Name === match);
                    return (
                      <Name
                        key={uuid()}
                        char={{
                          Name: match,
                          status: find ? find.status : false
                        }}
                        guild={false}
                        style={{ display: 'inline-block' }}
                      />
                    );
                  }
                );

                const module = log.module ? log.module : 'unknown';

                return (
                  <tr key={i}>
                    <td>{i + 1 + (page - 1) * perPage}</td>
                    <td>
                      <Link to={`/user/account/logs/${module}`}>{module}</Link>
                    </td>
                    <td style={{ textAlign: 'left' }}>{message}</td>
                    <td>
                      <Moment
                        style={{
                          display: 'inline-block',
                          whiteSpace: 'nowrap'
                        }}
                        fromNow
                        unix
                        withTitle
                      >
                        {Math.floor(log.timestamp / 1000)}
                      </Moment>
                    </td>
                    <td style={{ whiteSpace: 'nowrap' }}>{log.ip}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        ) : (
          <div className='NoData'>No logs</div>
        )}
      </div>
      <Pagination
        page={page}
        perPage={perPage}
        totalItems={count || 0}
        setPage={setPage}
        setPerPage={setPerPage}
      />
    </>
  );
}
Example #28
Source File: EntityList.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
EntityList = ({ match }: Props) => {
  const { application } = match.params;
  const [error, setError] = useState<Error>();

  useNavigationTabs(application, NAVIGATION_KEY, match.url, "Entities");

  const [searchPhrase, setSearchPhrase] = useState<string>("");
  const [newEntity, setNewEntity] = useState<boolean>(false);

  const handleNewEntityClick = useCallback(() => {
    setNewEntity(!newEntity);
  }, [newEntity, setNewEntity]);

  const {
    data,
    loading,
    error: errorLoading,
    refetch,
    stopPolling,
    startPolling,
  } = useQuery<TData>(GET_ENTITIES, {
    variables: {
      id: application,
      orderBy: {
        [NAME_FIELD]: models.SortOrder.Asc,
      },
      whereName:
        searchPhrase !== ""
          ? { contains: searchPhrase, mode: models.QueryMode.Insensitive }
          : undefined,
    },
  });

  const handleSearchChange = useCallback(
    (value) => {
      setSearchPhrase(value);
    },
    [setSearchPhrase]
  );

  //start polling with cleanup
  useEffect(() => {
    refetch().catch(console.error);
    startPolling(POLL_INTERVAL);
    return () => {
      stopPolling();
    };
  }, [refetch, stopPolling, startPolling]);

  const errorMessage =
    formatError(errorLoading) || (error && formatError(error));

  return (
    <PageContent className={CLASS_NAME}>
      <Dialog
        className="new-entity-dialog"
        isOpen={newEntity}
        onDismiss={handleNewEntityClick}
        title="New Entity"
      >
        <NewEntity applicationId={application} />
      </Dialog>
      <div className={`${CLASS_NAME}__header`}>
        <SearchField
          label="search"
          placeholder="search"
          onChange={handleSearchChange}
        />
        <Button
          className={`${CLASS_NAME}__add-button`}
          buttonStyle={EnumButtonStyle.Primary}
          onClick={handleNewEntityClick}
          icon="plus"
        >
          Add entity
        </Button>
      </div>
      <div className={`${CLASS_NAME}__title`}>
        {data?.entities.length} Entities
      </div>
      {loading && <CircularProgress />}

      {data?.entities.map((entity) => (
        <EntityListItem
          key={entity.id}
          entity={entity}
          applicationId={application}
          onError={setError}
        />
      ))}

      <Snackbar open={Boolean(error || errorLoading)} message={errorMessage} />
    </PageContent>
  );
  /**@todo: move error message to hosting page  */
}
Example #29
Source File: ApplicationDatabaseSettingsForms.tsx    From amplication with Apache License 2.0 4 votes vote down vote up
function ApplicationDatabaseSettingsForms({ match }: Props) {
  const applicationId = match.params.application;

  const { data, error } = useQuery<{
    appSettings: models.AppSettings;
  }>(GET_APP_SETTINGS, {
    variables: {
      id: applicationId,
    },
  });
  const pendingChangesContext = useContext(PendingChangesContext);

  const { trackEvent } = useTracking();

  const [updateAppSettings, { error: updateError }] = useMutation<TData>(
    UPDATE_APP_SETTINGS,
    {
      onCompleted: (data) => {
        pendingChangesContext.addBlock(data.updateAppSettings.id);
      },
    }
  );

  const handleSubmit = useCallback(
    (data: models.AppSettings) => {
      const { dbHost, dbName, dbPassword, dbPort, dbUser, authProvider } = data;
      trackEvent({
        eventName: "updateAppSettings",
      });
      updateAppSettings({
        variables: {
          data: {
            dbHost,
            dbName,
            dbPassword,
            dbPort,
            dbUser,
            authProvider,
          },
          appId: applicationId,
        },
      }).catch(console.error);
    },
    [updateAppSettings, applicationId, trackEvent]
  );

  const errorMessage = formatError(error || updateError);
  return (
    <div className={CLASS_NAME}>
      {data?.appSettings && (
        <Formik
          initialValues={data.appSettings}
          validate={(values: models.AppSettings) =>
            validate(values, FORM_SCHEMA)
          }
          enableReinitialize
          onSubmit={handleSubmit}
        >
          {(formik) => {
            return (
              <Form>
                <h3>DB Settings</h3>
                <p>
                  All the below settings will appear in clear text in the
                  generated app. <br />
                  It should only be used for the development environment
                  variables and should not include sensitive data.
                </p>
                <FormikAutoSave debounceMS={2000} />
                <TextField name="dbHost" autoComplete="off" label="Host" />
                <TextField
                  name="dbName"
                  autoComplete="off"
                  label="Database Name"
                />
                <TextField
                  name="dbPort"
                  type="number"
                  autoComplete="off"
                  label="Port"
                />
                <TextField name="dbUser" autoComplete="off" label="User" />
                <TextField
                  name="dbPassword"
                  autoComplete="off"
                  label="Password"
                />
              </Form>
            );
          }}
        </Formik>
      )}
      <Snackbar open={Boolean(error)} message={errorMessage} />
    </div>
  );
}