@ossy/resources 3.10.0 → 3.11.0

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 (86) hide show
  1. package/package.json +4 -4
  2. package/src/CreateDirectory.jsx +47 -53
  3. package/src/CreateDocument.jsx +1 -0
  4. package/src/GenericResourceForm.jsx +49 -31
  5. package/src/ResourceDialogMove.jsx +99 -50
  6. package/src/ResourceGrid.jsx +36 -84
  7. package/src/ResourceList.jsx +10 -1
  8. package/src/ResourcePanel.jsx +96 -17
  9. package/src/ResourceShareDialog.jsx +50 -12
  10. package/src/Upload.jsx +25 -8
  11. package/src/batch-delete.flow.js +81 -0
  12. package/src/batch-download.flow.js +69 -0
  13. package/src/cancel-upload.action.js +4 -0
  14. package/src/cancel-upload.flow.js +40 -0
  15. package/src/close-share.action.js +4 -0
  16. package/src/confirm-upload.action.js +4 -0
  17. package/src/copy-share-link.action.js +4 -0
  18. package/src/create-directory.action.js +5 -0
  19. package/src/create-directory.flow.js +30 -0
  20. package/src/create-directory.form.js +5 -0
  21. package/src/create-directory.schema.js +9 -0
  22. package/src/create-document.flow.js +36 -0
  23. package/src/create-document.form.js +5 -0
  24. package/src/create-document.schema.js +11 -0
  25. package/src/create.action.js +5 -1
  26. package/src/delete-directory.flow.js +39 -0
  27. package/src/delete-file.flow.js +32 -0
  28. package/src/done-selecting.action.js +5 -0
  29. package/src/download-file.flow.js +31 -0
  30. package/src/download-resource.action.js +4 -0
  31. package/src/download-selected.action.js +4 -0
  32. package/src/en.translations.json +18 -0
  33. package/src/finish-upload.action.js +4 -0
  34. package/src/index.js +42 -0
  35. package/src/list.task.js +17 -6
  36. package/src/list.task.spec.js +81 -0
  37. package/src/make-private-resource.flow.js +72 -0
  38. package/src/make-private.action.js +4 -0
  39. package/src/make-public.action.js +4 -0
  40. package/src/move-file.flow.js +78 -0
  41. package/src/move.form.js +5 -0
  42. package/src/move.schema.js +9 -0
  43. package/src/open-delete-directory.action.js +7 -0
  44. package/src/open-move.action.js +4 -0
  45. package/src/open-remove-selected.action.js +5 -0
  46. package/src/open-rename.action.js +4 -0
  47. package/src/open-share.action.js +4 -0
  48. package/src/open-sort.action.js +5 -0
  49. package/src/open-view.action.js +5 -0
  50. package/src/rename-file.flow.js +43 -0
  51. package/src/rename.form.js +5 -0
  52. package/src/rename.schema.js +9 -0
  53. package/src/resource-list-query.js +182 -0
  54. package/src/resource-list-query.spec.js +75 -0
  55. package/src/resource-selection.js +25 -0
  56. package/src/resource-selection.spec.js +40 -0
  57. package/src/resource.helpers.js +8 -1
  58. package/src/resources-list-options.js +169 -0
  59. package/src/resources-list-options.spec.js +141 -0
  60. package/src/resources.action-media.js +6 -0
  61. package/src/resources.queries.js +91 -6
  62. package/src/search-payload.js +10 -0
  63. package/src/search-payload.spec.js +20 -0
  64. package/src/search.task.js +2 -1
  65. package/src/share-resource.flow.js +59 -0
  66. package/src/sort-ascending.action.js +5 -0
  67. package/src/sort-by-created.action.js +5 -0
  68. package/src/sort-by-last-updated.action.js +5 -0
  69. package/src/sort-by-name.action.js +5 -0
  70. package/src/sort-by-size.action.js +5 -0
  71. package/src/sort-by-type.action.js +5 -0
  72. package/src/sort-descending.action.js +5 -0
  73. package/src/sort-storage-created.flow.js +87 -0
  74. package/src/sort-storage-last-updated.flow.js +119 -0
  75. package/src/sort-storage-name-asc.flow.js +34 -0
  76. package/src/sort-storage-size-desc.flow.js +34 -0
  77. package/src/sort-storage-size.flow.js +87 -0
  78. package/src/sort-storage-type.flow.js +83 -0
  79. package/src/sort-storage.flow.js +86 -0
  80. package/src/sv.translations.json +18 -0
  81. package/src/toggle-selection-mode.action.js +4 -0
  82. package/src/upload-file.flow.js +44 -0
  83. package/src/useResourceList.js +94 -0
  84. package/src/view-grid.action.js +5 -0
  85. package/src/view-list.action.js +5 -0
  86. package/src/view-storage.flow.js +49 -0
@@ -0,0 +1,34 @@
1
+ import { metadata as OpenSort } from './open-sort.action.js'
2
+ import { metadata as SortDescending } from './sort-descending.action.js'
3
+ import sortStorageSizeFlow from './sort-storage-size.flow.js'
4
+
5
+ export const metadata = {
6
+ id: '@ossy/resources/flows/sort-storage-size-desc',
7
+ feature: 'resources',
8
+ requires: ['server', 'database'],
9
+ timeout: 180_000,
10
+ }
11
+
12
+ /**
13
+ * After size-ascending sort is established, flip to descending and assert the
14
+ * larger file precedes the smaller one under size desc markers.
15
+ */
16
+ export default {
17
+ title: 'Sort storage resources by size descending',
18
+ description:
19
+ 'Signed-in user sorts by size ascending, then switches to descending and sees the reversed size order',
20
+ steps: [
21
+ ...sortStorageSizeFlow.steps,
22
+ { result: { action: OpenSort, timeout: 10000 } },
23
+ { action: OpenSort },
24
+ { result: { action: SortDescending, timeout: 10000 } },
25
+ { action: SortDescending },
26
+ { result: { selector: '[data-sort-by="size"][data-sort-order="desc"]', timeout: 10000 } },
27
+ {
28
+ result: {
29
+ selector: '[data-resource-name="e2e-sort-a.txt"] ~ [data-resource-name="e2e-sort-z.txt"]',
30
+ timeout: 10000,
31
+ },
32
+ },
33
+ ],
34
+ }
@@ -0,0 +1,87 @@
1
+ import signUpFlow from '@ossy/authentication/sign-up.flow.js'
2
+ import { metadata as ConfirmUpload } from './confirm-upload.action.js'
3
+ import { metadata as FinishUpload } from './finish-upload.action.js'
4
+ import { metadata as OpenSort } from './open-sort.action.js'
5
+ import { metadata as SortBySize } from './sort-by-size.action.js'
6
+
7
+ export const metadata = {
8
+ id: '@ossy/resources/flows/sort-storage-size',
9
+ feature: 'resources',
10
+ requires: ['server', 'database'],
11
+ timeout: 180_000,
12
+ }
13
+
14
+ /**
15
+ * Upload two files whose name order is opposite their size order, open the
16
+ * storage sort menu, switch to size ascending, and assert list order + markers.
17
+ */
18
+ export default {
19
+ title: 'Sort storage resources by size',
20
+ description:
21
+ 'Signed-in user uploads two differently sized files, opens sort, chooses size, and sees size ascending order',
22
+ steps: [
23
+ ...signUpFlow.steps,
24
+ { page: '@upload-media', search: { location: '/' } },
25
+ { result: { selector: '#upload-resources', timeout: 15000 } },
26
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
27
+ {
28
+ files: {
29
+ selector: '#upload-resources',
30
+ items: [{
31
+ name: 'e2e-sort-a.txt',
32
+ mimeType: 'text/plain',
33
+ // Larger body so size-asc places this after the small "z" file.
34
+ content: 'LARGE-SORT-A-CONTENT-'.repeat(40),
35
+ }],
36
+ },
37
+ },
38
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
39
+ { result: { action: ConfirmUpload, timeout: 10000 } },
40
+ { action: ConfirmUpload },
41
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
42
+ { result: { action: FinishUpload, timeout: 10000 } },
43
+ { action: FinishUpload },
44
+ { result: { page: '@storage/home', timeout: 20000 } },
45
+ { result: { selector: '[data-resource-name="e2e-sort-a.txt"]', timeout: 20000 } },
46
+ { page: '@upload-media', search: { location: '/' } },
47
+ { result: { selector: '#upload-resources', timeout: 15000 } },
48
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
49
+ {
50
+ files: {
51
+ selector: '#upload-resources',
52
+ items: [{
53
+ name: 'e2e-sort-z.txt',
54
+ mimeType: 'text/plain',
55
+ content: 'z',
56
+ }],
57
+ },
58
+ },
59
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
60
+ { result: { action: ConfirmUpload, timeout: 10000 } },
61
+ { action: ConfirmUpload },
62
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
63
+ { result: { action: FinishUpload, timeout: 10000 } },
64
+ { action: FinishUpload },
65
+ { result: { page: '@storage/home', timeout: 20000 } },
66
+ { result: { selector: '[data-resource-name="e2e-sort-a.txt"]', timeout: 20000 } },
67
+ { result: { selector: '[data-resource-name="e2e-sort-z.txt"]', timeout: 20000 } },
68
+ { result: { selector: '[data-sort-by="name"][data-sort-order="asc"]', timeout: 10000 } },
69
+ {
70
+ result: {
71
+ selector: '[data-resource-name="e2e-sort-a.txt"] ~ [data-resource-name="e2e-sort-z.txt"]',
72
+ timeout: 10000,
73
+ },
74
+ },
75
+ { result: { action: OpenSort, timeout: 10000 } },
76
+ { action: OpenSort },
77
+ { result: { action: SortBySize, timeout: 10000 } },
78
+ { action: SortBySize },
79
+ { result: { selector: '[data-sort-by="size"][data-sort-order="asc"]', timeout: 10000 } },
80
+ {
81
+ result: {
82
+ selector: '[data-resource-name="e2e-sort-z.txt"] ~ [data-resource-name="e2e-sort-a.txt"]',
83
+ timeout: 10000,
84
+ },
85
+ },
86
+ ],
87
+ }
@@ -0,0 +1,83 @@
1
+ import signUpFlow from '@ossy/authentication/sign-up.flow.js'
2
+ import { metadata as ConfirmUpload } from './confirm-upload.action.js'
3
+ import { metadata as CreateResource } from './create.action.js'
4
+ import { metadata as CreateDocumentForm } from './create-document.form.js'
5
+ import { metadata as FinishUpload } from './finish-upload.action.js'
6
+ import { metadata as OpenSort } from './open-sort.action.js'
7
+ import { metadata as SortByType } from './sort-by-type.action.js'
8
+
9
+ export const metadata = {
10
+ id: '@ossy/resources/flows/sort-storage-type',
11
+ feature: 'resources',
12
+ requires: ['server', 'database'],
13
+ timeout: 180_000,
14
+ }
15
+
16
+ /**
17
+ * Create a markdown doc and upload a file whose name order is opposite their
18
+ * schema-type order, then sort by type and assert the type order wins.
19
+ *
20
+ * Type asc: `@ossy/platform/schema/file` before `@ossy/resources/schema/markdown`.
21
+ * Names: aaa-note (markdown) vs zzz-file.txt (file) so name-asc is the reverse.
22
+ */
23
+ export default {
24
+ title: 'Sort storage resources by type',
25
+ description:
26
+ 'Signed-in user creates a markdown doc and uploads a file, opens sort, chooses type, and sees type ascending order',
27
+ steps: [
28
+ ...signUpFlow.steps,
29
+ {
30
+ page: '@create-document',
31
+ search: {
32
+ location: '/',
33
+ schemaId: '@ossy/resources/schema/markdown',
34
+ },
35
+ },
36
+ { result: { selector: '[data-flow-stage="create-document"]', timeout: 15000 } },
37
+ { result: { action: CreateResource, timeout: 10000 } },
38
+ { form: CreateDocumentForm, fields: { name: 'aaa-note' } },
39
+ { action: CreateResource },
40
+ { result: { page: '@storage/home', timeout: 20000 } },
41
+ { result: { selector: '[data-resource-name="aaa-note"]', timeout: 20000 } },
42
+ { page: '@upload-media', search: { location: '/' } },
43
+ { result: { selector: '#upload-resources', timeout: 15000 } },
44
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
45
+ {
46
+ files: {
47
+ selector: '#upload-resources',
48
+ items: [{
49
+ name: 'zzz-file.txt',
50
+ mimeType: 'text/plain',
51
+ content: 'type sort file',
52
+ }],
53
+ },
54
+ },
55
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
56
+ { result: { action: ConfirmUpload, timeout: 10000 } },
57
+ { action: ConfirmUpload },
58
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
59
+ { result: { action: FinishUpload, timeout: 10000 } },
60
+ { action: FinishUpload },
61
+ { result: { page: '@storage/home', timeout: 20000 } },
62
+ { result: { selector: '[data-resource-name="aaa-note"]', timeout: 20000 } },
63
+ { result: { selector: '[data-resource-name="zzz-file.txt"]', timeout: 20000 } },
64
+ { result: { selector: '[data-sort-by="name"][data-sort-order="asc"]', timeout: 10000 } },
65
+ {
66
+ result: {
67
+ selector: '[data-resource-name="aaa-note"] ~ [data-resource-name="zzz-file.txt"]',
68
+ timeout: 10000,
69
+ },
70
+ },
71
+ { result: { action: OpenSort, timeout: 10000 } },
72
+ { action: OpenSort },
73
+ { result: { action: SortByType, timeout: 10000 } },
74
+ { action: SortByType },
75
+ { result: { selector: '[data-sort-by="type"][data-sort-order="asc"]', timeout: 10000 } },
76
+ {
77
+ result: {
78
+ selector: '[data-resource-name="zzz-file.txt"] ~ [data-resource-name="aaa-note"]',
79
+ timeout: 10000,
80
+ },
81
+ },
82
+ ],
83
+ }
@@ -0,0 +1,86 @@
1
+ import signUpFlow from '@ossy/authentication/sign-up.flow.js'
2
+ import { metadata as ConfirmUpload } from './confirm-upload.action.js'
3
+ import { metadata as FinishUpload } from './finish-upload.action.js'
4
+ import { metadata as OpenSort } from './open-sort.action.js'
5
+ import { metadata as SortDescending } from './sort-descending.action.js'
6
+
7
+ export const metadata = {
8
+ id: '@ossy/resources/flows/sort-storage',
9
+ feature: 'resources',
10
+ requires: ['server', 'database'],
11
+ timeout: 180_000,
12
+ }
13
+
14
+ /**
15
+ * Upload two differently named files, open the storage sort menu, switch to
16
+ * name descending, and assert list order + sort markers.
17
+ */
18
+ export default {
19
+ title: 'Sort storage resources by name',
20
+ description:
21
+ 'Signed-in user uploads two files, opens sort, chooses name descending, and sees the reversed list order',
22
+ steps: [
23
+ ...signUpFlow.steps,
24
+ { page: '@upload-media', search: { location: '/' } },
25
+ { result: { selector: '#upload-resources', timeout: 15000 } },
26
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
27
+ {
28
+ files: {
29
+ selector: '#upload-resources',
30
+ items: [{
31
+ name: 'e2e-sort-a.txt',
32
+ mimeType: 'text/plain',
33
+ content: 'sort a',
34
+ }],
35
+ },
36
+ },
37
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
38
+ { result: { action: ConfirmUpload, timeout: 10000 } },
39
+ { action: ConfirmUpload },
40
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
41
+ { result: { action: FinishUpload, timeout: 10000 } },
42
+ { action: FinishUpload },
43
+ { result: { page: '@storage/home', timeout: 20000 } },
44
+ { result: { selector: '[data-resource-name="e2e-sort-a.txt"]', timeout: 20000 } },
45
+ { page: '@upload-media', search: { location: '/' } },
46
+ { result: { selector: '#upload-resources', timeout: 15000 } },
47
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
48
+ {
49
+ files: {
50
+ selector: '#upload-resources',
51
+ items: [{
52
+ name: 'e2e-sort-z.txt',
53
+ mimeType: 'text/plain',
54
+ content: 'sort z',
55
+ }],
56
+ },
57
+ },
58
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
59
+ { result: { action: ConfirmUpload, timeout: 10000 } },
60
+ { action: ConfirmUpload },
61
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
62
+ { result: { action: FinishUpload, timeout: 10000 } },
63
+ { action: FinishUpload },
64
+ { result: { page: '@storage/home', timeout: 20000 } },
65
+ { result: { selector: '[data-resource-name="e2e-sort-a.txt"]', timeout: 20000 } },
66
+ { result: { selector: '[data-resource-name="e2e-sort-z.txt"]', timeout: 20000 } },
67
+ { result: { selector: '[data-sort-by="name"][data-sort-order="asc"]', timeout: 10000 } },
68
+ {
69
+ result: {
70
+ selector: '[data-resource-name="e2e-sort-a.txt"] ~ [data-resource-name="e2e-sort-z.txt"]',
71
+ timeout: 10000,
72
+ },
73
+ },
74
+ { result: { action: OpenSort, timeout: 10000 } },
75
+ { action: OpenSort },
76
+ { result: { action: SortDescending, timeout: 10000 } },
77
+ { action: SortDescending },
78
+ { result: { selector: '[data-sort-by="name"][data-sort-order="desc"]', timeout: 10000 } },
79
+ {
80
+ result: {
81
+ selector: '[data-resource-name="e2e-sort-z.txt"] ~ [data-resource-name="e2e-sort-a.txt"]',
82
+ timeout: 10000,
83
+ },
84
+ },
85
+ ],
86
+ }
@@ -49,11 +49,29 @@
49
49
  "resources.createDirectory.submit": "Skapa mapp",
50
50
  "resources.createDirectory.errorEmpty": "Mappnamn får inte vara tomt",
51
51
  "resources.createDirectory.errorOssyPrefix": "Mappnamn får inte börja med @ossy",
52
+ "@ossy/resources/actions/create-directory.label": "Skapa mapp",
53
+ "@ossy/resources/form/create-directory.label": "Skapa mapp",
54
+ "@ossy/resources/schema/create-directory.name.label": "Mappnamn",
55
+ "@ossy/resources/form/create-document.label": "Skapa dokument",
56
+ "@ossy/resources/schema/create-document.name.label": "Dokumentnamn",
52
57
  "resources.panel.download": "Ladda ner",
58
+ "resources.panel.delete": "Ta bort",
59
+ "resources.panel.rename": "Byt namn",
60
+ "resources.panel.move": "Flytta",
61
+ "resources.move.title": "Flytta resurs",
62
+ "resources.move.errorEmpty": "Målplatsen får inte vara tom",
63
+ "resources.move.errorFailed": "Kunde inte flytta resursen",
64
+ "@ossy/resources/form/rename.label": "Byt namn på resurs",
65
+ "@ossy/resources/schema/rename.name.label": "Namn",
66
+ "@ossy/resources/form/move.label": "Flytta resurs",
67
+ "@ossy/resources/schema/move.target.label": "Ny plats",
68
+ "@ossy/resources/actions/open-move.label": "Flytta resurs",
69
+ "@ossy/resources/actions/open-move.description": "Öppna dialogen för att flytta en resurs till en ny plats",
53
70
  "resources.share.title": "Dela länk",
54
71
  "resources.share.confirmPublic": "Gör den här resursen publik så att alla med länken kan öppna den. Du kan kopiera länken efter bekräftelse.",
55
72
  "resources.share.alreadyPublic": "Den här resursen är publik. Kopiera länken nedan för att dela den.",
56
73
  "resources.share.makePublic": "Gör publik & kopiera länk",
74
+ "resources.share.makePrivate": "Gör privat",
57
75
  "resources.share.copyLink": "Kopiera länk",
58
76
  "resources.share.linkCopied": "Länk kopierad",
59
77
  "resources.share.copyFailed": "Kunde inte kopiera länken",
@@ -0,0 +1,4 @@
1
+ export const metadata = {
2
+ id: '@ossy/resources/actions/toggle-selection-mode',
3
+ access: 'workspace',
4
+ }
@@ -0,0 +1,44 @@
1
+ import signUpFlow from '@ossy/authentication/sign-up.flow.js'
2
+ import { metadata as ConfirmUpload } from './confirm-upload.action.js'
3
+ import { metadata as FinishUpload } from './finish-upload.action.js'
4
+
5
+ export const metadata = {
6
+ id: '@ossy/resources/flows/upload-file',
7
+ feature: 'resources',
8
+ requires: ['server', 'database'],
9
+ timeout: 120_000,
10
+ }
11
+
12
+ /**
13
+ * New user signs up, opens the media upload page, attaches a file via the native
14
+ * file input, confirms upload, and sees the file listed on storage home.
15
+ */
16
+ export default {
17
+ title: 'Upload file to storage',
18
+ description:
19
+ 'Signed-in user opens upload-media, selects a file, uploads it, and lands on storage home with the file listed',
20
+ steps: [
21
+ ...signUpFlow.steps,
22
+ { page: '@upload-media', search: { location: '/' } },
23
+ { result: { selector: '#upload-resources', timeout: 15000 } },
24
+ { result: { selector: '[data-flow-stage="upload-idle"]', timeout: 15000 } },
25
+ {
26
+ files: {
27
+ selector: '#upload-resources',
28
+ items: [{
29
+ name: 'e2e-upload.txt',
30
+ mimeType: 'text/plain',
31
+ content: 'hello from ossy e2e upload',
32
+ }],
33
+ },
34
+ },
35
+ { result: { selector: '[data-flow-stage="upload-preview"]', timeout: 15000 } },
36
+ { result: { action: ConfirmUpload, timeout: 10000 } },
37
+ { action: ConfirmUpload },
38
+ { result: { selector: '[data-flow-stage="upload-done"]', timeout: 30000 } },
39
+ { result: { action: FinishUpload, timeout: 10000 } },
40
+ { action: FinishUpload },
41
+ { result: { page: '@storage/home', timeout: 20000 } },
42
+ { result: { selector: '[data-resource-name="e2e-upload.txt"]', timeout: 20000 } },
43
+ ],
44
+ }
@@ -0,0 +1,94 @@
1
+ 'use client'
2
+ import { useEffect, useMemo } from 'react'
3
+ import { useCollectionView } from '@ossy/design-system'
4
+ import { useSdk } from '@ossy/sdk-react'
5
+ import { metadata as ListResources } from './list.action.js'
6
+ import { DEFAULT_RESOURCE_LIST_LIMIT } from './resources-list-options.js'
7
+ import { useResourceListQuery } from './resource-list-query.js'
8
+
9
+ /**
10
+ * Read a resource folder with shared list/grid collection controls.
11
+ *
12
+ * When `limit` is set, list options are sent to `ListResources` so Mongo
13
+ * `$sort` / `$skip` / `$limit` run server-side. Without `limit`, behavior
14
+ * matches the legacy full-folder array response.
15
+ *
16
+ * Page and sort are stored in the URL (`page`, `sort`, `order`), same as the
17
+ * open resource id (`r`).
18
+ *
19
+ * @param {{
20
+ * location: string,
21
+ * limit?: number | null,
22
+ * enabled?: boolean,
23
+ * initialSortBy?: string,
24
+ * initialSortOrder?: 'asc' | 'desc',
25
+ * }} options
26
+ */
27
+ export function useResourceList ({
28
+ location,
29
+ limit = DEFAULT_RESOURCE_LIST_LIMIT,
30
+ enabled = true,
31
+ initialSortBy = 'name',
32
+ initialSortOrder = 'asc',
33
+ } = {}) {
34
+ const sdk = useSdk()
35
+ const pageLimit = limit == null ? DEFAULT_RESOURCE_LIST_LIMIT : Math.max(1, Number(limit) || DEFAULT_RESOURCE_LIST_LIMIT)
36
+ const query = useResourceListQuery({
37
+ defaultSortBy: initialSortBy,
38
+ defaultSortOrder: initialSortOrder,
39
+ })
40
+ const {
41
+ pageIndex,
42
+ sortBy,
43
+ sortOrder,
44
+ setPage,
45
+ } = query
46
+ const offset = Math.max(0, pageIndex) * pageLimit
47
+
48
+ const controls = useMemo(() => ({
49
+ ...query,
50
+ limit: pageLimit,
51
+ offset,
52
+ }), [query, pageLimit, offset])
53
+
54
+ const payload = useMemo(() => {
55
+ const next = { location }
56
+ if (limit != null) {
57
+ next.limit = pageLimit
58
+ next.offset = offset
59
+ next.sortBy = sortBy
60
+ next.sortOrder = sortOrder
61
+ }
62
+ return next
63
+ }, [location, limit, pageLimit, offset, sortBy, sortOrder])
64
+
65
+ const {
66
+ status,
67
+ data,
68
+ error,
69
+ refetch,
70
+ } = sdk.read(ListResources, payload, { enabled: Boolean(enabled && location) })
71
+
72
+ const collection = useCollectionView(controls, { data, status, error })
73
+
74
+ useEffect(() => {
75
+ if (collection.total > 0 && pageIndex > collection.pageCount - 1) {
76
+ setPage(collection.pageCount - 1)
77
+ }
78
+ }, [collection.total, pageIndex, collection.pageCount, setPage])
79
+
80
+ return {
81
+ ...collection,
82
+ paginated: collection.paginated || limit != null,
83
+ refetch,
84
+ offset: collection.pageIndex * collection.limit,
85
+ }
86
+ }
87
+
88
+ export { normalizeCollectionResult } from '@ossy/design-system'
89
+ export {
90
+ parseResourceListQuery,
91
+ resourceListQuerySearchString,
92
+ serializeResourceListQuery,
93
+ useResourceListQuery,
94
+ } from './resource-list-query.js'
@@ -0,0 +1,5 @@
1
+ /** Client-only — switch storage resources to grid layout. */
2
+ export const metadata = {
3
+ id: '@ossy/resources/actions/view-grid',
4
+ access: 'workspace',
5
+ }
@@ -0,0 +1,5 @@
1
+ /** Client-only — switch storage resources to list layout. */
2
+ export const metadata = {
3
+ id: '@ossy/resources/actions/view-list',
4
+ access: 'workspace',
5
+ }
@@ -0,0 +1,49 @@
1
+ import { metadata as DoneSelecting } from './done-selecting.action.js'
2
+ import { metadata as OpenView } from './open-view.action.js'
3
+ import { metadata as ToggleSelectionMode } from './toggle-selection-mode.action.js'
4
+ import { metadata as ViewGrid } from './view-grid.action.js'
5
+ import { metadata as ViewList } from './view-list.action.js'
6
+ import uploadFileFlow from './upload-file.flow.js'
7
+
8
+ export const metadata = {
9
+ id: '@ossy/resources/flows/view-storage',
10
+ feature: 'resources',
11
+ requires: ['server', 'database'],
12
+ timeout: 180_000,
13
+ }
14
+
15
+ /**
16
+ * After an upload, toggle list/grid layout via the view menu, enter selection
17
+ * mode, then exit with Done selecting — asserting layout and selection hooks.
18
+ */
19
+ export default {
20
+ title: 'Toggle storage view and exit selection',
21
+ description:
22
+ 'Signed-in user switches storage between list and grid, enters selection mode, and exits with Done selecting',
23
+ steps: [
24
+ ...uploadFileFlow.steps,
25
+ { result: { selector: '[data-view-mode="list"]', timeout: 10000 } },
26
+ { result: { action: OpenView, timeout: 10000 } },
27
+ { action: OpenView },
28
+ { result: { action: ViewGrid, timeout: 10000 } },
29
+ { action: ViewGrid },
30
+ { result: { selector: '[data-view-mode="grid"]', timeout: 10000 } },
31
+ { result: { action: OpenView, timeout: 10000 } },
32
+ { action: OpenView },
33
+ { result: { action: ViewList, timeout: 10000 } },
34
+ { action: ViewList },
35
+ { result: { selector: '[data-view-mode="list"]', timeout: 10000 } },
36
+ { result: { action: ToggleSelectionMode, timeout: 10000 } },
37
+ { action: ToggleSelectionMode },
38
+ { result: { selector: '[data-selection-mode="on"]', timeout: 10000 } },
39
+ { result: { action: DoneSelecting, timeout: 10000 } },
40
+ { action: DoneSelecting },
41
+ {
42
+ result: {
43
+ selector: '[data-selection-mode="on"]',
44
+ hidden: true,
45
+ timeout: 10000,
46
+ },
47
+ },
48
+ ],
49
+ }