mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[PUI] download image (#8230)
* Add placeholder for download image button * Implement image download functionality * Increase timeout * Show timeout notification * Icon cleanup
This commit is contained in:
parent
c914d1c5af
commit
a323bf0007
@ -19,6 +19,8 @@ import { api } from '../../App';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { cancelEvent } from '../../functions/events';
|
||||
import { InvenTreeIcon } from '../../functions/icons';
|
||||
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||
import { useGlobalSettingsState } from '../../states/SettingsState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { PartThumbTable } from '../../tables/part/PartThumbTable';
|
||||
import { vars } from '../../theme';
|
||||
@ -42,11 +44,13 @@ export type DetailImageProps = {
|
||||
* Actions for Detail Images.
|
||||
* If true, the button type will be visible
|
||||
* @param {boolean} selectExisting - PART ONLY. Allows selecting existing images as part image
|
||||
* @param {boolean} downloadImage - Allows downloading image from a remote URL
|
||||
* @param {boolean} uploadFile - Allows uploading a new image
|
||||
* @param {boolean} deleteFile - Allows deleting the current image
|
||||
*/
|
||||
export type DetailImageButtonProps = {
|
||||
selectExisting?: boolean;
|
||||
downloadImage?: boolean;
|
||||
uploadFile?: boolean;
|
||||
deleteFile?: boolean;
|
||||
};
|
||||
@ -245,7 +249,8 @@ function ImageActionButtons({
|
||||
apiPath,
|
||||
hasImage,
|
||||
pk,
|
||||
setImage
|
||||
setImage,
|
||||
downloadImage
|
||||
}: Readonly<{
|
||||
actions?: DetailImageButtonProps;
|
||||
visible: boolean;
|
||||
@ -253,7 +258,10 @@ function ImageActionButtons({
|
||||
hasImage: boolean;
|
||||
pk: string;
|
||||
setImage: (image: string) => void;
|
||||
downloadImage: () => void;
|
||||
}>) {
|
||||
const globalSettings = useGlobalSettingsState();
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
@ -284,6 +292,25 @@ function ImageActionButtons({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{actions.downloadImage &&
|
||||
globalSettings.isSet('INVENTREE_DOWNLOAD_FROM_URL') && (
|
||||
<ActionButton
|
||||
icon={
|
||||
<InvenTreeIcon
|
||||
icon="download"
|
||||
iconProps={{ color: 'white' }}
|
||||
/>
|
||||
}
|
||||
tooltip={t`Download remote image`}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
tooltipAlignment="top"
|
||||
onClick={(event: any) => {
|
||||
cancelEvent(event);
|
||||
downloadImage();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{actions.uploadFile && (
|
||||
<ActionButton
|
||||
icon={
|
||||
@ -341,6 +368,21 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
|
||||
|
||||
const permissions = useUserState();
|
||||
|
||||
const downloadImage = useEditApiFormModal({
|
||||
url: props.apiPath,
|
||||
title: t`Download Image`,
|
||||
fields: {
|
||||
remote_image: {}
|
||||
},
|
||||
timeout: 10000,
|
||||
successMessage: t`Image downloaded successfully`,
|
||||
onFormSuccess: (response: any) => {
|
||||
if (response.image) {
|
||||
setAndRefresh(response.image);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hasOverlay: boolean = useMemo(() => {
|
||||
return (
|
||||
props.imageActions?.selectExisting ||
|
||||
@ -359,27 +401,33 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
|
||||
};
|
||||
|
||||
return (
|
||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
mah={IMAGE_DIMENSION}
|
||||
maw={IMAGE_DIMENSION}
|
||||
onClick={expandImage}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) && hasOverlay && hovered && (
|
||||
<Overlay color="black" opacity={0.8} onClick={expandImage}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={props.src ? true : false}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
</AspectRatio>
|
||||
<>
|
||||
{downloadImage.modal}
|
||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
mah={IMAGE_DIMENSION}
|
||||
maw={IMAGE_DIMENSION}
|
||||
onClick={expandImage}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) &&
|
||||
hasOverlay &&
|
||||
hovered && (
|
||||
<Overlay color="black" opacity={0.8} onClick={expandImage}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={props.src ? true : false}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
downloadImage={downloadImage.open}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
</AspectRatio>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -33,7 +33,10 @@ import {
|
||||
extractAvailableFields,
|
||||
mapFields
|
||||
} from '../../functions/forms';
|
||||
import { invalidResponse } from '../../functions/notifications';
|
||||
import {
|
||||
invalidResponse,
|
||||
showTimeoutNotification
|
||||
} from '../../functions/notifications';
|
||||
import { getDetailUrl } from '../../functions/urls';
|
||||
import { TableState } from '../../hooks/UseTable';
|
||||
import { PathParams } from '../../states/ApiState';
|
||||
@ -540,7 +543,7 @@ export function ApiForm({
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
invalidResponse(0);
|
||||
showTimeoutNotification();
|
||||
props.onFormError?.();
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
IconEdit,
|
||||
IconExclamationCircle,
|
||||
IconExternalLink,
|
||||
IconFileDownload,
|
||||
IconFileUpload,
|
||||
IconFlag,
|
||||
IconFlagShare,
|
||||
@ -143,6 +144,7 @@ const icons = {
|
||||
notes: IconNotes,
|
||||
photo: IconPhoto,
|
||||
upload: IconFileUpload,
|
||||
download: IconFileDownload,
|
||||
reject: IconX,
|
||||
refresh: IconRefresh,
|
||||
select_image: IconGridDots,
|
||||
|
@ -39,6 +39,17 @@ export function invalidResponse(returnCode: number) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a notification on timeout
|
||||
*/
|
||||
export function showTimeoutNotification() {
|
||||
notifications.show({
|
||||
title: t`Timeout`,
|
||||
message: t`The request timed out`,
|
||||
color: 'red'
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Display a login / logout notification message.
|
||||
* Any existing login notification(s) will be hidden.
|
||||
|
@ -153,6 +153,7 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
||||
refresh={refreshInstance}
|
||||
imageActions={{
|
||||
uploadFile: true,
|
||||
downloadImage: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
/>
|
||||
|
@ -511,6 +511,7 @@ export default function PartDetail() {
|
||||
appRole={UserRoles.part}
|
||||
imageActions={{
|
||||
selectExisting: true,
|
||||
downloadImage: true,
|
||||
uploadFile: true,
|
||||
deleteFile: true
|
||||
}}
|
||||
@ -531,7 +532,7 @@ export default function PartDetail() {
|
||||
) : (
|
||||
<Skeleton />
|
||||
);
|
||||
}, [part, instanceQuery]);
|
||||
}, [globalSettings, part, instanceQuery]);
|
||||
|
||||
// Part data panels (recalculate when part data changes)
|
||||
const partPanels: PanelType[] = useMemo(() => {
|
||||
|
@ -95,6 +95,7 @@ export default function ReturnOrderDetail() {
|
||||
type: 'text',
|
||||
name: 'customer_reference',
|
||||
label: t`Customer Reference`,
|
||||
icon: 'customer',
|
||||
copy: true,
|
||||
hidden: !order.customer_reference
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user