@scality/data-browser-library 1.0.0-preview.11 → 1.0.0-preview.13

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 (40) hide show
  1. package/dist/components/DataBrowserUI.d.ts +20 -0
  2. package/dist/components/DataBrowserUI.js +64 -0
  3. package/dist/components/__tests__/BucketDetails.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketDetails.test.js +421 -0
  5. package/dist/components/__tests__/BucketList.test.js +389 -164
  6. package/dist/components/__tests__/BucketOverview.test.js +19 -63
  7. package/dist/components/__tests__/ObjectList.test.js +719 -219
  8. package/dist/components/buckets/BucketDetails.d.ts +40 -0
  9. package/dist/components/buckets/BucketDetails.js +194 -86
  10. package/dist/components/buckets/BucketList.d.ts +5 -6
  11. package/dist/components/buckets/BucketList.js +152 -97
  12. package/dist/components/buckets/BucketOverview.d.ts +6 -0
  13. package/dist/components/buckets/BucketOverview.js +363 -179
  14. package/dist/components/buckets/BucketPage.js +1 -5
  15. package/dist/components/buckets/BucketVersioning.js +3 -0
  16. package/dist/components/buckets/EmptyBucketButton.js +1 -1
  17. package/dist/components/index.d.ts +2 -1
  18. package/dist/components/index.js +2 -1
  19. package/dist/components/layouts/ArrowNavigation.js +20 -8
  20. package/dist/components/objects/CreateFolderButton.js +1 -1
  21. package/dist/components/objects/ObjectDetails/ObjectSummary.js +287 -157
  22. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.d.ts +1 -0
  23. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +516 -0
  24. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.d.ts +1 -0
  25. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +813 -0
  26. package/dist/components/objects/ObjectDetails/index.d.ts +16 -0
  27. package/dist/components/objects/ObjectDetails/index.js +132 -46
  28. package/dist/components/objects/ObjectList.d.ts +7 -5
  29. package/dist/components/objects/ObjectList.js +566 -286
  30. package/dist/components/objects/UploadButton.js +1 -1
  31. package/dist/config/types.d.ts +117 -0
  32. package/dist/contexts/DataBrowserUICustomizationContext.d.ts +27 -0
  33. package/dist/contexts/DataBrowserUICustomizationContext.js +13 -0
  34. package/dist/test/testUtils.d.ts +64 -0
  35. package/dist/test/testUtils.js +100 -1
  36. package/dist/types/index.d.ts +5 -3
  37. package/dist/utils/constants.d.ts +7 -0
  38. package/dist/utils/constants.js +8 -1
  39. package/dist/utils/useFeatures.js +1 -1
  40. package/package.json +2 -2
@@ -1,37 +1,254 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { ConstrainedText, Icon, Loader, spacing } from "@scality/core-ui";
2
+ import { createContext, memo, useContext, useMemo } from "react";
3
+ import { ConstrainedText, Icon, Loader, Stack, spacing } from "@scality/core-ui";
3
4
  import { Box, Button } from "@scality/core-ui/dist/next";
4
- import { createContext, useContext } from "react";
5
+ import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
5
6
  import { useGetBucketAcl, useGetBucketCors, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketVersioning } from "../../hooks/index.js";
6
- import { useDataBrowserContext } from "../providers/DataBrowserProvider.js";
7
7
  import { isNotFoundError } from "../../utils/errorHandling.js";
8
+ import { EditRetentionButton } from "../objects/ObjectLock/EditRetentionButton.js";
9
+ import { useDataBrowserContext } from "../providers/DataBrowserProvider.js";
8
10
  import { Body, Group, GroupContent, GroupName, GroupValues, Key, Row, Table, TableContainer, Value } from "../ui/Table.elements.js";
9
11
  import { BucketLocation } from "./BucketLocation.js";
10
12
  import { BucketPolicyButton } from "./BucketPolicyButton.js";
