feat(frontend): Support Pipeline summary card and pulling version template for V2. Fix #6633 (#6634)
This commit is contained in:
parent
220d79df66
commit
e0f4f7ce99
|
|
@ -0,0 +1,263 @@
|
|||
{
|
||||
"pipelineSpec": {
|
||||
"components": {
|
||||
"comp-preprocess": {
|
||||
"executorLabel": "exec-preprocess",
|
||||
"inputDefinitions": {
|
||||
"parameters": {
|
||||
"input_dict_parameter": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"input_list_parameter": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"message": {
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
},
|
||||
"outputDefinitions": {
|
||||
"artifacts": {
|
||||
"output_dataset_one": {
|
||||
"artifactType": {
|
||||
"schemaTitle": "system.Dataset",
|
||||
"schemaVersion": "0.0.1"
|
||||
}
|
||||
},
|
||||
"output_dataset_two_path": {
|
||||
"artifactType": {
|
||||
"schemaTitle": "system.Dataset",
|
||||
"schemaVersion": "0.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"output_bool_parameter_path": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"output_dict_parameter_path": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"output_list_parameter_path": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"output_parameter_path": {
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"comp-train": {
|
||||
"executorLabel": "exec-train",
|
||||
"inputDefinitions": {
|
||||
"artifacts": {
|
||||
"dataset_one_path": {
|
||||
"artifactType": {
|
||||
"schemaTitle": "system.Dataset",
|
||||
"schemaVersion": "0.0.1"
|
||||
}
|
||||
},
|
||||
"dataset_two": {
|
||||
"artifactType": {
|
||||
"schemaTitle": "system.Dataset",
|
||||
"schemaVersion": "0.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"input_bool": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"input_dict": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"input_list": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"message": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"num_steps": {
|
||||
"type": "INT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"outputDefinitions": {
|
||||
"artifacts": {
|
||||
"model": {
|
||||
"artifactType": {
|
||||
"schemaTitle": "system.Model",
|
||||
"schemaVersion": "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deploymentSpec": {
|
||||
"executors": {
|
||||
"exec-preprocess": {
|
||||
"container": {
|
||||
"args": [
|
||||
"--executor_input",
|
||||
"{{$}}",
|
||||
"--function_to_execute",
|
||||
"preprocess"
|
||||
],
|
||||
"command": [
|
||||
"sh",
|
||||
"-c",
|
||||
"(python3 -m ensurepip || python3 -m ensurepip --user) && (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location 'kfp==1.8.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location 'kfp==1.8.0' --user) && \"$0\" \"$@\"",
|
||||
"sh",
|
||||
"-ec",
|
||||
"program_path=$(mktemp -d)\nprintf \"%s\" \"$0\" > \"$program_path/ephemeral_component.py\"\npython3 -m kfp.v2.components.executor_main --component_module_path \"$program_path/ephemeral_component.py\" \"$@\"\n",
|
||||
"\nfrom kfp.v2.dsl import *\nfrom typing import *\n\ndef preprocess(\n # An input parameter of type string.\n message: str,\n # An input parameter of type dict.\n input_dict_parameter: Dict[str, int],\n # An input parameter of type list.\n input_list_parameter: List[str],\n # Use Output[T] to get a metadata-rich handle to the output artifact\n # of type `Dataset`.\n output_dataset_one: Output[Dataset],\n # A locally accessible filepath for another output artifact of type\n # `Dataset`.\n output_dataset_two_path: OutputPath('Dataset'),\n # A locally accessible filepath for an output parameter of type string.\n output_parameter_path: OutputPath(str),\n # A locally accessible filepath for an output parameter of type bool.\n output_bool_parameter_path: OutputPath(bool),\n # A locally accessible filepath for an output parameter of type dict.\n output_dict_parameter_path: OutputPath(Dict[str, int]),\n # A locally accessible filepath for an output parameter of type list.\n output_list_parameter_path: OutputPath(List[str]),\n):\n \"\"\"Dummy preprocessing step.\"\"\"\n\n # Use Dataset.path to access a local file path for writing.\n # One can also use Dataset.uri to access the actual URI file path.\n with open(output_dataset_one.path, 'w') as f:\n f.write(message)\n\n # OutputPath is used to just pass the local file path of the output artifact\n # to the function.\n with open(output_dataset_two_path, 'w') as f:\n f.write(message)\n\n with open(output_parameter_path, 'w') as f:\n f.write(message)\n\n with open(output_bool_parameter_path, 'w') as f:\n f.write(\n str(True)) # use either `str()` or `json.dumps()` for bool values.\n\n import json\n with open(output_dict_parameter_path, 'w') as f:\n f.write(json.dumps(input_dict_parameter))\n\n with open(output_list_parameter_path, 'w') as f:\n f.write(json.dumps(input_list_parameter))\n\n"
|
||||
],
|
||||
"image": "python:3.7"
|
||||
}
|
||||
},
|
||||
"exec-train": {
|
||||
"container": {
|
||||
"args": [
|
||||
"--executor_input",
|
||||
"{{$}}",
|
||||
"--function_to_execute",
|
||||
"train"
|
||||
],
|
||||
"command": [
|
||||
"sh",
|
||||
"-c",
|
||||
"(python3 -m ensurepip || python3 -m ensurepip --user) && (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location 'kfp==1.8.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location 'kfp==1.8.0' --user) && \"$0\" \"$@\"",
|
||||
"sh",
|
||||
"-ec",
|
||||
"program_path=$(mktemp -d)\nprintf \"%s\" \"$0\" > \"$program_path/ephemeral_component.py\"\npython3 -m kfp.v2.components.executor_main --component_module_path \"$program_path/ephemeral_component.py\" \"$@\"\n",
|
||||
"\nfrom kfp.v2.dsl import *\nfrom typing import *\n\ndef train(\n # Use InputPath to get a locally accessible path for the input artifact\n # of type `Dataset`.\n dataset_one_path: InputPath('Dataset'),\n # Use Input[T] to get a metadata-rich handle to the input artifact\n # of type `Dataset`.\n dataset_two: Input[Dataset],\n # An input parameter of type string.\n message: str,\n # Use Output[T] to get a metadata-rich handle to the output artifact\n # of type `Dataset`.\n model: Output[Model],\n # An input parameter of type bool.\n input_bool: bool,\n # An input parameter of type dict.\n input_dict: Dict[str, int],\n # An input parameter of type List[str].\n input_list: List[str],\n # An input parameter of type int with a default value.\n num_steps: int = 100,\n):\n \"\"\"Dummy Training step.\"\"\"\n with open(dataset_one_path, 'r') as input_file:\n dataset_one_contents = input_file.read()\n\n with open(dataset_two.path, 'r') as input_file:\n dataset_two_contents = input_file.read()\n\n line = (f'dataset_one_contents: {dataset_one_contents} || '\n f'dataset_two_contents: {dataset_two_contents} || '\n f'message: {message} || '\n f'input_bool: {input_bool}, type {type(input_bool)} || '\n f'input_dict: {input_dict}, type {type(input_dict)} || '\n f'input_list: {input_list}, type {type(input_list)} \\n')\n\n with open(model.path, 'w') as output_file:\n for i in range(num_steps):\n output_file.write('Step {}\\n{}\\n=====\\n'.format(i, line))\n\n # model is an instance of Model artifact, which has a .metadata dictionary\n # to store arbitrary metadata for the output artifact.\n model.metadata['accuracy'] = 0.9\n\n"
|
||||
],
|
||||
"image": "python:3.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pipelineInfo": {
|
||||
"name": "my-test-pipeline-beta"
|
||||
},
|
||||
"root": {
|
||||
"dag": {
|
||||
"tasks": {
|
||||
"preprocess": {
|
||||
"cachingOptions": {
|
||||
"enableCache": true
|
||||
},
|
||||
"componentRef": {
|
||||
"name": "comp-preprocess"
|
||||
},
|
||||
"inputs": {
|
||||
"parameters": {
|
||||
"input_dict_parameter": {
|
||||
"componentInputParameter": "input_dict"
|
||||
},
|
||||
"input_list_parameter": {
|
||||
"runtimeValue": {
|
||||
"constantValue": {
|
||||
"stringValue": "[\"a\", \"b\", \"c\"]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"componentInputParameter": "message"
|
||||
}
|
||||
}
|
||||
},
|
||||
"taskInfo": {
|
||||
"name": "preprocess"
|
||||
}
|
||||
},
|
||||
"train": {
|
||||
"cachingOptions": {
|
||||
"enableCache": true
|
||||
},
|
||||
"componentRef": {
|
||||
"name": "comp-train"
|
||||
},
|
||||
"dependentTasks": [
|
||||
"preprocess"
|
||||
],
|
||||
"inputs": {
|
||||
"artifacts": {
|
||||
"dataset_one_path": {
|
||||
"taskOutputArtifact": {
|
||||
"outputArtifactKey": "output_dataset_one",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
},
|
||||
"dataset_two": {
|
||||
"taskOutputArtifact": {
|
||||
"outputArtifactKey": "output_dataset_two_path",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"input_bool": {
|
||||
"taskOutputParameter": {
|
||||
"outputParameterKey": "output_bool_parameter_path",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
},
|
||||
"input_dict": {
|
||||
"taskOutputParameter": {
|
||||
"outputParameterKey": "output_dict_parameter_path",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
},
|
||||
"input_list": {
|
||||
"taskOutputParameter": {
|
||||
"outputParameterKey": "output_list_parameter_path",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"taskOutputParameter": {
|
||||
"outputParameterKey": "output_parameter_path",
|
||||
"producerTask": "preprocess"
|
||||
}
|
||||
},
|
||||
"num_steps": {
|
||||
"runtimeValue": {
|
||||
"constantValue": {
|
||||
"intValue": "100"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"taskInfo": {
|
||||
"name": "train"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"inputDefinitions": {
|
||||
"parameters": {
|
||||
"input_dict": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"message": {
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemaVersion": "2.0.0",
|
||||
"sdkVersion": "kfp-1.8.0"
|
||||
},
|
||||
"runtimeConfig": {
|
||||
"gcsOutputDirectory": "dummy_root",
|
||||
"parameters": {
|
||||
"input_dict": {
|
||||
"stringValue": "{\"A\": 1, \"B\": 2}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,8 @@ const PIPELINE_ID_V2_PYTHON_TWO_STEPS = '8fbe3bd6-a01f-11e8-98d0-529269fb1460';
|
|||
const PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT: ApiPipelineVersion = {
|
||||
created_at: new Date('2021-11-24T20:58:23.000Z'),
|
||||
id: PIPELINE_ID_V2_PYTHON_TWO_STEPS,
|
||||
name: 'v2_lightweight_python_functions_pipeline',
|
||||
name: 'default version',
|
||||
description: 'This is default version description.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'message',
|
||||
|
|
@ -50,11 +51,27 @@ const PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT: ApiPipelineVersion = {
|
|||
],
|
||||
};
|
||||
const PIPELINE_V2_PYTHON_TWO_STEPS: ApiPipeline = {
|
||||
description: 'V2 two steps: preprocess and training.',
|
||||
...PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
description: 'This is pipeline level description.',
|
||||
name: 'v2_lightweight_python_functions_pipeline',
|
||||
default_version: PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
};
|
||||
|
||||
const PIPELINE_ID_V2_PYTHON_TWO_STEPS_REV = '9fbe3bd6-a01f-11e8-98d0-529269fb1460';
|
||||
const PIPELINE_V2_PYTHON_TWO_STEPS_REV: ApiPipelineVersion = {
|
||||
created_at: new Date('2021-12-24T20:58:23.000Z'),
|
||||
id: PIPELINE_ID_V2_PYTHON_TWO_STEPS_REV,
|
||||
name: 'revision',
|
||||
code_source_url:
|
||||
'https://github.com/kubeflow/pipelines/blob/master/sdk/python/kfp/v2/compiler_cli_tests/test_data/lightweight_python_functions_v2_pipeline.py',
|
||||
description: 'This is version description.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'revision-message',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const PIPELINE_ID_V2_LOOPS_CONDITIONS = '8fbe3bd6-a01f-11e8-98d0-529269fb1461';
|
||||
const PIPELINE_V2_LOOPS_CONDITIONS_DEFAULT: ApiPipelineVersion = {
|
||||
created_at: new Date('2021-04-13T20:58:23.000Z'),
|
||||
|
|
@ -942,9 +959,28 @@ export const v2PipelineSpecMap: Map<string, string> = new Map([
|
|||
PIPELINE_ID_V2_PYTHON_TWO_STEPS,
|
||||
'./mock-backend/data/v2/pipeline/mock_lightweight_python_functions_v2_pipeline.json',
|
||||
],
|
||||
[
|
||||
PIPELINE_ID_V2_PYTHON_TWO_STEPS_REV,
|
||||
'./mock-backend/data/v2/pipeline/mock_lightweight_python_functions_v2_pipeline_rev.json',
|
||||
],
|
||||
[
|
||||
PIPELINE_ID_V2_LOOPS_CONDITIONS,
|
||||
'./mock-backend/data/v2/pipeline/pipeline_with_loops_and_conditions.json',
|
||||
],
|
||||
[PIPELINE_ID_V2_XGBOOST, './mock-backend/data/v2/pipeline/xgboost_sample_pipeline.json'],
|
||||
]);
|
||||
|
||||
// Kubeflow versions
|
||||
export const V2_TWO_STEPS_VERSION_LIST: ApiPipelineVersion[] = [
|
||||
PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
PIPELINE_V2_PYTHON_TWO_STEPS_REV,
|
||||
];
|
||||
|
||||
export const PIPELINE_VERSIONS_LIST_MAP: Map<string, ApiPipelineVersion[]> = new Map([
|
||||
[PIPELINE_ID_V2_PYTHON_TWO_STEPS, V2_TWO_STEPS_VERSION_LIST],
|
||||
]);
|
||||
|
||||
export const PIPELINE_VERSIONS_LIST_FULL: ApiPipelineVersion[] = [
|
||||
...pipelines,
|
||||
PIPELINE_V2_PYTHON_TWO_STEPS_REV,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -12,25 +12,31 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import * as _path from 'path';
|
||||
import * as express from 'express';
|
||||
import { Response } from 'express-serve-static-core';
|
||||
import * as fs from 'fs';
|
||||
import RunUtils from '../src/lib/RunUtils';
|
||||
import helloWorldRuntime from './integration-test-runtime';
|
||||
import proxyMiddleware from './proxy-middleware';
|
||||
import * as _path from 'path';
|
||||
import { ApiExperiment, ApiListExperimentsResponse } from '../src/apis/experiment';
|
||||
import { ApiFilter, PredicateOp } from '../src/apis/filter';
|
||||
import { ApiListExperimentsResponse, ApiExperiment } from '../src/apis/experiment';
|
||||
import { ApiListJobsResponse, ApiJob } from '../src/apis/job';
|
||||
import { ApiJob, ApiListJobsResponse } from '../src/apis/job';
|
||||
import {
|
||||
ApiListPipelinesResponse,
|
||||
ApiPipeline,
|
||||
ApiListPipelineVersionsResponse,
|
||||
ApiPipeline,
|
||||
ApiPipelineVersion,
|
||||
} from '../src/apis/pipeline';
|
||||
import { ApiListRunsResponse, ApiResourceType, ApiRun, ApiRunStorageState } from '../src/apis/run';
|
||||
import { ExperimentSortKeys, PipelineSortKeys, RunSortKeys } from '../src/lib/Apis';
|
||||
import { Response } from 'express-serve-static-core';
|
||||
import { data as fixedData, namedPipelines, v2PipelineSpecMap } from './fixed-data';
|
||||
import RunUtils from '../src/lib/RunUtils';
|
||||
import {
|
||||
data as fixedData,
|
||||
namedPipelines,
|
||||
PIPELINE_VERSIONS_LIST_FULL,
|
||||
PIPELINE_VERSIONS_LIST_MAP,
|
||||
v2PipelineSpecMap,
|
||||
} from './fixed-data';
|
||||
import helloWorldRuntime from './integration-test-runtime';
|
||||
import proxyMiddleware from './proxy-middleware';
|
||||
|
||||
const rocMetadataJsonPath = './eval-output/metadata.json';
|
||||
const rocMetadataJsonPath2 = './eval-output/metadata2.json';
|
||||
|
|
@ -468,26 +474,6 @@ export default (app: express.Application) => {
|
|||
res.json(response);
|
||||
});
|
||||
|
||||
app.get(v1beta1Prefix + '/pipeline_versions', (req, res) => {
|
||||
res.header('Content-Type', 'application/json');
|
||||
const response: ApiListPipelineVersionsResponse = {
|
||||
next_page_token: '',
|
||||
versions: [],
|
||||
};
|
||||
|
||||
let versions: ApiPipelineVersion[] = fixedData.versions;
|
||||
|
||||
const start = req.query.page_token ? +req.query.page_token : 0;
|
||||
const end = start + (+req.query.page_size || 20);
|
||||
response.versions = versions.slice(start, end);
|
||||
|
||||
if (end < versions.length) {
|
||||
response.next_page_token = end + '';
|
||||
}
|
||||
|
||||
res.json(response);
|
||||
});
|
||||
|
||||
app.delete(v1beta1Prefix + '/pipelines/:pid', (req, res) => {
|
||||
res.header('Content-Type', 'application/json');
|
||||
const i = fixedData.pipelines.findIndex(p => p.id === req.params.pid);
|
||||
|
|
@ -543,6 +529,16 @@ export default (app: express.Application) => {
|
|||
|
||||
app.get(v1beta1Prefix + '/pipeline_versions/:pid/templates', (req, res) => {
|
||||
res.header('Content-Type', 'text/x-yaml');
|
||||
|
||||
// Find v2 pipeline template
|
||||
const templatePath = v2PipelineSpecMap.get(req.params.pid);
|
||||
if (templatePath != null) {
|
||||
console.log(templatePath);
|
||||
res.send(JSON.stringify({ template: fs.readFileSync(templatePath, 'utf-8') }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Default and v1 version list. Return mock template consistently.
|
||||
const version = fixedData.versions.find(p => p.id === req.params.pid);
|
||||
if (!version) {
|
||||
res.status(404).send(`No pipeline was found with ID: ${req.params.pid}`);
|
||||
|
|
@ -555,7 +551,7 @@ export default (app: express.Application) => {
|
|||
|
||||
app.get(v1beta1Prefix + '/pipeline_versions/:pid', (req, res) => {
|
||||
res.header('Content-Type', 'application/json');
|
||||
const pipeline = fixedData.versions.find(p => p.id === req.params.pid);
|
||||
const pipeline = PIPELINE_VERSIONS_LIST_FULL.find(p => p.id === req.params.pid);
|
||||
if (!pipeline) {
|
||||
res.status(404).send(`No pipeline was found with ID: ${req.params.pid}`);
|
||||
return;
|
||||
|
|
@ -576,16 +572,40 @@ export default (app: express.Application) => {
|
|||
req.query['resource_key.type'] === 'PIPELINE' &&
|
||||
req.query.page_size > 0
|
||||
) {
|
||||
const pipeline = fixedData.pipelines.find(p => p.id === req.query['resource_key.id']);
|
||||
|
||||
if (pipeline == null) {
|
||||
return;
|
||||
}
|
||||
const pipeline_versions_list_response: ApiListPipelinesResponse = {
|
||||
total_size: 1,
|
||||
pipelines: [pipeline],
|
||||
const response: ApiListPipelineVersionsResponse = {
|
||||
next_page_token: '',
|
||||
versions: [],
|
||||
};
|
||||
res.send(JSON.stringify(pipeline_versions_list_response));
|
||||
|
||||
let versions: ApiPipelineVersion[] =
|
||||
PIPELINE_VERSIONS_LIST_MAP.get(req.query['resource_key.id']) || [];
|
||||
|
||||
if (versions.length === 0) {
|
||||
const pipeline = fixedData.pipelines.find(p => p.id === req.query['resource_key.id']);
|
||||
|
||||
if (pipeline == null || !pipeline.default_version) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Default version list is pipeline with single default version.
|
||||
const pipeline_versions_list_response: ApiListPipelineVersionsResponse = {
|
||||
total_size: 1,
|
||||
versions: [pipeline.default_version],
|
||||
};
|
||||
res.json(pipeline_versions_list_response);
|
||||
}
|
||||
|
||||
const start = req.query.page_token ? +req.query.page_token : 0;
|
||||
const end = start + (+req.query.page_size || 20);
|
||||
response.versions = versions.slice(start, end);
|
||||
|
||||
if (end < versions.length) {
|
||||
response.next_page_token = end + '';
|
||||
}
|
||||
|
||||
res.json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8153,9 +8153,9 @@
|
|||
}
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.6.0.tgz",
|
||||
"integrity": "sha512-EDBMEWK8IVpNF7B7C1knb0lLB4Si9RWte/YTEi6CqmqUK5CYCoecwOOG9pEijU/H6s3u0drUxH5sKT07FCgFIg==",
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.7.0.tgz",
|
||||
"integrity": "sha512-8oOfBG51v8aN9D8eehwzgnEETf9Lxv/3dZyPZuar1JAp9OK0I9d7Y2MR6TEQyj/E/iN1kCIeYaCI445s5C9RDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
|
|
@ -8211,9 +8211,9 @@
|
|||
}
|
||||
},
|
||||
"@jest/types": {
|
||||
"version": "27.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.1.1.tgz",
|
||||
"integrity": "sha512-yqJPDDseb0mXgKqmNqypCsb85C22K1aY5+LUxh7syIM9n/b0AsaltxNy+o6tt29VcfGDpYEve175bm3uOhcehA==",
|
||||
"version": "27.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.4.tgz",
|
||||
"integrity": "sha512-IDO2ezTxeMvQAHxzG/ZvEyA47q0aVfzT95rGFl7bZs/Go0aIucvfDbS2rmnoEdXxlLQhcolmoG/wvL/uKx4tKA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
|
|
@ -8241,6 +8241,12 @@
|
|||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
|
|
@ -8293,13 +8299,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
"version": "27.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.2.0.tgz",
|
||||
"integrity": "sha512-KyJdmgBkMscLqo8A7K77omgLx5PWPiXJswtTtFV7XgVZv2+qPk6UivpXXO+5k6ZEbWIbLoKdx1pZ6ldINzbwTA==",
|
||||
"version": "27.2.4",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.2.4.tgz",
|
||||
"integrity": "sha512-NUjw22WJHldzxyps2YjLZkUj6q1HvjqFezkB9Y2cklN8NtVZN/kZEXGZdFw4uny3oENzV5EEMESrkI0YDUH8vg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.1",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"@jest/types": "^27.2.4",
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^17.0.1"
|
||||
},
|
||||
|
|
@ -11643,12 +11649,6 @@
|
|||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -16769,16 +16769,6 @@
|
|||
"vary": "~1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-types": "~2.1.24",
|
||||
"negotiator": "0.6.2"
|
||||
}
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
|
|
@ -16794,33 +16784,6 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.43.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.26",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.43.0"
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
|
||||
"dev": true
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
|
|
@ -16838,12 +16801,6 @@
|
|||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -17219,18 +17176,6 @@
|
|||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -17429,9 +17374,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"dev": true
|
||||
},
|
||||
"fragment-cache": {
|
||||
|
|
@ -27393,12 +27338,12 @@
|
|||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"forwarded": "~0.1.2",
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
}
|
||||
},
|
||||
|
|
@ -27646,12 +27591,6 @@
|
|||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -29480,6 +29419,13 @@
|
|||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
|
|
@ -30115,12 +30061,6 @@
|
|||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -30206,14 +30146,6 @@
|
|||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.17.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"set-blocking": {
|
||||
|
|
@ -31969,31 +31901,30 @@
|
|||
}
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "npm:@tailwindcss/postcss7-compat@2.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.14.tgz",
|
||||
"integrity": "sha512-vtzYILqywIY1GWELwHVF7goPhaJpm/1P5kJZ0Kx8lNlarALTFEWwIWCM6MxQ7pXzDWa1eUeozVJVeqfOBXKwXg==",
|
||||
"version": "npm:@tailwindcss/postcss7-compat@2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.7.tgz",
|
||||
"integrity": "sha512-1QkWUEeLV1AoNipMCE6IlL7XYScGb+DAzaXy35ooMDvl0G8kCMHBNqGxyVAnTcK8gyJNUzkKXExkUnbjAndd/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^5.0.1",
|
||||
"arg": "^5.0.0",
|
||||
"autoprefixer": "^9",
|
||||
"bytes": "^3.0.0",
|
||||
"chalk": "^4.1.2",
|
||||
"chalk": "^4.1.1",
|
||||
"chokidar": "^3.5.2",
|
||||
"color": "^4.0.1",
|
||||
"cosmiconfig": "^7.0.1",
|
||||
"color": "^3.2.0",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"detective": "^5.2.0",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.2.7",
|
||||
"fs-extra": "^10.0.0",
|
||||
"glob-parent": "^6.0.1",
|
||||
"glob-parent": "^6.0.0",
|
||||
"html-tags": "^3.1.0",
|
||||
"is-color-stop": "^1.1.0",
|
||||
"is-glob": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.topath": "^4.5.2",
|
||||
"modern-normalize": "^1.1.0",
|
||||
"node-emoji": "^1.11.0",
|
||||
"node-emoji": "^1.8.1",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"postcss": "^7",
|
||||
|
|
@ -32046,13 +31977,30 @@
|
|||
}
|
||||
},
|
||||
"color": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz",
|
||||
"integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-convert": "^1.9.3",
|
||||
"color-string": "^1.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
|
|
@ -32080,19 +32028,6 @@
|
|||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
|
||||
|
|
@ -32325,6 +32260,12 @@
|
|||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -33052,18 +32993,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"mime-db": {
|
||||
"version": "1.43.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
|
||||
"version": "1.49.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
|
||||
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.26",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||
"version": "2.1.32",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
|
||||
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.43.0"
|
||||
"mime-db": "1.49.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33597,11 +33538,6 @@
|
|||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"uuid-browser": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid-browser/-/uuid-browser-3.1.0.tgz",
|
||||
|
|
@ -34618,6 +34554,12 @@
|
|||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
|
||||
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2021 The Kubeflow Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { ApiPipeline, ApiPipelineVersion } from 'src/apis/pipeline/api';
|
||||
import { testBestPractices } from 'src/TestUtils';
|
||||
import { PipelineVersionCard } from './PipelineVersionCard';
|
||||
|
||||
const DEFAULT_VERSION_NAME = 'default version';
|
||||
const REVISION_NAME = 'revision';
|
||||
|
||||
const PIPELINE_ID_V2_PYTHON_TWO_STEPS = '8fbe3bd6-a01f-11e8-98d0-529269fb1460';
|
||||
const PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT: ApiPipelineVersion = {
|
||||
created_at: new Date('2021-11-24T20:58:23.000Z'),
|
||||
id: PIPELINE_ID_V2_PYTHON_TWO_STEPS,
|
||||
name: DEFAULT_VERSION_NAME,
|
||||
description: 'This is default version description.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'message',
|
||||
},
|
||||
],
|
||||
};
|
||||
const PIPELINE_ID_V2_PYTHON_TWO_STEPS_REV = '9fbe3bd6-a01f-11e8-98d0-529269fb1460';
|
||||
const PIPELINE_V2_PYTHON_TWO_STEPS_REV: ApiPipelineVersion = {
|
||||
created_at: new Date('2021-12-24T20:58:23.000Z'),
|
||||
id: PIPELINE_ID_V2_PYTHON_TWO_STEPS_REV,
|
||||
name: REVISION_NAME,
|
||||
description: 'This is version description.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'revision-message',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const V2_TWO_STEPS_VERSION_LIST: ApiPipelineVersion[] = [
|
||||
PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
PIPELINE_V2_PYTHON_TWO_STEPS_REV,
|
||||
];
|
||||
const PIPELINE_V2_PYTHON_TWO_STEPS: ApiPipeline = {
|
||||
...PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
description: 'This is pipeline level description.',
|
||||
name: 'v2_lightweight_python_functions_pipeline',
|
||||
default_version: PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT,
|
||||
};
|
||||
testBestPractices();
|
||||
describe('PipelineVersionCard', () => {
|
||||
it('makes Show Summary button visible by default', async () => {
|
||||
render(
|
||||
<PipelineVersionCard
|
||||
apiPipeline={PIPELINE_V2_PYTHON_TWO_STEPS}
|
||||
selectedVersion={PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT}
|
||||
versions={V2_TWO_STEPS_VERSION_LIST}
|
||||
handleVersionSelected={versionId => {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineVersionCard>,
|
||||
);
|
||||
|
||||
screen.getByText('Show Summary');
|
||||
expect(screen.queryByText('Hide')).toBeNull();
|
||||
});
|
||||
|
||||
it('clicks to open and hide Summary', async () => {
|
||||
render(
|
||||
<PipelineVersionCard
|
||||
apiPipeline={PIPELINE_V2_PYTHON_TWO_STEPS}
|
||||
selectedVersion={PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT}
|
||||
versions={V2_TWO_STEPS_VERSION_LIST}
|
||||
handleVersionSelected={versionId => {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineVersionCard>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByText('Show Summary'));
|
||||
expect(screen.queryByText('Show Summary')).toBeNull();
|
||||
userEvent.click(screen.getByText('Hide'));
|
||||
screen.getByText('Show Summary');
|
||||
});
|
||||
|
||||
it('shows Summary and checks detail', async () => {
|
||||
render(
|
||||
<PipelineVersionCard
|
||||
apiPipeline={PIPELINE_V2_PYTHON_TWO_STEPS}
|
||||
selectedVersion={PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT}
|
||||
versions={V2_TWO_STEPS_VERSION_LIST}
|
||||
handleVersionSelected={versionId => {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineVersionCard>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByText('Show Summary'));
|
||||
|
||||
screen.getByText('Pipeline ID');
|
||||
screen.getByText(PIPELINE_ID_V2_PYTHON_TWO_STEPS);
|
||||
screen.getByText('Version');
|
||||
screen.getByText(DEFAULT_VERSION_NAME);
|
||||
screen.getByText('Version source');
|
||||
screen.getByText('Uploaded on');
|
||||
screen.getByText('Pipeline Description');
|
||||
screen.getByText('This is pipeline level description.');
|
||||
screen.getByText('Default Version Description');
|
||||
screen.getByText('This is default version description.');
|
||||
});
|
||||
|
||||
it('shows version list', async () => {
|
||||
const { getByRole } = render(
|
||||
<PipelineVersionCard
|
||||
apiPipeline={PIPELINE_V2_PYTHON_TWO_STEPS}
|
||||
selectedVersion={PIPELINE_V2_PYTHON_TWO_STEPS_DEFAULT}
|
||||
versions={V2_TWO_STEPS_VERSION_LIST}
|
||||
handleVersionSelected={versionId => {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineVersionCard>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByText('Show Summary'));
|
||||
|
||||
fireEvent.click(getByRole('button', { name: DEFAULT_VERSION_NAME }));
|
||||
fireEvent.click(getByRole('listbox'));
|
||||
getByRole('option', { name: REVISION_NAME });
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2021 The Kubeflow Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import React, { useState } from 'react';
|
||||
import { ApiPipeline, ApiPipelineVersion } from 'src/apis/pipeline';
|
||||
import { Description } from 'src/components/Description';
|
||||
import { commonCss } from 'src/Css';
|
||||
import { formatDateString } from 'src/lib/Utils';
|
||||
|
||||
interface PipelineVersionCardProps {
|
||||
apiPipeline: ApiPipeline | null;
|
||||
selectedVersion: ApiPipelineVersion | undefined;
|
||||
versions: ApiPipelineVersion[];
|
||||
handleVersionSelected: (versionId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export function PipelineVersionCard({
|
||||
apiPipeline,
|
||||
selectedVersion,
|
||||
versions,
|
||||
handleVersionSelected,
|
||||
}: PipelineVersionCardProps) {
|
||||
const [summaryShown, setSummaryShown] = useState(false);
|
||||
|
||||
const createVersionUrl = () => {
|
||||
return selectedVersion?.code_source_url;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!!apiPipeline && summaryShown && (
|
||||
<Paper className='absolute bottom-3 left-20 p-5 w-136 z-20'>
|
||||
<div className='items-baseline flex justify-between'>
|
||||
<div className={commonCss.header}>Static Pipeline Summary</div>
|
||||
<Button onClick={() => setSummaryShown(false)} color='secondary'>
|
||||
Hide
|
||||
</Button>
|
||||
</div>
|
||||
<div className='text-gray-900 mt-5'>Pipeline ID</div>
|
||||
<div>{apiPipeline.id || 'Unable to obtain Pipeline ID'}</div>
|
||||
{versions.length > 0 && (
|
||||
<>
|
||||
<div className='text-gray-900 mt-5'>
|
||||
<form autoComplete='off'>
|
||||
<FormControl>
|
||||
<InputLabel>Version</InputLabel>
|
||||
<Select
|
||||
aria-label='version_selector'
|
||||
data-testid='version_selector'
|
||||
value={
|
||||
selectedVersion ? selectedVersion.id : apiPipeline.default_version!.id!
|
||||
}
|
||||
onChange={event => handleVersionSelected(event.target.value)}
|
||||
inputProps={{ id: 'version-selector', name: 'selectedVersion' }}
|
||||
>
|
||||
{versions.map((v, _) => (
|
||||
<MenuItem key={v.id} value={v.id}>
|
||||
{v.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</form>
|
||||
</div>
|
||||
<div className='text-blue-500 mt-5'>
|
||||
<a href={createVersionUrl()} target='_blank' rel='noopener noreferrer'>
|
||||
Version source
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className='text-gray-900 mt-5'>Uploaded on</div>
|
||||
<div>
|
||||
{selectedVersion
|
||||
? formatDateString(selectedVersion.created_at)
|
||||
: formatDateString(apiPipeline.created_at)}
|
||||
</div>
|
||||
|
||||
<div className='text-gray-900 mt-5'>Pipeline Description</div>
|
||||
<Description description={apiPipeline.description || 'empty pipeline description'} />
|
||||
|
||||
{/* selectedVersion is always populated by either selected or pipeline default version if it exists */}
|
||||
{selectedVersion && selectedVersion.description ? (
|
||||
<>
|
||||
<div className='text-gray-900 mt-5'>
|
||||
{selectedVersion.id === apiPipeline.default_version?.id
|
||||
? 'Default Version Description'
|
||||
: 'Version Description'}
|
||||
</div>
|
||||
<Description description={selectedVersion.description} />
|
||||
</>
|
||||
) : null}
|
||||
</Paper>
|
||||
)}
|
||||
{!summaryShown && (
|
||||
<div className='flex absolute bottom-5 left-10 pb-5 pl-10 bg-transparent z-20'>
|
||||
<Button onClick={() => setSummaryShown(!summaryShown)} color='secondary'>
|
||||
Show Summary
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -156,17 +156,21 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {
|
|||
templateString={templateString}
|
||||
pipelineFlowElements={graphV2!}
|
||||
setSubDagLayers={setLayers}
|
||||
apiPipeline={pipeline}
|
||||
selectedVersion={selectedVersion}
|
||||
versions={versions}
|
||||
handleVersionSelected={this.handleVersionSelected.bind(this)}
|
||||
/>
|
||||
)}
|
||||
{!showV2Pipeline && (
|
||||
<PipelineDetailsV1
|
||||
pipeline={pipeline}
|
||||
selectedVersion={selectedVersion}
|
||||
versions={versions}
|
||||
templateString={templateString}
|
||||
graph={graph}
|
||||
reducedGraph={reducedGraph}
|
||||
updateBanner={this.props.updateBanner}
|
||||
selectedVersion={selectedVersion}
|
||||
versions={versions}
|
||||
handleVersionSelected={this.handleVersionSelected.bind(this)}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -319,32 +323,7 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {
|
|||
|
||||
this.props.updateToolbar({ breadcrumbs, actions: toolbarActions, pageTitle });
|
||||
|
||||
let graph: graphlib.Graph | null = null;
|
||||
let reducedGraph: graphlib.Graph | null | undefined = null;
|
||||
let graphV2: PipelineFlowElement[] = [];
|
||||
if (templateString) {
|
||||
try {
|
||||
const template = JsYaml.safeLoad(templateString);
|
||||
if (WorkflowUtils.isArgoWorkflowTemplate(template)) {
|
||||
graph = StaticGraphParser.createGraph(template!);
|
||||
|
||||
reducedGraph = graph ? transitiveReduction(graph) : undefined;
|
||||
if (graph && reducedGraph && compareGraphEdges(graph, reducedGraph)) {
|
||||
reducedGraph = undefined; // disable reduction switch
|
||||
}
|
||||
} else if (isFeatureEnabled(FeatureKey.V2)) {
|
||||
const pipelineSpec = WorkflowUtils.convertJsonToV2PipelineSpec(templateString);
|
||||
graphV2 = convertFlowElements(pipelineSpec);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unable to convert string response from server to Argo workflow template' +
|
||||
': https://argoproj.github.io/argo-workflows/workflow-templates/',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
await this.showPageError('Error: failed to generate Pipeline graph.', err);
|
||||
}
|
||||
}
|
||||
const [graph, reducedGraph, graphV2] = await this._createGraph(templateString);
|
||||
|
||||
if (isFeatureEnabled(FeatureKey.V2) && graphV2.length > 0) {
|
||||
this.setStateSafe({
|
||||
|
|
@ -379,17 +358,27 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {
|
|||
this.props.history.replace({
|
||||
pathname: `/pipelines/details/${this.state.pipeline.id}/version/${versionId}`,
|
||||
});
|
||||
const graph = await this._createGraph(selectedVersionPipelineTemplate);
|
||||
let reducedGraph = graph ? transitiveReduction(graph) : undefined;
|
||||
if (graph && reducedGraph && compareGraphEdges(graph, reducedGraph)) {
|
||||
reducedGraph = undefined; // disable reduction switch
|
||||
|
||||
const [graph, reducedGraph, graphV2] = await this._createGraph(
|
||||
selectedVersionPipelineTemplate,
|
||||
);
|
||||
if (isFeatureEnabled(FeatureKey.V2) && graphV2.length > 0) {
|
||||
this.setStateSafe({
|
||||
graph: undefined,
|
||||
reducedGraph: undefined,
|
||||
graphV2,
|
||||
selectedVersion,
|
||||
templateString: selectedVersionPipelineTemplate,
|
||||
});
|
||||
} else {
|
||||
this.setStateSafe({
|
||||
graph,
|
||||
reducedGraph,
|
||||
graphV2: undefined,
|
||||
selectedVersion,
|
||||
templateString: selectedVersionPipelineTemplate,
|
||||
});
|
||||
}
|
||||
this.setStateSafe({
|
||||
graph,
|
||||
reducedGraph,
|
||||
selectedVersion,
|
||||
templateString: selectedVersionPipelineTemplate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -409,16 +398,38 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {
|
|||
return '';
|
||||
}
|
||||
|
||||
private async _createGraph(templateString: string): Promise<dagre.graphlib.Graph | null> {
|
||||
private async _createGraph(
|
||||
templateString: string,
|
||||
): Promise<
|
||||
[dagre.graphlib.Graph | null, dagre.graphlib.Graph | null | undefined, PipelineFlowElement[]]
|
||||
> {
|
||||
let graph: graphlib.Graph | null = null;
|
||||
let reducedGraph: graphlib.Graph | null | undefined = null;
|
||||
let graphV2: PipelineFlowElement[] = [];
|
||||
if (templateString) {
|
||||
try {
|
||||
const template = JsYaml.safeLoad(templateString);
|
||||
return StaticGraphParser.createGraph(template!);
|
||||
if (WorkflowUtils.isArgoWorkflowTemplate(template)) {
|
||||
graph = StaticGraphParser.createGraph(template!);
|
||||
|
||||
reducedGraph = graph ? transitiveReduction(graph) : undefined;
|
||||
if (graph && reducedGraph && compareGraphEdges(graph, reducedGraph)) {
|
||||
reducedGraph = undefined; // disable reduction switch
|
||||
}
|
||||
} else if (isFeatureEnabled(FeatureKey.V2)) {
|
||||
const pipelineSpec = WorkflowUtils.convertJsonToV2PipelineSpec(templateString);
|
||||
graphV2 = convertFlowElements(pipelineSpec);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unable to convert string response from server to Argo workflow template' +
|
||||
': https://argoproj.github.io/argo-workflows/workflow-templates/',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
await this.showPageError('Error: failed to generate Pipeline graph.', err);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return [graph, reducedGraph, graphV2];
|
||||
}
|
||||
|
||||
private _deleteCallback(_: string[], success: boolean): void {
|
||||
|
|
|
|||
|
|
@ -88,21 +88,21 @@ export interface PipelineDetailsV1Props {
|
|||
graph: dagre.graphlib.Graph | null;
|
||||
reducedGraph: dagre.graphlib.Graph | null;
|
||||
pipeline: ApiPipeline | null;
|
||||
selectedVersion: ApiPipelineVersion | undefined;
|
||||
versions: ApiPipelineVersion[];
|
||||
templateString?: string;
|
||||
updateBanner: (bannerProps: BannerProps) => void;
|
||||
selectedVersion: ApiPipelineVersion | undefined;
|
||||
versions: ApiPipelineVersion[];
|
||||
handleVersionSelected: (versionId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const PipelineDetailsV1: React.FC<PipelineDetailsV1Props> = ({
|
||||
pipeline,
|
||||
selectedVersion,
|
||||
versions,
|
||||
graph,
|
||||
reducedGraph,
|
||||
templateString,
|
||||
updateBanner,
|
||||
selectedVersion,
|
||||
versions,
|
||||
handleVersionSelected,
|
||||
}: PipelineDetailsV1Props) => {
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
|
|
|||
|
|
@ -33,13 +33,40 @@ describe('PipelineDetailsV2', () => {
|
|||
<CommonTestWrapper>
|
||||
<PipelineDetailsV2
|
||||
pipelineFlowElements={[]}
|
||||
setSubDagLayers={layers => {}}
|
||||
setSubDagLayers={function(layers: string[]): void {
|
||||
return;
|
||||
}}
|
||||
apiPipeline={null}
|
||||
selectedVersion={undefined}
|
||||
versions={[]}
|
||||
handleVersionSelected={function(versionId: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineDetailsV2>
|
||||
</CommonTestWrapper>,
|
||||
);
|
||||
expect(screen.getByTestId('StaticCanvas')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('Render summary card', async () => {
|
||||
render(
|
||||
<CommonTestWrapper>
|
||||
<PipelineDetailsV2
|
||||
pipelineFlowElements={[]}
|
||||
setSubDagLayers={function(layers: string[]): void {
|
||||
return;
|
||||
}}
|
||||
apiPipeline={null}
|
||||
selectedVersion={undefined}
|
||||
versions={[]}
|
||||
handleVersionSelected={function(versionId: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineDetailsV2>
|
||||
</CommonTestWrapper>,
|
||||
);
|
||||
userEvent.click(screen.getByText('Show Summary'));
|
||||
});
|
||||
it('Render Execution node', async () => {
|
||||
render(
|
||||
<CommonTestWrapper>
|
||||
|
|
@ -55,6 +82,12 @@ describe('PipelineDetailsV2', () => {
|
|||
},
|
||||
]}
|
||||
setSubDagLayers={layers => {}}
|
||||
apiPipeline={null}
|
||||
selectedVersion={undefined}
|
||||
versions={[]}
|
||||
handleVersionSelected={function(versionId: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineDetailsV2>
|
||||
</CommonTestWrapper>,
|
||||
);
|
||||
|
|
@ -78,6 +111,12 @@ describe('PipelineDetailsV2', () => {
|
|||
},
|
||||
]}
|
||||
setSubDagLayers={layers => {}}
|
||||
apiPipeline={null}
|
||||
selectedVersion={undefined}
|
||||
versions={[]}
|
||||
handleVersionSelected={function(versionId: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}}
|
||||
></PipelineDetailsV2>
|
||||
</CommonTestWrapper>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@
|
|||
*/
|
||||
import React, { useState } from 'react';
|
||||
import { Elements, FlowElement } from 'react-flow-renderer';
|
||||
import { ApiPipeline, ApiPipelineVersion } from 'src/apis/pipeline';
|
||||
import MD2Tabs from 'src/atoms/MD2Tabs';
|
||||
import Editor from 'src/components/Editor';
|
||||
import { FlowElementDataBase } from 'src/components/graph/Constants';
|
||||
import { PipelineVersionCard } from 'src/components/navigators/PipelineVersionCard';
|
||||
import SidePanel from 'src/components/SidePanel';
|
||||
import { StaticNodeDetailsV2 } from 'src/components/tabs/StaticNodeDetailsV2';
|
||||
import { isSafari } from 'src/lib/Utils';
|
||||
|
|
@ -31,12 +33,20 @@ interface PipelineDetailsV2Props {
|
|||
templateString?: string;
|
||||
pipelineFlowElements: PipelineFlowElement[];
|
||||
setSubDagLayers: (layers: string[]) => void;
|
||||
apiPipeline: ApiPipeline | null;
|
||||
selectedVersion: ApiPipelineVersion | undefined;
|
||||
versions: ApiPipelineVersion[];
|
||||
handleVersionSelected: (versionId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
function PipelineDetailsV2({
|
||||
templateString,
|
||||
pipelineFlowElements,
|
||||
setSubDagLayers,
|
||||
apiPipeline,
|
||||
selectedVersion,
|
||||
versions,
|
||||
handleVersionSelected,
|
||||
}: PipelineDetailsV2Props) {
|
||||
const [layers, setLayers] = useState(['root']);
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
|
@ -79,6 +89,12 @@ function PipelineDetailsV2({
|
|||
elements={pipelineFlowElements}
|
||||
onSelectionChange={onSelectionChange}
|
||||
></StaticCanvas>
|
||||
<PipelineVersionCard
|
||||
apiPipeline={apiPipeline}
|
||||
selectedVersion={selectedVersion}
|
||||
versions={versions}
|
||||
handleVersionSelected={handleVersionSelected}
|
||||
/>
|
||||
{templateString && (
|
||||
<div className='z-20'>
|
||||
<SidePanel
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ module.exports = {
|
|||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {
|
||||
spacing: {
|
||||
'112': '28rem',
|
||||
'136': '34rem',
|
||||
},
|
||||
// https://tailwindcss.com/docs/customizing-colors
|
||||
colors: {
|
||||
// https://material.io/resources/color
|
||||
|
|
|
|||
Loading…
Reference in New Issue