UNPKG

@croz/nrich-registry-core

Version:

Contains core utilities related to the nrich-registry module

1 lines 58.2 kB
{"version":3,"sources":["../src/component/RegistryEntityContext.tsx","../src/store/registry-configuration-store.ts","../src/component/RegistryProvider.tsx","../src/service/service.ts","../src/hook/use-registry-entity.ts","../src/hook/use-entity-configuration.ts","../src/hook/use-registry-entity-administration.ts","../src/util/registry-entity-utils.ts","../src/hook/use-update-effect.ts","../src/hook/use-registry-entity-form-configuration.ts","../src/hook/use-registry-filter.ts","../src/hook/use-registry-form.ts","../src/hook/use-registry-sort.ts"],"sourcesContent":["/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React, { useContext } from \"react\";\r\n\r\nimport { RegistryEntityConfiguration, RegistryPropertyConfiguration } from \"../api\";\r\nimport { useRegistryConfigurationStore } from \"../store\";\r\n\r\n/**\r\n * Type for {@link #RegistryEntityContext} value\r\n */\r\ninterface RegistryEntityContextType {\r\n /**\r\n * Configuration of administered registry entity\r\n */\r\n entityConfiguration: RegistryEntityConfiguration;\r\n\r\n /**\r\n * Final list of properties to be shown on table or in form. For simple registry, contains only propertyConfigurationList.\r\n * For entities with complex ids it contains all fields from id object mapped for easier usage and UX\r\n */\r\n finalProperties: RegistryPropertyConfiguration[];\r\n\r\n /**\r\n * Map containing entity configurations for all sub-entities of this entity.\r\n * Key is full class name, and value is configuration\r\n */\r\n singularAssociationsMap: Record<string, RegistryEntityConfiguration>;\r\n}\r\n\r\n/**\r\n * Helper context used for easier data sharing in registry entity administration components\r\n */\r\nexport const RegistryEntityContext = React.createContext<RegistryEntityContextType | undefined>(undefined);\r\n\r\n/**\r\n * Helper hook to get registry entity context. Throws if the user is not inside provider.\r\n */\r\nexport const useRegistryEntityContext = (): RegistryEntityContextType => {\r\n const context = useContext(RegistryEntityContext);\r\n if (!context) {\r\n throw new Error(\"Cannot use RegistryEntityContext without RegistryEntityContextProvider!\");\r\n }\r\n return context;\r\n};\r\n\r\n/**\r\n * {@link #RegistryEntityContextProvider} props\r\n */\r\ninterface Props {\r\n /**\r\n * Configuration of the administered entity\r\n */\r\n entityConfiguration: RegistryEntityConfiguration;\r\n\r\n /**\r\n * Children where this context is provided\r\n */\r\n children: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Helper provider for {@link #RegistryEntityContext} used for easier data sharing in registry entity administration components\r\n * @param entityConfiguration configuration of the administered entity\r\n * @param children children where this context is provided\r\n */\r\nexport const RegistryEntityContextProvider = ({ entityConfiguration, children }: Props) => {\r\n const entityConfigurations = useRegistryConfigurationStore((state) => state.groupConfigurations\r\n .flatMap((groupConfiguration) => groupConfiguration.entityConfigurationList));\r\n\r\n const singularAssociationsMap = React.useMemo(() => {\r\n const singularAssociations = [...entityConfiguration.propertyConfigurationList, ...entityConfiguration.embeddedIdPropertyConfigurationList]\r\n .filter((property) => property.singularAssociation)\r\n .map((property) => property.singularAssociationReferencedClass);\r\n\r\n return singularAssociations.reduce(\r\n (all, current) => ({ ...all, [current]: entityConfigurations.find((configuration) => configuration.classFullName === current) }),\r\n {},\r\n );\r\n }, [entityConfiguration]);\r\n\r\n const finalProperties = React.useMemo(() => {\r\n if (entityConfiguration.embeddedIdentity) {\r\n return entityConfiguration.propertyConfigurationList.flatMap((property) => {\r\n if (property.id) {\r\n return entityConfiguration.embeddedIdPropertyConfigurationList;\r\n }\r\n return property;\r\n });\r\n }\r\n return entityConfiguration.propertyConfigurationList;\r\n }, [entityConfiguration]);\r\n\r\n const value = React.useMemo(() => ({\r\n entityConfiguration,\r\n finalProperties,\r\n singularAssociationsMap,\r\n }), [entityConfiguration]);\r\n\r\n return (\r\n <RegistryEntityContext.Provider value={value}>\r\n {children}\r\n </RegistryEntityContext.Provider>\r\n );\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport create from \"zustand\";\r\n\r\nimport { RegistryConfiguration, RegistryGroupConfiguration } from \"../api\";\r\n\r\nexport interface RegistryConfigurationStore {\r\n\r\n /**\r\n * Array of group configurations.\r\n */\r\n groupConfigurations: RegistryGroupConfiguration[];\r\n\r\n /**\r\n * Load configuration\r\n */\r\n load: (groupConfigurations) => void;\r\n\r\n /**\r\n * Entity formatters used for rendering complex entity types\r\n */\r\n entityFormatters: Record<string, (value: any) => string>;\r\n\r\n /**\r\n * Entity formatters setter\r\n */\r\n setEntityFormatters: (entityFormatters: Record<string, (value: any) => string>) => void;\r\n\r\n /**\r\n * Registry configuration\r\n */\r\n registryConfiguration: RegistryConfiguration;\r\n\r\n /**\r\n * Registry configuration setter\r\n */\r\n setRegistryConfiguration: (registryConfiguration: RegistryConfiguration) => void;\r\n}\r\n\r\nexport const useRegistryConfigurationStore = create<RegistryConfigurationStore>((set) => ({\r\n groupConfigurations: [],\r\n load: (groupConfigurations) => set(() => ({\r\n groupConfigurations,\r\n })),\r\n entityFormatters: {},\r\n setEntityFormatters: (entityFormatters) => set(() => ({\r\n entityFormatters,\r\n })),\r\n registryConfiguration: {},\r\n setRegistryConfiguration: (registryConfiguration) => set(() => ({\r\n registryConfiguration,\r\n })),\r\n}));\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React from \"react\";\r\n\r\nimport { loadRegistryConfiguration } from \"../service\";\r\nimport { RegistryConfigurationStore, useRegistryConfigurationStore } from \"../store\";\r\n\r\n/**\r\n * {@link RegistryProvider} properties\r\n */\r\ninterface Props extends Partial<RegistryConfigurationStore> {\r\n /**\r\n * Children where registry configuration is provided\r\n */\r\n children: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Provider for registry configuration. Until registry is fetched, renders loading screen.\r\n * @param children Children where registry configuration is provided\r\n * @param registryConfiguration Registry configuration for baseURL and requestOptionsResolver\r\n * @param entityFormatters Formatters for complex object types\r\n */\r\nexport const RegistryProvider = ({ children, registryConfiguration = {}, entityFormatters = {} }: Props) => {\r\n const { load, setRegistryConfiguration, setEntityFormatters } = useRegistryConfigurationStore();\r\n const [loading, setLoading] = React.useState<boolean>(true);\r\n\r\n React.useEffect(() => {\r\n const loadConfiguration = async () => {\r\n setRegistryConfiguration({ ...registryConfiguration });\r\n setEntityFormatters(entityFormatters);\r\n\r\n const configuration = await loadRegistryConfiguration();\r\n load(configuration);\r\n setLoading(false);\r\n };\r\n loadConfiguration();\r\n }, []);\r\n\r\n if (loading) {\r\n return <div>Loading...</div>;\r\n }\r\n\r\n return <>{children}</>;\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport {\r\n PagingParameter, PagingResponse, RegistryGroupConfiguration, SearchParameter, SortProperty, SortResponse,\r\n} from \"../api\";\r\nimport { useRegistryConfigurationStore } from \"../store\";\r\n\r\n/**\r\n * Request when fetching a list of registry entries.\r\n */\r\nexport type RegistryRequest = {\r\n /**\r\n * Full Java class name of the registry entity\r\n */\r\n classFullName: string;\r\n\r\n /**\r\n * List of sort rules for this search request\r\n */\r\n sortPropertyList?: SortProperty[];\r\n\r\n /**\r\n * Search configuration for this request\r\n */\r\n searchParameter?: SearchParameter;\r\n\r\n} & PagingParameter;\r\n\r\n/**\r\n * Response of a single registry entity request.\r\n */\r\nexport type RegistryResponse<T> = {\r\n /**\r\n * List of result entities.\r\n */\r\n content: T[];\r\n\r\n /**\r\n * Result sort.\r\n */\r\n sort: SortResponse;\r\n} & PagingResponse;\r\n\r\n/**\r\n * Fetches whole registry configuration.\r\n */\r\nexport const loadRegistryConfiguration = async (): Promise<RegistryGroupConfiguration[]> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/configuration/fetch`, {\r\n method: \"POST\",\r\n ...additionalOptions,\r\n });\r\n const data = await response.json() as RegistryGroupConfiguration[];\r\n return data;\r\n};\r\n\r\n/**\r\n * Fetches single registry entity list with given paging and sorting parameters.\r\n * @param request Single load request which contains entity class, paging, sorting and search options\r\n * @returns List of entity results\r\n */\r\nexport const loadEntities = async (request: RegistryRequest): Promise<RegistryResponse<any>> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/data/list`, {\r\n method: \"POST\",\r\n body: JSON.stringify(request),\r\n headers: { \"Content-Type\": \"application/json\" },\r\n ...additionalOptions,\r\n });\r\n const data = await response.json() as RegistryResponse<any>;\r\n return data;\r\n};\r\n\r\n/**\r\n * Fetches multiple registry entity lists for given registry requests.\r\n * @param requests List of registry requests\r\n * @returns Map of results where key is full class name and value is single entity response list\r\n */\r\nexport const bulkLoadEntities = async (requests: RegistryRequest[]): Promise<Record<string, RegistryResponse<any>>> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/data/list-bulk`, {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n registryRequestList: requests,\r\n }),\r\n headers: { \"Content-Type\": \"application/json\" },\r\n ...additionalOptions,\r\n });\r\n const data = await response.json() as Record<string, RegistryResponse<any>>;\r\n return data;\r\n};\r\n\r\n/**\r\n * Creates new entry for given registry entity\r\n * @param classFullName Full Java class name of the registry entity\r\n * @param createData Object with data of new entity\r\n * @returns Created entity\r\n */\r\nexport const createEntity = async (classFullName: string, createData: any): Promise<any> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/data/create`, {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n classFullName,\r\n jsonEntityData: JSON.stringify(createData),\r\n }),\r\n headers: { \"Content-Type\": \"application/json\" },\r\n ...additionalOptions,\r\n });\r\n const data = await response.json();\r\n return data;\r\n};\r\n\r\n/**\r\n * Updates existing entity with new data\r\n * @param classFullName Full Java class name of the registry entity\r\n * @param id Id of the existing entity\r\n * @param updateData Object with update date for existing entity\r\n * @returns Updated entity\r\n */\r\nexport const updateEntity = async (classFullName: string, id: any, updateData: any): Promise<any> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/data/update`, {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n classFullName,\r\n id,\r\n jsonEntityData: JSON.stringify(updateData),\r\n }),\r\n headers: { \"Content-Type\": \"application/json\" },\r\n ...additionalOptions,\r\n });\r\n const data = await response.json();\r\n return data;\r\n};\r\n\r\n/**\r\n * Deletes existing entity\r\n * @param classFullName Full Java class name of the registry entity\r\n * @param id Id of the existing entity\r\n */\r\nexport const removeEntity = async (classFullName: string, id: any): Promise<any> => {\r\n const { registryConfiguration } = useRegistryConfigurationStore.getState();\r\n const additionalOptions = registryConfiguration.requestOptionsResolver?.() || {};\r\n const finalBaseUrl = registryConfiguration.baseURL || \"/nrich/registry\";\r\n\r\n const response = await fetch(`${finalBaseUrl}/data/delete`, {\r\n method: \"POST\",\r\n body: JSON.stringify({ classFullName, id }),\r\n headers: { \"Content-Type\": \"application/json\" },\r\n ...additionalOptions,\r\n });\r\n\r\n return response;\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React from \"react\";\r\n\r\nimport { RegistryEntityConfiguration, RegistryPropertyConfiguration } from \"../api\";\r\nimport {\r\n createEntity, loadEntities, RegistryRequest, RegistryResponse, removeEntity, updateEntity,\r\n} from \"../service\";\r\nimport { useEntityConfiguration } from \"./use-entity-configuration\";\r\n\r\nconst INITIAL_PAGE_SIZE = 25;\r\n\r\n/**\r\n * Request when fetching a list of registry entries, without class specifics\r\n */\r\nexport type EntityRegistryRequest = Omit<RegistryRequest, \"classFullName\">;\r\n\r\n/**\r\n * Return type of {@link useRegistryEntity}. Contains main data and methods for managing registry entities.\r\n */\r\nexport interface UseRegistryEntity {\r\n /**\r\n * Configuration of wanted entity\r\n */\r\n entityConfiguration: RegistryEntityConfiguration;\r\n\r\n /**\r\n * Id property of the entity\r\n */\r\n entityIdProperty: RegistryPropertyConfiguration;\r\n\r\n /**\r\n * Currently fetched data for this entity.\r\n */\r\n data: RegistryResponse<any[]>;\r\n\r\n /**\r\n * Method for reloading data explicitly with given request.\r\n * @param request search, filter and paging data of this request\r\n */\r\n load: (request: EntityRegistryRequest) => Promise<void>;\r\n\r\n /**\r\n * Adds new entity. Should conform to structure from {@link entityConfiguration}.\r\n * Automatically re-fetches with request provided to hook.\r\n * @param createData data for new entity\r\n */\r\n add: (createData: any) => Promise<any>;\r\n\r\n /**\r\n * Edits existing entity. Should conform to structure from {@link entityConfiguration}.\r\n * Automatically re-fetches with request provided to hook.\r\n * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.\r\n * @param updateData data for editable entity\r\n */\r\n edit: (id: any, updateData: any) => Promise<any>;\r\n\r\n /**\r\n * Deletes existing entity.\r\n * Automatically re-fetches with request provided to hook.\r\n * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.\r\n */\r\n remove: (id: any) => Promise<any>;\r\n}\r\n\r\n/**\r\n * Default request for fetching used when other is not provided. Starts on first page with size of 25.\r\n */\r\nexport const DEFAULT_INITIAL_REQUEST: EntityRegistryRequest = {\r\n pageNumber: 0,\r\n pageSize: INITIAL_PAGE_SIZE,\r\n};\r\n\r\nexport const useRegistryEntity = (name: string, initialRequest: EntityRegistryRequest = DEFAULT_INITIAL_REQUEST): UseRegistryEntity => {\r\n const { entityConfiguration, entityIdProperty } = useEntityConfiguration(name);\r\n\r\n const [data, setData] = React.useState<RegistryResponse<any[]>>({\r\n content: [],\r\n empty: true,\r\n first: true,\r\n last: true,\r\n sort: {\r\n sorted: false,\r\n unsorted: true,\r\n },\r\n numberOfElements: 0,\r\n totalElements: 0,\r\n totalPages: 0,\r\n });\r\n\r\n const load = async (request: EntityRegistryRequest) => {\r\n const loadedData = await loadEntities({ classFullName: entityConfiguration.classFullName, ...request });\r\n setData(loadedData);\r\n };\r\n\r\n React.useEffect(() => {\r\n load(initialRequest);\r\n }, []);\r\n\r\n const add = async (createData: any) => {\r\n const response = await createEntity(entityConfiguration.classFullName, createData);\r\n await load(initialRequest);\r\n\r\n return response;\r\n };\r\n\r\n const edit = async (id: any, updateData: any) => {\r\n const response = await updateEntity(entityConfiguration.classFullName, id, updateData);\r\n await load(initialRequest);\r\n\r\n return response;\r\n };\r\n\r\n const remove = async (id: any) => {\r\n const response = await removeEntity(entityConfiguration.classFullName, id);\r\n await load(initialRequest);\r\n\r\n return response;\r\n };\r\n\r\n return {\r\n entityConfiguration,\r\n entityIdProperty,\r\n data,\r\n load,\r\n add,\r\n edit,\r\n remove,\r\n };\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport { useRegistryConfigurationStore } from \"../store\";\r\n\r\n/**\r\n * Fetches {@link #RegistryEntityConfiguration} for given entity\r\n * @param name short name of entity or its full class name\r\n */\r\nexport const useEntityConfiguration = (name: string) => {\r\n const entityConfiguration = useRegistryConfigurationStore((state) => state.groupConfigurations\r\n .flatMap((groupConfiguration) => groupConfiguration.entityConfigurationList)\r\n .find((configuration) => configuration.name === name || configuration.classFullName === name));\r\n if (entityConfiguration === undefined) {\r\n return undefined;\r\n }\r\n\r\n const entityIdProperty = entityConfiguration.propertyConfigurationList.find((property) => property.id);\r\n\r\n return { entityConfiguration, entityIdProperty };\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React from \"react\";\r\n\r\nimport {\r\n FormType, RegistryEntityConfiguration, RegistryPropertyConfiguration, SearchParameter,\r\n} from \"../api\";\r\nimport { RegistryResponse } from \"../service\";\r\nimport { restructureSubmitValue } from \"../util\";\r\nimport { DEFAULT_INITIAL_REQUEST, EntityRegistryRequest, useRegistryEntity } from \"./use-registry-entity\";\r\nimport { useUpdateEffect } from \"./use-update-effect\";\r\n\r\n/**\r\n * Return value of {@link useRegistryEntityAdministration} hook. Contains all configuration, data and handlers for most of the administration operations.\r\n */\r\ninterface UseRegistryEntityAdministration {\r\n /**\r\n * Configuration of wanted entity\r\n */\r\n entityConfiguration: RegistryEntityConfiguration;\r\n\r\n /**\r\n * Id property of the entity\r\n */\r\n entityIdProperty: RegistryPropertyConfiguration;\r\n\r\n /**\r\n * Currently fetched data for this entity.\r\n */\r\n data: RegistryResponse<any[]>;\r\n\r\n /**\r\n * Currently used request for fetching.\r\n */\r\n request: EntityRegistryRequest;\r\n\r\n /**\r\n * Handler when paging (page number or size) is changed. Refeteches data with new paging configuration.\r\n * Reuses filter configuration if any is used.\r\n * @param newPagingRequest new paging configuration for fetching. Non paging fields are ignored.\r\n */\r\n handlePagingUpdate: (newPagingRequest: EntityRegistryRequest) => Promise<void>;\r\n\r\n /**\r\n * Handler when filter (query and search fields) is changed. Refeteches with new filter configuration.\r\n * Reuses page size and resets page to 0.\r\n */\r\n handleFilterUpdate: (searchParameter: SearchParameter) => Promise<void>;\r\n\r\n /**\r\n * Handler for adding new entry. Opens up modal for form to be filled.\r\n */\r\n handleAddClick: () => void;\r\n\r\n /**\r\n * Handler for adding new entry from copied one. Opens up modal with filled data from copied entry.\r\n * @param row data of the entry to be edited\r\n */\r\n handleAddFromCopyClick: (row: any) => void;\r\n\r\n /**\r\n * Handler for editing existing entry. Opens up modal with filled data to be edited.\r\n * @param id id of the entry to be edited\r\n * @param row data of the entry to be edited\r\n */\r\n handleEditClick: (id: any, row: any) => void;\r\n\r\n /**\r\n * Handler for entry preview. Opens up modal with filled data from entry.\r\n * @param row data of the entry to be previewed\r\n */\r\n handlePreviewClick: (row: any) => void;\r\n\r\n /**\r\n * Handler for submitting add or edit form. Closes modal and tries to save new data.\r\n * @param values data of new/edited entry for saving\r\n */\r\n handleSubmitClick: (values: any) => Promise<any>;\r\n\r\n /**\r\n * Current form type (create or update). Undefined if no form is currently active.\r\n */\r\n formType?: FormType;\r\n\r\n /**\r\n * Initial data when opening form. Empty object for adding, and row data for editing.\r\n */\r\n formData: any;\r\n\r\n /**\r\n * Flag which marks if form modal is open or not.\r\n */\r\n formModalOpen: boolean;\r\n\r\n /**\r\n * Method for explicit closing of form modal.\r\n */\r\n closeFormModal: () => void;\r\n\r\n /**\r\n * Flag if data is currently loading.\r\n */\r\n loading: boolean;\r\n\r\n /**\r\n * Deletes existing entity.\r\n * Automatically re-fetches with request provided to hook.\r\n * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.\r\n */\r\n remove: (id: any) => Promise<any>;\r\n}\r\n\r\n/**\r\n * Helper hook which handles most of the administration for registry entity. For given entity name you get all the data handling out of the box.\r\n * This hook assumes that modal is used for forms, but usage can be what fits the case.\r\n * @param entityName name of the entity for administration\r\n */\r\nexport const useRegistryEntityAdministration = (entityName: string): UseRegistryEntityAdministration => {\r\n const [request, setRequest] = React.useState<EntityRegistryRequest>(DEFAULT_INITIAL_REQUEST);\r\n const registryEntity = useRegistryEntity(entityName, request);\r\n const [formModalOpen, setFormModalOpen] = React.useState<boolean>(false);\r\n const [formType, setFormType] = React.useState<FormType | undefined>(undefined);\r\n const [formData, setFormData] = React.useState<any>();\r\n const [loading, setLoading] = React.useState<boolean>(false);\r\n\r\n const {\r\n entityConfiguration, entityIdProperty, data, add, edit, remove,\r\n } = registryEntity;\r\n\r\n const handlePagingUpdate = async (newPagingRequest: EntityRegistryRequest) => {\r\n setRequest((oldRequest) => ({ ...oldRequest, ...newPagingRequest }));\r\n };\r\n\r\n const handleFilterUpdate = async (searchParameter: SearchParameter) => {\r\n setRequest((oldRequest) => ({ ...oldRequest, pageNumber: 0, searchParameter }));\r\n };\r\n\r\n useUpdateEffect(() => {\r\n const reload = async () => {\r\n setLoading(true);\r\n await registryEntity.load(request);\r\n setLoading(false);\r\n };\r\n reload();\r\n }, [request]);\r\n\r\n const handleAddClick = () => {\r\n setFormModalOpen(true);\r\n setFormType(\"create\");\r\n setFormData(undefined);\r\n };\r\n\r\n const handleAddFromCopyClick = (row: any) => {\r\n setFormModalOpen(true);\r\n setFormType(\"create\");\r\n setFormData(row);\r\n };\r\n\r\n const handleEditClick = (id: any, row: any) => {\r\n setFormModalOpen(true);\r\n setFormType(\"update\");\r\n setFormData(row);\r\n };\r\n\r\n const handlePreviewClick = (row: any) => {\r\n setFormModalOpen(true);\r\n setFormType(\"preview\");\r\n setFormData(row);\r\n };\r\n\r\n const handleSubmitClick = async (values: any) => {\r\n const finalValue = restructureSubmitValue(values);\r\n let actionResponse: undefined;\r\n\r\n setFormModalOpen(false);\r\n setLoading(true);\r\n if (formType === \"update\") {\r\n actionResponse = await edit(formData[entityIdProperty.name], finalValue);\r\n }\r\n else {\r\n actionResponse = await add(finalValue);\r\n }\r\n setLoading(false);\r\n setFormType(undefined);\r\n setFormData(undefined);\r\n\r\n return actionResponse\r\n };\r\n\r\n const closeFormModal = () => {\r\n setFormModalOpen(false);\r\n setFormType(undefined);\r\n setFormData(undefined);\r\n };\r\n\r\n return {\r\n entityConfiguration,\r\n entityIdProperty,\r\n data,\r\n request,\r\n handlePagingUpdate,\r\n handleFilterUpdate,\r\n handleAddClick,\r\n handleAddFromCopyClick,\r\n handleEditClick,\r\n handlePreviewClick,\r\n handleSubmitClick,\r\n formType,\r\n formData,\r\n formModalOpen,\r\n closeFormModal,\r\n loading,\r\n remove,\r\n };\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport { RegistryEntityConfiguration } from \"../api\";\r\n\r\n/**\r\n * Returns an id representation for given entity configuration and data. Can be single value (e.g. when id is numeric) or an object\r\n * when embedded id or id class is used\r\n * @param entityConfiguration\r\n * @param data\r\n */\r\nexport const resolveId = (entityConfiguration, data) => {\r\n if (entityConfiguration.idClassIdentity) {\r\n const id = {};\r\n entityConfiguration.idClassPropertyNameList.forEach((property) => {\r\n id[property.name] = data[property.name];\r\n });\r\n return id;\r\n }\r\n const idField = entityConfiguration.propertyConfigurationList.find((property) => property.id === true);\r\n return data[idField.name];\r\n};\r\n\r\n/**\r\n * Resolves a value for flat property from nested data.\r\n * @param propertyConfiguration\r\n * @param data\r\n */\r\nexport const resolveValue = (propertyConfiguration, data) => {\r\n const fieldPath = propertyConfiguration.name;\r\n if (fieldPath.includes(\".\")) {\r\n let current = data;\r\n fieldPath.split(\".\").forEach((fieldPathPart) => {\r\n current = current[fieldPathPart];\r\n });\r\n return current;\r\n }\r\n return data[fieldPath];\r\n};\r\n\r\n/**\r\n * Rearranges object from flat structure to nested one, e.g.\r\n * { \"id.author\": { \"id\": 1 }, \"id.book\": { \"id\": 3 }, \"edition\": \"First edition\" } to\r\n * { \"id\": { \"author\": { \"id\": 1 }, \"book\": { \"id\": 3 } }, \"edition\": \"First edition\" }\r\n * @param value form value for submission\r\n */\r\nexport const restructureSubmitValue = (value) => {\r\n const result = {};\r\n Object.keys(value).forEach((key) => {\r\n if (key.includes(\".\")) {\r\n let current = result;\r\n key.split(\".\").forEach((fieldPathPart, i, arr) => {\r\n if (i === arr.length - 1) {\r\n current[fieldPathPart] = value[key];\r\n }\r\n else {\r\n current[fieldPathPart] = { ...(current[fieldPathPart] ?? {}) };\r\n current = current[fieldPathPart];\r\n }\r\n });\r\n }\r\n else {\r\n result[key] = value[key];\r\n }\r\n });\r\n return result;\r\n};\r\n\r\n/**\r\n * Formats id field as Class[id=3]. Used as default for rendering objects\r\n * @param classFullName full name of class for formatting\r\n * @param idFieldName name of the id field\r\n * @param id value of the id field\r\n */\r\nexport const formatIdName = (classFullName: string, idFieldName: string, id: any) => `${classFullName.split(\".\").pop()}[${idFieldName}=${id}]`;\r\n\r\n/**\r\n * Returns id field of the configuration, used when entity does not have embedded id nor id class.\r\n * @param entityConfiguration configuration of the registry entity\r\n */\r\nexport const findIdField = (entityConfiguration: RegistryEntityConfiguration) => entityConfiguration.propertyConfigurationList.find((property) => property.id);\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React, { DependencyList, EffectCallback } from \"react\";\r\n\r\n/**\r\n * Helper hook, similar to useEffect, but doesn't trigger on initial load.\r\n * @param effect effect to be triggered\r\n * @param dependencies dependency array of the effect\r\n */\r\nexport const useUpdateEffect = (effect: EffectCallback, dependencies?: DependencyList) => {\r\n const firstRender = React.useRef(true);\r\n\r\n React.useEffect(() => {\r\n firstRender.current = true;\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n if (!firstRender.current) {\r\n return effect();\r\n }\r\n firstRender.current = false;\r\n return undefined;\r\n }, dependencies);\r\n};\r\n\r\n/**\r\n * Helper hook, similar to useEffect, but has additional debounce effect. Doesn't trigger on initial load.\r\n * Calls effect only once for multiple triggers, when debounce timer expires. Usefully in search/filters, to filter out unnecessary requests.\r\n * @param effect effect to be triggered\r\n * @param debounceTime time in milliseconds for debounce\r\n * @param dependencies dependecy array of the effect\r\n */\r\nexport const useDebouncedUpdateEffect = (effect: EffectCallback, debounceTime: number, dependencies?: DependencyList) => {\r\n const firstRender = React.useRef(true);\r\n const timeoutRef = React.useRef(null);\r\n const clearTimeout = () => window.clearTimeout(timeoutRef.current);\r\n\r\n React.useEffect(() => {\r\n firstRender.current = true;\r\n clearTimeout();\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n clearTimeout();\r\n if (!firstRender.current) {\r\n timeoutRef.current = setTimeout(() => {\r\n effect();\r\n }, debounceTime);\r\n }\r\n firstRender.current = false;\r\n }, dependencies);\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport { useYupFormConfiguration } from \"@croz/nrich-form-configuration-core\";\r\n\r\nimport { FormType } from \"../api\";\r\n\r\n/**\r\n * Helper hook for fetching validation configuration for given entity\r\n * @param classFullName full class name of the entity\r\n * @param type type of form\r\n */\r\nexport const useRegistryEntityYupFormConfiguration = (classFullName: string, type: FormType) => useYupFormConfiguration(`${classFullName}:::${type}`);\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React, { ChangeEvent } from \"react\";\r\n\r\nimport { SearchParameter } from \"../api\";\r\nimport { RegistryEntityContext } from \"../component\";\r\n\r\n/**\r\n * Initial value for filter fields\r\n */\r\nconst INITIAL_FILTER_VALUE = { query: \"\", propertyNameList: [] };\r\n\r\n/**\r\n * Return value of {@link useRegistryFilter}. Contains configuration, value and change handlers for registry filter.\r\n */\r\ninterface UseRegistryFilter {\r\n /**\r\n * List of available fields for filtering\r\n */\r\n availableFields: { value: string, label: string }[];\r\n\r\n /**\r\n * Current value of request {@link SearchParameter}\r\n */\r\n searchParameter: SearchParameter;\r\n\r\n /**\r\n * Change handler for search fields\r\n * @param event change event (usually from multiselect field)\r\n */\r\n handleFieldsChange: (event: ChangeEvent) => void;\r\n\r\n /**\r\n * Change handler for query\r\n * @param event change event (usually from text field)\r\n */\r\n handleQueryChange: (event: ChangeEvent) => void;\r\n}\r\n\r\n/**\r\n * Helper hook that handles filtering states for administered registry entity. Only handles fields and leaves refeching to hook user.\r\n * @param initialFilterValue initial filter value\r\n */\r\nexport const useRegistryFilter = (initialFilterValue: SearchParameter = INITIAL_FILTER_VALUE): UseRegistryFilter => {\r\n const { finalProperties } = React.useContext(RegistryEntityContext);\r\n const [searchParameter, setSearchParameter] = React.useState<SearchParameter>(initialFilterValue);\r\n\r\n const handleFieldsChange = (event) => {\r\n const { value } = event.target;\r\n const newValue = typeof value === \"string\" ? value.split(\",\") : value;\r\n setSearchParameter((oldSearchParameter) => ({ ...oldSearchParameter, propertyNameList: newValue }));\r\n };\r\n\r\n const handleQueryChange = (event) => {\r\n const { value } = event.target;\r\n setSearchParameter((oldSearchParameter) => ({ ...oldSearchParameter, query: value }));\r\n };\r\n\r\n const availableFields = React.useMemo(\r\n () => finalProperties.filter((property) => property.searchable)\r\n .map((property) => ({ value: property.name, label: property.columnHeader })),\r\n [finalProperties],\r\n );\r\n\r\n return {\r\n availableFields,\r\n searchParameter,\r\n handleFieldsChange,\r\n handleQueryChange,\r\n };\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React from \"react\";\r\nimport * as yup from \"yup\";\r\n\r\nimport { FormType, RegistryEntityConfiguration } from \"../api\";\r\nimport { RegistryEntityContext } from \"../component\";\r\nimport { resolveValue } from \"../util\";\r\nimport { useRegistryEntityYupFormConfiguration } from \"./use-registry-entity-form-configuration\";\r\n\r\n/**\r\n * Return value of {@link useRegistryForm}. Contains configuration part of the form, with initial values, while leaving user to\r\n * handle form changes and errors.\r\n */\r\ninterface UseRegistryForm {\r\n /**\r\n * Configuration of wanted entity\r\n */\r\n entityConfiguration: RegistryEntityConfiguration;\r\n\r\n /**\r\n * Validation schema of the form\r\n */\r\n yupSchema: yup.ObjectSchema<any>;\r\n\r\n /**\r\n * Flattened initial values for easier access in form.\r\n */\r\n finalInitialValues: any;\r\n\r\n /**\r\n * Properties to be shown in the form.\r\n */\r\n properties,\r\n}\r\n\r\n/**\r\n * Helper hook used in registry forms. Returns initial data and configuration to be used in the form.\r\n * @param initialValues initial values of the form. Row data for update and empty object for create form.\r\n * @param type type of the form (update or create)\r\n */\r\nexport const useRegistryForm = (initialValues: any, type: FormType): UseRegistryForm => {\r\n const { entityConfiguration, finalProperties } = React.useContext(RegistryEntityContext);\r\n const yupSchema = useRegistryEntityYupFormConfiguration(entityConfiguration.classFullName, type);\r\n const finalInitialValues = React.useMemo(() => {\r\n if (initialValues === undefined) {\r\n const values = {};\r\n finalProperties.forEach((property) => {\r\n values[property.name] = null;\r\n });\r\n return values;\r\n }\r\n const values = {};\r\n finalProperties.forEach((property) => {\r\n values[property.name] = resolveValue(property, initialValues);\r\n });\r\n return values;\r\n }, [initialValues]);\r\n\r\n const properties = type === \"update\" ? finalProperties : finalProperties.filter((property) => property.editable);\r\n\r\n return {\r\n entityConfiguration,\r\n yupSchema,\r\n finalInitialValues,\r\n properties,\r\n };\r\n};\r\n","/*\r\n * Copyright 2023 CROZ d.o.o, the original author or authors.\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n *\r\n */\r\n\r\nimport React from \"react\";\r\n\r\nimport { RegistryPropertyConfiguration, SortDirection, SortProperty } from \"../api\";\r\n\r\n/**\r\n * Return value of {@link useRegistrySort} hook. Contains state and sort handler which uses natural flow for sorting (asc -> desc -> none)\r\n */\r\ninterface UseRegistrySort {\r\n /**\r\n * Current sort direction. Undefined if no sort is applied.\r\n */\r\n sortDirection?: SortDirection,\r\n\r\n /**\r\n * Change handler for sort, usually triggered on table header click. Changes sorting to next state. (asc -> desc -> none)\r\n */\r\n sortChangeHandler: () => void,\r\n}\r\n\r\n/**\r\n * Helper hook used for table headers to add functionality of sorting by specific field\r\n * @param propertyConfiguration configuration of the column property\r\n * @param value current list of sort properties\r\n * @param onChange change handler to be called with new sort properties\r\n * @param allowMultiple flag if multiple properties can be sorted at the same time. Default is true\r\n */\r\nexport const useRegistrySort = (\r\n propertyConfiguration: RegistryPropertyConfiguration,\r\n value: SortProperty[],\r\n onChange: (newValue: SortProperty[]) => void,\r\n allowMultiple: boolean = true,\r\n): UseRegistrySort => {\r\n const sortDirection = React.useMemo(() => {\r\n const sortProperty = value.find((pagingRequestSortProperty) => pagingRequestSortProperty.property === propertyConfiguration.name);\r\n if (sortProperty === undefined) {\r\n return undefined;\r\n }\r\n return sortProperty.direction;\r\n }, [propertyConfiguration, value]);\r\n\r\n const sortChangeHandler = () => {\r\n const sortProperty = value.find((pagingRequestSortProperty) => pagingRequestSortProperty.property === propertyConfiguration.name);\r\n if (sortProperty === undefined) {\r\n onChange([\r\n ...(allowMultiple ? value : []),\r\n { property: propertyConfiguration.name, direction: \"ASC\" },\r\n ]);\r\n }\r\n else if (sortProperty.direction === \"ASC\") {\r\n onChange(value.map((pagingRequestSortProperty) => {\r\n if (pagingRequestSortProperty.property === propertyConfiguration.name) {\r\n return { ...pagingRequestSortProperty, direction: \"DESC\" };\r\n }\r\n return pagingRequestSortProperty;\r\n }));\r\n }\r\n else {\r\n onChange(value.filter((pagingRequestSortProperty) => pagingRequestSortProperty.property !== propertyConfiguration.name));\r\n }\r\n };\r\n\r\n return {\r\n sortDirection,\r\n sortChangeHandler,\r\n };\r\n};\r\n"],"mappings":"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