11
- import { EditRetentionButton } from "../objects/ObjectLock/EditRetentionButton.js";
13
+ const ERROR_NO_SUCH_BUCKET_POLICY = "NoSuchBucketPolicy";
14
+ const DEFAULT_PUBLIC_ACL_URI = "http://acs.amazonaws.com/groups/global/AllUsers";
15
+ const STATUS_PENDING = "pending";
16
+ const STATUS_ERROR = "error";
17
+ const VERSIONING_STATUS_ENABLED = "Enabled";
18
+ const OBJECT_LOCK_ENABLED = "Enabled";
19
+ const ErrorField = ()=>/*#__PURE__*/ jsxs(Stack, {
20
+ direction: "horizontal",
21
+ children: [
22
+ /*#__PURE__*/ jsx(Icon, {
23
+ name: "Exclamation-circle",
24
+ color: "statusWarning"
25
+ }),
26
+ " Error"
27
+ ]
28
+ });
29
+ const resolveFieldValue = (bucketName, config)=>{
30
+ if (void 0 !== config.slotOverride) return config.slotOverride;
31
+ if (config.renderOverride) return config.renderOverride(bucketName);
32
+ return config.defaultValue;
33
+ };
34
+ const buildCustomField = (fieldConfig, entityName)=>{
35
+ const RenderComponent = fieldConfig.render;
36
+ return {
37
+ id: fieldConfig.id,
38
+ label: fieldConfig.label || fieldConfig.id,
39
+ value: /*#__PURE__*/ jsx(RenderComponent, {
40
+ entityName: entityName
41
+ })
42
+ };
43
+ };
44
+ const mergeFieldsWithExtras = (defaultFields, extraFieldConfigs, entityName, fieldOrder)=>{
45
+ const fieldsMap = {
46
+ ...defaultFields
47
+ };
48
+ const extraFields = [];
49
+ extraFieldConfigs?.forEach((fieldConfig)=>{
50
+ const fieldItem = buildCustomField(fieldConfig, entityName);
51
+ if (fieldsMap[fieldConfig.id]) fieldsMap[fieldConfig.id] = fieldItem;
52
+ else extraFields.push(fieldItem);
53
+ });
54
+ return [
55
+ ...fieldOrder.map((key)=>fieldsMap[key]),
56
+ ...extraFields
57
+ ];
58
+ };
59
+ const createNameField = (bucketName, nameField, renderName)=>({
60
+ id: "name",
61
+ label: "Name",
62
+ value: resolveFieldValue(bucketName, {
63
+ slotOverride: nameField,
64
+ renderOverride: renderName,
65
+ defaultValue: /*#__PURE__*/ jsx(ConstrainedText, {
66
+ text: bucketName,
67
+ lineClamp: 2
68
+ })
69
+ })
70
+ });
71
+ const createVersioningField = (bucketName, versioningData, versioningStatus, versioningField, renderVersioning)=>{
72
+ let defaultValue = "Inactive";
73
+ if (versioningStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
74
+ else if (versioningStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
75
+ else if (versioningData?.Status === VERSIONING_STATUS_ENABLED) defaultValue = "Active";
76
+ return {
77
+ id: "versioning",
78
+ label: "Versioning",
79
+ value: resolveFieldValue(bucketName, {
80
+ slotOverride: versioningField,
81
+ renderOverride: renderVersioning,
82
+ defaultValue
83
+ })
84
+ };
85
+ };
86
+ const createLocationField = (bucketName, locationField, renderLocation)=>({
87
+ id: "location",
88
+ label: "Location",
89
+ value: resolveFieldValue(bucketName, {
90
+ slotOverride: locationField,
91
+ renderOverride: renderLocation,
92
+ defaultValue: /*#__PURE__*/ jsx(BucketLocation, {
93
+ bucketName: bucketName
94
+ })
95
+ })
96
+ });
97
+ const createObjectLockField = (bucketName, objectLockStatus, isObjectLockNotConfigured, isObjectLockEnabled, objectLockField, renderObjectLock)=>{
98
+ let defaultValue;
99
+ defaultValue = objectLockStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : objectLockStatus !== STATUS_ERROR || isObjectLockNotConfigured ? isObjectLockEnabled ? "Enabled" : "Disabled" : /*#__PURE__*/ jsx(ErrorField, {});
100
+ return {
101
+ id: "objectLock",
102
+ label: "Object-lock",
103
+ value: resolveFieldValue(bucketName, {
104
+ slotOverride: objectLockField,
105
+ renderOverride: renderObjectLock,
106
+ defaultValue
107
+ })
108
+ };
109
+ };
110
+ const createDefaultRetentionField = (bucketName, objectLockData, objectLockStatus, defaultRetentionField, renderDefaultRetention)=>{
111
+ const defaultValue = objectLockStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : getDefaultBucketRetention(objectLockData);
112
+ return {
113
+ id: "defaultRetention",
114
+ label: "Default Retention",
115
+ value: resolveFieldValue(bucketName, {
116
+ slotOverride: defaultRetentionField,
117
+ renderOverride: renderDefaultRetention,
118
+ defaultValue
119
+ }),
120
+ actions: /*#__PURE__*/ jsx(EditRetentionButton, {
121
+ bucketName: bucketName
122
+ })
123
+ };
124
+ };
125
+ const createOwnerField = (bucketName, aclData, aclStatus, ownerField, renderOwner)=>{
126
+ let defaultValue = "N/A";
127
+ if (aclStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
128
+ else if (aclStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
129
+ else if (aclData?.Owner?.DisplayName) defaultValue = aclData.Owner.DisplayName;
130
+ return {
131
+ id: "owner",
132
+ label: "Owner",
133
+ value: resolveFieldValue(bucketName, {
134
+ slotOverride: ownerField,
135
+ renderOverride: renderOwner,
136
+ defaultValue
137
+ })
138
+ };
139
+ };
140
+ const createAclField = (bucketName, aclData, aclStatus, aclField, renderAcl)=>{
141
+ let defaultValue = "N/A";
142
+ if (aclStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
143
+ else if (aclStatus === STATUS_ERROR) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
144
+ else if (aclData?.Grants?.length) {
145
+ const count = aclData.Grants.length;
146
+ defaultValue = `${count} Grantee${1 !== count ? "s" : ""}`;
147
+ }
148
+ return {
149
+ id: "acl",
150
+ label: "ACL",
151
+ value: resolveFieldValue(bucketName, {
152
+ slotOverride: aclField,
153
+ renderOverride: renderAcl,
154
+ defaultValue
155
+ })
156
+ };
157
+ };
158
+ const createCorsField = (bucketName, corsData, corsStatus, corsField, renderCors)=>{
159
+ let defaultValue = "No";
160
+ if (corsStatus === STATUS_PENDING) defaultValue = /*#__PURE__*/ jsx(Loader, {});
161
+ else if (corsData?.CORSRules && corsData.CORSRules.length > 0) defaultValue = "Yes";
162
+ return {
163
+ id: "cors",
164
+ label: "CORS",
165
+ value: resolveFieldValue(bucketName, {
166
+ slotOverride: corsField,
167
+ renderOverride: renderCors,
168
+ defaultValue
169
+ })
170
+ };
171
+ };
172
+ const createPublicField = (bucketName, aclStatus, isPublic, publicField, renderPublic)=>{
173
+ const defaultValue = aclStatus === STATUS_PENDING ? /*#__PURE__*/ jsx(Loader, {}) : isPublic ? "Yes" : "No";
174
+ return {
175
+ id: "public",
176
+ label: "Public",
177
+ value: resolveFieldValue(bucketName, {
178
+ slotOverride: publicField,
179
+ renderOverride: renderPublic,
180
+ defaultValue
181
+ })
182
+ };
183
+ };
184
+ const createBucketPolicyField = (bucketName, policyData, policyStatus, policyError, onEditPolicy, bucketPolicyField, renderBucketPolicy)=>{
185
+ let defaultValue;
186
+ if (policyStatus === STATUS_ERROR && policyError?.name !== ERROR_NO_SUCH_BUCKET_POLICY) defaultValue = /*#__PURE__*/ jsx(ErrorField, {});
187
+ else {
188
+ const hasPolicy = !!policyData?.Policy;
189
+ const handleEditPolicy = ()=>onEditPolicy?.(bucketName);
190
+ defaultValue = /*#__PURE__*/ jsxs(Box, {
191
+ display: "flex",
192
+ justifyContent: "space-between",
193
+ alignItems: "center",
194
+ gap: spacing.r8,
195
+ children: [
196
+ /*#__PURE__*/ jsx("span", {
197
+ children: hasPolicy ? "Configured" : "Not configured"
198
+ }),
199
+ /*#__PURE__*/ jsx(BucketPolicyButton, {
200
+ hasPolicy: hasPolicy,
201
+ isLoading: policyStatus === STATUS_PENDING,
202
+ onEdit: handleEditPolicy
203
+ })
204
+ ]
205
+ });
206
+ }
207
+ return {
208
+ id: "bucketPolicy",
209
+ label: "Bucket Policy",
210
+ value: resolveFieldValue(bucketName, {
211
+ slotOverride: bucketPolicyField,
212
+ renderOverride: renderBucketPolicy ? (name)=>renderBucketPolicy(name, onEditPolicy) : void 0,
213
+ defaultValue
214
+ })
215
+ };
216
+ };
12
217
  const BucketOverviewContext = /*#__PURE__*/ createContext(null);
13
218
  function useBucketOverviewContext() {
14
219
  const context = useContext(BucketOverviewContext);
15
220
  if (!context) throw new Error("BucketOverview components must be used within BucketOverview");
16
221
  return context;
17
222
  }
18
- function getDefaultBucketRetention(objectLockData) {
19
- if (!objectLockData?.ObjectLockConfiguration?.Rule?.DefaultRetention || !objectLockData.ObjectLockConfiguration.Rule.DefaultRetention.Days && !objectLockData.ObjectLockConfiguration.Rule.DefaultRetention.Years) return "Inactive";
20
- const defaultRetention = objectLockData.ObjectLockConfiguration.Rule.DefaultRetention;
21
- const retentionPeriod = defaultRetention.Days ? `${defaultRetention.Days} ${1 === defaultRetention.Days ? "day" : "days"}` : defaultRetention.Years ? `${defaultRetention.Years} ${1 === defaultRetention.Years ? "year" : "years"}` : "";
223
+ const getDefaultBucketRetention = (objectLockData)=>{
224
+ const defaultRetention = objectLockData?.ObjectLockConfiguration?.Rule?.DefaultRetention;
225
+ if (!defaultRetention || !defaultRetention.Days && !defaultRetention.Years) return "Inactive";
226
+ let retentionPeriod = "";
227
+ if (defaultRetention.Days) {
228
+ const unit = 1 === defaultRetention.Days ? "day" : "days";
229
+ retentionPeriod = `${defaultRetention.Days} ${unit}`;
230
+ } else if (defaultRetention.Years) {
231
+ const unit = 1 === defaultRetention.Years ? "year" : "years";
232
+ retentionPeriod = `${defaultRetention.Years} ${unit}`;
233
+ }
22
234
  const mode = defaultRetention.Mode || "GOVERNANCE";
23
235
  const capitalizedMode = mode.charAt(0).toUpperCase() + mode.slice(1).toLowerCase();
24
236
  return `${capitalizedMode} - ${retentionPeriod}`;
25
- }
26
- const BucketOverviewRoot = ({ bucketName, children })=>/*#__PURE__*/ jsx(BucketOverviewContext.Provider, {
27
- value: {
237
+ };
238
+ const BucketOverviewRoot = ({ bucketName, children })=>{
239
+ const contextValue = useMemo(()=>({
28
240
  bucketName
29
- },
241
+ }), [
242
+ bucketName
243
+ ]);
244
+ return /*#__PURE__*/ jsx(BucketOverviewContext.Provider, {
245
+ value: contextValue,
30
246
  children: /*#__PURE__*/ jsx(TableContainer, {
31
247
  children: children
32
248
  })
33
249
  });
34
- const Actions = ({ onEmptyBucket, onDeleteBucket, renderEmptyButton, renderDeleteButton, isEmptyBucketDisabled = true, isDeleteBucketDisabled = false })=>{
250
+ };
251
+ const Actions = /*#__PURE__*/ memo(({ onEmptyBucket, onDeleteBucket, renderEmptyButton, renderDeleteButton, isEmptyBucketDisabled = true, isDeleteBucketDisabled = false })=>{
35
252
  const { bucketName } = useBucketOverviewContext();
36
253
  return /*#__PURE__*/ jsxs(Box, {
37
254
  display: "flex",
@@ -45,10 +262,7 @@ const Actions = ({ onEmptyBucket, onDeleteBucket, renderEmptyButton, renderDelet
45
262
  disabled: isEmptyBucketDisabled,
46
263
  variant: "danger",
47
264
  label: "Empty Bucket",
48
- onClick: onEmptyBucket,
49
- style: {
50
- marginRight: "1rem"
51
- }
265
+ onClick: onEmptyBucket
52
266
  }),
53
267
  renderDeleteButton ? renderDeleteButton(bucketName) : /*#__PURE__*/ jsx(Button, {
54
268
  icon: /*#__PURE__*/ jsx(Icon, {
@@ -61,8 +275,9 @@ const Actions = ({ onEmptyBucket, onDeleteBucket, renderEmptyButton, renderDelet
61
275
  })
62
276
  ]
63
277
  });
64
- };
65
- const Section = ({ title, children })=>/*#__PURE__*/ jsxs(Group, {
278
+ });
279
+ Actions.displayName = "BucketOverview.Actions";
280
+ const Section = /*#__PURE__*/ memo(({ title, children })=>/*#__PURE__*/ jsxs(Group, {
66
281
  children: [
67
282
  /*#__PURE__*/ jsx(GroupName, {
68
283
  children: title
@@ -71,8 +286,9 @@ const Section = ({ title, children })=>/*#__PURE__*/ jsxs(Group, {
71
286
  children: children
72
287
  })
73
288
  ]
74
- });
75
- const Field = ({ label, value, loading, error, errorMessage = "Error", children, actions })=>{
289
+ }));
290
+ Section.displayName = "BucketOverview.Section";
291
+ const Field = /*#__PURE__*/ memo(({ label, value, loading, error, errorMessage = "Error", children, actions })=>{
76
292
  const renderValue = ()=>{
77
293
  if (loading) return /*#__PURE__*/ jsx(Loader, {});
78
294
  if (error) return errorMessage;
@@ -93,103 +309,98 @@ const Field = ({ label, value, loading, error, errorMessage = "Error", children,
93
309
  })
94
310
  ]
95
311
  });
96
- };
97
- const Sections = ({ children })=>/*#__PURE__*/ jsx(Table, {
312
+ });
313
+ Field.displayName = "BucketOverview.Field";
314
+ const Sections = /*#__PURE__*/ memo(({ children })=>/*#__PURE__*/ jsx(Table, {
98
315
  children: /*#__PURE__*/ jsx(Body, {
99
316
  children: children
100
317
  })
101
- });
102
- const GeneralSection = ({ nameField, versioningField, locationField, renderName, renderVersioning, renderLocation })=>{
318
+ }));
319
+ Sections.displayName = "BucketOverview.Sections";
320
+ const GeneralSection = /*#__PURE__*/ memo(({ nameField, versioningField, locationField, renderName, renderVersioning, renderLocation })=>{
103
321
  const { bucketName } = useBucketOverviewContext();
322
+ const { extraBucketOverviewGeneral } = useDataBrowserUICustomization();
104
323
  const { data: versioningData, status: versioningStatus } = useGetBucketVersioning({
105
324
  Bucket: bucketName
106
325
  });
107
- return /*#__PURE__*/ jsxs(Section, {
326
+ const fields = useMemo(()=>{
327
+ const defaultFields = {
328
+ name: createNameField(bucketName, nameField, renderName),
329
+ versioning: createVersioningField(bucketName, versioningData, versioningStatus, versioningField, renderVersioning),
330
+ location: createLocationField(bucketName, locationField, renderLocation)
331
+ };
332
+ return mergeFieldsWithExtras(defaultFields, extraBucketOverviewGeneral, bucketName, [
333
+ "name",
334
+ "versioning",
335
+ "location"
336
+ ]);
337
+ }, [
338
+ bucketName,
339
+ extraBucketOverviewGeneral,
340
+ nameField,
341
+ versioningField,
342
+ locationField,
343
+ renderName,
344
+ renderVersioning,
345
+ renderLocation,
346
+ versioningData,
347
+ versioningStatus
348
+ ]);
349
+ return /*#__PURE__*/ jsx(Section, {
108
350
  title: "General",
109
- children: [
110
- nameField ? /*#__PURE__*/ jsx(Field, {
111
- label: "Name",
112
- children: nameField
113
- }) : renderName ? /*#__PURE__*/ jsx(Field, {
114
- label: "Name",
115
- children: renderName(bucketName)
116
- }) : /*#__PURE__*/ jsx(Field, {
117
- label: "Name",
118
- value: /*#__PURE__*/ jsx(ConstrainedText, {
119
- text: bucketName,
120
- lineClamp: 2
121
- })
122
- }),
123
- versioningField ? /*#__PURE__*/ jsx(Field, {
124
- label: "Versioning",
125
- children: versioningField
126
- }) : renderVersioning ? /*#__PURE__*/ jsx(Field, {
127
- label: "Versioning",
128
- children: renderVersioning(bucketName)
129
- }) : /*#__PURE__*/ jsx(Field, {
130
- label: "Versioning",
131
- loading: "pending" === versioningStatus,
132
- error: "error" === versioningStatus,
133
- value: versioningData?.Status === "Enabled" ? "Active" : "Inactive"
134
- }),
135
- locationField ? /*#__PURE__*/ jsx(Field, {
136
- label: "Location",
137
- children: locationField
138
- }) : renderLocation ? /*#__PURE__*/ jsx(Field, {
139
- label: "Location",
140
- children: renderLocation(bucketName)
141
- }) : /*#__PURE__*/ jsx(Field, {
142
- label: "Location",
143
- value: /*#__PURE__*/ jsx(BucketLocation, {
144
- bucketName: bucketName
145
- })
146
- })
147
- ]
351
+ children: fields.map((field)=>/*#__PURE__*/ jsx(Field, {
352
+ label: field.label,
353
+ actions: field.actions,
354
+ children: field.value
355
+ }, field.id))
148
356
  });
149
- };
150
- const DataProtectionSection = ({ objectLockField, defaultRetentionField, renderObjectLock, renderDefaultRetention })=>{
357
+ });
358
+ GeneralSection.displayName = "BucketOverview.GeneralSection";
359
+ const DataProtectionSection = /*#__PURE__*/ memo(({ objectLockField, defaultRetentionField, renderObjectLock, renderDefaultRetention })=>{
151
360
  const { bucketName } = useBucketOverviewContext();
361
+ const { extraBucketOverviewDataProtection } = useDataBrowserUICustomization();
152
362
  const { data: objectLockData, status: objectLockStatus, error: objectLockError } = useGetBucketObjectLockConfiguration({
153
363
  Bucket: bucketName
154
364
  });
155
- const isObjectLockNotConfigured = "error" === objectLockStatus && isNotFoundError(objectLockError);
156
- const isObjectLockEnabled = objectLockData?.ObjectLockConfiguration?.ObjectLockEnabled === "Enabled";
157
- return /*#__PURE__*/ jsxs(Section, {
365
+ const isObjectLockNotConfigured = objectLockStatus === STATUS_ERROR && isNotFoundError(objectLockError);
366
+ const isObjectLockEnabled = objectLockData?.ObjectLockConfiguration?.ObjectLockEnabled === OBJECT_LOCK_ENABLED;
367
+ const fields = useMemo(()=>{
368
+ const defaultFields = {
369
+ objectLock: createObjectLockField(bucketName, objectLockStatus, isObjectLockNotConfigured, isObjectLockEnabled, objectLockField, renderObjectLock),
370
+ defaultRetention: createDefaultRetentionField(bucketName, objectLockData, objectLockStatus, defaultRetentionField, renderDefaultRetention)
371
+ };
372
+ const fieldOrder = isObjectLockEnabled ? [
373
+ "objectLock",
374
+ "defaultRetention"
375
+ ] : [
376
+ "objectLock"
377
+ ];
378
+ return mergeFieldsWithExtras(defaultFields, extraBucketOverviewDataProtection, bucketName, fieldOrder);
379
+ }, [
380
+ bucketName,
381
+ extraBucketOverviewDataProtection,
382
+ objectLockField,
383
+ defaultRetentionField,
384
+ renderObjectLock,
385
+ renderDefaultRetention,
386
+ objectLockData,
387
+ objectLockStatus,
388
+ isObjectLockNotConfigured,
389
+ isObjectLockEnabled
390
+ ]);
391
+ return /*#__PURE__*/ jsx(Section, {
158
392
  title: "Data protection",
159
- children: [
160
- objectLockField ? /*#__PURE__*/ jsx(Field, {
161
- label: "Object-lock",
162
- children: objectLockField
163
- }) : renderObjectLock ? /*#__PURE__*/ jsx(Field, {
164
- label: "Object-lock",
165
- children: renderObjectLock(bucketName)
166
- }) : /*#__PURE__*/ jsx(Field, {
167
- label: "Object-lock",
168
- loading: "pending" === objectLockStatus,
169
- error: "error" === objectLockStatus && !isObjectLockNotConfigured,
170
- value: isObjectLockEnabled ? "Enabled" : "Disabled"
171
- }),
172
- !defaultRetentionField && !renderDefaultRetention && isObjectLockEnabled && /*#__PURE__*/ jsx(Field, {
173
- label: "Default Retention",
174
- loading: "pending" === objectLockStatus,
175
- value: getDefaultBucketRetention(objectLockData),
176
- actions: /*#__PURE__*/ jsx(EditRetentionButton, {
177
- bucketName: bucketName
178
- })
179
- }),
180
- defaultRetentionField && /*#__PURE__*/ jsx(Field, {
181
- label: "Default Retention",
182
- children: defaultRetentionField
183
- }),
184
- renderDefaultRetention && /*#__PURE__*/ jsx(Field, {
185
- label: "Default Retention",
186
- children: renderDefaultRetention(bucketName)
187
- })
188
- ]
393
+ children: fields.map((field)=>/*#__PURE__*/ jsx(Field, {
394
+ label: field.label,
395
+ actions: field.actions,
396
+ children: field.value
397
+ }, field.id))
189
398
  });
190
- };
191
- const PermissionsSection = ({ onEditPolicy, ownerField, aclField, corsField, publicField, bucketPolicyField, renderOwner, renderAcl, renderCors, renderPublic, renderBucketPolicy })=>{
399
+ });
400
+ DataProtectionSection.displayName = "BucketOverview.DataProtectionSection";
401
+ const PermissionsSection = /*#__PURE__*/ memo(({ onEditPolicy, ownerField, aclField, corsField, publicField, bucketPolicyField, renderOwner, renderAcl, renderCors, renderPublic, renderBucketPolicy })=>{
192
402
  const { bucketName } = useBucketOverviewContext();
403
+ const { extraBucketOverviewPermissions } = useDataBrowserUICustomization();
193
404
  const { getS3Config } = useDataBrowserContext();
194
405
  const config = getS3Config();
195
406
  const { data: aclData, status: aclStatus } = useGetBucketAcl({
@@ -201,85 +412,58 @@ const PermissionsSection = ({ onEditPolicy, ownerField, aclField, corsField, pub
201
412
  const { data: policyData, error: policyError, status: policyStatus } = useGetBucketPolicy({
202
413
  Bucket: bucketName
203
414
  });
204
- const isPublic = aclData?.Grants?.find((grant)=>grant.Grantee?.URI === (config.publicAclIndicator || "http://acs.amazonaws.com/groups/global/AllUsers"));
205
- return /*#__PURE__*/ jsxs(Section, {
415
+ const isPublic = useMemo(()=>aclData?.Grants?.some((grant)=>grant.Grantee?.URI === (config.publicAclIndicator || DEFAULT_PUBLIC_ACL_URI)) ?? false, [
416
+ aclData?.Grants,
417
+ config.publicAclIndicator
418
+ ]);
419
+ const fields = useMemo(()=>{
420
+ const defaultFields = {
421
+ owner: createOwnerField(bucketName, aclData, aclStatus, ownerField, renderOwner),
422
+ acl: createAclField(bucketName, aclData, aclStatus, aclField, renderAcl),
423
+ cors: createCorsField(bucketName, corsData, corsStatus, corsField, renderCors),
424
+ public: createPublicField(bucketName, aclStatus, isPublic, publicField, renderPublic),
425
+ bucketPolicy: createBucketPolicyField(bucketName, policyData, policyStatus, policyError, onEditPolicy, bucketPolicyField, renderBucketPolicy)
426
+ };
427
+ return mergeFieldsWithExtras(defaultFields, extraBucketOverviewPermissions, bucketName, [
428
+ "owner",
429
+ "acl",
430
+ "cors",
431
+ "public",
432
+ "bucketPolicy"
433
+ ]);
434
+ }, [
435
+ bucketName,
436
+ extraBucketOverviewPermissions,
437
+ ownerField,
438
+ aclField,
439
+ corsField,
440
+ publicField,
441
+ bucketPolicyField,
442
+ renderOwner,
443
+ renderAcl,
444
+ renderCors,
445
+ renderPublic,
446
+ renderBucketPolicy,
447
+ onEditPolicy,
448
+ aclData,
449
+ aclStatus,
450
+ corsData,
451
+ corsStatus,
452
+ policyData,
453
+ policyError,
454
+ policyStatus,
455
+ isPublic
456
+ ]);
457
+ return /*#__PURE__*/ jsx(Section, {
206
458
  title: "Permissions",
207
- children: [
208
- ownerField ? /*#__PURE__*/ jsx(Field, {
209
- label: "Owner",
210
- children: ownerField
211
- }) : renderOwner ? /*#__PURE__*/ jsx(Field, {
212
- label: "Owner",
213
- children: renderOwner(bucketName)
214
- }) : /*#__PURE__*/ jsx(Field, {
215
- label: "Owner",
216
- loading: "pending" === aclStatus,
217
- error: "error" === aclStatus,
218
- value: aclData?.Owner?.DisplayName || "N/A"
219
- }),
220
- aclField ? /*#__PURE__*/ jsx(Field, {
221
- label: "ACL",
222
- children: aclField
223
- }) : renderAcl ? /*#__PURE__*/ jsx(Field, {
224
- label: "ACL",
225
- children: renderAcl(bucketName)
226
- }) : /*#__PURE__*/ jsx(Field, {
227
- label: "ACL",
228
- loading: "pending" === aclStatus,
229
- error: "error" === aclStatus,
230
- value: aclData?.Grants?.length ? `${aclData.Grants.length} Grantee${1 !== aclData.Grants.length ? "s" : ""}` : "N/A"
231
- }),
232
- corsField ? /*#__PURE__*/ jsx(Field, {
233
- label: "CORS",
234
- children: corsField
235
- }) : renderCors ? /*#__PURE__*/ jsx(Field, {
236
- label: "CORS",
237
- children: renderCors(bucketName)
238
- }) : /*#__PURE__*/ jsx(Field, {
239
- label: "CORS",
240
- loading: "pending" === corsStatus,
241
- value: corsData?.CORSRules && corsData.CORSRules.length > 0 ? "Yes" : "No"
242
- }),
243
- publicField ? /*#__PURE__*/ jsx(Field, {
244
- label: "Public",
245
- children: publicField
246
- }) : renderPublic ? /*#__PURE__*/ jsx(Field, {
247
- label: "Public",
248
- children: renderPublic(bucketName)
249
- }) : /*#__PURE__*/ jsx(Field, {
250
- label: "Public",
251
- loading: "pending" === aclStatus,
252
- value: isPublic ? "Yes" : "No"
253
- }),
254
- bucketPolicyField ? /*#__PURE__*/ jsx(Field, {
255
- label: "Bucket Policy",
256
- children: bucketPolicyField
257
- }) : renderBucketPolicy ? /*#__PURE__*/ jsx(Field, {
258
- label: "Bucket Policy",
259
- children: renderBucketPolicy(bucketName, onEditPolicy)
260
- }) : /*#__PURE__*/ jsx(Field, {
261
- label: "Bucket Policy",
262
- error: "error" === policyStatus && policyError?.name !== "NoSuchBucketPolicy",
263
- children: /*#__PURE__*/ jsxs(Box, {
264
- display: "flex",
265
- justifyContent: "space-between",
266
- alignItems: "center",
267
- gap: spacing.r8,
268
- children: [
269
- /*#__PURE__*/ jsx("span", {
270
- children: policyData?.Policy ? "Configured" : "Not configured"
271
- }),
272
- /*#__PURE__*/ jsx(BucketPolicyButton, {
273
- hasPolicy: !!policyData?.Policy,
274
- isLoading: "pending" === policyStatus,
275
- onEdit: ()=>onEditPolicy?.(bucketName)
276
- })
277
- ]
278
- })
279
- })
280
- ]
459
+ children: fields.map((field)=>/*#__PURE__*/ jsx(Field, {
460
+ label: field.label,
461
+ actions: field.actions,
462
+ children: field.value
463
+ }, field.id))
281
464
  });
282
- };
465
+ });
466
+ PermissionsSection.displayName = "BucketOverview.PermissionsSection";
283
467
  const BucketOverview = BucketOverviewRoot;
284
468
  BucketOverview.Actions = Actions;
285
469
  BucketOverview.Sections = Sections;
@@ -5,7 +5,6 @@ import { Navigate, useNavigate, useParams } from "react-router-dom";
5
5
  import { BrowserPageLayout } from "../layouts/BrowserPageLayout.js";
6
6
  import { BucketDetails } from "./BucketDetails.js";
7
7
  import { BucketList } from "./BucketList.js";
8
- import { BucketLocation } from "./BucketLocation.js";
9
8
  const BucketPage_BucketPage = ()=>{
10
9
  const { data, status } = useBuckets();
11
10
  const { bucketName } = useParams();
@@ -35,10 +34,7 @@ const BucketPage_BucketPage = ()=>{
35
34
  selectedBucketName: bucketName,
36
35
  onBucketSelect: (bucketName)=>navigate(`/buckets/${bucketName}`),
37
36
  onCreateBucket: ()=>navigate("/buckets/-/create"),
38
- onNavigateToBucket: (bucketName)=>navigate(`/buckets/${bucketName}/objects`),
39
- renderBucketLocation: (bucketName)=>/*#__PURE__*/ jsx(BucketLocation, {
40
- bucketName: bucketName
41
- })
37
+ onNavigateToBucket: (bucketName)=>navigate(`/buckets/${bucketName}/objects`)
42
38
  }),
43
39
  rightPanel: /*#__PURE__*/ jsx(BucketDetails, {})
44
40
  });
@@ -58,6 +58,9 @@ function BucketVersioning({ tooltipOverlay }) {
58
58
  }) : "error" === versioningStatus ? /*#__PURE__*/ jsx(Fragment, {
59
59
  children: versioningError?.message
60
60
  }) : null,
61
+ overlayStyle: {
62
+ maxWidth: "16.5rem"
63
+ },
61
64
  children: /*#__PURE__*/ jsx(Toggle, {
62
65
  id: "versioningToggle",
63
66
  disabled: "pending" === versioningStatus || "pending" === objectLockStatus || "error" === versioningStatus || isVeeamBucket,
@@ -21,7 +21,7 @@ const RetentionText = ()=>/*#__PURE__*/ jsx(Text, {
21
21
  children: "If some of the objects you are trying to delete are locked in governance mode, confirming the deletion will effectively delete them as the governance retention will be bypassed."
22
22
  });
23
23
  const LifecycleText = ()=>/*#__PURE__*/ jsx(Text, {
24
- children: "The action of emptying can erase a maximum of 20,000 objects. For buckets with a substantial number of objects, an expiration workflow (lifecycle rule) could provide an effective alternative to emptying the bucket."
24
+ children: "The action of emptying can erase a maximum of 20 000 objects. For buckets with a substantial number of objects, an expiration workflow (lifecycle rule) could provide an effective alternative to emptying the bucket."
25
25
  });
26
26
  const EmptyBucketButton = ({ bucketName })=>{
27
27
  const [confirmText, setConfirmText] = useState("");