Skip to content

Commit

Permalink
[PUI] download image (#8230)
Browse files Browse the repository at this point in the history
* Add placeholder for download image button

* Implement image download functionality

* Increase timeout

* Show timeout notification

* Icon cleanup
  • Loading branch information
SchrodingersGat authored Oct 6, 2024
1 parent c914d1c commit a323bf0
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 26 deletions.
94 changes: 71 additions & 23 deletions src/frontend/src/components/details/DetailsImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
};
Expand Down Expand Up @@ -245,15 +249,19 @@ function ImageActionButtons({
apiPath,
hasImage,
pk,
setImage
setImage,
downloadImage
}: Readonly<{
actions?: DetailImageButtonProps;
visible: boolean;
apiPath: string;
hasImage: boolean;
pk: string;
setImage: (image: string) => void;
downloadImage: () => void;
}>) {
const globalSettings = useGlobalSettingsState();

return (
<>
{visible && (
Expand Down Expand Up @@ -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={
Expand Down Expand Up @@ -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 ||
Expand All @@ -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>
</>
);
}
7 changes: 5 additions & 2 deletions src/frontend/src/components/forms/ApiForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -540,7 +543,7 @@ export function ApiForm({
break;
}
} else {
invalidResponse(0);
showTimeoutNotification();
props.onFormError?.();
}

Expand Down
2 changes: 2 additions & 0 deletions src/frontend/src/functions/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
IconEdit,
IconExclamationCircle,
IconExternalLink,
IconFileDownload,
IconFileUpload,
IconFlag,
IconFlagShare,
Expand Down Expand Up @@ -143,6 +144,7 @@ const icons = {
notes: IconNotes,
photo: IconPhoto,
upload: IconFileUpload,
download: IconFileDownload,
reject: IconX,
refresh: IconRefresh,
select_image: IconGridDots,
Expand Down
11 changes: 11 additions & 0 deletions src/frontend/src/functions/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/pages/company/CompanyDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
refresh={refreshInstance}
imageActions={{
uploadFile: true,
downloadImage: true,
deleteFile: true
}}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/src/pages/part/PartDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ export default function PartDetail() {
appRole={UserRoles.part}
imageActions={{
selectExisting: true,
downloadImage: true,
uploadFile: true,
deleteFile: true
}}
Expand All @@ -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(() => {
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/pages/sales/ReturnOrderDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down

0 comments on commit a323bf0

Please sign in to comment.