Skip to content

Commit

Permalink
Merge pull request #744 from wri/epic/TM-1398-tree-species
Browse files Browse the repository at this point in the history
[TM-1398] Merge tree species epic
  • Loading branch information
roguenet authored Dec 13, 2024
2 parents bbc3a18 + 448211f commit 8cc7096
Show file tree
Hide file tree
Showing 43 changed files with 1,330 additions and 392 deletions.
1 change: 0 additions & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: pull-request
on:
pull_request:
branches: [main, staging, release/**]
jobs:
test:
runs-on: ubuntu-latest
Expand Down
20 changes: 14 additions & 6 deletions openapi-codegen.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,34 @@ type EnvironmentName = (typeof ENVIRONMENT_NAMES)[number];
type Environment = {
apiBaseUrl: string;
userServiceUrl: string;
entityServiceUrl: string;
};

const ENVIRONMENTS: { [Property in EnvironmentName]: Environment } = {
local: {
apiBaseUrl: "http://localhost:8080",
userServiceUrl: "http://localhost:4010"
userServiceUrl: "http://localhost:4010",
entityServiceUrl: "http://localhost:4050"
},
dev: {
apiBaseUrl: "https://api-dev.terramatch.org",
userServiceUrl: "https://api-dev.terramatch.org"
userServiceUrl: "https://api-dev.terramatch.org",
entityServiceUrl: "https://api-dev.terramatch.org"
},
test: {
apiBaseUrl: "https://api-test.terramatch.org",
userServiceUrl: "https://api-test.terramatch.org"
userServiceUrl: "https://api-test.terramatch.org",
entityServiceUrl: "https://api-test.terramatch.org"
},
staging: {
apiBaseUrl: "https://api-staging.terramatch.org",
userServiceUrl: "https://api-staging.terramatch.org"
userServiceUrl: "https://api-staging.terramatch.org",
entityServiceUrl: "https://api-staging.terramatch.org"
},
prod: {
apiBaseUrl: "https://api.terramatch.org",
userServiceUrl: "https://api.terramatch.org"
userServiceUrl: "https://api.terramatch.org",
entityServiceUrl: "https://api.terramatch.org"
}
};

Expand All @@ -60,13 +66,15 @@ if (!ENVIRONMENT_NAMES.includes(declaredEnv as EnvironmentName)) {
const DEFAULTS = ENVIRONMENTS[declaredEnv];
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? DEFAULTS.apiBaseUrl;
const userServiceUrl = process.env.NEXT_PUBLIC_USER_SERVICE_URL ?? DEFAULTS.userServiceUrl;
const entityServiceUrl = process.env.NEXT_PUBLIC_ENTITY_SERVICE_URL ?? DEFAULTS.entityServiceUrl;

// The services defined in the v3 Node BE codebase. Although the URL path for APIs in the v3 space
// are namespaced by feature set rather than service (a service may contain multiple namespaces), we
// isolate the generated API integration by service to make it easier for a developer to find where
// the associated BE code is for a given FE API integration.
const SERVICES = {
"user-service": userServiceUrl
"user-service": userServiceUrl,
"entity-service": entityServiceUrl
};

const config: Record<string, Config> = {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"build-storybook": "storybook build",
"generate:api": "openapi-codegen gen api",
"generate:userService": "openapi-codegen gen userService",
"generate:services": "npm run generate:userService",
"generate:entityService": "openapi-codegen gen entityService",
"generate:services": "yarn generate:userService && yarn generate:entityService",
"tx:push": "eval $(grep '^TRANSIFEX_TOKEN' .env) && eval $(grep '^TRANSIFEX_SECRET' .env) && txjs-cli push --key-generator=hash src/ --token=$TRANSIFEX_TOKEN --secret=$TRANSIFEX_SECRET",
"tx:pull": "eval $(grep '^TRANSIFEX_TOKEN' .env) && eval $(grep '^TRANSIFEX_SECRET' .env) && txjs-cli pull --token=$TRANSIFEX_TOKEN --secret=$TRANSIFEX_SECRET"
},
Expand Down
53 changes: 28 additions & 25 deletions src/admin/components/EntityEdit/EntityEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useNavigate, useParams } from "react-router-dom";
import modules from "@/admin/modules";
import WizardForm from "@/components/extensive/WizardForm";
import LoadingContainer from "@/components/generic/Loading/LoadingContainer";
import EntityProvider from "@/context/entity.provider";
import FrameworkProvider, { Framework } from "@/context/framework.provider";
import {
GetV2FormsENTITYUUIDResponse,
Expand Down Expand Up @@ -73,31 +74,33 @@ export const EntityEdit = () => {
<div className="mx-auto w-full max-w-7xl">
<LoadingContainer loading={isLoading}>
<FrameworkProvider frameworkKey={framework}>
<WizardForm
steps={formSteps!}
errors={error}
onBackFirstStep={() => navigate("..")}
onChange={data =>
updateEntity({
pathParams: { uuid: entityUUID, entity: entityName },
body: { answers: normalizedFormData(data, formSteps!) }
})
}
formStatus={isSuccess ? "saved" : isUpdating ? "saving" : undefined}
onSubmit={() => navigate(createPath({ resource, id, type: "show" }))}
defaultValues={defaultValues}
title={title}
tabOptions={{
markDone: true,
disableFutureTabs: true
}}
summaryOptions={{
title: "Review Details",
downloadButtonText: "Download"
}}
roundedCorners
hideSaveAndCloseButton
/>
<EntityProvider entityUuid={entityUUID} entityName={entityName}>
<WizardForm
steps={formSteps!}
errors={error}
onBackFirstStep={() => navigate("..")}
onChange={data =>
updateEntity({
pathParams: { uuid: entityUUID, entity: entityName },
body: { answers: normalizedFormData(data, formSteps!) }
})
}
formStatus={isSuccess ? "saved" : isUpdating ? "saving" : undefined}
onSubmit={() => navigate(createPath({ resource, id, type: "show" }))}
defaultValues={defaultValues}
title={title}
tabOptions={{
markDone: true,
disableFutureTabs: true
}}
summaryOptions={{
title: "Review Details",
downloadButtonText: "Download"
}}
roundedCorners
hideSaveAndCloseButton
/>
</EntityProvider>
</FrameworkProvider>
</LoadingContainer>
</div>
Expand Down
9 changes: 9 additions & 0 deletions src/assets/icons/add-button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/assets/icons/edit-ta.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/assets/icons/new-tag-tree-species.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/assets/icons/non-scientific name.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/assets/icons/trash-ta.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { Popover, Transition } from "@headlessui/react";
import { useT } from "@transifex/react";
import classNames from "classnames";
import { ChangeEvent, forwardRef, Fragment, Ref, useState } from "react";
import { Else, If, Then } from "react-if";

import { useDebounce } from "@/hooks/useDebounce";
import { useValueChanged } from "@/hooks/useValueChanged";

import Text from "../../Text/Text";
import Input, { InputProps } from "../Input/Input";

export interface AutoCompleteInputProps extends InputProps {
onSearch: (query: string) => Promise<string[]>;
disableAutoComplete?: boolean;
classNameMenu?: string;
}

const SEARCH_RESET = { list: [], query: "" };

//TODO: Bugfix: Users can enter space in this input
const AutoCompleteInput = forwardRef(
({ onSearch, disableAutoComplete, ...inputProps }: AutoCompleteInputProps, ref?: Ref<HTMLInputElement>) => {
(
{ onSearch, disableAutoComplete, classNameMenu, ...inputProps }: AutoCompleteInputProps,
ref?: Ref<HTMLInputElement>
) => {
const t = useT();
const [list, setList] = useState<string[]>([]);
const [searchResult, setSearchResult] = useState<{ list: string[]; query: string }>(SEARCH_RESET);
const [loading, setLoading] = useState(false);

const onSelect = (item: string) => {
Expand All @@ -27,51 +35,53 @@ const AutoCompleteInput = forwardRef(
inputProps.onChange?.({ target: { name: inputProps.name, value: item } } as ChangeEvent<HTMLInputElement>);
}

setList([]);
// Avoid showing the search result list unless the name changes again.
setSearchResult({ list: [], query: item });
};

const search = useDebounce(async (query: string) => {
if (query === searchResult.query) return;

setLoading(true);

onSearch(query)
.then(resp => {
setList(resp);
setLoading(false);
})
.catch(() => {
setList([]);
setLoading(false);
});
try {
setSearchResult({ list: await onSearch(query), query });
setLoading(false);
} catch {
setSearchResult(SEARCH_RESET);
setLoading(false);
}
});

useValueChanged(inputProps.value, () => search(String(inputProps.value ?? "")));

return (
<Popover as="div" className="w-full">
<Popover.Button as={Fragment}>
<Input
{...inputProps}
ref={ref}
onChangeCapture={e => !disableAutoComplete && search(e.currentTarget.value)}
/>
<Input {...inputProps} ref={ref} />
</Popover.Button>

<Transition
show={list.length > 0 || !!loading}
show={searchResult.list.length > 0 || !!loading}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Popover.Panel as="div" className="border-light mt-2 max-h-[230px] overflow-auto rounded-lg">
<Popover.Panel
as="div"
className={classNames("border-light mt-2 max-h-[230px] overflow-auto rounded-lg", classNameMenu)}
>
<If condition={loading}>
<Then>
<Text variant="text-body-600" className="p-3">
{t("Loading ...")}
</Text>
</Then>
<Else>
{list.map(item => (
{searchResult.list.map(item => (
<Text
key={item}
variant="text-body-600"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ exports[`Storyshots Components/Elements/Inputs/AutoComplete Default 1`] = `
className="w-full outline-none transition-all duration-300 ease-in-out focus:ring-transparent px-3 py-[9px] rounded-lg focus:border-primary-500 border border-neutral-200"
data-headlessui-state=""
id=":r9:"
onChangeCapture={[Function]}
onClick={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
Expand Down
7 changes: 6 additions & 1 deletion src/components/elements/Inputs/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface InputProps
extends InputWrapperProps,
Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "type" | "form"> {
name: string;
variant?: "secondary" | "default" | "login" | "signup" | "monitored";
variant?: "secondary" | "default" | "login" | "signup" | "monitored" | "treePlanted";
formHook?: UseFormReturn<any>;
clearable?: boolean;
iconButtonProps?: IconButtonProps;
Expand Down Expand Up @@ -112,6 +112,11 @@ const Input = forwardRef(
true,
"pl-4": inputProps.type === "number",
"border-neutral-300": !error
},
treePlanted: {
"py-[7.5px] py-1.5 !w-[100px] text-center border border-blueCustom-700 rounded hover:border-primary hover:shadow-blue-border opacity-60 outline-none text-14-light !font-primary":
true,
"text-center": inputProps.type === "number"
}
};

Expand Down
Loading

0 comments on commit 8cc7096

Please sign in to comment.