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 { UserRoles } from '../../enums/Roles';
|
||||||
import { cancelEvent } from '../../functions/events';
|
import { cancelEvent } from '../../functions/events';
|
||||||
import { InvenTreeIcon } from '../../functions/icons';
|
import { InvenTreeIcon } from '../../functions/icons';
|
||||||
|
import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||||
|
import { useGlobalSettingsState } from '../../states/SettingsState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { PartThumbTable } from '../../tables/part/PartThumbTable';
|
import { PartThumbTable } from '../../tables/part/PartThumbTable';
|
||||||
import { vars } from '../../theme';
|
import { vars } from '../../theme';
|
||||||
@ -42,11 +44,13 @@ export type DetailImageProps = {
|
|||||||
* Actions for Detail Images.
|
* Actions for Detail Images.
|
||||||
* If true, the button type will be visible
|
* If true, the button type will be visible
|
||||||
* @param {boolean} selectExisting - PART ONLY. Allows selecting existing images as part image
|
* @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} uploadFile - Allows uploading a new image
|
||||||
* @param {boolean} deleteFile - Allows deleting the current image
|
* @param {boolean} deleteFile - Allows deleting the current image
|
||||||
*/
|
*/
|
||||||
export type DetailImageButtonProps = {
|
export type DetailImageButtonProps = {
|
||||||
selectExisting?: boolean;
|
selectExisting?: boolean;
|
||||||
|
downloadImage?: boolean;
|
||||||
uploadFile?: boolean;
|
uploadFile?: boolean;
|
||||||
deleteFile?: boolean;
|
deleteFile?: boolean;
|
||||||
};
|
};
|
||||||
@ -245,7 +249,8 @@ function ImageActionButtons({
|
|||||||
apiPath,
|
apiPath,
|
||||||
hasImage,
|
hasImage,
|
||||||
pk,
|
pk,
|
||||||
setImage
|
setImage,
|
||||||
|
downloadImage
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
actions?: DetailImageButtonProps;
|
actions?: DetailImageButtonProps;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@ -253,7 +258,10 @@ function ImageActionButtons({
|
|||||||
hasImage: boolean;
|
hasImage: boolean;
|
||||||
pk: string;
|
pk: string;
|
||||||
setImage: (image: string) => void;
|
setImage: (image: string) => void;
|
||||||
|
downloadImage: () => void;
|
||||||
}>) {
|
}>) {
|
||||||
|
const globalSettings = useGlobalSettingsState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{visible && (
|
{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 && (
|
{actions.uploadFile && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={
|
icon={
|
||||||
@ -341,6 +368,21 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
|
|||||||
|
|
||||||
const permissions = useUserState();
|
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(() => {
|
const hasOverlay: boolean = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
props.imageActions?.selectExisting ||
|
props.imageActions?.selectExisting ||
|
||||||
@ -359,27 +401,33 @@ export function DetailsImage(props: Readonly<DetailImageProps>) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
|
<>
|
||||||
<>
|
{downloadImage.modal}
|
||||||
<ApiImage
|
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1} pos="relative">
|
||||||
src={img}
|
<>
|
||||||
mah={IMAGE_DIMENSION}
|
<ApiImage
|
||||||
maw={IMAGE_DIMENSION}
|
src={img}
|
||||||
onClick={expandImage}
|
mah={IMAGE_DIMENSION}
|
||||||
/>
|
maw={IMAGE_DIMENSION}
|
||||||
{permissions.hasChangeRole(props.appRole) && hasOverlay && hovered && (
|
onClick={expandImage}
|
||||||
<Overlay color="black" opacity={0.8} onClick={expandImage}>
|
/>
|
||||||
<ImageActionButtons
|
{permissions.hasChangeRole(props.appRole) &&
|
||||||
visible={hovered}
|
hasOverlay &&
|
||||||
actions={props.imageActions}
|
hovered && (
|
||||||
apiPath={props.apiPath}
|
<Overlay color="black" opacity={0.8} onClick={expandImage}>
|
||||||
hasImage={props.src ? true : false}
|
<ImageActionButtons
|
||||||
pk={props.pk}
|
visible={hovered}
|
||||||
setImage={setAndRefresh}
|
actions={props.imageActions}
|
||||||
/>
|
apiPath={props.apiPath}
|
||||||
</Overlay>
|
hasImage={props.src ? true : false}
|
||||||
)}
|
pk={props.pk}
|
||||||
</>
|
setImage={setAndRefresh}
|
||||||
</AspectRatio>
|
downloadImage={downloadImage.open}
|
||||||
|
/>
|
||||||
|
</Overlay>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</AspectRatio>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,10 @@ import {
|
|||||||
extractAvailableFields,
|
extractAvailableFields,
|
||||||
mapFields
|
mapFields
|
||||||
} from '../../functions/forms';
|
} from '../../functions/forms';
|
||||||
import { invalidResponse } from '../../functions/notifications';
|
import {
|
||||||
|
invalidResponse,
|
||||||
|
showTimeoutNotification
|
||||||
|
} from '../../functions/notifications';
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import { TableState } from '../../hooks/UseTable';
|
import { TableState } from '../../hooks/UseTable';
|
||||||
import { PathParams } from '../../states/ApiState';
|
import { PathParams } from '../../states/ApiState';
|
||||||
@ -540,7 +543,7 @@ export function ApiForm({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
invalidResponse(0);
|
showTimeoutNotification();
|
||||||
props.onFormError?.();
|
props.onFormError?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
IconEdit,
|
IconEdit,
|
||||||
IconExclamationCircle,
|
IconExclamationCircle,
|
||||||
IconExternalLink,
|
IconExternalLink,
|
||||||
|
IconFileDownload,
|
||||||
IconFileUpload,
|
IconFileUpload,
|
||||||
IconFlag,
|
IconFlag,
|
||||||
IconFlagShare,
|
IconFlagShare,
|
||||||
@ -143,6 +144,7 @@ const icons = {
|
|||||||
notes: IconNotes,
|
notes: IconNotes,
|
||||||
photo: IconPhoto,
|
photo: IconPhoto,
|
||||||
upload: IconFileUpload,
|
upload: IconFileUpload,
|
||||||
|
download: IconFileDownload,
|
||||||
reject: IconX,
|
reject: IconX,
|
||||||
refresh: IconRefresh,
|
refresh: IconRefresh,
|
||||||
select_image: IconGridDots,
|
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.
|
* Display a login / logout notification message.
|
||||||
* Any existing login notification(s) will be hidden.
|
* Any existing login notification(s) will be hidden.
|
||||||
|
@ -153,6 +153,7 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
|||||||
refresh={refreshInstance}
|
refresh={refreshInstance}
|
||||||
imageActions={{
|
imageActions={{
|
||||||
uploadFile: true,
|
uploadFile: true,
|
||||||
|
downloadImage: true,
|
||||||
deleteFile: true
|
deleteFile: true
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -511,6 +511,7 @@ export default function PartDetail() {
|
|||||||
appRole={UserRoles.part}
|
appRole={UserRoles.part}
|
||||||
imageActions={{
|
imageActions={{
|
||||||
selectExisting: true,
|
selectExisting: true,
|
||||||
|
downloadImage: true,
|
||||||
uploadFile: true,
|
uploadFile: true,
|
||||||
deleteFile: true
|
deleteFile: true
|
||||||
}}
|
}}
|
||||||
@ -531,7 +532,7 @@ export default function PartDetail() {
|
|||||||
) : (
|
) : (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
);
|
);
|
||||||
}, [part, instanceQuery]);
|
}, [globalSettings, part, instanceQuery]);
|
||||||
|
|
||||||
// Part data panels (recalculate when part data changes)
|
// Part data panels (recalculate when part data changes)
|
||||||
const partPanels: PanelType[] = useMemo(() => {
|
const partPanels: PanelType[] = useMemo(() => {
|
||||||
|
@ -95,6 +95,7 @@ export default function ReturnOrderDetail() {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'customer_reference',
|
name: 'customer_reference',
|
||||||
label: t`Customer Reference`,
|
label: t`Customer Reference`,
|
||||||
|
icon: 'customer',
|
||||||
copy: true,
|
copy: true,
|
||||||
hidden: !order.customer_reference
|
hidden: !order.customer_reference
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user