/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 { FunctionComponent, useState, useEffect, ChangeEvent } from 'react'; import { t } from '@apache-superset/core/translation'; import { styled } from '@apache-superset/core/theme'; import { useSingleViewResource } from 'src/views/CRUD/hooks'; import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { Typography } from '@superset-ui/core/components/Typography'; import { Input, Modal } from '@superset-ui/core/components'; import withToasts from 'src/components/MessageToasts/withToasts'; import { OnlyKeyWithType } from 'src/utils/types'; import { AnnotationLayerObject } from './types'; type AnnotationLayerObjectStringKeys = keyof Pick< AnnotationLayerObject, OnlyKeyWithType >; interface AnnotationLayerModalProps { addDangerToast: (msg: string) => void; addSuccessToast: (msg: string) => void; layer?: AnnotationLayerObject | null; onLayerAdd?: (layer?: AnnotationLayerObject) => void; onHide: () => void; show: boolean; } const StyledAnnotationLayerTitle = styled.div` margin: ${({ theme }) => theme.sizeUnit * 2}px auto ${({ theme }) => theme.sizeUnit * 4}px auto; `; const LayerContainer = styled.div` margin-bottom: ${({ theme }) => theme.sizeUnit * 10}px; .control-label { margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px; } .required { margin-left: ${({ theme }) => theme.sizeUnit / 2}px; color: ${({ theme }) => theme.colorError}; } textarea, input[type='text'] { padding: ${({ theme }) => theme.sizeUnit * 1.5}px ${({ theme }) => theme.sizeUnit * 2}px; border: 1px solid ${({ theme }) => theme.colorBorder}; border-radius: ${({ theme }) => theme.borderRadius}px; width: 50%; } input, textarea { flex: 1 1 auto; } textarea { width: 100%; height: 160px; resize: none; } input::placeholder, textarea::placeholder { color: ${({ theme }) => theme.colorTextPlaceholder}; } `; const AnnotationLayerModal: FunctionComponent = ({ addDangerToast, addSuccessToast, onLayerAdd, onHide, show, layer = null, }) => { const [disableSave, setDisableSave] = useState(true); const [currentLayer, setCurrentLayer] = useState(); const [isHidden, setIsHidden] = useState(true); const isEditMode = layer !== null; // annotation layer fetch logic const { state: { loading, resource }, fetchResource, createResource, updateResource, } = useSingleViewResource( 'annotation_layer', t('annotation_layer'), addDangerToast, ); const resetLayer = () => { // Reset layer setCurrentLayer({ name: '', descr: '', }); }; // Functions const hide = () => { setIsHidden(true); // Reset layer resetLayer(); onHide(); }; const onSave = () => { if (isEditMode) { // Edit if (currentLayer?.id) { const update_id = currentLayer.id; delete currentLayer.id; delete currentLayer.created_by; updateResource(update_id, currentLayer).then(response => { if (!response) { return; } hide(); addSuccessToast(t('Annotation template updated')); }); } } else if (currentLayer) { // Create createResource(currentLayer).then(response => { if (!response) { return; } if (onLayerAdd) { onLayerAdd(response); } hide(); addSuccessToast(t('Annotation template created')); }); } }; const onTextChange = ( event: ChangeEvent | ChangeEvent, ) => { const { target } = event; const data = { ...currentLayer, name: currentLayer ? currentLayer.name : '', descr: currentLayer ? currentLayer.descr : '', }; data[target.name as AnnotationLayerObjectStringKeys] = target.value; setCurrentLayer(data); }; const validate = () => { if (currentLayer?.name?.length) { setDisableSave(false); } else { setDisableSave(true); } }; // Initialize useEffect(() => { if ( isEditMode && (!currentLayer?.id || (layer && layer.id !== currentLayer.id) || (isHidden && show)) ) { if (show && layer && layer.id !== null && !loading) { const id = layer.id || 0; fetchResource(id); } } else if ( !isEditMode && (!currentLayer || currentLayer.id || (isHidden && show)) ) { // Reset layer resetLayer(); } }, [layer, show]); useEffect(() => { if (resource) { setCurrentLayer(resource); } }, [resource]); // Validation useEffect(() => { validate(); }, [ currentLayer ? currentLayer.name : '', currentLayer ? currentLayer.descr : '', ]); // Show/hide if (isHidden && show) { setIsHidden(false); } return ( } > {t('General information')}
{t('Annotation layer name')} *
{t('description')}
); }; export default withToasts(AnnotationLayerModal);