@finos/legend-application-studio
Version:
Legend Studio application core
176 lines • 18.6 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* 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
*
* http://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 { observer } from 'mobx-react-lite';
import { useEditorStore } from '../EditorStoreProvider.js';
import { forwardRef, useEffect, useState } from 'react';
import { PanelFormSection, PanelFormValidatedTextField, PanelHeader, PanelContent, Panel, clsx, Dialog, CheckCircleIcon, ModalBody, ModalFooterButton, ModalFooter, ModalHeader, Modal, ModalTitle, PanelDivider, CircleNotchIcon, PauseCircleIcon, TimesCircleIcon, BanIcon, QuestionCircleIcon, MenuContent, MenuContentItem, ContextMenu, PanelForm, PanelFormBooleanField, TrashIcon, PlusIcon, CogIcon, } from '@finos/legend-art';
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
import { CodeEditor } from '@finos/legend-lego/code-editor';
import { BuildPhaseStatus, LogType, MetadataRequestOptions, } from '@finos/legend-graph';
import { flowResult } from 'mobx';
const BuildOverrideEditor = observer((props) => {
const { buildOverrides, onUpdate } = props;
const [newKey, setNewKey] = useState('');
const [newValue, setNewValue] = useState('');
const handleAddOverride = () => {
if (newKey.trim() && newValue.trim()) {
onUpdate({
...buildOverrides,
[newKey.trim()]: newValue.trim(),
});
setNewKey('');
setNewValue('');
}
};
const handleDeleteOverride = (key) => {
const updatedOverrides = { ...buildOverrides };
delete updatedOverrides[key];
onUpdate(updatedOverrides);
};
const handleUpdateOverride = (key, value) => {
onUpdate({
...buildOverrides,
[key]: value,
});
};
return (_jsxs("div", { className: "build-override-editor", children: [_jsxs("div", { className: "build-override-editor__header", children: [_jsx("span", { className: "build-override-editor__title", children: "Build Overrides" }), _jsx("span", { className: "build-override-editor__description", children: "Key-value pairs to override build parameters" })] }), _jsx("div", { className: "build-override-editor__list", children: Object.entries(buildOverrides).map(([key, value]) => (_jsxs("div", { className: "build-override-editor__item", children: [_jsxs("div", { className: "build-override-editor__item__fields", children: [_jsx(PanelFormValidatedTextField, { name: `override-key-${key}`, prompt: "Key", value: key, update: (val) => {
if (val && val !== key && val.trim()) {
const newOverrides = { ...buildOverrides };
delete newOverrides[key];
newOverrides[val.trim()] = value;
onUpdate(newOverrides);
}
} }), _jsx(PanelFormValidatedTextField, { name: `override-value-${key}`, prompt: "Value", value: value, update: (val) => handleUpdateOverride(key, val ?? '') })] }), _jsx("button", { className: "build-override-editor__delete-btn", onClick: () => handleDeleteOverride(key), title: "Delete override", children: _jsx(TrashIcon, {}) })] }, key))) }), _jsxs("div", { className: "build-override-editor__add", children: [_jsx("div", { className: "build-override-editor__add__title", children: "Add New Override" }), _jsxs("div", { className: "build-override-editor__add__fields", children: [_jsx(PanelFormValidatedTextField, { name: "new-override-key", prompt: "Key", value: newKey, update: (val) => setNewKey(val ?? '') }), _jsx(PanelFormValidatedTextField, { name: "new-override-value", prompt: "Value", value: newValue, update: (val) => setNewValue(val ?? '') }), _jsx("button", { className: "build-override-editor__add-btn", onClick: handleAddOverride, disabled: !newKey.trim() || !newValue.trim(), title: "Add override", children: _jsx(PlusIcon, {}) })] })] })] }));
});
const DevMetadataOptionsModal = observer((props) => {
const { isOpen, onClose, options, onSave } = props;
const editorStore = useEditorStore();
const applicationStore = editorStore.applicationStore;
const [includeArtifacts, setIncludeArtifacts] = useState(options.includeArtifacts ?? false);
const [buildOverrides, setBuildOverrides] = useState(options.buildOverrides ?? {});
useEffect(() => {
if (isOpen) {
setIncludeArtifacts(options.includeArtifacts ?? false);
setBuildOverrides(options.buildOverrides ?? {});
}
}, [isOpen, options]);
const handleSave = () => {
const updatedOptions = new MetadataRequestOptions();
updatedOptions.includeArtifacts = includeArtifacts;
updatedOptions.buildOverrides =
Object.keys(buildOverrides).length > 0 ? buildOverrides : undefined;
onSave(updatedOptions);
onClose();
};
const handleCancel = () => {
setIncludeArtifacts(options.includeArtifacts ?? false);
setBuildOverrides(options.buildOverrides ?? {});
onClose();
};
if (!isOpen) {
return null;
}
return (_jsx(Dialog, { open: isOpen, onClose: handleCancel, classes: {
root: 'editor-modal__root-container',
container: 'editor-modal__container',
paper: 'editor-modal__content',
}, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: "editor-modal dev-metadata-options-modal", children: [_jsx(ModalHeader, { children: _jsx(ModalTitle, { title: "Deployment Configuration" }) }), _jsx(ModalBody, { className: "dev-metadata-options-modal__body", children: _jsxs(PanelForm, { children: [_jsxs(PanelFormSection, { children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Artifact Settings" }), _jsx(PanelFormBooleanField, { name: "includeArtifacts", prompt: "Include Artifacts", value: includeArtifacts, update: (val) => setIncludeArtifacts(Boolean(val)), isReadOnly: false }), _jsx("div", { className: "panel__content__form__section__desc", children: "When enabled, includes generated artifacts in the deployment" })] }), _jsx(PanelFormSection, { children: _jsx(BuildOverrideEditor, { buildOverrides: buildOverrides, onUpdate: setBuildOverrides }) })] }) }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { text: "Cancel", onClick: handleCancel, type: "secondary" }), _jsx(ModalFooterButton, { text: "Save Configuration", onClick: handleSave, type: "primary" })] })] }) }));
});
const getPhaseStatusIcon = (status) => {
switch (status) {
case BuildPhaseStatus.NOT_STARTED:
return (_jsx("div", { title: "Phase not started", className: "deployment-phase__status__indicator deployment-phase__status__indicator--not-started", children: _jsx(PauseCircleIcon, {}) }));
case BuildPhaseStatus.IN_PROGRESS:
return (_jsx("div", { title: "Phase in progress", className: "deployment-phase__status__indicator deployment-phase__status__indicator--in-progress", children: _jsx(CircleNotchIcon, {}) }));
case BuildPhaseStatus.SUCCESS:
return (_jsx("div", { title: "Phase succeeded", className: "deployment-phase__status__indicator deployment-phase__status__indicator--success", children: _jsx(CheckCircleIcon, {}) }));
case BuildPhaseStatus.FAIL:
return (_jsx("div", { title: "Phase failed", className: "deployment-phase__status__indicator deployment-phase__status__indicator--failed", children: _jsx(TimesCircleIcon, {}) }));
case BuildPhaseStatus.SKIPPED:
return (_jsx("div", { title: "Phase skipped", className: "deployment-phase__status__indicator deployment-phase__status__indicator--skipped", children: _jsx(BanIcon, {}) }));
default:
return (_jsx("div", { title: "Phase status unknown", className: "deployment-phase__status__indicator deployment-phase__status__indicator--unknown", children: _jsx(QuestionCircleIcon, {}) }));
}
};
const PhaseLogsViewer = observer((props) => {
const { phase, onClose } = props;
const editorStore = useEditorStore();
const applicationStore = editorStore.applicationStore;
const formatLogs = (logs) => {
return logs
.map((log) => {
const prefix = log.logType ? `[${log.logType}]` : '';
const title = log.title ? `${log.title}: ` : '';
return `${prefix} ${title}${log.log}`;
})
.join('\n');
};
const logs = phase.logs ? formatLogs(phase.logs) : 'No logs available';
return (_jsx(Dialog, { open: true, onClose: onClose, classes: {
root: 'editor-modal__root-container',
container: 'editor-modal__container',
paper: 'editor-modal__content',
}, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: "editor-modal", children: [_jsx(ModalHeader, { children: _jsx(ModalTitle, { title: `Logs for Phase: ${phase.phase}` }) }), _jsx(ModalBody, { children: _jsx(CodeEditor, { inputValue: logs, isReadOnly: true, language: CODE_EDITOR_LANGUAGE.TEXT, extraEditorOptions: {
wordWrap: 'on',
} }) }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { text: "Close", onClick: onClose, type: "secondary" }) })] }) }));
});
const PhaseContextMenu = observer(forwardRef(function PhaseContextMenu(props, ref) {
const { phase, onViewLogs } = props;
const hasLogs = phase.logs && phase.logs.length > 0;
return (_jsx(MenuContent, { children: hasLogs && (_jsxs(MenuContentItem, { onClick: onViewLogs, children: ["View Logs (", phase.logs?.length, " entries)"] })) }));
}));
const DeploymentPhaseNode = observer((props) => {
const { phase, onViewLogs } = props;
const statusIcon = getPhaseStatusIcon(phase.status);
const hasLogs = phase.logs && phase.logs.length > 0;
const errorLogs = phase.logs?.filter((log) => log.logType === LogType.ERROR).length ?? 0;
const warnLogs = phase.logs?.filter((log) => log.logType === LogType.WARN).length ?? 0;
return (_jsx(ContextMenu, { content: _jsx(PhaseContextMenu, { phase: phase, onViewLogs: () => onViewLogs(phase) }), menuProps: { elevation: 7 }, children: _jsxs("div", { className: "deployment-phase__node", children: [_jsx("div", { className: "deployment-phase__node__icon", children: statusIcon }), _jsxs("div", { className: "deployment-phase__node__content", children: [_jsx("div", { className: "deployment-phase__node__title", children: phase.phase }), _jsxs("div", { className: "deployment-phase__node__details", children: [_jsx("span", { className: `deployment-phase__status deployment-phase__status--${phase.status.toLowerCase()}`, children: phase.status }), phase.message && (_jsx("span", { className: "deployment-phase__message", children: phase.message })), hasLogs && (_jsxs("span", { className: "deployment-phase__logs-count", children: [errorLogs > 0 && (_jsxs("span", { className: "logs-count logs-count--error", children: [errorLogs, " errors"] })), warnLogs > 0 && (_jsxs("span", { className: "logs-count logs-count--warn", children: [warnLogs, " warnings"] })), _jsxs("span", { className: "logs-count logs-count--total", children: [phase.logs?.length, " logs"] })] }))] })] })] }) }));
});
const DeploymentStatusPanel = observer((props) => {
const { deploymentResponse } = props;
const [selectedPhaseForLogs, setSelectedPhaseForLogs] = useState(null);
const handleViewLogs = (phase) => {
setSelectedPhaseForLogs(phase);
};
const closeLogsViewer = () => {
setSelectedPhaseForLogs(null);
};
return (_jsxs("div", { className: "deployment-status-panel", children: [_jsxs("div", { className: "deployment-status-panel__header", children: [_jsx("div", { className: "deployment-status-panel__title", children: "Deployment Status" }), _jsxs("div", { className: `deployment-status-panel__final-status deployment-status-panel__final-status--${deploymentResponse.finalStatus.toLowerCase()}`, children: [getPhaseStatusIcon(deploymentResponse.finalStatus), _jsx("span", { children: deploymentResponse.finalStatus })] })] }), _jsx(PanelDivider, {}), _jsxs("div", { className: "deployment-status-panel__project-details", children: [_jsx("div", { className: "deployment-status-panel__section-title", children: "Project Details" }), _jsxs("div", { className: "deployment-status-panel__details-grid", children: [_jsxs("div", { className: "detail-item", children: [_jsx("span", { className: "detail-label", children: "Group ID:" }), _jsx("span", { className: "detail-value", children: deploymentResponse.projectDetails.groupId })] }), _jsxs("div", { className: "detail-item", children: [_jsx("span", { className: "detail-label", children: "Artifact ID:" }), _jsx("span", { className: "detail-value", children: deploymentResponse.projectDetails.artifactId })] }), _jsxs("div", { className: "detail-item", children: [_jsx("span", { className: "detail-label", children: "Version:" }), _jsx("span", { className: "detail-value", children: deploymentResponse.projectDetails.version })] })] })] }), _jsx(PanelDivider, {}), deploymentResponse.phaseStates &&
deploymentResponse.phaseStates.length > 0 && (_jsxs("div", { className: "deployment-status-panel__phases", children: [_jsxs("div", { className: "deployment-status-panel__section-title", children: ["Deployment Phases (", deploymentResponse.phaseStates.length, ")"] }), _jsx("div", { className: "deployment-phases-list", children: deploymentResponse.phaseStates.map((phase) => (_jsx(DeploymentPhaseNode, { phase: phase, onViewLogs: handleViewLogs }, `${phase.phase}`))) })] })), selectedPhaseForLogs && (_jsx(PhaseLogsViewer, { phase: selectedPhaseForLogs, onClose: closeLogsViewer }))] }));
});
export const DevMetadataPanel = observer(() => {
const editorStore = useEditorStore();
const devMetadataState = editorStore.devMetadataState;
const [isOptionsModalOpen, setIsOptionsModalOpen] = useState(false);
const handlePush = () => {
flowResult(devMetadataState.push()).catch(editorStore.applicationStore.alertUnhandledError);
};
const handleSaveOptions = (newOptions) => {
devMetadataState.setOptions(newOptions);
};
const isPushing = devMetadataState.pushState.isInProgress;
return (_jsxs(Panel, { children: [_jsx(PanelHeader, { className: "side-bar__header", children: _jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__label side-bar__header__title__content", children: "Push to Dev" }) }) }), _jsxs(PanelContent, { children: [_jsx(PanelFormSection, { children: _jsxs("div", { className: "dev-metadata-panel__project-info", children: [_jsx("div", { className: "dev-metadata-panel__info-header", children: "Project Information" }), _jsxs("div", { className: "dev-metadata-panel__info-content", children: [_jsxs("div", { className: "dev-metadata-panel__info-row", children: [_jsx("span", { className: "dev-metadata-panel__info-label", children: "Group ID:" }), _jsx("span", { className: "dev-metadata-panel__info-value", children: devMetadataState.projectGAV?.groupId ?? 'Not available' })] }), _jsxs("div", { className: "dev-metadata-panel__info-row", children: [_jsx("span", { className: "dev-metadata-panel__info-label", children: "Artifact ID:" }), _jsx("span", { className: "dev-metadata-panel__info-value", children: devMetadataState.projectGAV?.artifactId ?? 'Not available' })] })] })] }) }), _jsx(PanelDivider, {}), _jsx(PanelFormSection, { children: _jsxs("div", { className: "dev-metadata-panel__push-section", children: [_jsxs("div", { className: "dev-metadata-panel__push-header", children: [_jsxs("div", { className: "dev-metadata-panel__push-title-row", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Deploy Metadata" }), _jsx("button", { className: "dev-metadata-panel__settings-btn", onClick: () => setIsOptionsModalOpen(true), title: "Configure deployment options", disabled: isPushing, children: _jsx(CogIcon, {}) })] }), _jsx("div", { className: "dev-metadata-panel__push-description", children: isPushing
? 'Pushing metadata to dev environment...'
: 'Push current workspace metadata to dev' })] }), _jsx("button", { onClick: handlePush, type: "submit", className: clsx('btn btn--primary dev-metadata-panel__push-btn', {
'btn--loading': isPushing,
'btn--disabled': isPushing,
}), disabled: isPushing, title: isPushing
? 'Pushing metadata...'
: 'Push metadata to development environment', children: _jsxs("div", { className: "btn__content", children: [isPushing && (_jsx(CircleNotchIcon, { className: "dev-metadata-panel__push-btn__spinner" })), _jsx("div", { className: "btn__content__label", children: isPushing ? 'Pushing...' : 'Push to Dev' })] }) }), isPushing && (_jsx("div", { className: "dev-metadata-panel__loading-overlay", children: _jsxs("div", { className: "dev-metadata-panel__loading-content", children: [_jsx(CircleNotchIcon, { className: "dev-metadata-panel__loading-spinner" }), _jsx("div", { className: "dev-metadata-panel__loading-text", children: "Deploying metadata to development environment..." })] }) }))] }) }), _jsx(PanelFormSection, { children: devMetadataState.result && (_jsxs(_Fragment, { children: [_jsx(PanelDivider, {}), _jsx(DeploymentStatusPanel, { deploymentResponse: devMetadataState.result })] })) }), _jsx(DevMetadataOptionsModal, { isOpen: isOptionsModalOpen, onClose: () => setIsOptionsModalOpen(false), options: devMetadataState.options, onSave: handleSaveOptions })] })] }));
});
//# sourceMappingURL=DevMetadataPanel.js.map