mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-02-04 22:23:56 +00:00
Type ahead widget (#205)
This commit is contained in:
parent
4fc72f92b7
commit
20cec0f2a2
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
pyrightconfig.json
|
pyrightconfig.json
|
||||||
.idea/
|
.idea/
|
||||||
t
|
t
|
||||||
|
*~
|
||||||
.dccache
|
.dccache
|
||||||
*~
|
*~
|
@ -2089,6 +2089,37 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Secret"
|
$ref: "#/components/schemas/Secret"
|
||||||
|
|
||||||
|
/connector-proxy/type-ahead/{category}:
|
||||||
|
parameters:
|
||||||
|
- name: category
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The category for the type-ahead search
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: prefix
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: The prefix to search for
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: The maximum number of search results
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
operationId: spiffworkflow_backend.routes.connector_proxy_controller.type_ahead
|
||||||
|
summary: Return type ahead search results
|
||||||
|
tags:
|
||||||
|
- Type Ahead
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: We return type ahead search results
|
||||||
|
#content:
|
||||||
|
# - application/json
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
jwt:
|
jwt:
|
||||||
|
@ -41,6 +41,10 @@ SPIFFWORKFLOW_BACKEND_URL = environ.get("SPIFFWORKFLOW_BACKEND_URL", default="ht
|
|||||||
SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_URL = environ.get(
|
SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_URL = environ.get(
|
||||||
"SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_URL", default="http://localhost:7004"
|
"SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_URL", default="http://localhost:7004"
|
||||||
)
|
)
|
||||||
|
SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_TYPE_AHEAD_URL = environ.get(
|
||||||
|
"SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_TYPE_AHEAD_URL",
|
||||||
|
default="https://emehvlxpwodjawtgi7ctkbvpse0vmaow.lambda-url.us-east-1.on.aws",
|
||||||
|
)
|
||||||
|
|
||||||
# Open ID server
|
# Open ID server
|
||||||
# use "http://localhost:7000/openid" for running with simple openid
|
# use "http://localhost:7000/openid" for running with simple openid
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import flask.wrappers
|
||||||
|
import requests
|
||||||
|
from flask import current_app
|
||||||
|
from flask.wrappers import Response
|
||||||
|
|
||||||
|
|
||||||
|
def connector_proxy_type_ahead_url() -> Any:
|
||||||
|
"""Returns the connector proxy type ahead url."""
|
||||||
|
return current_app.config["SPIFFWORKFLOW_BACKEND_CONNECTOR_PROXY_TYPE_AHEAD_URL"]
|
||||||
|
|
||||||
|
|
||||||
|
def type_ahead(category: str, prefix: str, limit: int) -> flask.wrappers.Response:
|
||||||
|
url = f"{connector_proxy_type_ahead_url()}/v1/type-ahead/{category}?prefix={prefix}&limit={limit}"
|
||||||
|
|
||||||
|
proxy_response = requests.get(url)
|
||||||
|
status = proxy_response.status_code
|
||||||
|
if status // 100 == 2:
|
||||||
|
response = proxy_response.text
|
||||||
|
else:
|
||||||
|
# supress pop up errors on the client
|
||||||
|
status = 200
|
||||||
|
response = "[]"
|
||||||
|
return Response(response, status=status, mimetype="application/json")
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import validator from '@rjsf/validator-ajv8';
|
import validator from '@rjsf/validator-ajv8';
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ import {
|
|||||||
Tabs,
|
Tabs,
|
||||||
Grid,
|
Grid,
|
||||||
Column,
|
Column,
|
||||||
|
ComboBox,
|
||||||
Button,
|
Button,
|
||||||
ButtonSet,
|
ButtonSet,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -22,6 +23,73 @@ import { modifyProcessIdentifierForPathParam } from '../helpers';
|
|||||||
import { ProcessInstanceTask } from '../interfaces';
|
import { ProcessInstanceTask } from '../interfaces';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
|
|
||||||
|
// TODO: move this somewhere else
|
||||||
|
function TypeAheadWidget({
|
||||||
|
id,
|
||||||
|
onChange,
|
||||||
|
options: { category, itemFormat },
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
onChange: any;
|
||||||
|
options: any;
|
||||||
|
}) {
|
||||||
|
const pathForCategory = (inputText: string) => {
|
||||||
|
return `/connector-proxy/type-ahead/${category}?prefix=${inputText}&limit=100`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lastSearchTerm = useRef('');
|
||||||
|
const [items, setItems] = useState<any[]>([]);
|
||||||
|
const [selectedItem, setSelectedItem] = useState<any>(null);
|
||||||
|
const itemFormatRegex = /[^{}]+(?=})/g;
|
||||||
|
const itemFormatSubstitutions = itemFormat.match(itemFormatRegex);
|
||||||
|
|
||||||
|
const itemToString = (item: any) => {
|
||||||
|
if (!item) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = itemFormat;
|
||||||
|
itemFormatSubstitutions.forEach((key: string) => {
|
||||||
|
str = str.replace(`{${key}}`, item[key]);
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTypeAheadResult = (result: any, inputText: string) => {
|
||||||
|
if (lastSearchTerm.current === inputText) {
|
||||||
|
setItems(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeAheadSearch = (inputText: string) => {
|
||||||
|
if (inputText) {
|
||||||
|
lastSearchTerm.current = inputText;
|
||||||
|
// TODO: check cache of prefixes -> results
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: pathForCategory(inputText),
|
||||||
|
successCallback: (result: any) =>
|
||||||
|
handleTypeAheadResult(result, inputText),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ComboBox
|
||||||
|
onInputChange={typeAheadSearch}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
setSelectedItem(event.selectedItem);
|
||||||
|
onChange(itemToString(event.selectedItem));
|
||||||
|
}}
|
||||||
|
id={id}
|
||||||
|
items={items}
|
||||||
|
itemToString={itemToString}
|
||||||
|
placeholder={`Start typing to search for ${category}...`}
|
||||||
|
titleText={`Type ahead search for ${category}`}
|
||||||
|
selectedItem={selectedItem}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class UnexpectedHumanTaskType extends Error {
|
class UnexpectedHumanTaskType extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
@ -294,6 +362,8 @@ export default function TaskShow() {
|
|||||||
return getFieldsWithDateValidations(jsonSchema, formData, errors);
|
return getFieldsWithDateValidations(jsonSchema, formData, errors);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const widgets = { typeAhead: TypeAheadWidget };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid fullWidth condensed>
|
<Grid fullWidth condensed>
|
||||||
<Column sm={4} md={5} lg={8}>
|
<Column sm={4} md={5} lg={8}>
|
||||||
@ -303,6 +373,7 @@ export default function TaskShow() {
|
|||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
schema={jsonSchema}
|
schema={jsonSchema}
|
||||||
uiSchema={formUiSchema}
|
uiSchema={formUiSchema}
|
||||||
|
widgets={widgets}
|
||||||
validator={validator}
|
validator={validator}
|
||||||
onChange={updateFormData}
|
onChange={updateFormData}
|
||||||
customValidate={customValidate}
|
customValidate={customValidate}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user