import React, { useCallback, useEffect, useState } from 'react'; import { NavLink, useParams } from 'react-router-dom'; import Loader from '../common/components/Loader'; import { IRouteParams } from '../common/interfaces/IRouteParams'; import Repository from '../common/repository/repository'; import { AssetType, IAsset } from './interfaces/IAsset'; import styled from 'styled-components'; import config from '../config.json'; import Title from '../common/components/LayoutComponents/Title'; // import { Button, Col, Input, Row, UncontrolledAlert } from 'reactstrap'; import { Button, Col, Form, FormGroup, Input, Label, Row, UncontrolledAlert } from 'reactstrap'; import Asset from '../composition_riv/components/Asset'; import { IApiPostResponse } from '../common/interfaces/IApiPostResponse'; import { formatDateString } from '../common/utils/Date'; import AppRoutes from '../AppRoutes'; import { useTranslations } from '../i18n/useTranslations'; import * as Constants from "../common/constants/library-constants"; import * as Global_Constants from "../common/constants/shared-constants"; import * as Dam_Constants from "../digital_assets_management/constants/dam-constants"; const AssetContainer = styled.div` overflow-y: auto; height:100%; `; const AssetDiv = styled.div` display: flex; flex-direction: column; `; const AssetIllustration = styled.div` width: 100%; max-height: 270px; height: 270px; display: flex; justify-content: center; .sdc-asset { &.sdc-playlist { height: 100%; width: 100%; } } `; const AssetPropertyTitle = styled.div` width: 100%; font-weight: bold; justify-content: top; `; const AssetTagsContainer = styled.div` width: 100%; display: flex; width: 9rem; `; const TagContainer = styled.div.attrs({ 'data-testid': 'new-asset-tag-container', })` display: flex; align-items: center; width: 9rem; padding-bottom: 0.25rem; `; const ScrollableContainer = styled.div` max-height: 270px; overflow-y: auto; width: 100%; `; const TagsList = styled.div.attrs({ className: 'col-sm-9', })` display: flex; flex-wrap: wrap; `; const TagButton = styled.i` cursor: pointer; padding: 0.25rem 0.5rem; color: darkred; `; export const AssetDetails = () => { const i18n = useTranslations() const [changeNameErrorMessage, setChangeNameErrorMessage] = useState(''); const [tagsErrorMessage, setTagsErrorMessage] = useState(''); const [resultLoaded, setResultLoaded] = useState(false); const [assetVersions, setAssetVersions] = useState([] as IAsset[]); const [selectedAsset, setSelectedAsset] = useState(null as unknown as IAsset); const [selectedAssetIndex, setSelectedAssetIndex] = useState(0); const [selectedAssetRoute, setSelectedAssetRoute] = useState< string | undefined >(''); const [showEditNameForm, setShowEditNameForm] = useState(false); const [newAssetName, setNewAssetName] = useState(''); const [newTag, setNewTag] = useState(''); const [sequencesForScene, setSequencesForScene] = useState<IAsset[]>([]); const { id } = useParams<IRouteParams>(); const retrieveAssetVersions = useCallback(async () => { setResultLoaded(false); const assets = await Repository.getInstance().getAssetVersions( parseInt(id) ); setAssetVersions(assets); if ( assets !== null && assets[selectedAssetIndex] !== undefined && assets[selectedAssetIndex] !== null ) { setSelectedAsset(assets[selectedAssetIndex]); setSelectedAssetRoute(assetToRoute(assets[selectedAssetIndex])); } else { setSelectedAsset(null as unknown as IAsset); } setResultLoaded(true); }, [id, selectedAssetIndex]); useEffect(() => { retrieveAssetVersions(); }, [retrieveAssetVersions]); useEffect(() => { if (!selectedAsset) return; if (selectedAsset.file_type !== AssetType.Scene) return; const retrieveSequences = async () => { const sequenceNames = await Repository.getInstance().getSequencesUsingScene( selectedAsset.name ); setSequencesForScene(sequenceNames); }; retrieveSequences(); }, [selectedAsset]); function changeSelectedVersion(e: React.ChangeEvent<HTMLInputElement>) { const selectedId = e.target.value; const selectedIndex = assetVersions.findIndex((ast) => ast.id.toString() === selectedId); setSelectedAssetIndex(selectedIndex); } function toggleEditNameForm() { setShowEditNameForm(!showEditNameForm); } function changeNewAssetName(evt: React.ChangeEvent<HTMLInputElement>) { setNewAssetName(evt.target.value); } function changeNewTag(evt: React.ChangeEvent<HTMLInputElement>) { setNewTag(evt.target.value); } function validateNewNameForm() { return newAssetName !== '' && newAssetName !== selectedAsset?.name; } async function sendNewName() { setChangeNameErrorMessage(''); setResultLoaded(false); const data = new FormData(); data.append('newName', newAssetName); const apiResponse = new IApiPostResponse(); await Repository.getInstance().editAssetName( selectedAsset?.id, data, apiResponse, i18n(Global_Constants.GLOBAL, Dam_Constants.CHECK_NETWORK_STATUS) ); if (apiResponse.errorMessage != null) { setChangeNameErrorMessage(apiResponse.errorMessage); } await retrieveAssetVersions(); } async function removeTag(tagId: number) { setResultLoaded(false); await Repository.getInstance().removeTagFromAsset(selectedAsset?.id, tagId); await retrieveAssetVersions(); } async function addTag(tagName: string) { if (selectedAsset?.tags.findIndex((t) => t.name === tagName) !== -1) return; setTagsErrorMessage(''); setResultLoaded(false); const data = new FormData(); data.append('assetId', selectedAsset?.id.toString()); data.append('tagName', tagName); const apiResponse = new IApiPostResponse(); await Repository.getInstance().addTagToAsset(data, apiResponse); if (apiResponse.errorMessage != null) { setTagsErrorMessage(apiResponse.errorMessage); } setNewTag(''); await retrieveAssetVersions(); } const setAssetIsSubstitution = async (checked: boolean) => { setResultLoaded(false); if (checked) await Repository.getInstance().setIsSubstitutionAsset(selectedAsset.id); else await Repository.getInstance().UnsetIsSubstitutionAsset(selectedAsset.id); await retrieveAssetVersions(); }; const assetTypeToDescription = (assetType: AssetType) => { const types = new Map([ [AssetType.Image, 'Image'], [AssetType.Scene, 'Scene'], [AssetType.Sound, 'Son'], [AssetType.Video, 'Vidéo'], [AssetType.Sequence, 'Séquence'], [AssetType.Text, 'Texte'], [AssetType.Font, 'Police'], [AssetType.Style, 'Style'], [AssetType.PredefMessage, 'Message'], [AssetType.Programmation, 'Programmation'], ]); return types.get(assetType); }; const assetToRoute = (asset: IAsset) => { const types = new Map([ [AssetType.Sequence, AppRoutes.sequence(asset.id.toString())], [AssetType.Scene, AppRoutes.editor(asset.id.toString())], [ AssetType.PredefMessage, AppRoutes.predefinedMessage(asset.id.toString()), ], [AssetType.Playlist, AppRoutes.audioPlaylist(asset.id.toString())], [AssetType.Style, AppRoutes.style(asset.id.toString())], [AssetType.Programmation, AppRoutes.programmation(asset.id.toString())], ]); return types.get(asset.file_type); }; return ( <AssetContainer> {!resultLoaded && <Loader />} <Title>{i18n(Global_Constants.LIBRARY, Constants.RESOURCE_DETAILS)}: {selectedAsset?.name}</Title> <Form> <AssetDiv> <AssetIllustration> {selectedAsset && <Asset asset={selectedAsset} />} </AssetIllustration> <FormGroup row > <Label for="version" sm={3}><AssetPropertyTitle>Version</AssetPropertyTitle></Label> <Col sm={3}> <Input type="select" id="version" value={selectedAsset?.id} onChange={changeSelectedVersion} disabled={!assetVersions || !assetVersions.length || assetVersions.length === 1} > {assetVersions?.map((asset, index) => ( <option key={`${asset.version}-${index}`} value={asset.id}> {asset.version} </option> ))} </Input> </Col> </FormGroup> <FormGroup row> <Label for="assetName" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.GLOBAL, Global_Constants.NAME)}</AssetPropertyTitle></Label> <Col sm={3}> <div className="d-flex align-items-center"> {selectedAsset?.name} <i className={`ms-1 fa fa-lg fa-pencil-square${showEditNameForm ? '' : '-o'}`} style={{ cursor: 'pointer' }} onClick={toggleEditNameForm} /> </div> </Col> </FormGroup> {showEditNameForm && ( <FormGroup row> <Label for="newAssetName" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.LIBRARY, Constants.NEW_NAME)}</AssetPropertyTitle></Label> <Col sm={3}> <Input type="text" id="newAssetName" value={newAssetName} onChange={changeNewAssetName} placeholder={selectedAsset.name} /> <Button className="btn btn-danger mt-1" disabled={!validateNewNameForm()} onClick={sendNewName} > {i18n(Global_Constants.LIBRARY, Global_Constants.CHANGE)} </Button> </Col> {changeNameErrorMessage && ( <UncontrolledAlert color="danger"> Error: {changeNameErrorMessage} </UncontrolledAlert> )} </FormGroup> )} <FormGroup row> <Label for="creationDate" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.GLOBAL, Global_Constants.CREATION_DATE)}</AssetPropertyTitle></Label> <Col sm={3}> <Input type="text" id="creationDate" value={formatDateString(selectedAsset?.created_at)} readOnly /> </Col> </FormGroup> <FormGroup row> <Label for="modificationDate" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.GLOBAL, Global_Constants.LAST_MODIFICATION_DATE)}</AssetPropertyTitle></Label> <Col sm={3}> <Input type="text" id="modificationDate" value={formatDateString(selectedAsset?.updated_at)} readOnly /> </Col> </FormGroup> <FormGroup row> <Label for="resourceType" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.LIBRARY, Constants.RESOURCE_TYPE)}</AssetPropertyTitle></Label> <Col sm={3}> <Input type="text" id="resourceType" value={assetTypeToDescription(selectedAsset?.file_type)} readOnly /> </Col> </FormGroup> <FormGroup row> <Label for="dimension" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.LIBRARY, Constants.SIZE)}</AssetPropertyTitle></Label> <Col sm={3}> <Input type="text" id="dimension" value={String(selectedAsset?.dimension)} readOnly /> </Col> </FormGroup> {selectedAsset && (selectedAsset?.file_type === AssetType.Image || selectedAsset?.file_type === AssetType.Text || selectedAsset?.file_type === AssetType.Style) && ( <FormGroup row> <Label for="substitute" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.LIBRARY, Constants.SUBSTITUTE_MESSAGE)}</AssetPropertyTitle></Label> <Col sm={3}> <Input id="isSubstitution" type="checkbox" className="form-check-input" checked={selectedAsset?.isSubstitution ?? false} onChange={(e) => setAssetIsSubstitution(e.target.checked)} /> </Col> </FormGroup> )} <FormGroup row> <Label for="keywords" sm={3}><AssetPropertyTitle>{i18n(Global_Constants.LIBRARY, Constants.KEYWORDS)}</AssetPropertyTitle></Label> <Col sm={3}> <AssetTagsContainer> <TagsList> {selectedAsset?.tags.map((tag, i) => ( <TagContainer key={`asset-tag-${selectedAsset.name}-${i}`}> <Input type="text" value={tag.name} readOnly /> <TagButton className="fa fa-xs fa-close" onClick={() => removeTag(tag.id)} /> </TagContainer> ))} <TagContainer> <Input type="text" id="new-asset-tag" value={newTag} placeholder={i18n(Global_Constants.LIBRARY, Constants.KEYWORD)} onChange={changeNewTag} /> <TagButton className={`fa fa-plus ${newTag === '' ? 'sdc-disabled' : ''}`} onClick={() => addTag(newTag)} /> </TagContainer> </TagsList> </AssetTagsContainer> {tagsErrorMessage && ( <UncontrolledAlert color="danger"> Error: {tagsErrorMessage} </UncontrolledAlert> )} </Col> </FormGroup> {selectedAsset?.file_type === AssetType.Scene && sequencesForScene.length > 0 && ( <FormGroup row> <Label for="sequences" sm={3}><AssetPropertyTitle>Utilized in sequences</AssetPropertyTitle></Label> <Col sm={3}> {sequencesForScene.map((s) => ( <div key={`seq-for-scene-${s.name}`}> <NavLink to={AppRoutes.assetDetails(s.id.toString())}> {s.name} </NavLink> </div> ))} </Col> </FormGroup> )} {selectedAssetRoute && ( <div> <NavLink className="btn btn-secondary" to={selectedAssetRoute}> {i18n(Global_Constants.INFORMATION, Global_Constants.MODIFY_BUTTON)} </NavLink> </div> )} </AssetDiv> </Form> </AssetContainer> ); }; export default AssetDetails;
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter