@griddo/ax 10.3.9 → 10.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/Gallery/GalleryPanel/GalleryDragAndDrop/GalleryDragAndDrop.test.tsx +2 -1
  3. package/src/api/files.tsx +10 -5
  4. package/src/api/images.tsx +3 -3
  5. package/src/api/utils.tsx +30 -1
  6. package/src/components/DragAndDrop/index.tsx +5 -0
  7. package/src/components/Fields/UrlField/index.tsx +10 -2
  8. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +15 -10
  9. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/style.tsx +11 -10
  10. package/src/containers/FileDrive/actions.tsx +20 -7
  11. package/src/containers/Gallery/actions.tsx +6 -2
  12. package/src/containers/Sites/actions.tsx +30 -4
  13. package/src/containers/Sites/reducer.tsx +1 -1
  14. package/src/modules/App/Routing/NavMenu/index.tsx +1 -1
  15. package/src/modules/Content/index.tsx +30 -3
  16. package/src/modules/FileDrive/FileDragAndDrop/index.tsx +56 -21
  17. package/src/modules/FileDrive/FileDragAndDrop/style.tsx +69 -27
  18. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/Item/index.tsx +31 -0
  19. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/Item/style.tsx +43 -0
  20. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/ItemGroup/index.tsx +42 -0
  21. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/ItemGroup/style.tsx +34 -0
  22. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/index.tsx +138 -0
  23. package/src/modules/FileDrive/FileModal/DetailPanel/UsageContent/style.tsx +21 -0
  24. package/src/modules/FileDrive/FileModal/DetailPanel/index.tsx +86 -68
  25. package/src/modules/FileDrive/FileModal/DetailPanel/style.tsx +1 -1
  26. package/src/modules/FileDrive/UploadItem/index.tsx +32 -0
  27. package/src/modules/FileDrive/UploadItem/style.tsx +40 -0
  28. package/src/modules/FileDrive/index.tsx +71 -30
  29. package/src/types/index.tsx +18 -0
@@ -1,12 +1,13 @@
1
- import React, { useEffect } from "react";
1
+ import React, { useEffect, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
5
- import { Button, FieldsBehavior, IconAction } from "@ax/components";
5
+ import { Button, FieldsBehavior, IconAction, Tabs } from "@ax/components";
6
6
  import { IFile, IRootState } from "@ax/types";
7
7
  import { fileDriveActions } from "@ax/containers/FileDrive";
8
8
  import { useModal } from "@ax/hooks";
9
9
  import { DeleteFileModal } from "../../atoms";
10
+ import UsageContent from "./UsageContent";
10
11
 
11
12
  import * as S from "./style";
12
13
 
@@ -26,6 +27,7 @@ const DetailPanel = (props: IProps) => {
26
27
  } = props;
27
28
  const { title, alt, tags, site } = file;
28
29
 
30
+ const [selectedTab, setSelectedTab] = useState("details");
29
31
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
30
32
 
31
33
  useEffect(() => {
@@ -59,6 +61,12 @@ const DetailPanel = (props: IProps) => {
59
61
  toggleModal();
60
62
  };
61
63
 
64
+ const handleSelectedTab = (tab: "details" | "in use") => {
65
+ if (tab !== selectedTab) {
66
+ setSelectedTab(tab);
67
+ }
68
+ };
69
+
62
70
  const mainNewModalAction = {
63
71
  title: "Delete document",
64
72
  onClick: () => handleDeleteFile(),
@@ -66,78 +74,88 @@ const DetailPanel = (props: IProps) => {
66
74
 
67
75
  const secondaryNewModalAction = { title: "Cancel", onClick: toggleDeleteModal };
68
76
 
77
+ const tabs = ["details", "in use"];
78
+
69
79
  return (
70
80
  <>
71
81
  <S.Wrapper>
72
- <S.Header>DETAILS</S.Header>
73
- <S.Content>
74
- <S.FileInfo>
75
- <S.FileName>{file.fileName}</S.FileName>
76
- <S.InfoLine>
77
- <strong>Uploaded:</strong> {getFormattedDateWithTimezone(file.uploadDate, "d MMM Y")}
78
- </S.InfoLine>
79
- <S.InfoLine>
80
- <strong>Type:</strong> {file.fileType}
81
- </S.InfoLine>
82
- <S.InfoLine>
83
- <strong>Size:</strong> {formatBytes(file.sizeBytes)}
84
- </S.InfoLine>
85
- <S.FileActions>
86
- <Button type="button" onClick={handleCopyUrl} buttonStyle="line">
87
- Copy URL
88
- </Button>
89
- <IconAction icon="OpenOutside" size="m" onClick={handleOpenUrl} />
90
- </S.FileActions>
91
- </S.FileInfo>
92
- <S.FileForm>
93
- {isAllowedToEdit ? (
94
- <>
82
+ <S.Header>
83
+ <Tabs tabs={tabs} active={selectedTab} setSelectedTab={handleSelectedTab} noMargins />
84
+ </S.Header>
85
+ {selectedTab === "in use" ? (
86
+ <UsageContent file={file} />
87
+ ) : (
88
+ <>
89
+ <S.Content>
90
+ <S.FileInfo>
91
+ <S.FileName>{file.fileName}</S.FileName>
92
+ <S.InfoLine>
93
+ <strong>Uploaded:</strong> {getFormattedDateWithTimezone(file.uploadDate, "d MMM Y")}
94
+ </S.InfoLine>
95
+ <S.InfoLine>
96
+ <strong>Type:</strong> {file.fileType}
97
+ </S.InfoLine>
98
+ <S.InfoLine>
99
+ <strong>Size:</strong> {formatBytes(file.sizeBytes)}
100
+ </S.InfoLine>
101
+ <S.FileActions>
102
+ <Button type="button" onClick={handleCopyUrl} buttonStyle="line">
103
+ Copy URL
104
+ </Button>
105
+ <IconAction icon="OpenOutside" size="m" onClick={handleOpenUrl} />
106
+ </S.FileActions>
107
+ </S.FileInfo>
108
+ <S.FileForm>
109
+ {isAllowedToEdit ? (
110
+ <>
111
+ <FieldsBehavior
112
+ title="Title"
113
+ name="title"
114
+ value={form.title}
115
+ fieldType="TextField"
116
+ onChange={handleTitle}
117
+ />
118
+ <FieldsBehavior
119
+ title="Alternative text"
120
+ name="alt"
121
+ value={form.alt}
122
+ fieldType="TextField"
123
+ onChange={handleAlt}
124
+ />
125
+ </>
126
+ ) : (
127
+ <>
128
+ <S.Label>Title</S.Label>
129
+ <S.FieldText>{form.title}</S.FieldText>
130
+ <S.Label>Alternative text</S.Label>
131
+ <S.FieldText>{form.alt}</S.FieldText>
132
+ </>
133
+ )}
95
134
  <FieldsBehavior
96
- title="Title"
97
- name="title"
98
- value={form.title}
99
- fieldType="TextField"
100
- onChange={handleTitle}
135
+ title="Tags"
136
+ value={form.tags}
137
+ fieldType="TagsField"
138
+ onChange={handleTags}
139
+ disabled={!isAllowedToEdit}
140
+ helptext="Type a tag and press enter to create it"
101
141
  />
102
- <FieldsBehavior
103
- title="Alternative text"
104
- name="alt"
105
- value={form.alt}
106
- fieldType="TextField"
107
- onChange={handleAlt}
108
- />
109
- </>
110
- ) : (
111
- <>
112
- <S.Label>Title</S.Label>
113
- <S.FieldText>{form.title}</S.FieldText>
114
- <S.Label>Alternative text</S.Label>
115
- <S.FieldText>{form.alt}</S.FieldText>
116
- </>
117
- )}
118
- <FieldsBehavior
119
- title="Tags"
120
- value={form.tags}
121
- fieldType="TagsField"
122
- onChange={handleTags}
123
- disabled={!isAllowedToEdit}
124
- helptext="Type a tag and press enter to create it"
125
- />
126
- </S.FileForm>
127
- </S.Content>
128
- {(isAllowedToEdit || activeDelete) && (
129
- <S.Footer>
130
- {activeDelete && (
131
- <Button type="button" buttonStyle="text" onClick={toggleDeleteModal}>
132
- Delete
133
- </Button>
134
- )}
135
- {isAllowedToEdit && (
136
- <Button type="button" onClick={handleSave} disabled={isSaving || !isDirty}>
137
- {isSaving ? "Saving" : "Save"}
138
- </Button>
142
+ </S.FileForm>
143
+ </S.Content>
144
+ {(isAllowedToEdit || activeDelete) && (
145
+ <S.Footer>
146
+ {activeDelete && (
147
+ <Button type="button" buttonStyle="text" onClick={toggleDeleteModal}>
148
+ Delete
149
+ </Button>
150
+ )}
151
+ {isAllowedToEdit && (
152
+ <Button type="button" onClick={handleSave} disabled={isSaving || !isDirty}>
153
+ {isSaving ? "Saving" : "Save"}
154
+ </Button>
155
+ )}
156
+ </S.Footer>
139
157
  )}
140
- </S.Footer>
158
+ </>
141
159
  )}
142
160
  </S.Wrapper>
143
161
  {isDeleteOpen && (
@@ -14,7 +14,7 @@ const Wrapper = styled.div`
14
14
 
15
15
  const Header = styled.div`
16
16
  ${(p) => p.theme.textStyle.headingXS};
17
- padding: ${(p) => p.theme.spacing.s};
17
+ padding-top: ${(p) => p.theme.spacing.xs};
18
18
  text-align: center;
19
19
  border-bottom: ${(p) => `1px solid ${p.theme.color.uiLine}`};
20
20
  `;
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { CheckField, Icon, ProgressBar } from "@ax/components";
3
+
4
+ import * as S from "./style";
5
+
6
+ const UploadItem = (props: IProps) => {
7
+ const { uploadPercentage, grid } = props;
8
+
9
+ return (
10
+ <S.Wrapper grid={grid}>
11
+ {!grid && (
12
+ <S.CheckWrapper role="cell">
13
+ <CheckField name="check" value={0} checked={false} />
14
+ </S.CheckWrapper>
15
+ )}
16
+ <S.IconWrapper>
17
+ <Icon name="page" size="32" />
18
+ </S.IconWrapper>
19
+ <S.BarWrapper>
20
+ <ProgressBar percentage={uploadPercentage} />
21
+ </S.BarWrapper>
22
+ <S.Text>Uploading...</S.Text>
23
+ </S.Wrapper>
24
+ );
25
+ };
26
+
27
+ interface IProps {
28
+ uploadPercentage: number;
29
+ grid: boolean;
30
+ }
31
+
32
+ export default UploadItem;
@@ -0,0 +1,40 @@
1
+ import styled from "styled-components";
2
+ import { Cell } from "@ax/components/TableList/TableItem/style";
3
+
4
+ const BarWrapper = styled.div``;
5
+
6
+ const Wrapper = styled.div<{ grid: boolean }>`
7
+ display: flex;
8
+ flex-direction: ${(p) => (p.grid ? "column" : "row")};
9
+ border: ${(p) => `1px dashed ${p.theme.color.interactive01}`};
10
+ border-radius: ${(p) => p.theme.radii.s};
11
+ align-items: center;
12
+ padding: ${(p) => (p.grid ? p.theme.spacing.m : `${p.theme.spacing.xs} ${p.theme.spacing.xs} ${p.theme.spacing.xs} 0`)};
13
+ margin-bottom: ${(p) => (p.grid ? "0" : p.theme.spacing.xs)};
14
+
15
+ ${BarWrapper} {
16
+ width: ${(p) => (p.grid ? "150px" : "250px")};
17
+ margin: ${(p) => (p.grid ? `${p.theme.spacing.s} 0`: `0 ${p.theme.spacing.m}`)};
18
+ }
19
+ `;
20
+
21
+ const IconWrapper = styled.div`
22
+ width: 32px;
23
+ height: 32px;
24
+ `;
25
+
26
+ const Text = styled.div`
27
+ ${(p) => p.theme.textStyle.fieldLabel};
28
+ color: ${(p) => p.theme.colors.interactive01};
29
+ `;
30
+
31
+ const CheckWrapper = styled(Cell)`
32
+ label {
33
+ margin-bottom: ${(p) => p.theme.spacing.s};
34
+ }
35
+ > div {
36
+ width: ${(p) => p.theme.spacing.s};
37
+ }
38
+ `;
39
+
40
+ export { Wrapper, IconWrapper, BarWrapper, Text, CheckWrapper };
@@ -36,6 +36,7 @@ import { getSortedListStatus } from "./utils";
36
36
  import Type from "./FileFilters/Type";
37
37
  import SortBy from "./FileFilters/SortBy";
38
38
  import Usage from "./FileFilters/Usage";
39
+ import UploadItem from "./UploadItem";
39
40
 
40
41
  import * as S from "./style";
41
42
 
@@ -54,6 +55,7 @@ const FileDrive = (props: IProps) => {
54
55
  updateDisplayMode,
55
56
  updateTab,
56
57
  downloadFiles,
58
+ uploadFile,
57
59
  breadcrumb,
58
60
  isLoading,
59
61
  isSaving,
@@ -73,6 +75,7 @@ const FileDrive = (props: IProps) => {
73
75
  const [galleryItems, setGalleryItems] = useState<number[]>(filesIds);
74
76
  const [galleryDelete, setGalleryDelete] = useState(true);
75
77
  const [numDocs, setNumDocs] = useState(1);
78
+ const [progressItems, setProgressItems] = useState<number[]>([]);
76
79
  const tableRef = useRef<HTMLDivElement>(null);
77
80
  const { isOpen: isNewOpen, toggleModal: toggleNewModal } = useModal();
78
81
  const { isOpen: isUploadOpen, toggleModal: toggleUploadModal } = useModal();
@@ -147,6 +150,7 @@ const FileDrive = (props: IProps) => {
147
150
  folderID: currentFolderID,
148
151
  search: searchQuery,
149
152
  query: currentFilterQuery,
153
+ loading: true,
150
154
  };
151
155
 
152
156
  return params;
@@ -238,24 +242,46 @@ const FileDrive = (props: IProps) => {
238
242
  toggleModal();
239
243
  };
240
244
 
241
- /*const handleSingleUpload = async (file: IFile) => {
242
- setFileSelected(file);
245
+ const handleUpload = async (files: IFile[]) => {
246
+ setFileSelected(files[0]);
243
247
  isUploadOpen && toggleUploadModal();
244
248
  toggleModal();
245
249
  const params = getParams();
246
250
  await getFolderContent(params);
247
- };*/
251
+ };
248
252
 
249
- const handleMultipleUpload = async (files: IFile[]) => {
250
- setFileSelected(files[0]);
253
+ const uploadMultipleFiles = async (files: File[], progressArray: number[]) => {
254
+ try {
255
+ let index = 0;
256
+ while (files.length) {
257
+ const handleProgress = (progress: number) => {
258
+ setProgressItems((prevState) => [...prevState.slice(0, index), progress, ...prevState.slice(index + 1)]);
259
+ };
260
+
261
+ const file = files.shift();
262
+ file && (await uploadFile(file, currentFolderID, siteID || "global", handleProgress));
263
+ index = index + 1;
264
+ }
265
+ } catch (error) {
266
+ console.log(error);
267
+ }
268
+ };
269
+
270
+ const handleMultipleUpload = async (files: File[]) => {
251
271
  isUploadOpen && toggleUploadModal();
252
- toggleModal();
272
+
273
+ const initialProgress = files.map(() => 0);
274
+ setProgressItems(initialProgress);
275
+
276
+ await uploadMultipleFiles(files, initialProgress);
277
+
278
+ setProgressItems([]);
253
279
  const params = getParams();
254
280
  await getFolderContent(params);
255
281
  };
256
282
 
257
283
  const handleCloseModal = () => {
258
- if(isDirty){
284
+ if (isDirty) {
259
285
  toggleSaveModal();
260
286
  } else {
261
287
  resetFileModal();
@@ -268,7 +294,7 @@ const FileDrive = (props: IProps) => {
268
294
  setGalleryDelete(true);
269
295
  resetDirty();
270
296
  toggleModal();
271
- }
297
+ };
272
298
 
273
299
  const handleDeleteFile = async (fileID: number) => {
274
300
  const isDeleted = await deleteFile(fileID, siteID);
@@ -407,6 +433,9 @@ const FileDrive = (props: IProps) => {
407
433
 
408
434
  const GridList = () => (
409
435
  <S.GridWrapper isSearching={isSearching}>
436
+ {progressItems.map((progress: number, index: number) => (
437
+ <UploadItem uploadPercentage={progress} grid={true} key={index} />
438
+ ))}
410
439
  {items.map((file: IFile, i: number) => {
411
440
  const isItemSelected = isSelected(file.id);
412
441
  return (
@@ -431,25 +460,30 @@ const FileDrive = (props: IProps) => {
431
460
 
432
461
  const ListTable = () => (
433
462
  <TableList tableHeader={Header} hasFixedHeader={true} tableRef={tableRef} className="no-padding">
434
- {items.map((file: IFile, i: number) => {
435
- const isItemSelected = isSelected(file.id);
436
- return (
437
- <ListItem
438
- file={file}
439
- isSelected={isItemSelected}
440
- onChange={addToBulkSelection}
441
- key={`${file.fileName}${file.id}`}
442
- onClick={handleClick}
443
- onDelete={handleDeleteFile}
444
- onMove={handleMoveFile}
445
- currentFolderID={currentFolderID}
446
- isAllowedToDelete={allowedToDeleteFile}
447
- isAllowedToEdit={allowedToEditFile}
448
- isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
449
- isSearching={isSearching}
450
- />
451
- );
452
- })}
463
+ <>
464
+ {progressItems.map((progress: number, index: number) => (
465
+ <UploadItem uploadPercentage={progress} grid={false} key={index} />
466
+ ))}
467
+ {items.map((file: IFile, i: number) => {
468
+ const isItemSelected = isSelected(file.id);
469
+ return (
470
+ <ListItem
471
+ file={file}
472
+ isSelected={isItemSelected}
473
+ onChange={addToBulkSelection}
474
+ key={`${file.fileName}${file.id}`}
475
+ onClick={handleClick}
476
+ onDelete={handleDeleteFile}
477
+ onMove={handleMoveFile}
478
+ currentFolderID={currentFolderID}
479
+ isAllowedToDelete={allowedToDeleteFile}
480
+ isAllowedToEdit={allowedToEditFile}
481
+ isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
482
+ isSearching={isSearching}
483
+ />
484
+ );
485
+ })}
486
+ </>
453
487
  </TableList>
454
488
  );
455
489
 
@@ -522,7 +556,7 @@ const FileDrive = (props: IProps) => {
522
556
  const handleMainAction = () => {
523
557
  toggleSaveModal();
524
558
  resetFileModal();
525
- }
559
+ };
526
560
 
527
561
  const mainSaveModalAction = {
528
562
  title: "Yes, discard changes",
@@ -550,7 +584,6 @@ const FileDrive = (props: IProps) => {
550
584
  <S.Filters>
551
585
  <Type filterItems={filterItems} value={filterValues["filterType"]} />
552
586
  <SortBy sortItems={sortItems} sortedState={sortedListStatus} />
553
- <Usage filterItems={filterItems} value={filterValues["filterUsage"]} />
554
587
  </S.Filters>
555
588
  );
556
589
 
@@ -673,7 +706,8 @@ const FileDrive = (props: IProps) => {
673
706
  <FileDragAndDrop
674
707
  validFormats={validFormats}
675
708
  folderID={currentFolderID}
676
- handleUpload={handleMultipleUpload}
709
+ handleUpload={handleUpload}
710
+ handleMultipleUpload={handleMultipleUpload}
677
711
  siteID={siteID}
678
712
  />
679
713
  )}
@@ -752,6 +786,12 @@ interface IProps {
752
786
  updateDisplayMode(displayMode: "grid" | "list"): Promise<void>;
753
787
  updateTab(tab: "local" | "global"): Promise<void>;
754
788
  downloadFiles(fileID: number | number[], zip: boolean, fileName?: string): Promise<void>;
789
+ uploadFile: (
790
+ docFiles: File | File[],
791
+ folderID: number | null,
792
+ siteID: number | "global",
793
+ setProgress?: (progress: number) => void
794
+ ) => Promise<IFile | null>;
755
795
  }
756
796
 
757
797
  export interface IFolderFormState {
@@ -776,6 +816,7 @@ const mapDispatchToProps = {
776
816
  updateDisplayMode: fileDriveActions.updateDisplayMode,
777
817
  updateTab: fileDriveActions.updateTab,
778
818
  downloadFiles: fileDriveActions.downloadFiles,
819
+ uploadFile: fileDriveActions.uploadFile,
779
820
  };
780
821
 
781
822
  const mapStateToProps = (state: IRootState) => ({
@@ -912,6 +912,24 @@ export interface IFile {
912
912
  title: string;
913
913
  site: number | null;
914
914
  folder: { folderId: number; folderName: string } | null;
915
+ contentInUse: {
916
+ pages: IFileUsePages[];
917
+ simpleStructuredData: IFileUseItem[];
918
+ globalPages: IFileUseItem[];
919
+ };
920
+ }
921
+
922
+ export interface IFileUsePages {
923
+ siteId: number;
924
+ siteName: string;
925
+ pages: IFileUseItem[];
926
+ }
927
+
928
+ export interface IFileUseItem {
929
+ id: number;
930
+ title: string;
931
+ structuredDataId?: string;
932
+ published: Date;
915
933
  }
916
934
 
917
935
  export interface IFolder {