Skip to content

Commit 45619de

Browse files
committed
feat: Add file upload UX with advanced options
- Show Advanced Options (userId, groupId, fileMode) only when device supports `POSIX_FILE_TRANSFER_STORAGE` or `POSIX_FILE_TRANSFER_STREAM` - add a folder icon to the file dropzone for clearer visual guidance - fix some i18n keys in the forms Signed-off-by: Omar <omar.brbutovic@secomind.com>
1 parent 006c916 commit 45619de

6 files changed

Lines changed: 303 additions & 83 deletions

File tree

frontend/src/components/DeviceTabs/FilesUploadTab.tsx

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,13 @@ const formatRelayErrors = (
182182
type ManualFileDownloadRequestFormWrapperProps = {
183183
setErrorFeedback: (feedback: React.ReactNode) => void;
184184
deviceId: string;
185+
showAdvancedOptions: boolean;
185186
};
186187

187188
const ManualFileDownloadRequestFormWrapper = ({
188189
setErrorFeedback,
189190
deviceId,
191+
showAdvancedOptions,
190192
}: ManualFileDownloadRequestFormWrapperProps) => {
191193
const intl = useIntl();
192194
const [isUploading, setIsUploading] = useState(false);
@@ -229,6 +231,9 @@ const ManualFileDownloadRequestFormWrapper = ({
229231
destination,
230232
ttlSeconds,
231233
progressTracked,
234+
fileMode,
235+
userId,
236+
groupId,
232237
} = values;
233238

234239
let uploadBlob: Blob;
@@ -265,13 +270,6 @@ const ManualFileDownloadRequestFormWrapper = ({
265270
const fileDownloadRequestId = uuidv7();
266271
const digest = await computeDigest(archiveData);
267272

268-
// Note: The browser File API does not expose Unix file permissions (fileMode),
269-
// userId, or groupId. These are OS-level metadata not available in web browsers.
270-
// We use sensible defaults: fileMode 0 (rw-r--r--), userId -1, groupId -1.
271-
const fileMode = 0;
272-
const userId = -1;
273-
const groupId = -1;
274-
275273
// Get presigned URL from the backend
276274
const presignedUrls = await new Promise<{
277275
get_url: string;
@@ -433,6 +431,7 @@ const ManualFileDownloadRequestFormWrapper = ({
433431
<ManualFileDownloadRequestForm
434432
isLoading={isUploading}
435433
onFileSubmit={handleFileUpload}
434+
showAdvancedOptions={showAdvancedOptions}
436435
/>
437436
);
438437
};
@@ -441,12 +440,14 @@ type ManualFileDownloadRequestFromRepositoryFormWrapperProps = {
441440
setErrorFeedback: (feedback: React.ReactNode) => void;
442441
repositoriesQueryRef: PreloadedQuery<FilesUploadTab_getRepositories_Query>;
443442
deviceId: string;
443+
showAdvancedOptions: boolean;
444444
};
445445

446446
const ManualFileDownloadRequestFromRepositoryFormWrapper = ({
447447
setErrorFeedback,
448448
repositoriesQueryRef,
449449
deviceId,
450+
showAdvancedOptions,
450451
}: ManualFileDownloadRequestFromRepositoryFormWrapperProps) => {
451452
const intl = useIntl();
452453
const [isUploading, setIsUploading] = useState(false);
@@ -473,6 +474,9 @@ const ManualFileDownloadRequestFromRepositoryFormWrapper = ({
473474
destination,
474475
ttlSeconds,
475476
progressTracked,
477+
fileMode,
478+
userId,
479+
groupId,
476480
} = values;
477481

478482
let compression: string | null = null;
@@ -483,12 +487,6 @@ const ManualFileDownloadRequestFromRepositoryFormWrapper = ({
483487

484488
const fileDownloadRequestId = uuidv7();
485489

486-
// Default Unix file permissions since the repository doesn't store this metadata.
487-
// Defaults: fileMode 0 (rw-r--r--), userId -1, groupId -1.
488-
const fileMode = 0;
489-
const userId = -1;
490-
const groupId = -1;
491-
492490
// Create the file download request with all metadata
493491
await new Promise<void>((resolve, reject) => {
494492
createFileDownloadRequest({
@@ -566,17 +564,23 @@ const ManualFileDownloadRequestFromRepositoryFormWrapper = ({
566564
repositoriesData={repositoriesData}
567565
isLoading={isUploading}
568566
onFileSubmit={handleFileUpload}
567+
showAdvancedOptions={showAdvancedOptions}
569568
/>
570569
);
571570
};
572571

573-
type FileDownloadRequestsContentProps = {
572+
type FilesUploadTabProps = {
574573
deviceRef: FilesUploadTab_fileDownloadRequests$key;
575574
};
576575

577-
const FileDownloadRequestsContent = ({
578-
deviceRef,
579-
}: FileDownloadRequestsContentProps) => {
576+
const FilesUploadTab = ({ deviceRef }: FilesUploadTabProps) => {
577+
const intl = useIntl();
578+
const { deviceId } = useParams();
579+
580+
const [updateMode, setUpdateMode] = useState<"repository" | "file">("file");
581+
582+
const [errorFeedback, setErrorFeedback] = useState<React.ReactNode>(null);
583+
580584
const { data } = usePaginationFragment<
581585
FilesUploadTab_PaginationQuery,
582586
FilesUploadTab_fileDownloadRequests$key
@@ -587,20 +591,9 @@ const FileDownloadRequestsContent = ({
587591
[data.fileDownloadRequests],
588592
);
589593

590-
return <FileDownloadRequestsTable requests={fileDownloadRequests} />;
591-
};
592-
593-
type FilesUploadTabProps = {
594-
deviceRef: FilesUploadTab_fileDownloadRequests$key;
595-
};
596-
597-
const FilesUploadTab = ({ deviceRef }: FilesUploadTabProps) => {
598-
const intl = useIntl();
599-
const { deviceId } = useParams();
600-
601-
const [updateMode, setUpdateMode] = useState<"repository" | "file">("file");
602-
603-
const [errorFeedback, setErrorFeedback] = useState<React.ReactNode>(null);
594+
const showAdvancedOptions =
595+
data.capabilities.includes("POSIX_FILE_TRANSFER_STORAGE") ||
596+
data.capabilities.includes("POSIX_FILE_TRANSFER_STREAM");
604597

605598
const [getRepositoriesQuery, getRepositories] =
606599
useQueryLoader<FilesUploadTab_getRepositories_Query>(
@@ -687,13 +680,15 @@ const FilesUploadTab = ({ deviceRef }: FilesUploadTabProps) => {
687680
<ManualFileDownloadRequestFormWrapper
688681
setErrorFeedback={setErrorFeedback}
689682
deviceId={deviceId || ""}
683+
showAdvancedOptions={showAdvancedOptions}
690684
/>
691685
) : (
692686
getRepositoriesQuery && (
693687
<ManualFileDownloadRequestFromRepositoryFormWrapper
694688
repositoriesQueryRef={getRepositoriesQuery}
695689
setErrorFeedback={setErrorFeedback}
696690
deviceId={deviceId || ""}
691+
showAdvancedOptions={showAdvancedOptions}
697692
/>
698693
)
699694
)}
@@ -711,7 +706,7 @@ const FilesUploadTab = ({ deviceRef }: FilesUploadTabProps) => {
711706
/>
712707
</h5>
713708

714-
<FileDownloadRequestsContent deviceRef={deviceRef} />
709+
<FileDownloadRequestsTable requests={fileDownloadRequests} />
715710
</div>
716711
</Tab>
717712
);

frontend/src/components/FileDropzone.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
2222
import { FormattedMessage } from "react-intl";
2323

2424
import CloseButton from "@/components/CloseButton";
25+
import Icon from "@/components/Icon";
2526
import Tag from "@/components/Tag";
2627
import { formatFileSize } from "@/lib/files";
2728

@@ -193,6 +194,9 @@ const FileDropzone = ({ files, onChange, isInvalid }: FileDropzoneProps) => {
193194
>
194195
{files.length === 0 ? (
195196
<div className="py-2">
197+
<div className="mb-2 text-secondary">
198+
<Icon icon="folder" size="3x" />
199+
</div>
196200
<p className="mb-1 text-muted">
197201
<FormattedMessage
198202
id="components.FileDropzone.dropzonePrompt"

0 commit comments

Comments
 (0)