@boxyhq/react-ui
Version:
React UI components from BoxyHQ
1 lines • 65.7 kB
Source Map (JSON)
{"version":3,"file":"dsync.cjs","sources":["../src/dsync/types.ts","../src/dsync/CreateDirectory/index.tsx","../src/dsync/DirectoryList/index.tsx","../src/dsync/ToggleConnectionStatus/index.tsx","../src/dsync/EditDirectory/index.tsx","../src/dsync/DirectoriesWrapper/index.tsx"],"sourcesContent":["import { ConfirmationPromptProps, PaginateProps, TableCol, TableProps } from '../shared/types';\nexport interface CreateDirectoryProps {\n excludeFields?: Array<keyof UnSavedDirectory>;\n urls: {\n post: string;\n };\n defaultWebhookEndpoint?: string;\n defaultWebhookSecret?: string;\n successCallback?: (info: {\n operation: 'CREATE';\n connection?: Directory;\n }) => void;\n errorCallback?: (errMsg: string) => void;\n // To handle cancel button click\n cancelCallback?: () => void;\n classNames?: {\n fieldContainer?: string;\n input?: string;\n select?: string;\n label?: string;\n button?: {\n ctoa?: string;\n cancel?: string;\n };\n };\n /** Use this boolean to toggle the header display on/off. Useful when using the create component standalone */\n displayHeader?: boolean;\n disableGoogleProvider?: boolean;\n tenant?: string;\n product?: string;\n}\nexport interface DeleteDirectoryProps {\n urls: {\n delete: string;\n };\n cb: () => void;\n successCallback: (successMsg: string) => void;\n errorCallback: (errMsg: string) => void;\n classNames?: {\n section?: string;\n };\n}\nexport interface DirectoryListProps {\n cols?: ('name' | 'tenant' | 'product' | 'type' | 'status' | 'actions' | TableCol)[];\n urls: {\n get: string;\n };\n errorCallback?: (errMessage: string) => void;\n handleListFetchComplete?: (directories: Directory[]) => void;\n handleActionClick: (action: 'edit' | 'view', directory: any) => void;\n hideViewAction?: boolean;\n classNames?: {\n tableContainer?: string;\n };\n tableProps?: Pick<TableProps, 'tableCaption' | 'classNames'>;\n tenant?: string;\n product?: string;\n paginate?: Pick<PaginateProps, 'itemsPerPage'> & Partial<Pick<PaginateProps, 'handlePageChange'>>;\n}\nexport interface EditDirectoryProps {\n urls: {\n patch: string;\n delete: string;\n get: string;\n };\n errorCallback?: (errMessage: string) => void;\n successCallback?: (info: {\n operation: 'UPDATE' | 'DELETE' | 'COPY';\n connection?: Directory;\n }) => void;\n cancelCallback?: () => void;\n classNames?: {\n button?: {\n ctoa?: string;\n destructive?: string;\n cancel?: string;\n };\n confirmationPrompt?: ConfirmationPromptProps['classNames'];\n fieldContainer?: string;\n label?: string;\n input?: string;\n section?: string;\n };\n excludeFields?: Array<keyof (UnSavedDirectory & {\n scim_endpoint: string;\n scim_token: string;\n })>;\n /** Use this boolean to toggle the header display on/off. Useful when using the edit component standalone */\n displayHeader?: boolean;\n /** Use this boolean to toggle the save button display on/off. Useful when using the edit component in setup links */\n hideSave?: boolean;\n}\nexport interface ToggleDirectoryStatusProps {\n connection: Directory | null;\n urls: {\n patch: string;\n };\n errorCallback?: (errMsg: string) => void;\n successCallback?: (info: {\n operation: 'UPDATE';\n connection?: Directory;\n }) => void;\n classNames?: {\n container?: string;\n confirmationPrompt?: ConfirmationPromptProps['classNames'];\n };\n}\nexport interface DirectoriesWrapperProps {\n classNames?: {\n button?: {\n ctoa?: string;\n destructive?: string;\n cancel?: string;\n };\n input?: string;\n textarea?: string;\n select?: string;\n confirmationPrompt?: ConfirmationPromptProps['classNames'];\n secretInput?: string;\n section?: string;\n };\n componentProps?: {\n directoryList?: Partial<DirectoryListProps>;\n createDirectory?: Partial<CreateDirectoryProps>;\n editDirectory?: Partial<EditDirectoryProps>;\n };\n successCallback?: (info: {\n operation: 'CREATE' | 'UPDATE' | 'DELETE' | 'COPY';\n connection?: Partial<Directory | undefined>;\n }) => void;\n errorCallback?: (errMessage: string) => void;\n urls: {\n get: string;\n post: string;\n patch: string;\n delete: string;\n };\n title?: string;\n tenant?: string;\n product?: string;\n}\nexport type ApiSuccess<T> = {\n data: T;\n pageToken?: string;\n};\nexport interface ApiError extends Error {\n info?: string;\n status: number;\n}\nexport type ApiResponse<T = any> = ApiSuccess<T> | {\n error: ApiError;\n};\nexport enum DirectorySyncProviders {\n 'azure-scim-v2' = 'Entra ID SCIM v2.0',\n 'onelogin-scim-v2' = 'OneLogin SCIM v2.0',\n 'okta-scim-v2' = 'Okta SCIM v2.0',\n 'jumpcloud-scim-v2' = 'JumpCloud v2.0',\n 'generic-scim-v2' = 'Generic SCIM v2.0',\n 'google' = 'Google',\n}\nexport type DirectoryType = keyof typeof DirectorySyncProviders;\nexport type Directory = {\n id: string;\n name: string;\n tenant: string;\n product: string;\n type: DirectoryType;\n log_webhook_events: boolean;\n scim: {\n path: string;\n endpoint?: string;\n secret: string;\n };\n webhook: {\n endpoint: string;\n secret: string;\n };\n deactivated?: boolean;\n google_domain?: string;\n google_access_token?: string;\n google_refresh_token?: string;\n google_authorization_url?: string;\n};\nexport type UnSavedDirectory = Omit<Directory, 'id' | 'scim' | 'deactivated' | 'webhook'> & {\n webhook_url: string;\n webhook_secret: string;\n}","\"use client\";\nimport * as React from \"react\";\nimport { useState, useEffect } from \"react\";\n\ntype Keys = keyof typeof DEFAULT_DIRECTORY_VALUES;\nimport {\n CreateDirectoryProps,\n Directory,\n UnSavedDirectory,\n DirectorySyncProviders,\n} from \"../types\";\nimport defaultClasses from \"./index.module.css\";\nimport cssClassAssembler from \"../../sso/utils/cssClassAssembler\";\nimport Button from \"../../shared/Button/index\";\nimport Spacer from \"../../shared/Spacer/index\";\nimport Select from \"../../shared/Select/index\";\nimport InputField from \"../../shared/inputs/InputField/index\";\nimport SecretInputFormControl from \"../../shared/inputs/SecretInputFormControl/index\";\nimport { sendHTTPRequest } from \"../../shared/http\";\nimport Checkbox from \"../../shared/Checkbox/index\";\nconst DEFAULT_DIRECTORY_VALUES: UnSavedDirectory = {\n name: \"\",\n tenant: \"\",\n product: \"\",\n webhook_url: \"\",\n webhook_secret: \"\",\n type: \"azure-scim-v2\",\n google_domain: \"\",\n log_webhook_events: false,\n};\n\nfunction CreateDirectory(props: CreateDirectoryProps) {\n const [directory, setDirectory] = useState(() => DEFAULT_DIRECTORY_VALUES);\n\n const [showDomain, setShowDomain] = useState(() => false);\n\n const [isSaving, setIsSaving] = useState(() => false);\n\n function providers() {\n return Object.entries<string>(DirectorySyncProviders)\n ?.map(([value, text]) => ({\n value,\n text,\n }))\n .filter(({ value }) => {\n if (props.disableGoogleProvider) {\n return value !== \"google\";\n }\n return true;\n });\n }\n\n function setProvider(event: any) {\n const _val = event?.target?.value;\n if (_val === \"google\") {\n setShowDomain(true);\n } else {\n setShowDomain(false);\n }\n setDirectory({\n ...directory,\n type: _val,\n });\n }\n\n function classes() {\n return {\n fieldContainer: cssClassAssembler(\n props.classNames?.fieldContainer,\n defaultClasses.fieldContainer\n ),\n inputField: {\n label: props.classNames?.label,\n input: props.classNames?.input,\n container: props.classNames?.fieldContainer,\n },\n select: {\n label: props.classNames?.label,\n select: props.classNames?.select,\n },\n };\n }\n\n function shouldDisplayHeader() {\n if (props.displayHeader !== undefined) {\n return props.displayHeader;\n }\n return true;\n }\n\n function updateDirectory(data: Partial<typeof DEFAULT_DIRECTORY_VALUES>) {\n return {\n ...directory,\n ...data,\n };\n }\n\n function handleChange(event: Event) {\n const target = event.target as HTMLInputElement;\n const value = target.type === \"checkbox\" ? target.checked : target.value;\n setDirectory(\n updateDirectory({\n [target.id as Keys]: value,\n })\n );\n }\n\n function onSubmit(event: Event) {\n event.preventDefault();\n async function saveDirectory(body: any, url: string) {\n setIsSaving(true);\n const response = await sendHTTPRequest<{\n data: Directory;\n }>(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n setIsSaving(false);\n if (response && typeof response === \"object\") {\n if (\"error\" in response && response.error) {\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else if (\"data\" in response && response.data) {\n typeof props.successCallback === \"function\" &&\n props.successCallback({\n operation: \"CREATE\",\n connection: response.data,\n });\n }\n }\n }\n saveDirectory(directory, props.urls.post);\n }\n\n function isExcluded(fieldName: keyof UnSavedDirectory) {\n return !!(props.excludeFields as (keyof UnSavedDirectory)[])?.includes(\n fieldName\n );\n }\n\n useEffect(() => {\n setDirectory(\n updateDirectory({\n tenant: props.tenant ?? directory.tenant,\n product: props.product ?? directory.product,\n webhook_url: props.defaultWebhookEndpoint ?? directory.webhook_url,\n webhook_secret: props.defaultWebhookSecret ?? directory.webhook_secret,\n })\n );\n }, [props.tenant, props.product, props.defaultWebhookEndpoint]);\n\n return (\n <div>\n {shouldDisplayHeader() ? (\n <h2 className={defaultClasses.heading}>Create Directory</h2>\n ) : null}\n <form onSubmit={(event) => onSubmit(event)}>\n {!isExcluded(\"name\") ? (\n <>\n <InputField\n label=\"Directory name\"\n id=\"name\"\n name=\"name\"\n value={directory.name}\n handleInputChange={handleChange}\n required\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"type\") ? (\n <div className={classes().fieldContainer}>\n <Select\n label=\"Directory provider\"\n name=\"type\"\n id=\"type\"\n classNames={classes().select}\n options={providers()}\n selectedValue={directory.type}\n handleChange={setProvider}\n />\n <Spacer y={6} />\n </div>\n ) : null}\n {showDomain ? (\n <>\n <InputField\n label=\"Directory domain\"\n id=\"google_domain\"\n name=\"google_domain\"\n title=\"Please enter a valid domain (e.g: boxyhq.com)\"\n value={directory.google_domain || \"\"}\n handleInputChange={handleChange}\n pattern={`^[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z]{2,}$`}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"tenant\") ? (\n <>\n <InputField\n label=\"Tenant\"\n id=\"tenant\"\n name=\"tenant\"\n value={directory.tenant}\n handleInputChange={handleChange}\n required\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"product\") ? (\n <>\n <InputField\n label=\"Product\"\n id=\"product\"\n name=\"product\"\n value={directory.product}\n handleInputChange={handleChange}\n required\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"webhook_url\") ? (\n <>\n <InputField\n type=\"url\"\n label=\"Webhook URL\"\n id=\"webhook_url\"\n name=\"webhook_url\"\n value={directory.webhook_url}\n handleInputChange={handleChange}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"webhook_secret\") ? (\n <>\n <SecretInputFormControl\n label=\"Webhook secret\"\n id=\"webhook_secret\"\n name=\"webhook_secret\"\n value={directory.webhook_secret}\n handleChange={handleChange}\n classNames={classes().inputField}\n required={false}\n readOnly={false}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"log_webhook_events\") ? (\n <div className={defaultClasses.checkboxFieldsDiv}>\n <Checkbox\n label=\"Enable Webhook events logging\"\n id=\"log_webhook_events\"\n name=\"log_webhook_events\"\n checked={directory.log_webhook_events}\n handleChange={handleChange}\n />\n <Spacer y={6} />\n </div>\n ) : null}\n <div className={defaultClasses.formAction}>\n {typeof props.cancelCallback === \"function\" ? (\n <Button\n type=\"button\"\n name=\"Cancel\"\n variant=\"outline\"\n handleClick={props.cancelCallback}\n classNames={props.classNames?.button?.cancel}\n />\n ) : null}\n <Button\n variant=\"primary\"\n type=\"submit\"\n name=\"Create Directory\"\n classNames={props.classNames?.button?.ctoa}\n isLoading={isSaving}\n />\n </div>\n </form>\n </div>\n );\n}\n\nexport default CreateDirectory;\n","\"use client\";\nimport * as React from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { DirectorySyncProviders, Directory } from \"../types\";\nimport LoadingContainer from \"../../shared/LoadingContainer/index\";\nimport type { DirectoryListProps, DirectoryType } from \"../types\";\nimport defaultClasses from \"./index.module.css\";\nimport {\n BadgeProps,\n PageToken,\n PaginatePayload,\n TableProps,\n} from \"../../shared/types\";\nimport { sendHTTPRequest } from \"../../shared/http\";\nimport Paginate from \"../../shared/Paginate/index\";\nimport PaginatedTable from \"../../shared/Table/paginated\";\nimport NonPaginatedTable from \"../../shared/Table/non-paginated\";\nconst DEFAULT_VALUES = {\n directoryListData: [] as Directory[],\n};\n\nfunction DirectoryList(props: DirectoryListProps) {\n const [directoryListData, setDirectoryListData] = useState(\n () => DEFAULT_VALUES.directoryListData\n );\n\n function providers() {\n return Object.entries<string>(DirectorySyncProviders)?.map(\n ([value, text]) => ({\n value,\n text,\n })\n );\n }\n\n const [isDirectoryListLoading, setIsDirectoryListLoading] = useState(\n () => true\n );\n\n const [pageTokenMap, setPageTokenMap] = useState(() => ({}));\n\n const [showErrorComponent, setShowErrorComponent] = useState(() => false);\n\n const [errorMessage, setErrorMessage] = useState(() => \"\");\n\n function getUrl() {\n return props.urls.get;\n }\n\n function isPaginated() {\n return props.paginate?.itemsPerPage !== undefined;\n }\n\n function actions() {\n if (props.hideViewAction) {\n return [\n {\n icon: \"PencilIcon\",\n label: \"Edit\",\n handleClick: (directory: Directory) =>\n props.handleActionClick(\"edit\", directory),\n },\n ];\n }\n return [\n {\n icon: \"EyeIcon\",\n label: \"View\",\n handleClick: (directory: Directory) =>\n props.handleActionClick(\"view\", directory),\n },\n {\n icon: \"PencilIcon\",\n label: \"Edit\",\n handleClick: (directory: Directory) =>\n props.handleActionClick(\"edit\", directory),\n },\n ];\n }\n\n function colsToDisplay() {\n return (\n props.cols || [\"name\", \"tenant\", \"product\", \"type\", \"status\", \"actions\"]\n ).map((_col) => {\n if (_col === \"status\") {\n return {\n name: \"status\",\n badge: {\n position: \"surround\",\n variantSelector(rowData) {\n let _variant: BadgeProps[\"variant\"];\n if (rowData.deactivated) {\n _variant = \"warning\";\n }\n if (!rowData.deactivated) {\n _variant = \"success\";\n }\n return _variant;\n },\n },\n };\n } else {\n return _col;\n }\n }) as TableProps[\"cols\"];\n }\n\n function listFetchUrl(\n params: Partial<PaginatePayload> &\n Pick<DirectoryListProps, \"tenant\" | \"product\"> & {\n getUrl: string;\n }\n ) {\n let _url = params.getUrl;\n const [urlPath, qs] = _url.split(\"?\");\n const urlParams = new URLSearchParams(qs);\n if (params.tenant) {\n urlParams.set(\"tenant\", params.tenant);\n }\n if (params.product) {\n urlParams.set(\"product\", params.product);\n }\n if (params.pageToken) {\n urlParams.set(\"pageToken\", params.pageToken);\n }\n if (params?.offset !== undefined) {\n urlParams.set(\"pageOffset\", `${params.offset}`);\n urlParams.set(\"pageLimit\", `${params.limit}`);\n }\n if (urlParams.toString()) {\n return `${urlPath}?${urlParams}`;\n }\n return _url;\n }\n\n function baseFetchUrl() {\n return listFetchUrl({\n getUrl: getUrl(),\n tenant: props.tenant,\n product: props.product,\n });\n }\n\n function tablePropsComputed() {\n return {\n ...props.tableProps,\n classNames: {\n ...props.tableProps?.classNames,\n iconSpan: defaultClasses.iconSpan,\n },\n };\n }\n\n function updateTokenMap(offset: number, token: PageToken) {\n return {\n ...pageTokenMap,\n [offset]: token,\n };\n }\n\n async function getFieldsData(directoryListUrl: string) {\n // fetch request for obtaining directory lists data\n const response = await sendHTTPRequest<\n | {\n data: Directory[];\n }\n | {\n data: {\n data: Directory[];\n };\n pageToken: PageToken;\n }\n >(directoryListUrl);\n setIsDirectoryListLoading(false);\n if (response && typeof response === \"object\") {\n if (\"error\" in response && response.error) {\n setShowErrorComponent(true);\n setErrorMessage(response.error.message);\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else if (\"data\" in response) {\n const isTokenizedPagination = \"pageToken\" in response;\n const _data = isTokenizedPagination\n ? response.data.data\n : response.data;\n if (Array.isArray(_data)) {\n const directoriesListData = _data.map((directory: Directory) => {\n return {\n ...directory,\n id: directory.id,\n name: directory.name,\n tenant: directory.tenant,\n product: directory.product,\n type: providers().find(({ value }) => value === directory.type)\n ?.text as DirectoryType,\n status: directory.deactivated ? \"Inactive\" : \"Active\",\n };\n });\n setDirectoryListData(directoriesListData);\n typeof props.handleListFetchComplete === \"function\" &&\n props.handleListFetchComplete(directoriesListData);\n }\n if (isTokenizedPagination) {\n return response.pageToken;\n }\n }\n }\n }\n\n async function reFetch(payload: PaginatePayload) {\n const pageToken = await getFieldsData(\n listFetchUrl({\n getUrl: baseFetchUrl(),\n ...payload,\n })\n );\n if (pageToken) {\n setPageTokenMap(updateTokenMap(payload.offset, pageToken));\n }\n }\n\n useEffect(() => {\n if (!isPaginated()) {\n getFieldsData(baseFetchUrl());\n }\n }, [baseFetchUrl(), isPaginated()]);\n\n return (\n <LoadingContainer isBusy={isDirectoryListLoading}>\n {isPaginated() ? (\n <Paginate\n itemsPerPage={props.paginate!.itemsPerPage}\n currentPageItemsCount={directoryListData.length}\n handlePageChange={props.paginate?.handlePageChange}\n reFetch={reFetch}\n pageTokenMap={pageTokenMap}\n >\n <PaginatedTable\n emptyStateMessage=\"No directories found.\"\n cols={colsToDisplay()}\n data={directoryListData}\n actions={actions()}\n showErrorComponent={showErrorComponent}\n errorMessage={errorMessage}\n tableProps={tablePropsComputed()}\n />\n </Paginate>\n ) : null}\n {!isPaginated() ? (\n <NonPaginatedTable\n emptyStateMessage=\"No directories found.\"\n cols={colsToDisplay()}\n data={directoryListData}\n actions={actions()}\n showErrorComponent={showErrorComponent}\n errorMessage={errorMessage}\n tableProps={tablePropsComputed()}\n />\n ) : null}\n </LoadingContainer>\n );\n}\n\nexport default DirectoryList;\n","\"use client\";\nimport * as React from \"react\";\nimport { useState } from \"react\";\nimport type { ToggleDirectoryStatusProps, Directory } from \"../types\";\nimport ToggleSwitch from \"../../shared/ToggleSwitch/index\";\nimport defaultClasses from \"./index.module.css\";\nimport cssClassAssembler from \"../../sso/utils/cssClassAssembler\";\nimport ConfirmationPrompt from \"../../shared/ConfirmationPrompt/index\";\nimport { sendHTTPRequest } from \"../../shared/http\";\n\nfunction ToggleConnectionStatus(props: ToggleDirectoryStatusProps) {\n const [displayPrompt, setDisplayPrompt] = useState(() => false);\n\n function connectionStatus() {\n return props.connection?.deactivated ? \"Inactive\" : \"Active\";\n }\n\n function connectionAction() {\n return props.connection?.deactivated ? \"activate\" : \"deactivate\";\n }\n\n function askForConfirmation() {\n setDisplayPrompt(true);\n }\n\n function onCancel() {\n setDisplayPrompt(false);\n }\n\n function onConfirm() {\n updateConnectionStatus(!props.connection?.deactivated);\n }\n\n function classes() {\n return {\n container: cssClassAssembler(\n props.classNames?.container,\n defaultClasses.container\n ),\n };\n }\n\n function updateConnectionStatus(status: boolean) {\n async function toggle() {\n const body = {\n directoryId: props.connection?.id,\n deactivated: status,\n };\n const response = await sendHTTPRequest<{\n data: Directory;\n }>(props.urls.patch, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n setDisplayPrompt(false);\n if (response && typeof response === \"object\") {\n if (\"error\" in response && response.error) {\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else {\n typeof props.successCallback === \"function\" &&\n props.successCallback({\n operation: \"UPDATE\",\n });\n }\n }\n }\n toggle();\n }\n\n return (\n <>\n {props.connection !== undefined || props.connection !== null ? (\n <>\n <div className={classes().container}>\n {displayPrompt ? (\n <ConfirmationPrompt\n ctoaVariant={\n props.connection?.deactivated ? \"primary\" : \"destructive\"\n }\n classNames={props.classNames?.confirmationPrompt}\n cancelCallback={onCancel}\n confirmationCallback={onConfirm}\n promptMessage={`Do you want to ${connectionAction()} the connection?`}\n />\n ) : null}\n {!displayPrompt ? (\n <ToggleSwitch\n label={connectionStatus()}\n disabled={displayPrompt}\n checked={!props.connection?.deactivated}\n handleChange={askForConfirmation}\n />\n ) : null}\n </div>\n </>\n ) : null}\n </>\n );\n}\n\nexport default ToggleConnectionStatus;\n","\"use client\";\nimport * as React from \"react\";\nimport { useState, useEffect } from \"react\";\n\ntype FormState = Pick<\n UnSavedDirectory,\n | \"name\"\n | \"log_webhook_events\"\n | \"webhook_url\"\n | \"webhook_secret\"\n | \"google_domain\"\n | \"google_authorization_url\"\n>;\nimport type { Directory, EditDirectoryProps, UnSavedDirectory } from \"../types\";\nimport ToggleConnectionStatus from \"../ToggleConnectionStatus/index\";\nimport defaultClasses from \"./index.module.css\";\nimport cssClassAssembler from \"../../sso/utils/cssClassAssembler\";\nimport Button from \"../../shared/Button/index\";\nimport Spacer from \"../../shared/Spacer/index\";\nimport ConfirmationPrompt from \"../../shared/ConfirmationPrompt/index\";\nimport Checkbox from \"../../shared/Checkbox/index\";\nimport InputField from \"../../shared/inputs/InputField/index\";\nimport SecretInputFormControl from \"../../shared/inputs/SecretInputFormControl/index\";\nimport { InputWithCopyButton } from \"../../shared\";\nimport LoadingContainer from \"../../shared/LoadingContainer/index\";\nimport { sendHTTPRequest } from \"../../shared/http\";\nconst DEFAULT_FORM_STATE: FormState = {\n name: \"\",\n log_webhook_events: false,\n webhook_url: \"\",\n webhook_secret: \"\",\n google_domain: \"\",\n google_authorization_url: \"\",\n};\n\nfunction EditDirectory(props: EditDirectoryProps) {\n const [showDelConfirmation, setShowDelConfirmation] = useState(() => false);\n\n function toggleDelConfirmation() {\n setShowDelConfirmation(!showDelConfirmation);\n }\n\n const [isSaving, setIsSaving] = useState(() => false);\n\n const [isDirectoryLoading, setIsDirectoryLoading] = useState(() => true);\n\n const [directoryUpdated, setDirectoryUpdated] = useState(\n () => DEFAULT_FORM_STATE\n );\n\n function classes() {\n return {\n inputField: {\n label: props.classNames?.label,\n input: props.classNames?.input,\n container: props.classNames?.fieldContainer,\n },\n section: cssClassAssembler(\n props.classNames?.section,\n defaultClasses.section\n ),\n };\n }\n\n function updateFormState(key: string, newValue: string | boolean) {\n return {\n ...directoryUpdated,\n [key]: newValue,\n };\n }\n\n function handleChange(event: Event) {\n const target = event.target as HTMLInputElement;\n const id = target.id;\n const value = target.type === \"checkbox\" ? target.checked : target.value;\n setDirectoryUpdated(updateFormState(id, value));\n }\n\n function onSubmit(event: Event) {\n event.preventDefault();\n setIsSaving(true);\n async function saveDirectory(url: string) {\n const response = await sendHTTPRequest<{\n data: Directory;\n }>(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(directoryUpdated),\n });\n setIsSaving(false);\n if (response && typeof response === \"object\") {\n if (\"error\" in response && response.error) {\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else if (\"data\" in response && response.data) {\n typeof props.successCallback === \"function\" &&\n props.successCallback({\n operation: \"UPDATE\",\n connection: response.data,\n });\n }\n }\n }\n saveDirectory(props.urls.patch);\n }\n\n function deleteDirectory() {\n async function deleteConnection(url: string) {\n const response = await sendHTTPRequest<null>(url, {\n method: \"DELETE\",\n });\n if (response && typeof response === \"object\") {\n if (response.error) {\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else {\n typeof props.successCallback === \"function\" &&\n props.successCallback({\n operation: \"DELETE\",\n });\n }\n }\n }\n deleteConnection(props.urls.delete);\n }\n\n function isExcluded(\n fieldName: Exclude<EditDirectoryProps[\"excludeFields\"], undefined>[number]\n ) {\n return !!props.excludeFields?.includes(fieldName);\n }\n\n function shouldDisplayHeader() {\n if (props.displayHeader !== undefined) {\n return props.displayHeader;\n }\n return true;\n }\n\n function directoryFetchUrl() {\n return props.urls.get;\n }\n\n function googleSCIMAuthzURL() {\n if (!directoryUpdated.google_authorization_url) {\n return undefined;\n }\n let _url = directoryUpdated.google_authorization_url;\n const [urlPath, qs] = _url.split(\"?\");\n const urlParams = new URLSearchParams(qs);\n urlParams.set(\"directoryId\", directoryUpdated.id);\n return `${urlPath}?${urlParams}`;\n }\n\n useEffect(() => {\n async function getDirectory(url: string) {\n const response = await sendHTTPRequest<{\n data: Directory;\n }>(url);\n setIsDirectoryLoading(false);\n if (response && typeof response === \"object\") {\n if (\"error\" in response && response.error) {\n typeof props.errorCallback === \"function\" &&\n props.errorCallback(response.error.message);\n } else if (\"data\" in response) {\n const directoryData = response.data;\n setDirectoryUpdated({\n ...directoryData,\n name: directoryData.name,\n log_webhook_events: directoryData.log_webhook_events,\n webhook_url: directoryData.webhook?.endpoint || \"\",\n webhook_secret: directoryData.webhook?.secret || \"\",\n google_authorization_url:\n directoryData.google_authorization_url || \"\",\n google_domain: directoryData.google_domain || \"\",\n deactivated: directoryData.deactivated,\n });\n }\n }\n }\n getDirectory(directoryFetchUrl());\n }, [directoryFetchUrl()]);\n\n return (\n <LoadingContainer isBusy={isDirectoryLoading}>\n <div className={defaultClasses.headingContainer}>\n {shouldDisplayHeader() ? (\n <h2 className={defaultClasses.heading}>Update Directory</h2>\n ) : null}\n <ToggleConnectionStatus\n connection={directoryUpdated}\n urls={{\n patch: props.urls.patch,\n }}\n classNames={{\n confirmationPrompt: {\n button: {\n ctoa: `${props.classNames?.confirmationPrompt?.button?.ctoa} ${\n directoryUpdated?.deactivated\n ? props.classNames?.button?.ctoa\n : props.classNames?.button?.destructive\n }`.trim(),\n cancel: props.classNames?.confirmationPrompt?.button?.cancel,\n },\n },\n }}\n errorCallback={props.errorCallback}\n successCallback={props.successCallback}\n />\n </div>\n <form onSubmit={(event) => onSubmit(event)}>\n {!isExcluded(\"name\") ? (\n <>\n <InputField\n label=\"Directory name\"\n id=\"name\"\n value={directoryUpdated.name}\n handleInputChange={handleChange}\n required\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"scim_endpoint\") && directoryUpdated?.type !== \"google\" ? (\n <>\n <InputWithCopyButton\n label=\"SCIM Endpoint\"\n text={directoryUpdated.scim?.endpoint}\n copyDoneCallback={props.successCallback}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"scim_token\") && directoryUpdated?.type !== \"google\" ? (\n <>\n <InputWithCopyButton\n label=\"SCIM Token\"\n text={directoryUpdated.scim?.secret}\n copyDoneCallback={props.successCallback}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"google_domain\") && directoryUpdated?.type === \"google\" ? (\n <>\n <InputField\n label=\"Directory domain\"\n id=\"google_domain\"\n value={directoryUpdated.google_domain}\n handleInputChange={handleChange}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"google_authorization_url\") &&\n directoryUpdated?.type === \"google\" &&\n googleSCIMAuthzURL() ? (\n <>\n <InputWithCopyButton\n label=\"Google SCIM Authorization url\"\n text={googleSCIMAuthzURL()}\n copyDoneCallback={props.successCallback}\n classNames={classes().inputField}\n />\n <div id=\"scim-authz-hint\" className={defaultClasses.hint}>\n The URL that your tenant needs to authorize the application to\n access their Google Directory.\n </div>\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"webhook_url\") ? (\n <>\n <InputField\n type=\"url\"\n label=\"Webhook URL\"\n id=\"webhook_url\"\n value={directoryUpdated.webhook_url}\n handleInputChange={handleChange}\n classNames={classes().inputField}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"webhook_secret\") ? (\n <>\n <SecretInputFormControl\n label=\"Webhook secret\"\n id=\"webhook_secret\"\n classNames={classes().inputField}\n handleChange={handleChange}\n value={directoryUpdated.webhook_secret}\n copyDoneCallback={props.successCallback}\n required={false}\n readOnly={false}\n />\n <Spacer y={6} />\n </>\n ) : null}\n {!isExcluded(\"log_webhook_events\") ? (\n <>\n <Checkbox\n label=\"Enable Webhook events logging\"\n id=\"log_webhook_events\"\n name=\"log_webhook_events\"\n checked={directoryUpdated?.log_webhook_events}\n handleChange={handleChange}\n />\n <Spacer y={6} />\n </>\n ) : null}\n <div className={defaultClasses.formAction}>\n {typeof props.cancelCallback === \"function\" ? (\n <Button\n type=\"button\"\n name=\"Cancel\"\n variant=\"outline\"\n handleClick={props.cancelCallback}\n classNames={props.classNames?.button?.cancel}\n />\n ) : null}\n {!props.hideSave ? (\n <Button\n type=\"submit\"\n name=\"Save\"\n variant=\"primary\"\n classNames={props.classNames?.button?.ctoa}\n isLoading={isSaving}\n />\n ) : null}\n </div>\n </form>\n <section className={classes().section}>\n <div className={defaultClasses.info}>\n <h6 className={defaultClasses.sectionHeading}>\n Delete this directory connection\n </h6>\n <p className={defaultClasses.sectionPara}>\n All your apps using this connection will stop working.\n </p>\n </div>\n {!showDelConfirmation ? (\n <Button\n name=\"Delete\"\n variant=\"destructive\"\n type=\"button\"\n handleClick={toggleDelConfirmation}\n classNames={props.classNames?.button?.destructive}\n />\n ) : null}\n {showDelConfirmation ? (\n <ConfirmationPrompt\n ctoaVariant=\"destructive\"\n promptMessage=\" Are you sure you want to delete the directory connection? This will permanently delete the directory connection, users, and groups.\"\n classNames={{\n button: {\n ctoa: `${props.classNames?.button?.destructive} ${props.classNames?.confirmationPrompt?.button?.ctoa}`.trim(),\n cancel: props.classNames?.confirmationPrompt?.button?.cancel,\n },\n }}\n cancelCallback={toggleDelConfirmation}\n confirmationCallback={deleteDirectory}\n />\n ) : null}\n </section>\n </LoadingContainer>\n );\n}\n\nexport default EditDirectory;\n","\"use client\";\nimport * as React from \"react\";\nimport { useState } from \"react\";\nimport type { Directory, DirectoriesWrapperProps } from \"../types\";\nimport CreateDirectory from \"../CreateDirectory/index\";\nimport Spacer from \"../../shared/Spacer/index\";\nimport EditDirectory from \"../EditDirectory/index\";\nimport DirectoryList from \"../DirectoryList/index\";\nimport Button from \"../../shared/Button/index\";\nimport styles from \"./index.module.css\";\nconst DEFAULT_VALUES = {\n directoryListData: [] as Directory[],\n view: \"LIST\" as \"LIST\" | \"EDIT\" | \"CREATE\",\n directoryToEdit: {} as Directory,\n};\n\nfunction DirectoriesWrapper(props: DirectoriesWrapperProps) {\n const [directories, setDirectories] = useState(\n () => DEFAULT_VALUES.directoryListData\n );\n\n const [view, setView] = useState(() => DEFAULT_VALUES.view);\n\n function handleListFetchComplete(directoryList: Directory[]) {\n setDirectories(directoryList);\n }\n\n function directoriesAdded() {\n return directories.length > 0;\n }\n\n function dsyncEnabled() {\n return (\n directoriesAdded() &&\n directories.some((directory) => directory.deactivated === false)\n );\n }\n\n const [directoryToEdit, setDirectoryToEdit] = useState(\n () => DEFAULT_VALUES.directoryToEdit\n );\n\n function directoryFetchURL() {\n let _url = props.urls.get;\n const [urlPath, qs] = _url.split(\"?\");\n const urlParams = new URLSearchParams(qs);\n if (urlParams.toString()) {\n return `${urlPath}/${directoryToEdit.id}?${urlParams}`;\n }\n return `${urlPath}/${directoryToEdit.id}`;\n }\n\n function switchToCreateView() {\n setView(\"CREATE\");\n }\n\n function switchToEditView(directory: Directory) {\n setView(\"EDIT\");\n setDirectoryToEdit(directory);\n }\n\n function handleActionClick(action: \"edit\" | \"view\", directory: any) {\n if (action === \"edit\") {\n switchToEditView(directory);\n } else {\n typeof props.componentProps?.directoryList?.handleActionClick ===\n \"function\" &&\n props.componentProps.directoryList.handleActionClick(\"view\", directory);\n }\n }\n\n function switchToListView() {\n setView(\"LIST\");\n }\n\n function successHandler(info: {\n connection?: Directory;\n operation: \"CREATE\" | \"UPDATE\" | \"DELETE\" | \"COPY\";\n }) {\n const { connection, operation } = info;\n if (typeof props.successCallback === \"function\") {\n props.successCallback({\n operation,\n connection,\n });\n }\n if (operation === \"CREATE\") {\n switchToEditView(connection!);\n } else if (operation !== \"COPY\") {\n switchToListView();\n }\n }\n\n return (\n <div>\n {view === \"LIST\" ? (\n <div className={styles.listview}>\n <div className={styles.header}>\n <h5 className={styles.h5}>\n {props.title || \"Manage DSync Connections\"}\n </h5>\n <div className={styles.ctoa}>\n <Button\n name=\"New Directory\"\n handleClick={switchToCreateView}\n classNames={props.classNames?.button?.ctoa}\n />\n </div>\n </div>\n <Spacer y={8} />\n <DirectoryList\n {...props.componentProps?.directoryList}\n urls={{\n get: props.urls.get,\n }}\n handleActionClick={handleActionClick}\n handleListFetchComplete={handleListFetchComplete}\n tenant={props.tenant}\n product={props.product}\n />\n </div>\n ) : null}\n {view === \"EDIT\" ? (\n <>\n <div className={styles.header}>\n <h5 className={styles.h5}>Edit DSync Connection</h5>\n </div>\n <EditDirectory\n {...props.componentProps?.editDirectory}\n classNames={props.classNames}\n successCallback={successHandler}\n errorCallback={props.errorCallback}\n cancelCallback={switchToListView}\n displayHeader={false}\n urls={{\n patch: `${props.urls.patch}/${directoryToEdit.id}`,\n delete: `${props.urls.delete}/${directoryToEdit.id}`,\n get: directoryFetchURL(),\n }}\n />\n </>\n ) : null}\n {view === \"CREATE\" ? (\n <>\n <div className={styles.header}>\n <h5 className={styles.h5}>Create DSync Connection</h5>\n </div>\n <Spacer y={5} />\n <CreateDirectory\n {...props.componentProps?.createDirectory}\n classNames={props.classNames}\n successCallback={successHandler}\n errorCallback={props.errorCallback}\n cancelCallback={switchToListView}\n displayHeader={false}\n urls={{\n post: props.urls.post,\n }}\n tenant={props.tenant}\n product={props.product}\n />\n </>\n ) : null}\n </div>\n );\n}\n\nexport default DirectoriesWrapper;\n"],"names":["DirectorySyncProviders","DEFAULT_DIRECTORY_VALUES","CreateDirectory","props","directory","setDirectory","useState","showDomain","setShowDomain","isSaving","setIsSaving","providers","_a","value","text","setProvider","event","_val","classes","cssClassAssembler","defaultClasses","_b","_c","_d","_e","_f","shouldDisplayHeader","updateDirectory","data","handleChange","target","onSubmit","saveDirectory","body","url","response","sendHTTPRequest","isExcluded","fieldName","useEffect","jsx","jsxs","Fragment","InputField","Spacer","Select","SecretInputFormControl","Checkbox","Button","DEFAULT_VALUES","DirectoryList","directoryListData","setDirectoryListData","isDirectoryListLoading","setIsDirectoryListLoading","pageTokenMap","setPageTokenMap","showErrorComponent","setShowErrorComponent","errorMessage","setErrorMessage","getUrl","isPaginated","actions","colsToDisplay","_col","rowData","_variant","listFetchUrl","params","_url","urlPath","qs","urlParams","baseFetchUrl","tablePropsComputed","updateTokenMap","offset","token","getFieldsData","directoryListUrl","isTokenizedPagination","_data","directoriesListData","reFetch","payload","pageToken","LoadingContainer","Paginate","PaginatedTable","NonPaginatedTable","ToggleConnectionStatus","displayPrompt","setDisplayPrompt","connectionStatus","connectionAction","askForConfirmation","onCancel","onConfirm","updateConnectionStatus","status","toggle","ConfirmationPrompt","ToggleSwitch","DEFAULT_FORM_STATE","EditDirectory","showDelConfirmation","setShowDelConfirmation","toggleDelConfirmation","isDirectoryLoading","setIsDirectoryLoading","directoryUpdated","setDirectoryUpdated","updateFormState","key","newValue","id","deleteDirectory","deleteConnection","directoryFetchUrl","googleSCIMAuthzURL","getDirectory","directoryData","_g","_j","_i","_h","InputWithCopyButton","_k","_l","_n","_m","_p","_o","_r","_q","_t","_s","_w","_v","_u","_z","_y","_x","DirectoriesWrapper","directories","setDirectories","view","setView","handleListFetchComplete","directoryList","directoryToEdit","setDirectoryToEdit","directoryFetchURL","switchToCreateView","switchToEditView","handleActionClick","action","switchToListView","successHandler","info","connection","operation","styles"],"mappings":"6KAwJY,IAAAA,GAAAA,IACVA,EAAA,eAAkB,EAAA,qBAClBA,EAAA,kBAAqB,EAAA,qBACrBA,EAAA,cAAiB,EAAA,iBACjBA,EAAA,mBAAsB,EAAA,iBACtBA,EAAA,iBAAoB,EAAA,oBACpBA,EAAA,OAAW,SANDA,IAAAA,GAAA,CAAA,CAAA,qICpINC,GAA6C,CACjD,KAAM,GACN,OAAQ,GACR,QAAS,GACT,YAAa,GACb,eAAgB,GAChB,KAAM,gBACN,cAAe,GACf,mBAAoB,EACtB,EAEA,SAASC,GAAgBC,EAA6B,aACpD,KAAM,CAACC,EAAWC,CAAY,EAAIC,EAAA,SAAS,IAAML,EAAwB,EAEnE,CAACM,EAAYC,CAAa,EAAIF,EAAA,SAAS,IAAM,EAAK,EAElD,CAACG,EAAUC,CAAW,EAAIJ,EAAA,SAAS,IAAM,EAAK,EAEpD,SAASK,GAAY,OACZ,OAAAC,EAAA,OAAO,QAAgBZ,CAAsB,IAA7C,YAAAY,EACH,IAAI,CAAC,CAACC,EAAOC,CAAI,KAAO,CACxB,MAAAD,EACA,KAAAC,KAED,OAAO,CAAC,CAAE,MAAAD,KACLV,EAAM,sBACDU,IAAU,SAEZ,GACR,CAGL,SAASE,EAAYC,EAAY,OACzB,MAAAC,GAAOL,EAAAI,GAAA,YAAAA,EAAO,SAAP,YAAAJ,EAAe,MAE1BJ,EADES,IAAS,QACO,EAIPZ,EAAA,CACX,GAAGD,EACH,KAAMa,CAAA,CACP,CAAA,CAGH,SAASC,GAAU,iBACV,MAAA,CACL,eAAgBC,EAAA,mBACdP,EAAAT,EAAM,aAAN,YAAAS,EAAkB,eAClBQ,EAAe,cACjB,EACA,WAAY,CACV,OAAOC,EAAAlB,EAAM,aAAN,YAAAkB,EAAkB,MACzB,OAAOC,EAAAnB,EAAM,aAAN,YAAAmB,EAAkB,MACzB,WAAWC,EAAApB,EAAM,aAAN,YAAAoB,EAAkB,cAC/B,EACA,OAAQ,CACN,OAAOC,EAAArB,EAAM,aAAN,YAAAqB,EAAkB,MACzB,QAAQC,EAAAtB,EAAM,aAAN,YAAAsB,EAAkB,MAAA,CAE9B,CAAA,CAGF,SAASC,GAAsB,CACzB,OAAAvB,EAAM,gBAAkB,OACnBA,EAAM,cAER,EAAA,CAGT,SAASwB,EAAgBC,EAAgD,CAChE,MAAA,CACL,GAAGxB,EACH,GAAGwB,CACL,CAAA,CAGF,SAASC,EAAab,EAAc,CAClC,MAAMc,EAASd,EAAM,OACfH,EAAQiB,EAAO,OAAS,WAAaA,EAAO,QAAUA,EAAO,MACnEzB,EACEsB,EAAgB,CACd,CAACG,EAAO,EAAU,EAAGjB,CACtB,CAAA,CACH,CAAA,CAGF,SAASkB,EAASf,EAAc,CAC9BA,EAAM,eAAe,EACN,eAAAgB,EAAcC,EAAWC,EAAa,CACnDxB,EAAY,EAAI,EACV,MAAAyB,EAAW,MAAMC,EAAA,gBAEpBF,EAAK,CACN,OAAQ,OACR,QAAS,CACP,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAUD,CAAI,CAAA,CAC1B,EACDvB,EAAY,EAAK,EACbyB,GAAY,OAAOA,GAAa,WAC9B,UAAWA,GAAYA,EAAS,MAClC,OAAOhC,EAAM,eAAkB,YAC7BA,EAAM,cAAcgC,EAAS,MAAM,OAAO,EACnC,SAAUA,GAAYA,EAAS,MACxC,OAAOhC,EAAM,iBAAoB,YAC/BA,EAAM,gBAAgB,CACpB,UAAW,SACX,WAAYgC,EAAS,IAAA,CACtB,EAEP,CAEYH,EAAA5B,EAAWD,EAAM,KAAK,IAAI,CAAA,CAG1C,SAASkC,EAAWC,EAAmC,OAC9C,MAAA,CAAC,GAAE1B,EAAAT,EAAM,gBAAN,MAAAS,EAAoD,SAC5D0B,GACF,CAGFC,OAAAA,EAAAA,UAAU,IAAM,CACdlC,EACEsB,EAAgB,CACd,OAAQxB,EAAM,QAAUC,EAAU,OAClC,QAASD,EAAM,SAAWC,EAAU,QACpC,YAAaD,EAAM,wBAA0BC,EAAU,YACvD,eAAgBD,EAAM,sBAAwBC,EAAU,cACzD,CAAA,CACH,CAAA,EACC,CAACD,EAAM,OAAQA,EAAM,QAASA,EAAM,sBAAsB,CAAC,2BAG3D,MACE,CAAA,SAAA,CAAAuB,IACEc,EAAAA,kBAAAA,IAAA,KAAA,CAAG,UAAWpB,EAAe,QAAS,2BAAgB,CAAA,EACrD,8BACH,OAAK,CAAA,SAAWJ,GAAUe,EAASf,CAAK,EACtC,SAAA,CAACqB,EAAW,MAAM,EAaf,KAXAI,EAAA,kBAAA,KAAAC,EAAA,kBAAA,SAAA,CAAA,SAAA,CAAAF,EAAA,kBAAA,IAACG,EAAA,WAAA,CACC,MAAM,iBACN,GAAG,OACH,KAAK,OACL,MAAOvC,EAAU,KACjB,kBAAmByB,EACnB,SAAQ,GACR,WAAYX,IAAU,UAAA,CACxB,EACAsB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,CAAG,CAAA,CAAA,CAAA,CAChB,EAEAP,EAAW,MAAM,EAaf,8BAZD,MAAI,CAAA,UAAWnB,EAAQ,EAAE,eACxB,SAAA,CAAAsB,EAAA,kBAAA,IAACK,EAAA,OAAA,CACC,MAAM,qBACN,KAAK,OACL,GAAG,OACH,WAAY3B,IAAU,OACtB,QAASP,EAAU,EACnB,cAAeP,EAAU,KACzB,aAAcW,CAAA,CAChB,EACAyB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,CAAG,CAAA,CAAA,CAAA,CAChB,EAEDrC,EAEGkC,EAAA,kBAAA,KAAAC,6BAAA,CAAA,SAAA,CAAAF,EAAA,kBAAA,IAACG,EAAA,WAAA,CACC,MAAM,mBACN,GAAG,gBACH,KAAK,gBACL,MAAM,gDACN,MAAOvC,EAAU,eAAiB,GAClC,kBAAmByB,EACnB,QAAS,gDACT,WAAYX,IAAU,UAAA,CACxB,EACAsB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,CAAG,CAAA,CAAA,CAAA,CAChB,EACE,KACFP,EAAW,QAAQ,EAajB,KAXAI,EAAA,kBAAA,KAAAC,EAAA,kBAAA,SAAA,CAAA,SAAA,CAAAF,EAAA,kBAAA,IAACG,EAAA,WAAA,CACC,MAAM,SACN,GAAG,SACH,KAAK,SACL,MAAOvC,EAAU,OACjB,kBAAmByB,EACnB,SAAQ,GACR,WAAYX,IAAU,UAAA,CACxB,EACAsB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,CAAG,CAAA,CAAA,CAAA,CAChB,EAEAP,EAAW,SAAS,EAalB,KAXAI,EAAA,kBAAA,KAAAC,EAAA,kBAAA,SAAA,CAAA,SAAA,CAAAF,EAAA,kBAAA,IAACG,EAAA,WAAA,CACC,MAAM,UACN,GAAG,UACH,KAAK,UACL,MAAOvC,EAAU,QACjB,kBAAmByB,EACnB,SAAQ,GACR,WAAYX,IAAU,UAAA,CACxB,EACAsB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,CAAG,CAAA,CAAA,CAAA,CAChB,EAEAP,EAAW,aAAa,EAatB,KAXAI,EAAA,kBAAA,KAAAC,EAAA,kBAAA,SAAA,CAAA,SAAA,CAAAF,EAAA,kBAAA,IAACG,EAAA,WAAA,CACC,KAAK,MACL,MAAM,cACN,GAAG,cACH,KAAK,cACL,MAAOvC,EAAU,YACjB,kBAAmByB,EACnB,WAAYX,IAAU,UAAA,CACxB,EACAsB,EAAAA,kBAAAA,IAACI,EAAO,OAAA,CAAA,EAAG,