Index: components/EntityMediaList/EntityMediaList.columns.tsx =================================================================== diff -u -r553d861f45033d5546b32923f4ab7c685a382d36 -r68008a953e5a9f01bfa7dd92a456260017b2d158 --- components/EntityMediaList/EntityMediaList.columns.tsx (.../EntityMediaList.columns.tsx) (revision 553d861f45033d5546b32923f4ab7c685a382d36) +++ components/EntityMediaList/EntityMediaList.columns.tsx (.../EntityMediaList.columns.tsx) (revision 68008a953e5a9f01bfa7dd92a456260017b2d158) @@ -1,133 +1,140 @@ -import { ExportOutlined, InfoCircleFilled } from '@ant-design/icons'; -import { DatePicker, message, Spin, Tag, Tooltip } from 'antd'; -import { IEntityMedia, IMediaClassificationFields, IMediaDetails, IMediaFile, IMediaType } from 'lemans-api/models/Media/media.types'; -import { IMediaClassification } from 'lemans-api/models/Media/mediaClassification.types'; -import { InlineEdit } from 'lemans-common'; -import { IOptionProps } from 'lemans-common/components/InlineEdit/Select/Select.types'; -import { useLoader } from 'lemans-common/libs/useToggle'; -import { Link, service } from 'lemans-dashboard-common'; -import moment from 'moment'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import { MediaPreview } from '../MediaPreview'; -import { useEffect, useState } from 'react'; - -export const MediaImageColumn = (_: any, media: IMediaFile & IMediaType) => - ; - -export const MediaEntityTagsColumn = (entityId:string, primaryMediaId:number) => (_:any, media: IEntityMedia) => { - const filteredNumbers = (media.webPartNumber || "").split(",").filter(number => entityId !== `${number}`); - - return <> - {filteredNumbers.length > 0 && - Web Part Numbers
- {filteredNumbers.map(number => {number})} - } - > - -
} - {primaryMediaId === media.mediaId && Primary} - {media.isHidden && Hidden} - ; -} - -export const MediaFileTypeColumn = (_: any, media: IMediaFile) => <>{media.mediaTypeDescription}; - -export const MediaFileNameColumn = (_: any, media: IMediaFile) => { - const partNumber = media.fullFileName.split(/[\.\_]/)[0]; - - const onCopy = () => { - message.success(`${partNumber} copied to your clipboard`); - } - - return <> - - {media.fullFileName} - -   - - - - ; -} - -type MediaInfo = IMediaClassificationFields & IMediaDetails & IMediaType; -const ClassificationSelect = ({mediaId, mediaClassificationId, mediaTypeId}: MediaInfo) => { - const [classificationId, setClassificationId] = useState(mediaClassificationId); - - const [options, setOptions] = useState([]); - useEffect(() => { - service.media.classification.get("ds").then((classifications: IMediaClassification[]) => { - setOptions(classifications - .filter(c => c.mediaTypeId === mediaTypeId) - .map(c => ({ - text: c.classificationName || "", - value: c.mediaClassificationId || "" - })) - ); - }); - }, []); - - const saveChanges = (newMediaClassificationId: number) => - service.media.update("ds", mediaId, { mediaClassificationId: newMediaClassificationId }) - .then(() => { - setClassificationId(newMediaClassificationId); - }); - - return ; -} - -export const MediaClassificationColumn = (_: any, media: MediaInfo) => - ; - -const DescriptionEdit = (media:IMediaDetails) => { - const [descr, setDescr] = useState(media.description); - const saveChanges = (description: string) => service.media.update('ds', media.mediaId, { description }) - .then(() => { - setDescr(description); - }); - - return ; -} - -export const MediaDescriptionColumn = (_: any, media: IMediaDetails) => - ; - -const DateEdit = ({media, field}:{media:IMediaDetails, field:keyof IMediaDetails}) => { - const [date, setDate] = useState(media[field]); - const loading = useLoader(); - - const onChange = (newDate: moment.Moment | null) => { - loading.start(); - const n = newDate ? newDate.toISOString() : null; - return service.media.update("ds", media.mediaId, { [field]: n }) - .then(() => { - setDate(n); - }) - .finally(loading.done); - } - - return - - ; -} - -const dateColumn = (field:keyof IMediaDetails) => (_: any, media: IMediaDetails) => - ; - -export const MediaEffectiveDateColumn = dateColumn("effectiveDate"); -export const MediaRemoveDateColumn = dateColumn("removeDate"); +import { ExportOutlined, InfoCircleFilled } from '@ant-design/icons'; +import { DatePicker, message, Spin, Tag, Tooltip } from 'antd'; +import { IEntityMedia, IMediaClassificationFields, IMediaDetails, IMediaFile, IMediaType } from 'lemans-api/models/Media/media.types'; +import { IMediaClassification } from 'lemans-api/models/Media/mediaClassification.types'; +import { InlineEdit } from 'lemans-common'; +import { IOptionProps } from 'lemans-common/components/InlineEdit/Select/Select.types'; +import { useLoader } from 'lemans-common/libs/useToggle'; +import { Link, service } from 'lemans-dashboard-common'; +import moment from 'moment'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import { MediaPreview } from '../MediaPreview'; +import { useEffect, useState } from 'react'; + +export const MediaImageColumn = (_: any, media: IMediaFile & IMediaType) => + ; + +export const MediaEntityTagsColumn = (entityId:string, primaryMediaId:number) => (_:any, media: IEntityMedia) => { + const filteredNumbers = (media.webPartNumber || "").split(",").filter(number => entityId !== `${number}`); + + return <> + {filteredNumbers.length > 0 && + Web Part Numbers
+ {filteredNumbers.map(number => {number})} + } + > + +
} + {primaryMediaId === media.mediaId && Primary} + {media.isHidden && Hidden} + ; +} + +export const MediaFileTypeColumn = (_: any, media: IMediaFile) => <>{media.mediaTypeDescription}; + +export const MediaFileNameColumn = (_: any, media: IMediaFile) => { + const partNumber = media.fullFileName.split(/[\.\_]/)[0]; + + const onCopy = () => { + message.success(`${partNumber} copied to your clipboard`); + } + + return <> + + {media.fullFileName} + +   + + + + ; +} + +type MediaInfo = IMediaClassificationFields & IMediaDetails & IMediaType; +const ClassificationSelect = ({mediaId, mediaClassificationId, mediaTypeId}: MediaInfo) => { + const [classificationId, setClassificationId] = useState(mediaClassificationId); + + const [options, setOptions] = useState([]); + useEffect(() => { + service.media.classification.get("ds").then((classifications: IMediaClassification[]) => { + setOptions(classifications + .filter(c => c.mediaTypeId === mediaTypeId) + .map(c => ({ + text: c.classificationName || "", + value: c.mediaClassificationId || "" + })) + ); + }); + }, []); + + const saveChanges = (newMediaClassificationId: number) => + service.media.update("ds", mediaId, { mediaClassificationId: newMediaClassificationId }) + .then(() => { + setClassificationId(newMediaClassificationId); + }); + + return ; +} + +export const MediaClassificationColumn = (_: any, media: MediaInfo) => + ; + +const DescriptionEdit = (media:IMediaDetails) => { + const [descr, setDescr] = useState(media.description); + const saveChanges = (description: string) => service.media.update('ds', media.mediaId, { description }) + .then(() => { + setDescr(description); + }); + + return ; +} + +export const MediaDescriptionColumn = (_: any, media: IMediaDetails) => + ; + +const DateEdit = ({media, field}:{media:IMediaDetails, field:keyof IMediaDetails}) => { + const [date, setDate] = useState(media[field]); + const loading = useLoader(); + + const onChange = (newDate: moment.Moment | null) => { + loading.start(); + const n = newDate ? newDate.toISOString() : null; + return service.media.update("ds", media.mediaId, { [field]: n }) + .then(() => { + setDate(n); + }) + .finally(loading.done); + } + + return + + ; +} + +const dateColumn = (field:keyof IMediaDetails) => (_: any, media: IMediaDetails) => + ; + +export const MediaEffectiveDateColumn = dateColumn("effectiveDate"); +export const MediaRemoveDateColumn = dateColumn("removeDate"); + +// Added Date column - displays the creation date from audit fields +export const MediaAddedDateColumn = (_: any, media: IEntityMedia) => { + return media.dateCreated ? + {moment(media.dateCreated).format('MM/DD/YYYY')} : + -; +}; Index: components/EntityMediaList/EntityMediaList.component.tsx =================================================================== diff -u -re1e0304a8b1124189c33e7413c3cbecc2667b504 -r68008a953e5a9f01bfa7dd92a456260017b2d158 --- components/EntityMediaList/EntityMediaList.component.tsx (.../EntityMediaList.component.tsx) (revision e1e0304a8b1124189c33e7413c3cbecc2667b504) +++ components/EntityMediaList/EntityMediaList.component.tsx (.../EntityMediaList.component.tsx) (revision 68008a953e5a9f01bfa7dd92a456260017b2d158) @@ -1,110 +1,111 @@ -import { OrderedListOutlined } from "@ant-design/icons/lib"; -import { Col, Row, Spin, Table, Tabs } from 'antd'; -import { IEntityMedia } from 'lemans-api/models/Media/media.types'; -import { SortableTable, useRowSelection } from 'lemans-common'; -import { GenericActionBar } from 'lemans-common/components/GenericActionBar'; -import { IGenericActionBarOption } from "lemans-common/components/GenericActionBar/GenericActionBar.types"; -import { useLoader, useTrigger } from 'lemans-common/libs/useToggle'; -import { api, service } from 'lemans-dashboard-common'; -import React, { Key } from 'react'; -import { brandMediaOptions, partMediaOptions, productMediaOptions } from "../MediaSearch/MediaSearch.helpers"; -import { MediaClassificationColumn, MediaDescriptionColumn, MediaEffectiveDateColumn, MediaEntityTagsColumn, MediaFileNameColumn, MediaFileTypeColumn, MediaImageColumn, MediaRemoveDateColumn } from "./EntityMediaList.columns"; -import './EntityMediaList.styles.less'; -import { EntityMediaListProps } from './EntityMediaList.types'; - -export const EntityMediaListComponent = (props:EntityMediaListProps) => { - const [media, setMedia] = React.useState([]); - const loading = useLoader(); - const [selectedMedia, setSelectedMedia, rowSelection] = useRowSelection(); - - const [primaryMediaId, setPrimaryMediaId] = React.useState(1234); - - const [refreshTrigger, refresh] = useTrigger(); - - React.useEffect(() => { - loading.start(); - - const loadEntity:any = - props.entity === "part" ? service.part.get : - props.entity === "product" ? service.product.get : - service.brand.get; - loadEntity(props.entityId as never).then((e:any) => setPrimaryMediaId(e.primaryMediaId)); - - setSelectedMedia([]); - - service.media.entity.search(props.entity, props.entityId, {...props.filters, sorting: "sequence ASC", pageSize: 1000}) - .then(setMedia) - .catch(api.handleError) - .finally(loading.done); - }, [props.entity, props.entityId, props.filters, refreshTrigger]); - - const options:IGenericActionBarOption[] = { - part: partMediaOptions, - product: productMediaOptions, - brand: brandMediaOptions, - }[props.entity] as IGenericActionBarOption[]; - - const onActionComplete = () => { - refresh(); - setSelectedMedia([]); - }; - - const sortEntities = (src: IEntityMedia, dst: IEntityMedia, position: "BEFORE" | "AFTER") => { - loading.start(); - - // Update local state before calling backend - const newMedia = [...media]; - const oldIndex = newMedia.findIndex(item => item.mediaId === src.mediaId); - const newIndex = newMedia.findIndex(item => item.mediaId === dst.mediaId); - - if (oldIndex === -1 || newIndex === -1) return; - - // Move the item locally - const [movedItem] = newMedia.splice(oldIndex, 1); - newMedia.splice(position === "BEFORE" ? newIndex : newIndex + 1, 0, movedItem); - - setMedia(newMedia); - - // Call backend to persist sorting - service.media.entity.move(props.entity, props.entityId, src.mediaId, dst.mediaId, position) - .catch(api.handleError) // Handle API failure gracefully - .finally(loading.done); - }; - - const showTotal = (total: number, range: [number, number]) => `${range[0]} - ${range[1]} of ${total}`; - - return - - - - } - > - List} key="list"> - - - - - - - - - - - - - ; -} +import { OrderedListOutlined } from "@ant-design/icons/lib"; +import { Col, Row, Spin, Table, Tabs } from 'antd'; +import { IEntityMedia } from 'lemans-api/models/Media/media.types'; +import { SortableTable, useRowSelection } from 'lemans-common'; +import { GenericActionBar } from 'lemans-common/components/GenericActionBar'; +import { IGenericActionBarOption } from "lemans-common/components/GenericActionBar/GenericActionBar.types"; +import { useLoader, useTrigger } from 'lemans-common/libs/useToggle'; +import { api, service } from 'lemans-dashboard-common'; +import React, { Key } from 'react'; +import { brandMediaOptions, partMediaOptions, productMediaOptions } from "../MediaSearch/MediaSearch.helpers"; +import { MediaClassificationColumn, MediaDescriptionColumn, MediaEffectiveDateColumn, MediaEntityTagsColumn, MediaFileNameColumn, MediaFileTypeColumn, MediaImageColumn, MediaRemoveDateColumn, MediaAddedDateColumn } from "./EntityMediaList.columns"; +import './EntityMediaList.styles.less'; +import { EntityMediaListProps } from './EntityMediaList.types'; + +export const EntityMediaListComponent = (props:EntityMediaListProps) => { + const [media, setMedia] = React.useState([]); + const loading = useLoader(); + const [selectedMedia, setSelectedMedia, rowSelection] = useRowSelection(); + + const [primaryMediaId, setPrimaryMediaId] = React.useState(1234); + + const [refreshTrigger, refresh] = useTrigger(); + + React.useEffect(() => { + loading.start(); + + const loadEntity:any = + props.entity === "part" ? service.part.get : + props.entity === "product" ? service.product.get : + service.brand.get; + loadEntity(props.entityId as never).then((e:any) => setPrimaryMediaId(e.primaryMediaId)); + + setSelectedMedia([]); + + service.media.entity.search(props.entity, props.entityId, {...props.filters, sorting: "sequence ASC", pageSize: 1000}) + .then(setMedia) + .catch(api.handleError) + .finally(loading.done); + }, [props.entity, props.entityId, props.filters, refreshTrigger]); + + const options:IGenericActionBarOption[] = { + part: partMediaOptions, + product: productMediaOptions, + brand: brandMediaOptions, + }[props.entity] as IGenericActionBarOption[]; + + const onActionComplete = () => { + refresh(); + setSelectedMedia([]); + }; + + const sortEntities = (src: IEntityMedia, dst: IEntityMedia, position: "BEFORE" | "AFTER") => { + loading.start(); + + // Update local state before calling backend + const newMedia = [...media]; + const oldIndex = newMedia.findIndex(item => item.mediaId === src.mediaId); + const newIndex = newMedia.findIndex(item => item.mediaId === dst.mediaId); + + if (oldIndex === -1 || newIndex === -1) return; + + // Move the item locally + const [movedItem] = newMedia.splice(oldIndex, 1); + newMedia.splice(position === "BEFORE" ? newIndex : newIndex + 1, 0, movedItem); + + setMedia(newMedia); + + // Call backend to persist sorting + service.media.entity.move(props.entity, props.entityId, src.mediaId, dst.mediaId, position) + .catch(api.handleError) // Handle API failure gracefully + .finally(loading.done); + }; + + const showTotal = (total: number, range: [number, number]) => `${range[0]} - ${range[1]} of ${total}`; + + return + + + + } + > + List} key="list"> + + + + + + + + + + + + + + ; +}