@sinequa/atomic-angular 2.15.0 → 2.16.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.
- package/document-workspaces/i18n/de.json +2 -0
- package/document-workspaces/i18n/en.json +2 -0
- package/document-workspaces/i18n/fr.json +2 -0
- package/document-workspaces/i18n/ja.json +2 -0
- package/features/auth/i18n/de.json +1 -0
- package/features/auth/i18n/en.json +1 -0
- package/features/auth/i18n/fr.json +1 -0
- package/features/collections/i18n/de.json +39 -1
- package/features/collections/i18n/en.json +39 -1
- package/features/collections/i18n/fr.json +39 -1
- package/features/user-profile/i18n/de.json +12 -3
- package/features/user-profile/i18n/en.json +12 -3
- package/features/user-profile/i18n/fr.json +12 -3
- package/fesm2022/sinequa-atomic-angular-charts.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs +263 -54
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular.mjs +6350 -1241
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/package.json +2 -2
- package/types/sinequa-atomic-angular-document-workspaces.d.ts +68 -18
- package/types/sinequa-atomic-angular.d.ts +868 -257
|
@@ -2,7 +2,7 @@ import * as i0 from "@angular/core";
|
|
|
2
2
|
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, Injectable, InjectionToken, Injector, Pipe, ViewEncapsulation, afterNextRender, assertInInjectionContext, booleanAttribute, computed, effect, forwardRef, inject, input, model, output, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { withDevtools } from "@ngrx-toolkit/core";
|
|
4
4
|
import { patchState, signalStore, withComputed, withMethods, withState } from "@ngrx/signals";
|
|
5
|
-
import { ApiError, addWorkspaceDocuments, createHeaders, createWorkspace, deleteWorkspaceDocuments, deleteWorkspaces, fetchPreview, fetchPrincipal, fetchPrincipalsByIds, fetchUploadStatus,
|
|
5
|
+
import { ApiError, addWorkspaceDocuments, createHeaders, createWorkspace, deleteWorkspaceDocuments, deleteWorkspaces, fetchPreview, fetchPrincipal, fetchPrincipalsByIds, fetchUploadStatus, fetchWorkspace, fetchWorkspaceContent, fetchWorkspaces, globalConfig, onAuthEvent, searchPrincipals, tryAutoAuthentication, updateWorkspace } from "@sinequa/atomic";
|
|
6
6
|
import { Upload } from "tus-js-client";
|
|
7
7
|
import { toSignal } from "@angular/core/rxjs-interop";
|
|
8
8
|
import { TranslocoPipe, TranslocoService, provideTranslocoScope } from "@jsverse/transloco";
|
|
@@ -42,7 +42,46 @@ function mapWireWorkspaceToWorkspace(dto) {
|
|
|
42
42
|
creatorId: dto.creator,
|
|
43
43
|
ownerIds: dto.owners ?? [],
|
|
44
44
|
readerIds: dto.readers ?? [],
|
|
45
|
-
currentUserRole: dto.currentUserRole
|
|
45
|
+
currentUserRole: dto.currentUserRole,
|
|
46
|
+
membersKnown: true
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function parseMeta(record) {
|
|
50
|
+
const meta = record.meta;
|
|
51
|
+
if (typeof meta !== "string") return {};
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(meta);
|
|
54
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
55
|
+
} catch {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const asString = (value) => typeof value === "string" ? value : void 0;
|
|
60
|
+
function mapWorkspaceRecordToWorkspace(record) {
|
|
61
|
+
const flags = record;
|
|
62
|
+
const meta = parseMeta(record);
|
|
63
|
+
return {
|
|
64
|
+
id: record.id,
|
|
65
|
+
name: record.title ?? "",
|
|
66
|
+
color: workspaceColorFor(record.id),
|
|
67
|
+
description: asString(meta["description"]) ?? "",
|
|
68
|
+
tags: Array.isArray(meta["tags"]) ? meta["tags"].filter((t) => typeof t === "string") : [],
|
|
69
|
+
documentCount: void 0,
|
|
70
|
+
creatorId: "",
|
|
71
|
+
ownerIds: [],
|
|
72
|
+
readerIds: [],
|
|
73
|
+
currentUserRole: flags.isCreator ? "creator" : flags.isOwner ? "owner" : "reader",
|
|
74
|
+
membersKnown: false
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function mapDocumentRecordToWorkspaceDocument(record, workspaceId) {
|
|
78
|
+
const name = record.title ?? "";
|
|
79
|
+
return {
|
|
80
|
+
id: record.id,
|
|
81
|
+
name,
|
|
82
|
+
workspaceId,
|
|
83
|
+
type: getDocumentType(name),
|
|
84
|
+
createdAt: asString(parseMeta(record)["createdAt"])
|
|
46
85
|
};
|
|
47
86
|
}
|
|
48
87
|
function mapDocumentStatus(status) {
|
|
@@ -71,6 +110,7 @@ function mapWireDocumentToWorkspaceDocument(dto) {
|
|
|
71
110
|
};
|
|
72
111
|
}
|
|
73
112
|
const PAGE_SIZE = 100;
|
|
113
|
+
const WORKSPACES_QUERY_NAME = "query_Web";
|
|
74
114
|
const initialState$1 = {
|
|
75
115
|
workspaces: [],
|
|
76
116
|
loaded: false,
|
|
@@ -81,10 +121,12 @@ const initialState$1 = {
|
|
|
81
121
|
},
|
|
82
122
|
documents: {},
|
|
83
123
|
documentPagination: {},
|
|
84
|
-
selectedIds: []
|
|
124
|
+
selectedIds: [],
|
|
125
|
+
searchText: "",
|
|
126
|
+
documentSearch: {}
|
|
85
127
|
};
|
|
86
128
|
function withDocumentCountAdjusted(workspaces, id, delta) {
|
|
87
|
-
return workspaces.map((w) => w.id === id ? {
|
|
129
|
+
return workspaces.map((w) => w.id === id && w.documentCount !== void 0 ? {
|
|
88
130
|
...w,
|
|
89
131
|
documentCount: Math.max(0, w.documentCount + delta)
|
|
90
132
|
} : w);
|
|
@@ -118,19 +160,30 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
118
160
|
};
|
|
119
161
|
});
|
|
120
162
|
};
|
|
163
|
+
const documentText = (workspaceId) => store.documentSearch()[workspaceId] ?? "";
|
|
121
164
|
const loadDocuments = async (workspaceId, signal) => {
|
|
122
|
-
const
|
|
165
|
+
const text = documentText(workspaceId);
|
|
166
|
+
const res = await fetchWorkspaceContent(workspaceId, {
|
|
167
|
+
queryName: WORKSPACES_QUERY_NAME,
|
|
168
|
+
text,
|
|
123
169
|
page: 1,
|
|
124
170
|
pageSize: PAGE_SIZE
|
|
125
171
|
}, { signal });
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
172
|
+
if (documentText(workspaceId) !== text) return store.documents()[workspaceId] ?? [];
|
|
173
|
+
const documents = res.records.map((r) => mapDocumentRecordToWorkspaceDocument(r, workspaceId));
|
|
174
|
+
patchState(store, (state) => ({
|
|
175
|
+
documentPagination: {
|
|
176
|
+
...state.documentPagination,
|
|
177
|
+
[workspaceId]: {
|
|
178
|
+
page: res.page,
|
|
179
|
+
total: res.rowCount
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
workspaces: text ? state.workspaces : state.workspaces.map((w) => w.id === workspaceId ? {
|
|
183
|
+
...w,
|
|
184
|
+
documentCount: res.rowCount
|
|
185
|
+
} : w)
|
|
186
|
+
}));
|
|
134
187
|
setDocuments(workspaceId, documents);
|
|
135
188
|
return documents;
|
|
136
189
|
};
|
|
@@ -140,25 +193,32 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
140
193
|
await loadDocuments(workspaceId);
|
|
141
194
|
} catch {}
|
|
142
195
|
};
|
|
196
|
+
let loadSeq = 0;
|
|
143
197
|
const load = async () => {
|
|
198
|
+
const seq = ++loadSeq;
|
|
144
199
|
patchState(store, { loading: true });
|
|
145
200
|
try {
|
|
146
|
-
const res = await
|
|
201
|
+
const res = await fetchWorkspaces({
|
|
202
|
+
queryName: WORKSPACES_QUERY_NAME,
|
|
203
|
+
text: store.searchText(),
|
|
147
204
|
page: 1,
|
|
148
205
|
pageSize: PAGE_SIZE
|
|
149
206
|
});
|
|
207
|
+
if (seq !== loadSeq) return store.workspaces();
|
|
150
208
|
patchState(store, {
|
|
151
|
-
workspaces: res.
|
|
209
|
+
workspaces: res.records.map(mapWorkspaceRecordToWorkspace),
|
|
152
210
|
pagination: {
|
|
153
211
|
page: res.page,
|
|
154
|
-
total: res.
|
|
212
|
+
total: res.rowCount
|
|
155
213
|
},
|
|
156
214
|
loaded: true
|
|
157
215
|
});
|
|
158
216
|
return store.workspaces();
|
|
159
217
|
} finally {
|
|
160
|
-
|
|
161
|
-
|
|
218
|
+
if (seq === loadSeq) {
|
|
219
|
+
patchState(store, { loading: false });
|
|
220
|
+
loadingPromise = null;
|
|
221
|
+
}
|
|
162
222
|
}
|
|
163
223
|
};
|
|
164
224
|
return {
|
|
@@ -181,24 +241,35 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
181
241
|
loadingPromise ??= load();
|
|
182
242
|
return loadingPromise;
|
|
183
243
|
},
|
|
244
|
+
search(text) {
|
|
245
|
+
const value = text.trim();
|
|
246
|
+
if (value === store.searchText()) return loadingPromise ?? Promise.resolve(store.workspaces());
|
|
247
|
+
patchState(store, { searchText: value });
|
|
248
|
+
loadingPromise = load();
|
|
249
|
+
return loadingPromise;
|
|
250
|
+
},
|
|
184
251
|
loadMore() {
|
|
185
252
|
if (!store.hasMore()) return Promise.resolve(store.workspaces());
|
|
186
253
|
loadingMorePromise ??= (async () => {
|
|
187
254
|
try {
|
|
188
255
|
const current = store.workspaces();
|
|
189
256
|
const page = Math.min(store.pagination().page + 1, Math.floor(current.length / PAGE_SIZE) + 1);
|
|
190
|
-
const
|
|
257
|
+
const text = store.searchText();
|
|
258
|
+
const res = await fetchWorkspaces({
|
|
259
|
+
queryName: WORKSPACES_QUERY_NAME,
|
|
260
|
+
text,
|
|
191
261
|
page,
|
|
192
262
|
pageSize: PAGE_SIZE
|
|
193
263
|
});
|
|
264
|
+
if (text !== store.searchText()) return store.workspaces();
|
|
194
265
|
patchState(store, (state) => {
|
|
195
266
|
const loaded = new Set(state.workspaces.map((w) => w.id));
|
|
196
|
-
const fresh = res.
|
|
267
|
+
const fresh = res.records.map(mapWorkspaceRecordToWorkspace).filter((w) => !loaded.has(w.id));
|
|
197
268
|
return {
|
|
198
269
|
workspaces: [...state.workspaces, ...fresh],
|
|
199
270
|
pagination: {
|
|
200
271
|
page: res.page,
|
|
201
|
-
total: res.
|
|
272
|
+
total: res.rowCount
|
|
202
273
|
}
|
|
203
274
|
};
|
|
204
275
|
});
|
|
@@ -210,7 +281,17 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
210
281
|
return loadingMorePromise;
|
|
211
282
|
},
|
|
212
283
|
async fetch(id) {
|
|
213
|
-
const
|
|
284
|
+
const fetched = mapWorkspaceRecordToWorkspace(await fetchWorkspace(id, { queryName: WORKSPACES_QUERY_NAME }));
|
|
285
|
+
if (fetched.id !== id) throw new ApiError(`Workspace ${id} not found`, 404);
|
|
286
|
+
const cached = store.workspaces().find((w) => w.id === id);
|
|
287
|
+
const workspace = cached ? {
|
|
288
|
+
...fetched,
|
|
289
|
+
documentCount: cached.documentCount,
|
|
290
|
+
creatorId: cached.creatorId,
|
|
291
|
+
ownerIds: cached.ownerIds,
|
|
292
|
+
readerIds: cached.readerIds,
|
|
293
|
+
membersKnown: cached.membersKnown
|
|
294
|
+
} : fetched;
|
|
214
295
|
patchState(store, (state) => ({ workspaces: state.workspaces.some((w) => w.id === id) ? state.workspaces.map((w) => w.id === id ? workspace : w) : [...state.workspaces, workspace] }));
|
|
215
296
|
return workspace;
|
|
216
297
|
},
|
|
@@ -270,27 +351,48 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
270
351
|
} : state.pagination,
|
|
271
352
|
documents: Object.fromEntries(Object.entries(state.documents).filter(([wid]) => wid !== id)),
|
|
272
353
|
documentPagination: Object.fromEntries(Object.entries(state.documentPagination).filter(([wid]) => wid !== id)),
|
|
354
|
+
documentSearch: Object.fromEntries(Object.entries(state.documentSearch).filter(([wid]) => wid !== id)),
|
|
273
355
|
selectedIds: state.selectedIds.filter((selected) => selected !== id)
|
|
274
356
|
}));
|
|
275
357
|
},
|
|
276
358
|
loadDocuments,
|
|
359
|
+
searchDocuments(workspaceId, text, signal) {
|
|
360
|
+
const value = text.trim();
|
|
361
|
+
if (value === documentText(workspaceId)) return Promise.resolve(store.documents()[workspaceId] ?? []);
|
|
362
|
+
patchState(store, (state) => ({ documentSearch: {
|
|
363
|
+
...state.documentSearch,
|
|
364
|
+
[workspaceId]: value
|
|
365
|
+
} }));
|
|
366
|
+
return loadDocuments(workspaceId, signal);
|
|
367
|
+
},
|
|
368
|
+
resetDocumentSearch(workspaceId) {
|
|
369
|
+
if (!documentText(workspaceId)) return;
|
|
370
|
+
patchState(store, (state) => ({ documentSearch: {
|
|
371
|
+
...state.documentSearch,
|
|
372
|
+
[workspaceId]: ""
|
|
373
|
+
} }));
|
|
374
|
+
},
|
|
277
375
|
async loadMoreDocuments(workspaceId) {
|
|
278
376
|
const current = store.documents()[workspaceId] ?? [];
|
|
279
377
|
const pagination = store.documentPagination()[workspaceId];
|
|
280
378
|
if (!pagination || current.length >= pagination.total) return current;
|
|
281
379
|
const page = Math.min(pagination.page + 1, Math.floor(current.length / PAGE_SIZE) + 1);
|
|
282
|
-
const
|
|
380
|
+
const text = documentText(workspaceId);
|
|
381
|
+
const res = await fetchWorkspaceContent(workspaceId, {
|
|
382
|
+
queryName: WORKSPACES_QUERY_NAME,
|
|
383
|
+
text,
|
|
283
384
|
page,
|
|
284
385
|
pageSize: PAGE_SIZE
|
|
285
386
|
});
|
|
387
|
+
if (documentText(workspaceId) !== text) return store.documents()[workspaceId] ?? [];
|
|
286
388
|
const loaded = new Set(current.map((d) => d.id));
|
|
287
|
-
const fresh = res.
|
|
389
|
+
const fresh = res.records.map((r) => mapDocumentRecordToWorkspaceDocument(r, workspaceId)).filter((d) => !loaded.has(d.id));
|
|
288
390
|
const merged = [...current, ...fresh];
|
|
289
391
|
patchState(store, (state) => ({ documentPagination: {
|
|
290
392
|
...state.documentPagination,
|
|
291
393
|
[workspaceId]: {
|
|
292
394
|
page: res.page,
|
|
293
|
-
total: res.
|
|
395
|
+
total: res.rowCount
|
|
294
396
|
}
|
|
295
397
|
} }));
|
|
296
398
|
setDocuments(workspaceId, merged);
|
|
@@ -2089,7 +2191,7 @@ function injectWorkspaceFilter(workspaces, options = {}) {
|
|
|
2089
2191
|
}, ...ngDevMode ? [{ debugName: "counts" }] : /* istanbul ignore next */ []),
|
|
2090
2192
|
filtered: computed(() => workspaces().filter((w) => {
|
|
2091
2193
|
const inTab = tab() === "all" || tab() === "editor" === canWrite(w.currentUserRole);
|
|
2092
|
-
const inQuery = matchesText(w.name, query()) || options.fields?.() === "name+description" && matchesText(w.description, query());
|
|
2194
|
+
const inQuery = options.serverSide || matchesText(w.name, query()) || options.fields?.() === "name+description" && matchesText(w.description, query());
|
|
2093
2195
|
return inTab && inQuery;
|
|
2094
2196
|
}), ...ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []),
|
|
2095
2197
|
clearQuery: () => query.set("")
|
|
@@ -5123,8 +5225,10 @@ var WorkspaceRow = class WorkspaceRow {
|
|
|
5123
5225
|
</div>
|
|
5124
5226
|
|
|
5125
5227
|
<div role="cell" class="flex items-center gap-1 text-xs text-(--font-primary-base)">
|
|
5126
|
-
|
|
5127
|
-
|
|
5228
|
+
@if (workspace().documentCount !== undefined) {
|
|
5229
|
+
<file-icon class="size-3 text-(--font-primary-muted)" />
|
|
5230
|
+
{{ workspace().documentCount }}
|
|
5231
|
+
}
|
|
5128
5232
|
</div>
|
|
5129
5233
|
|
|
5130
5234
|
<div role="cell"><workspace-role-tag [role]="workspace().currentUserRole" /></div>
|
|
@@ -5320,8 +5424,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
5320
5424
|
</div>
|
|
5321
5425
|
|
|
5322
5426
|
<div role="cell" class="flex items-center gap-1 text-xs text-(--font-primary-base)">
|
|
5323
|
-
|
|
5324
|
-
|
|
5427
|
+
@if (workspace().documentCount !== undefined) {
|
|
5428
|
+
<file-icon class="size-3 text-(--font-primary-muted)" />
|
|
5429
|
+
{{ workspace().documentCount }}
|
|
5430
|
+
}
|
|
5325
5431
|
</div>
|
|
5326
5432
|
|
|
5327
5433
|
<div role="cell"><workspace-role-tag [role]="workspace().currentUserRole" /></div>
|
|
@@ -5537,10 +5643,12 @@ var WorkspaceCard = class WorkspaceCard {
|
|
|
5537
5643
|
<div class="mb-3 h-px bg-(--stroke-primary-lighter)"></div>
|
|
5538
5644
|
<div class="flex items-center justify-between gap-2">
|
|
5539
5645
|
<workspace-role-tag [role]="workspace().currentUserRole" />
|
|
5540
|
-
|
|
5541
|
-
<
|
|
5542
|
-
|
|
5543
|
-
|
|
5646
|
+
@if (workspace().documentCount !== undefined) {
|
|
5647
|
+
<span class="ml-auto flex shrink-0 items-center gap-1 text-xs text-(--font-primary-muted)">
|
|
5648
|
+
<file-icon class="size-3" />
|
|
5649
|
+
{{ workspace().documentCount }}
|
|
5650
|
+
</span>
|
|
5651
|
+
}
|
|
5544
5652
|
</div>
|
|
5545
5653
|
`,
|
|
5546
5654
|
isInline: true,
|
|
@@ -5727,10 +5835,12 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
5727
5835
|
<div class="mb-3 h-px bg-(--stroke-primary-lighter)"></div>
|
|
5728
5836
|
<div class="flex items-center justify-between gap-2">
|
|
5729
5837
|
<workspace-role-tag [role]="workspace().currentUserRole" />
|
|
5730
|
-
|
|
5731
|
-
<
|
|
5732
|
-
|
|
5733
|
-
|
|
5838
|
+
@if (workspace().documentCount !== undefined) {
|
|
5839
|
+
<span class="ml-auto flex shrink-0 items-center gap-1 text-xs text-(--font-primary-muted)">
|
|
5840
|
+
<file-icon class="size-3" />
|
|
5841
|
+
{{ workspace().documentCount }}
|
|
5842
|
+
</span>
|
|
5843
|
+
}
|
|
5734
5844
|
</div>
|
|
5735
5845
|
`
|
|
5736
5846
|
}]
|
|
@@ -5804,6 +5914,7 @@ const TABS = [
|
|
|
5804
5914
|
labelKey: "documentWorkspaces.list.tabReader"
|
|
5805
5915
|
}
|
|
5806
5916
|
];
|
|
5917
|
+
const SEARCH_DEBOUNCE_MS$1 = 300;
|
|
5807
5918
|
var WorkspaceList = class WorkspaceList {
|
|
5808
5919
|
opened = output();
|
|
5809
5920
|
createRequested = output();
|
|
@@ -5818,7 +5929,7 @@ var WorkspaceList = class WorkspaceList {
|
|
|
5818
5929
|
refreshError = signal(null, ...ngDevMode ? [{ debugName: "refreshError" }] : /* istanbul ignore next */ []);
|
|
5819
5930
|
loadingMore = signal(false, ...ngDevMode ? [{ debugName: "loadingMore" }] : /* istanbul ignore next */ []);
|
|
5820
5931
|
loadMoreError = signal(null, ...ngDevMode ? [{ debugName: "loadMoreError" }] : /* istanbul ignore next */ []);
|
|
5821
|
-
filter = injectWorkspaceFilter(this.store.workspaces);
|
|
5932
|
+
filter = injectWorkspaceFilter(this.store.workspaces, { serverSide: true });
|
|
5822
5933
|
searchForm = form(this.filter.query);
|
|
5823
5934
|
selection = injectSelection({
|
|
5824
5935
|
items: this.filter.filtered,
|
|
@@ -5839,6 +5950,24 @@ var WorkspaceList = class WorkspaceList {
|
|
|
5839
5950
|
constructor() {
|
|
5840
5951
|
this.uploads.startPolling();
|
|
5841
5952
|
this.load();
|
|
5953
|
+
effect((onCleanup) => {
|
|
5954
|
+
const value = this.filter.query().trim();
|
|
5955
|
+
const timer = setTimeout(() => untracked(() => void this.search(value)), value ? SEARCH_DEBOUNCE_MS$1 : 0);
|
|
5956
|
+
onCleanup(() => clearTimeout(timer));
|
|
5957
|
+
});
|
|
5958
|
+
inject(DestroyRef).onDestroy(() => {
|
|
5959
|
+
if (this.store.searchText()) this.store.search("").catch(() => void 0);
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
async search(value) {
|
|
5963
|
+
if (this.status() !== "ready" || value === this.store.searchText()) return;
|
|
5964
|
+
this.refreshError.set(null);
|
|
5965
|
+
try {
|
|
5966
|
+
await this.store.search(value);
|
|
5967
|
+
this.selection.exit();
|
|
5968
|
+
} catch (error) {
|
|
5969
|
+
this.refreshError.set(this.translate(workspaceErrorMessage(error)));
|
|
5970
|
+
}
|
|
5842
5971
|
}
|
|
5843
5972
|
async load() {
|
|
5844
5973
|
this.loadError.set(null);
|
|
@@ -5931,7 +6060,7 @@ var WorkspaceList = class WorkspaceList {
|
|
|
5931
6060
|
host: { classAttribute: "@container flex h-full min-h-0 flex-col p-3 pb-2 md:p-8 md:pb-4" },
|
|
5932
6061
|
providers: [provideTranslocoScope("document-workspaces")],
|
|
5933
6062
|
ngImport: i0,
|
|
5934
|
-
template: "\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <Tabs class=\"mb-3 max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"'documentWorkspaces.list.title' | transloco\"\n [selectedTab]=\"filter.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of tabs; track item.tab) {\n <Tab [value]=\"item.tab\" variant=\"inner\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"shrink-0\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"shrink-0\" />\n }\n }\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.tab\">{{ filter.counts()[item.tab] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <p class=\"py-8 text-center text-sm text-(--font-primary-muted)\" data-testid=\"empty\">\n {{ (store.workspaces().length ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n\n @if (store.hasMore() || store.truncated()) {\n <div class=\"flex flex-col items-center gap-2 pb-8\" data-testid=\"load-more\">\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ \"documentWorkspaces.list.partial\" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}\n </p>\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.loadMoreError\" | transloco: { error: message } }}</p>\n }\n @if (store.hasMore()) {\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.list.loadMore\" | transloco }}\n </button>\n }\n </div>\n }\n }\n }\n </div>\n ",
|
|
6063
|
+
template: "\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <Tabs class=\"mb-3 max-w-full\">\n <TabList\n variant=\"inner\"\n class=\"w-fit max-w-full\"\n selectionMode=\"explicit\"\n [softDisabled]=\"false\"\n [attr.aria-label]=\"'documentWorkspaces.list.title' | transloco\"\n [selectedTab]=\"filter.tab()\"\n (selectedTabChange)=\"onTabChange($event)\">\n @for (item of tabs; track item.tab) {\n <Tab [value]=\"item.tab\" variant=\"inner\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"shrink-0\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"shrink-0\" />\n }\n }\n <span>{{ item.labelKey | transloco }}</span>\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"shrink-0\" [attr.data-testid]=\"'badge-' + item.tab\">{{ filter.counts()[item.tab] }}</Badge>\n </Tab>\n }\n </TabList>\n </Tabs>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <p class=\"py-8 text-center text-sm text-(--font-primary-muted)\" data-testid=\"empty\">\n {{ ((store.workspaces().length || filter.query().trim()) ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n\n @if (store.hasMore() || store.truncated()) {\n <div class=\"flex flex-col items-center gap-2 pb-8\" data-testid=\"load-more\">\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ \"documentWorkspaces.list.partial\" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}\n </p>\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.loadMoreError\" | transloco: { error: message } }}</p>\n }\n @if (store.hasMore()) {\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.list.loadMore\" | transloco }}\n </button>\n }\n </div>\n }\n }\n }\n </div>\n ",
|
|
5935
6064
|
isInline: true,
|
|
5936
6065
|
dependencies: [
|
|
5937
6066
|
{
|
|
@@ -6262,7 +6391,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
6262
6391
|
@default {
|
|
6263
6392
|
@if (!filter.filtered().length) {
|
|
6264
6393
|
<p class="py-8 text-center text-sm text-(--font-primary-muted)" data-testid="empty">
|
|
6265
|
-
{{ (store.workspaces().length ? "documentWorkspaces.list.noMatch" : "documentWorkspaces.list.empty") | transloco }}
|
|
6394
|
+
{{ ((store.workspaces().length || filter.query().trim()) ? "documentWorkspaces.list.noMatch" : "documentWorkspaces.list.empty") | transloco }}
|
|
6266
6395
|
</p>
|
|
6267
6396
|
} @else if (viewMode() === "grid") {
|
|
6268
6397
|
<div class="grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8">
|
|
@@ -8203,24 +8332,43 @@ var ShareDialog = class ShareDialog {
|
|
|
8203
8332
|
staged = signal([], ...ngDevMode ? [{ debugName: "staged" }] : /* istanbul ignore next */ []);
|
|
8204
8333
|
adding = signal(false, ...ngDevMode ? [{ debugName: "adding" }] : /* istanbul ignore next */ []);
|
|
8205
8334
|
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
8335
|
+
loadingMembers = signal(false, ...ngDevMode ? [{ debugName: "loadingMembers" }] : /* istanbul ignore next */ []);
|
|
8206
8336
|
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
8207
8337
|
constructor() {
|
|
8208
8338
|
afterNextRender(() => {
|
|
8209
8339
|
this.dialog().showModal();
|
|
8210
|
-
this.
|
|
8340
|
+
this.loadMembers();
|
|
8211
8341
|
});
|
|
8212
8342
|
}
|
|
8343
|
+
async loadMembers() {
|
|
8344
|
+
const workspace = this.workspace();
|
|
8345
|
+
if (workspace && !workspace.membersKnown) {
|
|
8346
|
+
this.error.set(null);
|
|
8347
|
+
this.loadingMembers.set(true);
|
|
8348
|
+
try {
|
|
8349
|
+
await this.store.update(workspace.id, {});
|
|
8350
|
+
} catch (error) {
|
|
8351
|
+
this.error.set(workspaceErrorMessage(error));
|
|
8352
|
+
} finally {
|
|
8353
|
+
this.loadingMembers.set(false);
|
|
8354
|
+
}
|
|
8355
|
+
}
|
|
8356
|
+
this.directory.ensureResolved(this.memberIds());
|
|
8357
|
+
}
|
|
8358
|
+
reloadMembers() {
|
|
8359
|
+
this.loadMembers();
|
|
8360
|
+
}
|
|
8213
8361
|
async add() {
|
|
8214
8362
|
const workspace = this.workspace();
|
|
8215
8363
|
const picked = this.staged();
|
|
8216
|
-
if (!workspace || !picked.length) return;
|
|
8364
|
+
if (!workspace?.membersKnown || !picked.length) return;
|
|
8217
8365
|
this.adding.set(true);
|
|
8218
8366
|
await this.save(() => withAdded(workspace, picked), () => this.staged.set([]));
|
|
8219
8367
|
this.adding.set(false);
|
|
8220
8368
|
}
|
|
8221
8369
|
async change(id, role) {
|
|
8222
8370
|
const workspace = this.workspace();
|
|
8223
|
-
if (!workspace) return;
|
|
8371
|
+
if (!workspace?.membersKnown) return;
|
|
8224
8372
|
await this.save(() => withRole(workspace, id, role));
|
|
8225
8373
|
}
|
|
8226
8374
|
async save(access, onSaved) {
|
|
@@ -8272,6 +8420,25 @@ var ShareDialog = class ShareDialog {
|
|
|
8272
8420
|
@if (workspace(); as workspace) {
|
|
8273
8421
|
<p class="text-(--font-primary-muted) mb-3 text-sm">{{ workspace.name }}</p>
|
|
8274
8422
|
|
|
8423
|
+
@if (!workspace.membersKnown) {
|
|
8424
|
+
@if (loadingMembers()) {
|
|
8425
|
+
<p class="text-(--font-primary-muted) text-sm" aria-live="polite" data-testid="members-loading">{{ "documentWorkspaces.share.membersLoading" | transloco }}</p>
|
|
8426
|
+
} @else {
|
|
8427
|
+
<div class="flex flex-col items-start gap-2">
|
|
8428
|
+
<p role="alert" class="text-(--font-primary-muted) text-sm" data-testid="members-unavailable">{{ "documentWorkspaces.share.membersUnavailable" | transloco }}</p>
|
|
8429
|
+
@if (error(); as message) {
|
|
8430
|
+
<p class="text-(--font-error-base) text-sm" data-testid="members-error">
|
|
8431
|
+
@if ("key" in message) {
|
|
8432
|
+
{{ message.key | transloco }}
|
|
8433
|
+
} @else {
|
|
8434
|
+
{{ message.text }}
|
|
8435
|
+
}
|
|
8436
|
+
</p>
|
|
8437
|
+
}
|
|
8438
|
+
<button variant="secondary" size="sm" (click)="reloadMembers()">{{ "documentWorkspaces.common.retry" | transloco }}</button>
|
|
8439
|
+
</div>
|
|
8440
|
+
}
|
|
8441
|
+
} @else {
|
|
8275
8442
|
<div class="flex items-start gap-2">
|
|
8276
8443
|
<principal-picker class="flex-1" [(entries)]="staged" [excludeIds]="memberIds()" />
|
|
8277
8444
|
<button [disabled]="!staged().length || busy()" (click)="add()">
|
|
@@ -8297,6 +8464,7 @@ var ShareDialog = class ShareDialog {
|
|
|
8297
8464
|
</div>
|
|
8298
8465
|
|
|
8299
8466
|
<p class="text-(--font-primary-muted) text-xs">{{ "documentWorkspaces.share.accessNotice" | transloco }}</p>
|
|
8467
|
+
}
|
|
8300
8468
|
}
|
|
8301
8469
|
</DialogBody>
|
|
8302
8470
|
|
|
@@ -8422,6 +8590,25 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
8422
8590
|
@if (workspace(); as workspace) {
|
|
8423
8591
|
<p class="text-(--font-primary-muted) mb-3 text-sm">{{ workspace.name }}</p>
|
|
8424
8592
|
|
|
8593
|
+
@if (!workspace.membersKnown) {
|
|
8594
|
+
@if (loadingMembers()) {
|
|
8595
|
+
<p class="text-(--font-primary-muted) text-sm" aria-live="polite" data-testid="members-loading">{{ "documentWorkspaces.share.membersLoading" | transloco }}</p>
|
|
8596
|
+
} @else {
|
|
8597
|
+
<div class="flex flex-col items-start gap-2">
|
|
8598
|
+
<p role="alert" class="text-(--font-primary-muted) text-sm" data-testid="members-unavailable">{{ "documentWorkspaces.share.membersUnavailable" | transloco }}</p>
|
|
8599
|
+
@if (error(); as message) {
|
|
8600
|
+
<p class="text-(--font-error-base) text-sm" data-testid="members-error">
|
|
8601
|
+
@if ("key" in message) {
|
|
8602
|
+
{{ message.key | transloco }}
|
|
8603
|
+
} @else {
|
|
8604
|
+
{{ message.text }}
|
|
8605
|
+
}
|
|
8606
|
+
</p>
|
|
8607
|
+
}
|
|
8608
|
+
<button variant="secondary" size="sm" (click)="reloadMembers()">{{ "documentWorkspaces.common.retry" | transloco }}</button>
|
|
8609
|
+
</div>
|
|
8610
|
+
}
|
|
8611
|
+
} @else {
|
|
8425
8612
|
<div class="flex items-start gap-2">
|
|
8426
8613
|
<principal-picker class="flex-1" [(entries)]="staged" [excludeIds]="memberIds()" />
|
|
8427
8614
|
<button [disabled]="!staged().length || busy()" (click)="add()">
|
|
@@ -8447,6 +8634,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
8447
8634
|
</div>
|
|
8448
8635
|
|
|
8449
8636
|
<p class="text-(--font-primary-muted) text-xs">{{ "documentWorkspaces.share.accessNotice" | transloco }}</p>
|
|
8637
|
+
}
|
|
8450
8638
|
}
|
|
8451
8639
|
</DialogBody>
|
|
8452
8640
|
|
|
@@ -9534,6 +9722,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
9534
9722
|
}
|
|
9535
9723
|
});
|
|
9536
9724
|
const EditWorkspace = createCallable(WorkspaceEditDialog);
|
|
9725
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
9537
9726
|
var WorkspaceDetail = class WorkspaceDetail {
|
|
9538
9727
|
workspaceId = input.required(...ngDevMode ? [{ debugName: "workspaceId" }] : /* istanbul ignore next */ []);
|
|
9539
9728
|
uploadEndpoint = input("", ...ngDevMode ? [{ debugName: "uploadEndpoint" }] : /* istanbul ignore next */ []);
|
|
@@ -9565,10 +9754,7 @@ var WorkspaceDetail = class WorkspaceDetail {
|
|
|
9565
9754
|
previewWidth = computed(() => this.userPreviewWidth() ?? Math.max(280, Math.round(this.hostWidth() * .4)), ...ngDevMode ? [{ debugName: "previewWidth" }] : /* istanbul ignore next */ []);
|
|
9566
9755
|
maxPreviewWidth = computed(() => this.hostWidth() ? this.hostWidth() - 360 : void 0, ...ngDevMode ? [{ debugName: "maxPreviewWidth" }] : /* istanbul ignore next */ []);
|
|
9567
9756
|
hasMore = computed(() => this.store.hasMoreDocuments(this.workspaceId()), ...ngDevMode ? [{ debugName: "hasMore" }] : /* istanbul ignore next */ []);
|
|
9568
|
-
filtered =
|
|
9569
|
-
const query = this.query().trim();
|
|
9570
|
-
return query ? this.documents().filter((d) => matchesText(d.name, query)) : this.documents();
|
|
9571
|
-
}, ...ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []);
|
|
9757
|
+
filtered = this.documents;
|
|
9572
9758
|
writable = computed(() => canWrite(this.workspace()?.currentUserRole), ...ngDevMode ? [{ debugName: "writable" }] : /* istanbul ignore next */ []);
|
|
9573
9759
|
selection = injectSelection({
|
|
9574
9760
|
items: this.filtered,
|
|
@@ -9604,6 +9790,12 @@ var WorkspaceDetail = class WorkspaceDetail {
|
|
|
9604
9790
|
this.workspaceId();
|
|
9605
9791
|
untracked(() => void this.open());
|
|
9606
9792
|
});
|
|
9793
|
+
effect((onCleanup) => {
|
|
9794
|
+
const value = this.query().trim();
|
|
9795
|
+
const id = untracked(() => this.workspaceId());
|
|
9796
|
+
const timer = setTimeout(() => untracked(() => void this.search(id, value)), value ? SEARCH_DEBOUNCE_MS : 0);
|
|
9797
|
+
onCleanup(() => clearTimeout(timer));
|
|
9798
|
+
});
|
|
9607
9799
|
effect(() => {
|
|
9608
9800
|
if (!this.writable()) untracked(() => this.selection.exit());
|
|
9609
9801
|
});
|
|
@@ -9613,12 +9805,25 @@ var WorkspaceDetail = class WorkspaceDetail {
|
|
|
9613
9805
|
observer.observe(host);
|
|
9614
9806
|
inject(DestroyRef).onDestroy(() => observer.disconnect());
|
|
9615
9807
|
}
|
|
9616
|
-
inject(DestroyRef).onDestroy(() =>
|
|
9808
|
+
inject(DestroyRef).onDestroy(() => {
|
|
9809
|
+
this.uploads.watch(null);
|
|
9810
|
+
this.store.resetDocumentSearch(this.workspaceId());
|
|
9811
|
+
});
|
|
9812
|
+
}
|
|
9813
|
+
async search(id, value) {
|
|
9814
|
+
if (this.status() !== "ready" || this.workspaceId() !== id || value === (this.store.documentSearch()[id] ?? "")) return;
|
|
9815
|
+
this.refreshError.set(null);
|
|
9816
|
+
try {
|
|
9817
|
+
await this.store.searchDocuments(id, value);
|
|
9818
|
+
} catch (error) {
|
|
9819
|
+
if (this.workspaceId() === id) this.refreshError.set(this.translate(workspaceErrorMessage(error)));
|
|
9820
|
+
}
|
|
9617
9821
|
}
|
|
9618
9822
|
async open() {
|
|
9619
9823
|
this.selection.exit();
|
|
9620
9824
|
this.previewedDocumentId.set(null);
|
|
9621
9825
|
this.query.set("");
|
|
9826
|
+
this.store.resetDocumentSearch(this.workspaceId());
|
|
9622
9827
|
this.refreshError.set(null);
|
|
9623
9828
|
this.loadMoreError.set(null);
|
|
9624
9829
|
this.deletedElsewhere.set(false);
|
|
@@ -9780,7 +9985,7 @@ var WorkspaceDetail = class WorkspaceDetail {
|
|
|
9780
9985
|
host: { classAttribute: "flex h-full min-h-0" },
|
|
9781
9986
|
providers: [provideTranslocoScope("document-workspaces")],
|
|
9782
9987
|
ngImport: i0,
|
|
9783
|
-
template: "\n <div class=\"@container flex min-h-0 min-w-0 flex-1 flex-col p-3 pb-2 md:p-8 md:pb-4\">\n <div class=\"mb-3 flex flex-wrap items-center gap-x-3 gap-y-2\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.detail.back' | transloco\"\n [tooltip]=\"'documentWorkspaces.detail.back' | transloco\"\n (click)=\"leave()\">\n <chevron-left-icon />\n </button>\n\n @if (workspace(); as workspace) {\n <h1 class=\"min-w-28 flex-[1_1_16rem] text-2xl leading-[31px] font-bold break-words text-(--font-neutral-black)\">{{ workspace.name }}</h1>\n\n <!-- Beside the title while they fit next to its 16rem, on a line of their own under it once they do not. -->\n @if (!deletedElsewhere()) {\n <div class=\"ml-auto flex shrink-0 items-center gap-1\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\"\n [writerOnly]=\"workspace.currentUserRole\"\n (click)=\"confirmDeleteWorkspace(workspace)\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"share(workspace)\">\n <user-round-icon />\n {{ \"documentWorkspaces.share.title\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"edit(workspace)\">\n <pen-to-square-icon />\n {{ \"documentWorkspaces.common.edit\" | transloco }}\n </button>\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [disabled]=\"refreshing()\"\n [attr.aria-label]=\"'documentWorkspaces.common.refresh' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.refresh' | transloco\"\n (click)=\"refresh()\">\n <refresh-icon />\n </button>\n </div>\n }\n }\n </div>\n\n @if (workspace(); as workspace) {\n @if (workspace.description) {\n <p class=\"mb-3 text-base text-(--font-primary-base)\">{{ workspace.description }}</p>\n }\n @if (workspace.tags.length) {\n <p class=\"mb-6 flex items-center gap-2 text-sm text-(--font-primary-base)\" data-testid=\"tags\">\n <tag-icon class=\"size-4\" />\n {{ workspace.tags.join(\", \") }}\n </p>\n }\n\n @if (!deletedElsewhere()) {\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n <button class=\"@max-md:px-2\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"upload(workspace)\">\n <upload-icon />\n <span class=\"@max-md:sr-only\">{{ \"documentWorkspaces.detail.upload\" | transloco }}</span>\n </button>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n <p class=\"mb-6 text-sm text-(--font-primary-base)\" aria-live=\"polite\" data-testid=\"count\">\n @if (selection.active()) {\n {{ \"documentWorkspaces.detail.selectedDocumentCount\" | transloco: { count: selection.count() } }}\n } @else {\n {{ \"documentWorkspaces.detail.fileCount\" | transloco: { count: workspace.documentCount } }}\n }\n </p>\n }\n }\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the documents scroll: the title, the toolbar and the count stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-(--font-primary-muted) text-sm\">{{ \"documentWorkspaces.detail.docsLoading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.detail.docsLoadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (deletedElsewhere()) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"not-found\">\n <div class=\"mb-1 inline-flex size-14 shrink-0 items-center justify-center rounded-(--radius-xl) bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\">\n <file-search-icon class=\"size-6\" />\n </div>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n <button variant=\"secondary\" size=\"sm\" class=\"mt-2\" (click)=\"leave()\">{{ \"documentWorkspaces.detail.back\" | transloco }}</button>\n </div>\n } @else if (workspace(); as workspace) {\n @if (!documents().length) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <svg class=\"mb-1 block size-14 shrink-0\" width=\"56\" height=\"56\" viewBox=\"0 0 38 38\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-testid=\"empty-icon\">\n <g opacity=\"0.8\">\n <rect class=\"fill-(--teal-100,#E0F4F1)\" width=\"38\" height=\"38\" rx=\"12\" />\n <path\n d=\"M11.2 24.8504H26.8C27.1575 24.8504 27.45 24.5579 27.45 24.2004V14.4504C27.45 14.0929 27.1575 13.8004 26.8 13.8004H20.7347C20.0319 13.8004 19.3453 13.5729 18.7847 13.1504L17.2247 11.9804C17.1109 11.8951 16.9769 11.8504 16.8347 11.8504H11.2C10.8425 11.8504 10.55 12.1429 10.55 12.5004V24.2004C10.55 24.5579 10.8425 24.8504 11.2 24.8504ZM26.8 26.8004H11.2C9.76591 26.8004 8.59998 25.6345 8.59998 24.2004V12.5004C8.59998 11.0663 9.76591 9.90039 11.2 9.90039H16.8347C17.3953 9.90039 17.9437 10.0832 18.3947 10.4204L19.9547 11.5904C20.1781 11.761 20.4544 11.8504 20.7347 11.8504H26.8C28.234 11.8504 29.4 13.0163 29.4 14.4504V24.2004C29.4 25.6345 28.234 26.8004 26.8 26.8004Z\"\n class=\"fill-(--teal-400,#4EBFB0)\" />\n </g>\n </svg>\n <h5 class=\"text-sm font-semibold text-(--font-primary-dark)\">{{ \"documentWorkspaces.detail.emptyTitle\" | transloco }}</h5>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.detail.emptySubtitle\" | transloco }}</p>\n </div>\n } @else if (!filtered().length) {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"no-match\">{{ \"documentWorkspaces.detail.noMatch\" | transloco }}</p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] content-start gap-4 pb-8\">\n @for (document of filtered(); track document.id) {\n <document-card\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnName\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSource\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSize\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnDate\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (document of filtered(); track document.id) {\n <document-row\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n }\n\n @if (hasMore()) {\n <div class=\"flex flex-col items-center gap-2\">\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.loadMoreError\" | transloco: { error: message } }}</p>\n }\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.detail.loadMore\" | transloco }}\n </button>\n </div>\n }\n } @else {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"not-found\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n }\n }\n }\n </div>\n </div>\n\n <!-- The list gives up part of its width to the preview, and takes it back when the preview closes. -->\n <div\n #handle=\"resizeHandle\"\n class=\"group/handle flex w-3 shrink-0 cursor-col-resize items-center justify-center max-md:hidden\"\n [class.hidden]=\"!previewed()\"\n [resizeHandle]=\"previewWidth()\"\n (resizeHandleChange)=\"userPreviewWidth.set($event)\"\n [resizeMin]=\"280\"\n [resizeMax]=\"maxPreviewWidth()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.resize' | transloco\">\n <span class=\"h-6 w-0.5 rounded-full bg-(--stroke-primary-light) opacity-0 group-hover/handle:opacity-100 group-focus-visible/handle:opacity-100 group-data-resizing/handle:opacity-100 pointer-coarse:opacity-100\"></span>\n </div>\n <aside\n class=\"sticky top-3 my-3 mr-3 flex h-[calc(100dvh-1.5rem)] max-h-[calc(100%-1.5rem)] shrink-0 self-start overflow-hidden rounded-(--radius-xl) bg-(--bg-neutral-white) transition-[width] duration-[350ms] ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none data-open:border data-open:border-(--stroke-primary-lighter) data-open:shadow-[0_4px_6px_-2px_rgba(45,57,58,0.05),0_10px_15px_-3px_rgba(45,57,58,0.1)] max-md:data-open:fixed max-md:data-open:inset-0 max-md:data-open:z-50 max-md:data-open:m-0 max-md:data-open:w-full! max-md:data-open:max-h-none max-md:data-open:rounded-none\"\n [class.transition-none]=\"handle.resizing()\"\n [style.width.px]=\"previewed() ? previewWidth() : 0\"\n [attr.data-open]=\"previewed() ? '' : null\"\n [attr.aria-hidden]=\"previewed() ? null : 'true'\"\n data-testid=\"preview\">\n @if (previewed(); as previewed) {\n <!-- Its own width, so the content does not reflow while the card opens. -->\n <document-preview-panel\n class=\"shrink-0 max-md:w-full!\"\n [style.width.px]=\"previewWidth()\"\n [document]=\"previewed\"\n [queryName]=\"queryName()\"\n (closed)=\"previewedDocumentId.set(null)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n }\n </aside>\n ",
|
|
9988
|
+
template: "\n <div class=\"@container flex min-h-0 min-w-0 flex-1 flex-col p-3 pb-2 md:p-8 md:pb-4\">\n <div class=\"mb-3 flex flex-wrap items-center gap-x-3 gap-y-2\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'documentWorkspaces.detail.back' | transloco\"\n [tooltip]=\"'documentWorkspaces.detail.back' | transloco\"\n (click)=\"leave()\">\n <chevron-left-icon />\n </button>\n\n @if (workspace(); as workspace) {\n <h1 class=\"min-w-28 flex-[1_1_16rem] text-2xl leading-[31px] font-bold break-words text-(--font-neutral-black)\">{{ workspace.name }}</h1>\n\n <!-- Beside the title while they fit next to its 16rem, on a line of their own under it once they do not. -->\n @if (!deletedElsewhere()) {\n <div class=\"ml-auto flex shrink-0 items-center gap-1\">\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\"\n [writerOnly]=\"workspace.currentUserRole\"\n (click)=\"confirmDeleteWorkspace(workspace)\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"share(workspace)\">\n <user-round-icon />\n {{ \"documentWorkspaces.share.title\" | transloco }}\n </button>\n <button size=\"sm\" variant=\"tertiary\" scheme=\"neutral\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"edit(workspace)\">\n <pen-to-square-icon />\n {{ \"documentWorkspaces.common.edit\" | transloco }}\n </button>\n <button\n size=\"sm\"\n variant=\"tertiary\"\n scheme=\"neutral\"\n [iconOnly]=\"true\"\n [disabled]=\"refreshing()\"\n [attr.aria-label]=\"'documentWorkspaces.common.refresh' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.refresh' | transloco\"\n (click)=\"refresh()\">\n <refresh-icon />\n </button>\n </div>\n }\n }\n </div>\n\n @if (workspace(); as workspace) {\n @if (workspace.description) {\n <p class=\"mb-3 text-base text-(--font-primary-base)\">{{ workspace.description }}</p>\n }\n @if (workspace.tags.length) {\n <p class=\"mb-6 flex items-center gap-2 text-sm text-(--font-primary-base)\" data-testid=\"tags\">\n <tag-icon class=\"size-4\" />\n {{ workspace.tags.join(\", \") }}\n </p>\n }\n\n @if (!deletedElsewhere()) {\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <SearchInput\n role=\"search\"\n class=\"min-w-32 flex-[0_1_15rem]\"\n [formField]=\"searchForm\"\n [placeholder]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [aria-label]=\"'documentWorkspaces.detail.searchPlaceholder' | transloco\"\n [clearLabel]=\"'documentWorkspaces.common.clearSearch' | transloco\" />\n\n <button class=\"@max-md:px-2\" [writerOnly]=\"workspace.currentUserRole\" (click)=\"upload(workspace)\">\n <upload-icon />\n <span class=\"@max-md:sr-only\">{{ \"documentWorkspaces.detail.upload\" | transloco }}</span>\n </button>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"text-(--font-error-base) enabled:hover:bg-(--bg-error-lightest)\" variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <x-mark-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"tertiary\" scheme=\"neutral\" size=\"sm\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n <p class=\"mb-6 text-sm text-(--font-primary-base)\" aria-live=\"polite\" data-testid=\"count\">\n @if (selection.active()) {\n {{ \"documentWorkspaces.detail.selectedDocumentCount\" | transloco: { count: selection.count() } }}\n } @else if (workspace.documentCount !== undefined) {\n {{ \"documentWorkspaces.detail.fileCount\" | transloco: { count: workspace.documentCount } }}\n }\n </p>\n }\n }\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the documents scroll: the title, the toolbar and the count stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-(--font-primary-muted) text-sm\">{{ \"documentWorkspaces.detail.docsLoading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.detail.docsLoadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"sm\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (deletedElsewhere()) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"not-found\">\n <div class=\"mb-1 inline-flex size-14 shrink-0 items-center justify-center rounded-(--radius-xl) bg-(--teal-100,#E0F4F1) text-(--teal-400,#4EBFB0)\">\n <file-search-icon class=\"size-6\" />\n </div>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n <button variant=\"secondary\" size=\"sm\" class=\"mt-2\" (click)=\"leave()\">{{ \"documentWorkspaces.detail.back\" | transloco }}</button>\n </div>\n } @else if (workspace(); as workspace) {\n @if (!documents().length && !query().trim()) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <svg class=\"mb-1 block size-14 shrink-0\" width=\"56\" height=\"56\" viewBox=\"0 0 38 38\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-testid=\"empty-icon\">\n <g opacity=\"0.8\">\n <rect class=\"fill-(--teal-100,#E0F4F1)\" width=\"38\" height=\"38\" rx=\"12\" />\n <path\n d=\"M11.2 24.8504H26.8C27.1575 24.8504 27.45 24.5579 27.45 24.2004V14.4504C27.45 14.0929 27.1575 13.8004 26.8 13.8004H20.7347C20.0319 13.8004 19.3453 13.5729 18.7847 13.1504L17.2247 11.9804C17.1109 11.8951 16.9769 11.8504 16.8347 11.8504H11.2C10.8425 11.8504 10.55 12.1429 10.55 12.5004V24.2004C10.55 24.5579 10.8425 24.8504 11.2 24.8504ZM26.8 26.8004H11.2C9.76591 26.8004 8.59998 25.6345 8.59998 24.2004V12.5004C8.59998 11.0663 9.76591 9.90039 11.2 9.90039H16.8347C17.3953 9.90039 17.9437 10.0832 18.3947 10.4204L19.9547 11.5904C20.1781 11.761 20.4544 11.8504 20.7347 11.8504H26.8C28.234 11.8504 29.4 13.0163 29.4 14.4504V24.2004C29.4 25.6345 28.234 26.8004 26.8 26.8004Z\"\n class=\"fill-(--teal-400,#4EBFB0)\" />\n </g>\n </svg>\n <h5 class=\"text-sm font-semibold text-(--font-primary-dark)\">{{ \"documentWorkspaces.detail.emptyTitle\" | transloco }}</h5>\n <p class=\"max-w-70 text-sm text-(--font-primary-base)\">{{ \"documentWorkspaces.detail.emptySubtitle\" | transloco }}</p>\n </div>\n } @else if (!filtered().length) {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"no-match\">{{ \"documentWorkspaces.detail.noMatch\" | transloco }}</p>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] content-start gap-4 pb-8\">\n @for (document of filtered(); track document.id) {\n <document-card\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_minmax(0,1fr)_6rem_10rem_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <checkbox\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (checkedChange)=\"selection.toggleAll()\" />\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnName\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSource\" | transloco }}</div>\n <div role=\"columnheader\" class=\"@max-3xl:hidden\">{{ \"documentWorkspaces.detail.columnSize\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.detail.columnDate\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (document of filtered(); track document.id) {\n <document-row\n [document]=\"document\"\n [workspaceRole]=\"workspace.currentUserRole\"\n [query]=\"query()\"\n [active]=\"document.id === previewedDocumentId()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(document)\"\n [selectable]=\"selection.canSelect(document)\"\n (opened)=\"togglePreview(document)\"\n (deleteRequested)=\"confirmDeleteDocuments([document])\"\n (selectedChange)=\"selection.toggle(document, $event)\" />\n }\n </div>\n }\n\n @if (hasMore()) {\n <div class=\"flex flex-col items-center gap-2\">\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-(--font-error-base) text-sm\">{{ \"documentWorkspaces.detail.loadMoreError\" | transloco: { error: message } }}</p>\n }\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.detail.loadMore\" | transloco }}\n </button>\n </div>\n }\n } @else {\n <p class=\"text-(--font-primary-muted) py-8 text-center text-sm\" data-testid=\"not-found\">{{ \"documentWorkspaces.errors.notFound\" | transloco }}</p>\n }\n }\n }\n </div>\n </div>\n\n <!-- The list gives up part of its width to the preview, and takes it back when the preview closes. -->\n <div\n #handle=\"resizeHandle\"\n class=\"group/handle flex w-3 shrink-0 cursor-col-resize items-center justify-center max-md:hidden\"\n [class.hidden]=\"!previewed()\"\n [resizeHandle]=\"previewWidth()\"\n (resizeHandleChange)=\"userPreviewWidth.set($event)\"\n [resizeMin]=\"280\"\n [resizeMax]=\"maxPreviewWidth()\"\n [attr.aria-label]=\"'documentWorkspaces.preview.resize' | transloco\">\n <span class=\"h-6 w-0.5 rounded-full bg-(--stroke-primary-light) opacity-0 group-hover/handle:opacity-100 group-focus-visible/handle:opacity-100 group-data-resizing/handle:opacity-100 pointer-coarse:opacity-100\"></span>\n </div>\n <aside\n class=\"sticky top-3 my-3 mr-3 flex h-[calc(100dvh-1.5rem)] max-h-[calc(100%-1.5rem)] shrink-0 self-start overflow-hidden rounded-(--radius-xl) bg-(--bg-neutral-white) transition-[width] duration-[350ms] ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none data-open:border data-open:border-(--stroke-primary-lighter) data-open:shadow-[0_4px_6px_-2px_rgba(45,57,58,0.05),0_10px_15px_-3px_rgba(45,57,58,0.1)] max-md:data-open:fixed max-md:data-open:inset-0 max-md:data-open:z-50 max-md:data-open:m-0 max-md:data-open:w-full! max-md:data-open:max-h-none max-md:data-open:rounded-none\"\n [class.transition-none]=\"handle.resizing()\"\n [style.width.px]=\"previewed() ? previewWidth() : 0\"\n [attr.data-open]=\"previewed() ? '' : null\"\n [attr.aria-hidden]=\"previewed() ? null : 'true'\"\n data-testid=\"preview\">\n @if (previewed(); as previewed) {\n <!-- Its own width, so the content does not reflow while the card opens. -->\n <document-preview-panel\n class=\"shrink-0 max-md:w-full!\"\n [style.width.px]=\"previewWidth()\"\n [document]=\"previewed\"\n [queryName]=\"queryName()\"\n (closed)=\"previewedDocumentId.set(null)\"\n (askFollowUp)=\"askFollowUp.emit($event)\" />\n }\n </aside>\n ",
|
|
9784
9989
|
isInline: true,
|
|
9785
9990
|
dependencies: [
|
|
9786
9991
|
{
|
|
@@ -10145,7 +10350,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
10145
10350
|
<p class="mb-6 text-sm text-(--font-primary-base)" aria-live="polite" data-testid="count">
|
|
10146
10351
|
@if (selection.active()) {
|
|
10147
10352
|
{{ "documentWorkspaces.detail.selectedDocumentCount" | transloco: { count: selection.count() } }}
|
|
10148
|
-
} @else {
|
|
10353
|
+
} @else if (workspace.documentCount !== undefined) {
|
|
10149
10354
|
{{ "documentWorkspaces.detail.fileCount" | transloco: { count: workspace.documentCount } }}
|
|
10150
10355
|
}
|
|
10151
10356
|
</p>
|
|
@@ -10184,7 +10389,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
10184
10389
|
<button variant="secondary" size="sm" class="mt-2" (click)="leave()">{{ "documentWorkspaces.detail.back" | transloco }}</button>
|
|
10185
10390
|
</div>
|
|
10186
10391
|
} @else if (workspace(); as workspace) {
|
|
10187
|
-
@if (!documents().length) {
|
|
10392
|
+
@if (!documents().length && !query().trim()) {
|
|
10188
10393
|
<div class="flex flex-col items-center gap-1 py-8 text-center" data-testid="empty">
|
|
10189
10394
|
<svg class="mb-1 block size-14 shrink-0" width="56" height="56" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-testid="empty-icon">
|
|
10190
10395
|
<g opacity="0.8">
|
|
@@ -10682,7 +10887,9 @@ var WorkspaceMentionRows = class WorkspaceMentionRows {
|
|
|
10682
10887
|
<workspace-icon size="md" [color]="item.color" />
|
|
10683
10888
|
<span class="flex min-w-0 flex-col">
|
|
10684
10889
|
<span class="truncate text-(length:--size-base) font-semibold text-(--font-neutral-black)">{{ item.name }}</span>
|
|
10685
|
-
|
|
10890
|
+
@if (item.documentCount !== undefined) {
|
|
10891
|
+
<span class="text-(length:--size-sm) text-(--font-primary-muted)">{{ "documentWorkspaces.mention.documentCount" | transloco: { count: item.documentCount } }}</span>
|
|
10892
|
+
}
|
|
10686
10893
|
</span>
|
|
10687
10894
|
</span>
|
|
10688
10895
|
</checkbox>
|
|
@@ -10766,7 +10973,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
10766
10973
|
<workspace-icon size="md" [color]="item.color" />
|
|
10767
10974
|
<span class="flex min-w-0 flex-col">
|
|
10768
10975
|
<span class="truncate text-(length:--size-base) font-semibold text-(--font-neutral-black)">{{ item.name }}</span>
|
|
10769
|
-
|
|
10976
|
+
@if (item.documentCount !== undefined) {
|
|
10977
|
+
<span class="text-(length:--size-sm) text-(--font-primary-muted)">{{ "documentWorkspaces.mention.documentCount" | transloco: { count: item.documentCount } }}</span>
|
|
10978
|
+
}
|
|
10770
10979
|
</span>
|
|
10771
10980
|
</span>
|
|
10772
10981
|
</checkbox>
|