Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
hooks.process.ts Maven / Gradle / Ivy
import _ from 'lodash';
import { toPathsObject, translateDefinitions } from 'components/process/utils/process';
import Report from 'helpers/report';
import Settings from 'helpers/settings';
import Translator, { T } from 'helpers/translator';
import { Notifier, useNotifier } from 'hooks/notification';
import { SubmitHandler, useSubmitHandler } from 'hooks/submit';
import { useGearsTranslation } from 'hooks/translation';
import {
GET_MY_PROCESS_INSTANCES, GET_PROCESS_DEFINITIONS, GET_PROCESS_DEFINITION_BY_KEY, START_PROCESS, STOP_PROCESS
} from 'queries/process';
import { CLAIM_TASK } from 'queries/task';
import { useEffect, useState } from 'react';
import { NavigateFunction, useNavigate } from 'react-router';
import { LinkKind, ProcessDefinition, Task } from 'types/graphql';
import { bannerQueries } from 'utils/banner-utils';
import { reformQuery, removerById } from 'utils/utils';
import { ApolloClient, useApolloClient, useMutation, useQuery } from '@apollo/client';
import { useLocale } from './locale';
import { useTranslator } from './translator';
export type ProcessRendering = "standard" | "tiles" | "table";
export type PathObject = { [key: string]: T | PathObject; };
export type PathOrder = { [key: string]: number; };
export type PathEntry = [string, T];
export type ProcessPathObject = PathObject;
export type ProcessGroupBy = "category" | "subcategory" | "none";
export interface TranslatedProcessDefinition extends ProcessDefinition { title: string }
export type ProcessInfo = {
processes: TranslatedProcessDefinition[];
paths: ProcessPathObject;
order: PathOrder;
};
export type FormValues = Promise
export type StartProcessFunction = (e: MouseEvent | null, processDefinition: Partial, kind: LinkKind, values?: FormValues) => void
type GraphqlMutationVariables = {
variables: object
}
interface StartProcessOptions extends StartLinkProcessOptions,OpenFormOptions,OpenLinkFormOptions,GoToOptions,ClaimOptions {}
interface StartLinkProcessOptions extends ClaimOptions {
doStartProcess: (variables: GraphqlMutationVariables) => Promise
}
interface ClaimLinkOptions extends ClaimOptions,OpenLinkFormOptions {}
interface OpenLinkFormOptions extends OpenFormOptions {
handleSubmit: SubmitHandler
}
interface ClaimOptions extends OpenFormOptions {
doClaimTask: (variables: GraphqlMutationVariables) => Promise
translator: T
}
type OpenFormOptions = {
register?: boolean
navigate : NavigateFunction
notifier : Notifier
translator : Translator
}
type GoToOptions = {
navigate: NavigateFunction
}
export const useTranslatedProcesses = () => {
const processDefinitionsResult = useQuery(GET_PROCESS_DEFINITIONS)
const processInfo = useProcessTranslator(processDefinitionsResult.data?.processDefinitions)
return {result: processDefinitionsResult, loading: processDefinitionsResult.loading || Boolean(!processInfo), processInfo}
}
export const useProcessTranslator = (processes?: ProcessDefinition[]): ProcessInfo | undefined => {
const { language } = useLocale()
const { translator } = useTranslator()
const [processInfo, setProcessInfo] = useState()
useEffect(() => {
if (processes) {
const sortedProcesses = _.sortBy(processes, def => def.key)
const translatedProcesses = translateDefinitions(translator, language, sortedProcesses)
const { pathObject, pathOrder } = toPathsObject(translatedProcesses)
setProcessInfo({
processes: translatedProcesses,
paths: pathObject,
order: pathOrder
})
}
}, [processes])
return processInfo
}
// a hook that helps to start a process and go to the initial form
const useStartProcess = (): StartProcessFunction => {
const navigate = useNavigate()
const [doStartProcess] = useMutation(START_PROCESS, {refetchQueries: bannerQueries})
const [doClaimTask] = useMutation(CLAIM_TASK, {refetchQueries: bannerQueries})
const { translator } = useGearsTranslation()
const {handleSubmit} = useSubmitHandler()
const notifier = useNotifier()
const client = useApolloClient()
const options = {notifier, navigate, translator, handleSubmit, doStartProcess, doClaimTask}
return (e: MouseEvent | null, partialProcessDefinition: Partial, kind: LinkKind, values?: FormValues) => {
e?.preventDefault()
e?.stopPropagation()
console.log("startProcess: partialProcessDefinition=%o", partialProcessDefinition)
const eventualProcessDefinition = partialProcessDefinition.hasStartForm !== undefined
? Promise.resolve(partialProcessDefinition)
: resolveProcessDefinition(client, partialProcessDefinition.key || 'unknown');
eventualProcessDefinition
.then(pd => {
if (pd.hasStartForm)
openLinkForm({register: true, ...options}, e, true, pd.key!, pd.id!, kind, values)
else
startProcessAndOpenForm({register: true, ...options}, e, pd.key!, kind, values)
})
.catch(e => notifier.error("Unable to start process: " + e))
}
}
function resolveProcessDefinition(client: ApolloClient, key: string): Promise {
return client.query({ query: GET_PROCESS_DEFINITION_BY_KEY, variables: { key } })
.then(res => res.data.processDefinitionByKey)
}
export const useStopProcess = (deleteLine?: (id: any) => {}): (id: any) => Promise => {
const notifier = useNotifier()
const [doStopProcess] = useMutation(STOP_PROCESS, {
// update the cache
update: (cache, { data: { stopProcess: id } }) => {
reformQuery(cache, GET_MY_PROCESS_INSTANCES, { instances: removerById(id) });
},
refetchQueries: bannerQueries
})
const stopProcess = (id: number): Promise => {
return doStopProcess({ variables: { id }})
.then(result => {
deleteLine?.(id)
notifier.success("Stopped process successfully")
}, error => {
notifier.error("Could not stop process: " + error)
console.error("stopping processes has failed: %o", error)
})
.catch(reason => {
notifier.error("Could not stop process")
console.error("the frontend has an issue with stopping the process: " + reason)
})
}
return stopProcess
}
// simply open a (start) form
function openLinkForm(options: OpenLinkFormOptions, e: MouseEvent | null, isStartForm: boolean, key: string, id: string, kind: LinkKind, values?: FormValues) {
const { handleSubmit } = options
console.log('openForm: id=%o, key=%o, kind=%s, isStartform=%s', id, key, kind, isStartForm)
if (options.register)
setProcessOrigin()
switch (kind) {
case "PROCESS_SUBMIT":
if (values)
values.then(values => handleSubmit(values, {} as any, { id, isStartForm}))
else {
window.alert("submit to a startform (without values)")
}
break
case "PROCESS_FILL":
// values are stored in FORM.VALUES (local storage)
default:
openForm(options, isStartForm, e, key, id )
}
}
// first start a process, then claim the only task en open the form. Usefull if a process has no start form
function startProcessAndOpenForm (options: StartProcessOptions, e: MouseEvent | null, processKey: string, kind: LinkKind, values?: FormValues) {
const open = !e?.ctrlKey
const {translator, doStartProcess, notifier} = options
// start the process, claim the only task, and go to the form
doStartProcess({ variables: { key: processKey } })
.then(result => {
const { startProcessByKey: { id: instanceId, tasks } } = result.data
console.log("startProcess: key=%o, open=%o, instanceId=%o, tasks=%o kind=%o", processKey, open, instanceId, tasks, kind)
if (!open) {
notifier.info(`Process ${translator.toProcessTitle(processKey)} has started`)
return
}
if (tasks.length > 1) {
notifier.info(`Process ${translator.toProcessTitle(processKey)} has create ${tasks.length} tasks`)
console.log(`The ${translator.toProcessTitle(processKey)} form has been prevented from opening, because there are ${tasks.length} tasks to choose from`)
return
}
if (tasks.length == 0) {
notifier.info(`Process ${translator.toProcessTitle(processKey)} has not resulted in tasks for you`)
return
}
return claimLinkTask(options, e, tasks[0], kind, values)
})
.catch(reason => {
console.error(reason)
notifier.error("Unable to start the process: " + translator.toProcessTitle(processKey))
})
}
export function claimLinkTask(options: ClaimLinkOptions, e: MouseEvent | null, task: Partial, kind: LinkKind, values?: FormValues){
const key = task.processDefinition?.key
const id = task.id
return claim(options, e, task, result => openLinkForm(options, e, false, key || "?", id || "?", kind, values))
}
export function claimTask(options: ClaimOptions, e: MouseEvent | null, task: Partial ) {
return claim(options, e, task, result => openTaskForm(options, e, task))
}
function claim(options: ClaimOptions, e: MouseEvent | null, task: Partial, success: (result: any) => void){
const { notifier, doClaimTask, translator } = options
return doClaimTask({ variables: { id: task.id } })
.then(result => {
console.log("claimTask: result=%o", result)
return success(result)
}, error => {
const report = Report.from(error, translator, { category: Report.backend })
report.addToNotifier(notifier)
return Promise.reject(report.message)
})
}
const processOrigin = "PROCESS_ORIGIN"
export function setProcessOrigin(origin?: string){
const pathname = window.location.pathname;
const hash = window.location.hash;
const path = pathname == "/" ? hash.replace('^#', '') : pathname
return Settings.session.write(processOrigin, origin || path)
}
export function getProcessOrigin() {
return Settings.session.read(processOrigin, '/gears/processes/start').replace(/^#/, '')
}
export function goToProcessOrigin(navigate: NavigateFunction) {
const url = getProcessOrigin()
if (url)
navigate(url)
else
navigate(-1)
}
export function openTasksForm(options: OpenFormOptions, tasks: Partial[]) {
const {navigate, notifier} = options
switch (tasks.length) {
case 0:
goToProcessOrigin(navigate)
break
case 1:
openTaskForm(options, null, tasks[0])
break
default:
notifier.info("There are multiple tasks to choose from. Redirecting momentarily...")
setTimeout(() => { navigate('/gears/tasks/assigned') }, 3000)
break
}
}
export function openTaskForm(options: OpenFormOptions, e: MouseEvent | null, task: Partial) {
const key = task?.processDefinition?.key
const id = task?.id
openForm(options, false, e, key, id)
}
export function openForm(options: OpenFormOptions, isStartForm: boolean, e: MouseEvent | null, key: string | undefined, id: string | undefined) {
const {translator, notifier, navigate} = options
if (!key || !id) {
notifier.error(`Could not go to form of ${key ? `${translator.toProcessTitle(key)} ` : `key ${key} `}with id ${id}`)
return
}
const open = !e?.ctrlKey
if (!open) {
notifier.info(`Ctrl-click has prevented the ${translator.toProcessTitle(key)} ${isStartForm ? "start " : ""}form to open`)
return
}
if (options.register)
setProcessOrigin()
if (isStartForm)
goToUrl(options, e, `/gears/start/form/${key}/${id}`)
else
goToUrl(options, e, `/gears/tasks/${key}/${id}`)
}
function goToUrl({navigate}: GoToOptions, e: MouseEvent | null, url: String) {
if (e?.ctrlKey) {
const uri = `http://${window.location.host}/#${url}`
window.open(uri)
} else {
// @ts-ignore
navigate(url)
}
}
export default useStartProcess