From fc2df47753b5b8ac96a0a6688f697299f2d2c51f Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Tue, 28 Feb 2023 20:55:40 +1000 Subject: [PATCH] Add certificate model for http and dns change is_ecc to boolean, its still stored as int in sqlite --- backend/internal/api/schema/certificates.go | 8 +- backend/internal/entity/certificate/model.go | 2 +- .../internal/entity/certificate/template.go | 2 +- frontend/package.json | 1 + frontend/src/api/npm/models.ts | 14 ++- frontend/src/hooks/useCertificate.ts | 2 +- frontend/src/hooks/useCertificates.ts | 2 +- .../src/locale/src/HelpDoc/en/Certificates.md | 14 +++ frontend/src/locale/src/en.json | 21 ++++ .../CertificateCreateModal.tsx | 98 ++++++++-------- .../Common/CertificateAuthorityField.tsx | 77 +++++++++++++ .../Common/DNSProviderField.tsx | 73 ++++++++++++ .../Common/DomainNamesField.tsx | 109 ++++++++++++++++++ .../Common/EccField.tsx | 31 +++++ .../Common/NameField.tsx | 37 ++++++ .../CertificateCreateModal/Common/index.ts | 5 + .../CertificateCreateModal/CustomForm.tsx | 9 +- .../modals/CertificateCreateModal/DNSForm.tsx | 31 ++++- .../CertificateCreateModal/HTTPForm.tsx | 29 ++++- .../CertificateCreateModal/MKCertForm.tsx | 12 ++ frontend/src/pages/Certificates/index.tsx | 11 +- frontend/yarn.lock | 82 ++++++++++++- 22 files changed, 599 insertions(+), 71 deletions(-) create mode 100644 frontend/src/modals/CertificateCreateModal/Common/CertificateAuthorityField.tsx create mode 100644 frontend/src/modals/CertificateCreateModal/Common/DNSProviderField.tsx create mode 100644 frontend/src/modals/CertificateCreateModal/Common/DomainNamesField.tsx create mode 100644 frontend/src/modals/CertificateCreateModal/Common/EccField.tsx create mode 100644 frontend/src/modals/CertificateCreateModal/Common/NameField.tsx create mode 100644 frontend/src/modals/CertificateCreateModal/Common/index.ts create mode 100644 frontend/src/modals/CertificateCreateModal/MKCertForm.tsx diff --git a/backend/internal/api/schema/certificates.go b/backend/internal/api/schema/certificates.go index 1326f6f..780b027 100644 --- a/backend/internal/api/schema/certificates.go +++ b/backend/internal/api/schema/certificates.go @@ -51,9 +51,7 @@ func createCertificateHTTP() string { "type": "object" }, "is_ecc": { - "type": "integer", - "minimum": 0, - "maximum": 1 + "type": "boolean" } } }`, strictString("http"), intMinOne, stringMinMax(1, 100), domainNames()) @@ -83,9 +81,7 @@ func createCertificateDNS() string { "type": "object" }, "is_ecc": { - "type": "integer", - "minimum": 0, - "maximum": 1 + "type": "boolean" } } }`, strictString("dns"), intMinOne, intMinOne, stringMinMax(1, 100), domainNames()) diff --git a/backend/internal/entity/certificate/model.go b/backend/internal/entity/certificate/model.go index d5ce2b5..06e8d4a 100644 --- a/backend/internal/entity/certificate/model.go +++ b/backend/internal/entity/certificate/model.go @@ -57,7 +57,7 @@ type Model struct { Status string `json:"status" db:"status" filter:"status,string"` ErrorMessage string `json:"error_message" db:"error_message" filter:"error_message,string"` Meta types.JSONB `json:"-" db:"meta"` - IsECC int `json:"is_ecc" db:"is_ecc" filter:"is_ecc,integer"` + IsECC bool `json:"is_ecc" db:"is_ecc" filter:"is_ecc,bool"` IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` // Expansions: CertificateAuthority *certificateauthority.Model `json:"certificate_authority,omitempty"` diff --git a/backend/internal/entity/certificate/template.go b/backend/internal/entity/certificate/template.go index 7ba49ad..fd35814 100644 --- a/backend/internal/entity/certificate/template.go +++ b/backend/internal/entity/certificate/template.go @@ -13,7 +13,7 @@ type Template struct { Name string DomainNames []string Status string - IsECC int + IsECC bool // These are helpers for template generation IsCustom bool IsAcme bool // non-custom diff --git a/frontend/package.json b/frontend/package.json index b31d2ce..5b64b4e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@typescript-eslint/parser": "^5.30.6", "ajv": "^8.12.0", "babel-eslint": "^10.1.0", + "chakra-react-select": "^4.5.0", "classnames": "^2.3.1", "country-flag-icons": "^1.5.5", "date-fns": "2.28.0", diff --git a/frontend/src/api/npm/models.ts b/frontend/src/api/npm/models.ts index 3f2574e..1237e1c 100644 --- a/frontend/src/api/npm/models.ts +++ b/frontend/src/api/npm/models.ts @@ -52,12 +52,16 @@ export interface Certificate { id: number; createdOn: number; modifiedOn: number; + expiresOn: number | null; + type: string; + userId: number; + certificateAuthorityId: number; + dnsProviderId: number; name: string; - acmeshServer: string; - caBundle: string; - maxDomains: number; - isWildcardSupported: boolean; - isSetup: boolean; + domainNames: string[]; + status: string; + errorMessage: string; + isEcc: boolean; } export interface CertificateAuthority { diff --git a/frontend/src/hooks/useCertificate.ts b/frontend/src/hooks/useCertificate.ts index 31dbead..5751b60 100644 --- a/frontend/src/hooks/useCertificate.ts +++ b/frontend/src/hooks/useCertificate.ts @@ -47,7 +47,7 @@ const useSetCertificate = () => { onError: (error, values, rollback: any) => rollback(), onSuccess: async ({ id }: Certificate) => { queryClient.invalidateQueries(["certificate", id]); - queryClient.invalidateQueries("certificate"); + queryClient.invalidateQueries("certificates"); }, }, ); diff --git a/frontend/src/hooks/useCertificates.ts b/frontend/src/hooks/useCertificates.ts index 866c3b3..b14f858 100644 --- a/frontend/src/hooks/useCertificates.ts +++ b/frontend/src/hooks/useCertificates.ts @@ -28,7 +28,7 @@ const useCertificates = ( options = {}, ) => { return useQuery( - ["hosts", { offset, limit, sortBy, filters }], + ["certificates", { offset, limit, sortBy, filters }], () => fetchCertificates(offset, limit, sortBy, filters), { keepPreviousData: true, diff --git a/frontend/src/locale/src/HelpDoc/en/Certificates.md b/frontend/src/locale/src/HelpDoc/en/Certificates.md index 6488936..6beb55b 100644 --- a/frontend/src/locale/src/HelpDoc/en/Certificates.md +++ b/frontend/src/locale/src/HelpDoc/en/Certificates.md @@ -1,3 +1,17 @@ # Certificates Help +## HTTP Certificate + +todo + +## DNS Certificate + +todo + +## Custom Certificate + +todo + +## MKCert Certificate + todo diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json index d657a71..8c2f284 100644 --- a/frontend/src/locale/src/en.json +++ b/frontend/src/locale/src/en.json @@ -314,6 +314,9 @@ "certificate-authorities.title": { "defaultMessage": "Certificate Authorities" }, + "certificate-authority": { + "defaultMessage": "Certificate Authority" + }, "certificate-authority.acmesh-server": { "defaultMessage": "ACME Server" }, @@ -404,12 +407,24 @@ "disabled": { "defaultMessage": "Disabled" }, + "dns-provider": { + "defaultMessage": "DNS Provider" + }, "dns-provider.acmesh-name": { "defaultMessage": "Acme.sh Provider" }, "dns-provider.create": { "defaultMessage": "Create DNS Provider" }, + "dns-providers-empty": { + "defaultMessage": "No DNS Providers - Create one first" + }, + "domain_names": { + "defaultMessage": "Domain Names" + }, + "domain_names.max": { + "defaultMessage": "{count} domain names maximum" + }, "name": { "defaultMessage": "Name" }, @@ -524,6 +539,9 @@ "https-only": { "defaultMessage": "HTTPS Only" }, + "is-ecc": { + "defaultMessage": "ECC Certificate" + }, "lets-go": { "defaultMessage": "Let's go" }, @@ -638,6 +656,9 @@ "type.http": { "defaultMessage": "HTTP" }, + "type.mkcert": { + "defaultMessage": "MKCert" + }, "type.proxy": { "defaultMessage": "Proxy Host" }, diff --git a/frontend/src/modals/CertificateCreateModal/CertificateCreateModal.tsx b/frontend/src/modals/CertificateCreateModal/CertificateCreateModal.tsx index 6c899fb..98cf8fe 100644 --- a/frontend/src/modals/CertificateCreateModal/CertificateCreateModal.tsx +++ b/frontend/src/modals/CertificateCreateModal/CertificateCreateModal.tsx @@ -1,9 +1,5 @@ import { Button, - FormControl, - FormErrorMessage, - FormLabel, - Input, Modal, ModalOverlay, ModalContent, @@ -16,14 +12,14 @@ import { } from "@chakra-ui/react"; import { Certificate } from "api/npm"; import { PrettyButton } from "components"; -import { Formik, Form, Field } from "formik"; +import { Formik, Form } from "formik"; import { useSetCertificate } from "hooks"; import { intl } from "locale"; -import { validateString } from "modules/Validations"; import CustomForm from "./CustomForm"; import DNSForm from "./DNSForm"; import HTTPForm from "./HTTPForm"; +import MKCertForm from "./MKCertForm"; interface CertificateCreateModalProps { isOpen: boolean; @@ -44,8 +40,9 @@ function CertificateCreateModal({ const onSubmit = async ( payload: Certificate, - { setErrors, setSubmitting }: any, + { /*setErrors,*/ setSubmitting }: any, ) => { + payload.type = certType; const showErr = (msg: string) => { toast({ description: intl.formatMessage({ @@ -60,6 +57,8 @@ function CertificateCreateModal({ setCertificate(payload, { onError: (err: any) => { + showErr(err.message); + /* if (err.message === "ca-bundle-does-not-exist") { setErrors({ caBundle: intl.formatMessage({ @@ -68,28 +67,52 @@ function CertificateCreateModal({ }); } else { showErr(err.message); - } + }*/ }, onSuccess: () => onModalClose(), onSettled: () => setSubmitting(false), }); }; + const getInitialValues = (type: string): any => { + switch (type) { + case "http": + return { + certificateAuthorityId: 0, + name: "", + domainNames: [], + isEcc: false, + } as any; + case "dns": + return { + certificateAuthorityId: 0, + dnsProviderId: 0, + name: "", + domainNames: [], + isEcc: false, + } as any; + case "custom": + return { + name: "", + domainNames: [], + // isEcc: false, // todo, required? + // todo: add meta? + } as any; + case "mkcert": + return { + name: "", + domainNames: [], + // isEcc: false, // todo, supported? + // todo: add meta? + } as any; + } + }; + return ( - + {({ isSubmitting }) => (
@@ -98,39 +121,16 @@ function CertificateCreateModal({ - - {({ field, form }: any) => ( - - - {intl.formatMessage({ - id: "name", - })} - - - {form.errors.name} - - )} - + {certType === "http" ? : null} + {certType === "dns" ? : null} + {certType === "custom" ? : null} + {certType === "mkcert" ? : null} - - {certType === "http" ? : null} - {certType === "dns" ? : null} - {certType === "custom" ? : null} - {certType !== "" ? ( - - {intl.formatMessage({ id: "form.save" })} - - ) : null} + + {intl.formatMessage({ id: "form.save" })} + diff --git a/frontend/src/modals/CertificateCreateModal/Common/CertificateAuthorityField.tsx b/frontend/src/modals/CertificateCreateModal/Common/CertificateAuthorityField.tsx new file mode 100644 index 0000000..3573bfc --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/CertificateAuthorityField.tsx @@ -0,0 +1,77 @@ +import { + FormControl, + FormErrorMessage, + FormLabel, + Select, +} from "@chakra-ui/react"; +import { CertificateAuthority } from "api/npm"; +import { Field, useFormikContext } from "formik"; +import { useCertificateAuthorities } from "hooks"; +import { intl } from "locale"; + +const fieldName = "certificateAuthorityId"; + +interface CertificateAuthorityFieldProps { + onChange?: (maxDomains: number, isWildcardSupported: boolean) => any; +} +function CertificateAuthorityField({ + onChange, +}: CertificateAuthorityFieldProps) { + const { setFieldValue } = useFormikContext(); + const { data, isLoading } = useCertificateAuthorities(0, 999, [{ id: "id" }]); + + const handleOnChange = (e: any) => { + if (e.currentTarget.value) { + const id = parseInt(e.currentTarget.value, 10); + // This step enforces that the formik payload has a + // string number instead of a string as the value + // for this field + setFieldValue(fieldName, id); + if (onChange) { + // find items in list of data + const ca = data?.items.find((item) => item.id === id); + if (ca) { + onChange(ca.maxDomains, ca.isWildcardSupported); + } else { + onChange(0, false); + } + } + } + }; + + return ( + + {({ field, form }: any) => ( + + + {intl.formatMessage({ + id: "certificate-authority", + })} + + + {form.errors[fieldName]} + + )} + + ); +} + +export { CertificateAuthorityField }; diff --git a/frontend/src/modals/CertificateCreateModal/Common/DNSProviderField.tsx b/frontend/src/modals/CertificateCreateModal/Common/DNSProviderField.tsx new file mode 100644 index 0000000..75aa815 --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/DNSProviderField.tsx @@ -0,0 +1,73 @@ +import { + FormControl, + FormErrorMessage, + FormLabel, + Select, +} from "@chakra-ui/react"; +import { DNSProvider } from "api/npm"; +import { Field, useFormikContext } from "formik"; +import { useDNSProviders } from "hooks"; +import { intl } from "locale"; + +const fieldName = "dnsProviderId"; + +function DNSProviderField() { + const { setFieldValue } = useFormikContext(); + const { data, isLoading } = useDNSProviders(0, 999); + + const handleOnChange = (e: any) => { + if (e.currentTarget.value) { + const id = parseInt(e.currentTarget.value, 10); + // This step enforces that the formik payload has a + // string number instead of a string as the value + // for this field + setFieldValue(fieldName, id); + } + }; + + return ( + + {({ field, form }: any) => ( + + + {intl.formatMessage({ + id: "dns-provider", + })} + + + + {!isLoading && !data?.total + ? intl.formatMessage({ + id: "dns-providers-empty", + }) + : form.errors[fieldName]} + + + )} + + ); +} + +export { DNSProviderField }; diff --git a/frontend/src/modals/CertificateCreateModal/Common/DomainNamesField.tsx b/frontend/src/modals/CertificateCreateModal/Common/DomainNamesField.tsx new file mode 100644 index 0000000..c567cb7 --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/DomainNamesField.tsx @@ -0,0 +1,109 @@ +import { + FormControl, + FormErrorMessage, + FormLabel, + FormHelperText, +} from "@chakra-ui/react"; +import { CreatableSelect, OptionBase } from "chakra-react-select"; +import { Field, useFormikContext } from "formik"; +import { intl } from "locale"; + +interface SelectOption extends OptionBase { + label: string; + value: string; +} +interface DomainNamesFieldProps { + maxDomains?: number; + isWildcardSupported?: boolean; + onChange?: (i: string[]) => any; +} +function DomainNamesField({ + maxDomains, + isWildcardSupported, + onChange, +}: DomainNamesFieldProps) { + const { values, setFieldValue } = useFormikContext(); + + const getDomainCount = (v: string[] | undefined) => { + if (typeof v !== "undefined" && v?.length) { + return v.length; + } + return 0; + }; + + const isDomainValid = (d: string): boolean => { + const dom = d.trim().toLowerCase(); + const v: any = values; + + // Deny if the list of domains is hit + if (maxDomains && getDomainCount(v?.domainNames) >= maxDomains) { + return false; + } + + if (dom.length < 3) { + return false; + } + + // Prevent wildcards + if (!isWildcardSupported && dom.indexOf("*") !== -1) { + return false; + } + + // Prevent duplicate * in domain + if ((dom.match(/\*/g) || []).length > 1) { + return false; + } + + // Prevent some invalid characters + // @ , + if ((dom.match(/(@|,)/g) || []).length > 0) { + return false; + } + + // This will match *.com type domains, + return dom.match(/\*\.[^.]+$/m) === null; + }; + + const handleChange = (values: any) => { + const doms = values?.map((i: SelectOption) => { + return i.value; + }); + setFieldValue("domainNames", doms); + }; + + return ( + + {({ field, form }: any) => ( + + + {intl.formatMessage({ + id: "domain_names", + })} + + + {maxDomains ? ( + + {intl.formatMessage( + { id: "domain_names.max" }, + { count: maxDomains }, + )} + + ) : null} + {form.errors.domainNames} + + )} + + ); +} + +export { DomainNamesField }; diff --git a/frontend/src/modals/CertificateCreateModal/Common/EccField.tsx b/frontend/src/modals/CertificateCreateModal/Common/EccField.tsx new file mode 100644 index 0000000..0852aa6 --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/EccField.tsx @@ -0,0 +1,31 @@ +import { + FormControl, + FormErrorMessage, + FormLabel, + Switch, +} from "@chakra-ui/react"; +import { Field } from "formik"; +import { intl } from "locale"; + +const fieldName = "isEcc"; + +function EccField() { + return ( + + {({ field, form }: any) => ( + + + {intl.formatMessage({ + id: "is-ecc", + })} + + + {form.errors[fieldName]} + + )} + + ); +} + +export { EccField }; diff --git a/frontend/src/modals/CertificateCreateModal/Common/NameField.tsx b/frontend/src/modals/CertificateCreateModal/Common/NameField.tsx new file mode 100644 index 0000000..ef04269 --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/NameField.tsx @@ -0,0 +1,37 @@ +import { + FormControl, + FormErrorMessage, + FormLabel, + Input, +} from "@chakra-ui/react"; +import { Field } from "formik"; +import { intl } from "locale"; +import { validateString } from "modules/Validations"; + +function NameField() { + return ( + + {({ field, form }: any) => ( + + + {intl.formatMessage({ + id: "name", + })} + + + {form.errors.name} + + )} + + ); +} + +export { NameField }; diff --git a/frontend/src/modals/CertificateCreateModal/Common/index.ts b/frontend/src/modals/CertificateCreateModal/Common/index.ts new file mode 100644 index 0000000..fde5c13 --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/Common/index.ts @@ -0,0 +1,5 @@ +export * from "./CertificateAuthorityField"; +export * from "./DNSProviderField"; +export * from "./DomainNamesField"; +export * from "./EccField"; +export * from "./NameField"; diff --git a/frontend/src/modals/CertificateCreateModal/CustomForm.tsx b/frontend/src/modals/CertificateCreateModal/CustomForm.tsx index e76cbbb..75291ee 100644 --- a/frontend/src/modals/CertificateCreateModal/CustomForm.tsx +++ b/frontend/src/modals/CertificateCreateModal/CustomForm.tsx @@ -1,5 +1,12 @@ +import { DomainNamesField, NameField } from "./Common"; + function CustomForm() { - return

Custom form

; + return ( + <> + + + + ); } export default CustomForm; diff --git a/frontend/src/modals/CertificateCreateModal/DNSForm.tsx b/frontend/src/modals/CertificateCreateModal/DNSForm.tsx index adf1f5e..e47087d 100644 --- a/frontend/src/modals/CertificateCreateModal/DNSForm.tsx +++ b/frontend/src/modals/CertificateCreateModal/DNSForm.tsx @@ -1,5 +1,34 @@ +import { useState } from "react"; + +import { + CertificateAuthorityField, + DNSProviderField, + DomainNamesField, + EccField, + NameField, +} from "./Common"; + function DNSForm() { - return

DNS form

; + const [maxDomains, setMaxDomains] = useState(0); + const [isWildcardSupported, setIsWildcardSupported] = useState(false); + + const handleCAChange = (maxD: number, wildcards: boolean) => { + setMaxDomains(maxD); + setIsWildcardSupported(wildcards); + }; + + return ( + <> + + + + + + + ); } export default DNSForm; diff --git a/frontend/src/modals/CertificateCreateModal/HTTPForm.tsx b/frontend/src/modals/CertificateCreateModal/HTTPForm.tsx index 82e1373..1fdd921 100644 --- a/frontend/src/modals/CertificateCreateModal/HTTPForm.tsx +++ b/frontend/src/modals/CertificateCreateModal/HTTPForm.tsx @@ -1,5 +1,32 @@ +import { useState } from "react"; + +import { + CertificateAuthorityField, + DomainNamesField, + EccField, + NameField, +} from "./Common"; + function HTTPForm() { - return

Http form

; + const [maxDomains, setMaxDomains] = useState(0); + const [isWildcardSupported, setIsWildcardSupported] = useState(false); + + const handleCAChange = (maxD: number, wildcards: boolean) => { + setMaxDomains(maxD); + setIsWildcardSupported(wildcards); + }; + + return ( + <> + + + + + + ); } export default HTTPForm; diff --git a/frontend/src/modals/CertificateCreateModal/MKCertForm.tsx b/frontend/src/modals/CertificateCreateModal/MKCertForm.tsx new file mode 100644 index 0000000..513631e --- /dev/null +++ b/frontend/src/modals/CertificateCreateModal/MKCertForm.tsx @@ -0,0 +1,12 @@ +import { DomainNamesField, NameField } from "./Common"; + +function MKCertForm() { + return ( + <> + + + + ); +} + +export default MKCertForm; diff --git a/frontend/src/pages/Certificates/index.tsx b/frontend/src/pages/Certificates/index.tsx index 6477d4d..53474e8 100644 --- a/frontend/src/pages/Certificates/index.tsx +++ b/frontend/src/pages/Certificates/index.tsx @@ -9,14 +9,17 @@ import { MenuDivider, } from "@chakra-ui/react"; import { HelpDrawer, PrettyMenuButton } from "components"; +import { useDNSProviders } from "hooks"; import { intl } from "locale"; import { CertificateCreateModal } from "modals"; -import { FiGlobe, FiServer, FiUpload } from "react-icons/fi"; +import { FiGlobe, FiServer, FiShieldOff, FiUpload } from "react-icons/fi"; import TableWrapper from "./TableWrapper"; function Certificates() { const [createShown, setCreateShown] = useState(""); + const { data: dnsProviders, isLoading: dnsProvidersIsLoading } = + useDNSProviders(0, 999); return ( <> @@ -37,6 +40,7 @@ function Certificates() { {intl.formatMessage({ id: "type.http" })} } onClick={() => setCreateShown("dns")}> {intl.formatMessage({ id: "type.dns" })} @@ -47,6 +51,11 @@ function Certificates() { onClick={() => setCreateShown("custom")}> {intl.formatMessage({ id: "type.custom" })} + } + onClick={() => setCreateShown("mkcert")}> + {intl.formatMessage({ id: "type.mkcert" })} + diff --git a/frontend/yarn.lock b/frontend/yarn.lock index fae4b88..80bd98e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1034,6 +1034,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.0", "@babel/runtime@^7.8.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/runtime@^7.3.1": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" @@ -1982,7 +1989,7 @@ source-map "^0.5.7" stylis "4.1.3" -"@emotion/cache@^11.10.5": +"@emotion/cache@^11.10.5", "@emotion/cache@^11.4.0": version "11.10.5" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12" integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA== @@ -2022,7 +2029,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== -"@emotion/react@^11.10.6": +"@emotion/react@^11.10.6", "@emotion/react@^11.8.1": version "11.10.6" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11" integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== @@ -2099,6 +2106,18 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@floating-ui/core@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.1.tgz#074182a1d277f94569c50a6b456e62585d463c8e" + integrity sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg== + +"@floating-ui/dom@^1.0.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.1.tgz#8f93906e1a3b9f606ce78afb058e874344dcbe07" + integrity sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA== + dependencies: + "@floating-ui/core" "^1.2.1" + "@formatjs/cli@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@formatjs/cli/-/cli-5.0.2.tgz#1df27fc52aae0ddcfc34ac0d2d8504fe244e6f0f" @@ -3132,6 +3151,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.4.0": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" + integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.0.15": version "18.0.15" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe" @@ -4271,6 +4297,13 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== +chakra-react-select@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/chakra-react-select/-/chakra-react-select-4.5.0.tgz#9b596cb8f346608c4c59457dde4b300770e01123" + integrity sha512-5oxVH9tmn3kVVLt9m/zT28Efv44mk30BZETubE2MhkGMIeM9oJsiL2+dhgKLNUMLRxglmacY00oGQTSI0JrRTA== + dependencies: + react-select "5.7.0" + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -5133,6 +5166,14 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -8110,6 +8151,11 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.3" +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + meow@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" @@ -9737,7 +9783,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.8.1: +prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10108,6 +10154,21 @@ react-scripts@5.0.1: optionalDependencies: fsevents "^2.3.2" +react-select@5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.0.tgz#82921b38f1fcf1471a0b62304da01f2896cd8ce6" + integrity sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ== + dependencies: + "@babel/runtime" "^7.12.0" + "@emotion/cache" "^11.4.0" + "@emotion/react" "^11.8.1" + "@floating-ui/dom" "^1.0.1" + "@types/react-transition-group" "^4.4.0" + memoize-one "^6.0.0" + prop-types "^15.6.0" + react-transition-group "^4.3.0" + use-isomorphic-layout-effect "^1.1.2" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" @@ -10133,6 +10194,16 @@ react-table@7.8.0: resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== +react-transition-group@^4.3.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -11705,6 +11776,11 @@ use-callback-ref@^1.3.0: dependencies: tslib "^2.0.0" +use-isomorphic-layout-effect@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + use-sidecar@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"