UNPKG

@finos/legend-application-studio

Version:
176 lines 18.6 kB
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