@sanity/dashboard 5.0.1 → 6.0.1

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 (38) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -51
  3. package/dist/index.d.ts +54 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +605 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +37 -79
  8. package/lib/index.d.mts +0 -65
  9. package/lib/index.d.ts +0 -65
  10. package/lib/index.esm.js +0 -568
  11. package/lib/index.esm.js.map +0 -1
  12. package/lib/index.js +0 -564
  13. package/lib/index.js.map +0 -1
  14. package/lib/index.mjs +0 -568
  15. package/lib/index.mjs.map +0 -1
  16. package/sanity.json +0 -8
  17. package/src/components/DashboardLayout.tsx +0 -10
  18. package/src/components/DashboardWidgetContainer.tsx +0 -69
  19. package/src/components/NotFoundWidget.tsx +0 -30
  20. package/src/components/WidgetGroup.tsx +0 -106
  21. package/src/containers/Dashboard.tsx +0 -19
  22. package/src/containers/DashboardContext.tsx +0 -8
  23. package/src/containers/WidgetContainer.tsx +0 -21
  24. package/src/index.ts +0 -7
  25. package/src/plugin.tsx +0 -72
  26. package/src/types.ts +0 -21
  27. package/src/versionedClient.ts +0 -5
  28. package/src/widgets/projectInfo/ProjectInfo.tsx +0 -221
  29. package/src/widgets/projectInfo/index.ts +0 -10
  30. package/src/widgets/projectInfo/types.ts +0 -28
  31. package/src/widgets/projectUsers/ProjectUser.tsx +0 -45
  32. package/src/widgets/projectUsers/ProjectUsers.tsx +0 -148
  33. package/src/widgets/projectUsers/index.ts +0 -10
  34. package/src/widgets/sanityTutorials/SanityTutorials.tsx +0 -77
  35. package/src/widgets/sanityTutorials/Tutorial.tsx +0 -111
  36. package/src/widgets/sanityTutorials/dataAdapter.ts +0 -49
  37. package/src/widgets/sanityTutorials/index.ts +0 -10
  38. package/v2-incompatible.js +0 -11
package/lib/index.esm.js DELETED
@@ -1,568 +0,0 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { forwardRef, createContext, useContext, useMemo, createElement, useState, useEffect, useCallback } from "react";
3
- import { Card, Box, Heading, Stack, Label, Grid, Text, Code, Button, rem, Flex, Spinner, Container } from "@sanity/ui";
4
- import { styled, css } from "styled-components";
5
- import { useClient, useListFormat, UserAvatar, useUserStore, definePlugin } from "sanity";
6
- import { from } from "rxjs";
7
- import { switchMap, map } from "rxjs/operators";
8
- import { RobotIcon, PlayIcon } from "@sanity/icons";
9
- import imageUrlBuilder from "@sanity/image-url";
10
- const Root$3 = styled(Card)`
11
- display: flex;
12
- flex-direction: column;
13
- justify-content: stretch;
14
- height: 100%;
15
- box-sizing: border-box;
16
- position: relative;
17
- `, Header = styled(Card)`
18
- position: sticky;
19
- top: 0;
20
- z-index: 2;
21
- border-top-left-radius: inherit;
22
- border-top-right-radius: inherit;
23
- `, Footer = styled(Card)`
24
- position: sticky;
25
- overflow: hidden;
26
- bottom: 0;
27
- z-index: 2;
28
- border-bottom-right-radius: inherit;
29
- border-bottom-left-radius: inherit;
30
- margin-top: auto;
31
- `, Content = styled(Box)`
32
- position: relative;
33
- z-index: 1;
34
- height: stretch;
35
- min-height: 21.5em;
36
-
37
- @media (min-width: ${({ theme }) => theme.sanity.media[0]}px) {
38
- overflow-y: auto;
39
- outline: none;
40
- }
41
- `, DashboardWidgetContainer = forwardRef(function(props, ref) {
42
- const { header, children, footer } = props;
43
- return /* @__PURE__ */ jsxs(Root$3, { radius: 3, display: "flex", ref, children: [
44
- header && /* @__PURE__ */ jsx(Header, { borderBottom: !0, paddingX: 3, paddingY: 4, children: /* @__PURE__ */ jsx(Heading, { size: 1, textOverflow: "ellipsis", children: header }) }),
45
- children && /* @__PURE__ */ jsx(Content, { children }),
46
- footer && /* @__PURE__ */ jsx(Footer, { borderTop: !0, children: footer })
47
- ] });
48
- });
49
- function useVersionedClient() {
50
- return useClient({ apiVersion: "2024-08-01" });
51
- }
52
- const DashboardContext = createContext({ widgets: [] });
53
- function useDashboardConfig() {
54
- return useContext(DashboardContext);
55
- }
56
- function WidgetContainer(props) {
57
- const config = useDashboardConfig(), layout = useMemo(
58
- () => ({
59
- ...props.layout || {},
60
- ...config.layout || {}
61
- }),
62
- [props.layout, config.layout]
63
- );
64
- return /* @__PURE__ */ jsx(Card, { shadow: 1, "data-width": layout.width, "data-height": layout.height, children: createElement(props.component, {}) });
65
- }
66
- function isUrl(url) {
67
- return url && /^https?:\/\//.test(`${url}`);
68
- }
69
- function getGraphQLUrl(projectId, dataset) {
70
- return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`;
71
- }
72
- function getGroqUrl(projectId, dataset) {
73
- return `https://${projectId}.api.sanity.io/v1/groq/${dataset}`;
74
- }
75
- function getManageUrl(projectId) {
76
- return `https://manage.sanity.io/projects/${projectId}`;
77
- }
78
- const NO_EXPERIMENTAL = [], NO_DATA = [];
79
- function ProjectInfo(props) {
80
- const { __experimental_before = NO_EXPERIMENTAL, data = NO_DATA } = props, [studioApps, setStudioApps] = useState(), [graphQLApi, setGraphQLApi] = useState(), versionedClient = useVersionedClient(), { projectId = "unknown", dataset = "unknown" } = versionedClient.config();
81
- useEffect(() => {
82
- const subscriptions = [];
83
- return subscriptions.push(
84
- versionedClient.observable.request({ uri: "/user-applications", tag: "dashboard.project-info" }).subscribe({
85
- next: (result) => setStudioApps(result.filter((app) => app.type === "studio")),
86
- error: (error) => {
87
- console.error("Error while resolving user applications", error), setStudioApps({
88
- error: "Something went wrong while resolving user applications. See console."
89
- });
90
- }
91
- })
92
- ), subscriptions.push(
93
- versionedClient.observable.request({
94
- method: "HEAD",
95
- uri: `/graphql/${dataset}/default`,
96
- tag: "dashboard.project-info.graphql-api"
97
- }).subscribe({
98
- next: () => setGraphQLApi(getGraphQLUrl(projectId, dataset)),
99
- error: (error) => {
100
- error.statusCode === 404 ? setGraphQLApi(void 0) : (console.error("Error while looking for graphQLApi", error), setGraphQLApi({
101
- error: "Something went wrong while looking up graphQLApi. See console."
102
- }));
103
- }
104
- })
105
- ), () => {
106
- subscriptions.forEach((s) => s.unsubscribe());
107
- };
108
- }, [dataset, projectId, versionedClient, setGraphQLApi]);
109
- const assembleTableRows = useMemo(() => {
110
- let result = [
111
- {
112
- title: "Sanity project",
113
- rows: [
114
- { title: "Project ID", value: projectId },
115
- { title: "Dataset", value: dataset }
116
- ]
117
- }
118
- ];
119
- const apps = data.filter((item) => item.category === "apps");
120
- (Array.isArray(studioApps) ? studioApps : []).forEach((app) => {
121
- apps.push({
122
- title: app.title || "Studio",
123
- value: app.urlType === "internal" ? `https://${app.appHost}.sanity.studio` : app.appHost
124
- });
125
- }), apps.length > 0 && (result = result.concat([{ title: "Apps", rows: apps }])), result = result.concat(
126
- [
127
- {
128
- title: "APIs",
129
- rows: [
130
- { title: "GROQ", value: getGroqUrl(projectId, dataset) },
131
- {
132
- title: "GraphQL",
133
- value: (typeof graphQLApi == "object" ? "Error" : graphQLApi) ?? "Not deployed"
134
- }
135
- ]
136
- }
137
- ],
138
- data.filter((item) => item.category === "apis")
139
- );
140
- const otherStuff = {};
141
- return data.forEach((item) => {
142
- item.category && item.category !== "apps" && item.category !== "apis" && (otherStuff[item.category] || (otherStuff[item.category] = []), otherStuff[item.category].push(item));
143
- }), Object.keys(otherStuff).forEach((category) => {
144
- result.push({ title: category, rows: otherStuff[category] });
145
- }), result;
146
- }, [graphQLApi, studioApps, projectId, dataset, data]);
147
- return /* @__PURE__ */ jsxs(Fragment, { children: [
148
- __experimental_before.map((widgetConfig, idx) => /* @__PURE__ */ jsx(WidgetContainer, { ...widgetConfig }, idx)),
149
- /* @__PURE__ */ jsx(Box, { height: "fill", marginTop: __experimental_before?.length > 0 ? 4 : 0, children: /* @__PURE__ */ jsx(
150
- DashboardWidgetContainer,
151
- {
152
- footer: /* @__PURE__ */ jsx(
153
- Button,
154
- {
155
- style: { width: "100%" },
156
- paddingX: 2,
157
- paddingY: 4,
158
- mode: "bleed",
159
- tone: "primary",
160
- text: "Manage project",
161
- as: "a",
162
- href: getManageUrl(projectId)
163
- }
164
- ),
165
- children: /* @__PURE__ */ jsx(
166
- Card,
167
- {
168
- paddingY: 4,
169
- radius: 2,
170
- role: "table",
171
- "aria-label": "Project info",
172
- "aria-describedby": "project_info_table",
173
- children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
174
- /* @__PURE__ */ jsx(Box, { paddingX: 3, as: "header", children: /* @__PURE__ */ jsx(Heading, { size: 1, as: "h2", id: "project_info_table", children: "Project info" }) }),
175
- assembleTableRows.map((item) => !item || !item.rows ? null : /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
176
- /* @__PURE__ */ jsx(Card, { borderBottom: !0, padding: 3, children: /* @__PURE__ */ jsx(Label, { size: 0, muted: !0, role: "columnheader", children: item.title }) }),
177
- /* @__PURE__ */ jsx(Stack, { space: 4, paddingX: 3, role: "rowgroup", children: item.rows.map((row) => /* @__PURE__ */ jsxs(Grid, { columns: 2, role: "row", children: [
178
- /* @__PURE__ */ jsx(Text, { weight: "medium", role: "rowheader", children: row.title }),
179
- typeof row.value == "object" && /* @__PURE__ */ jsx(Text, { size: 1, children: row.value?.error }),
180
- typeof row.value == "string" && /* @__PURE__ */ jsx(Fragment, { children: isUrl(row.value) ? /* @__PURE__ */ jsx(Text, { size: 1, role: "cell", style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx("a", { href: row.value, children: row.value }) }) : /* @__PURE__ */ jsx(Code, { size: 1, role: "cell", style: { wordBreak: "break-word" }, children: row.value }) })
181
- ] }, `${row.value}-${row.title}`)) })
182
- ] }, item.title))
183
- ] })
184
- }
185
- )
186
- }
187
- ) })
188
- ] });
189
- }
190
- function projectInfoWidget(config) {
191
- return {
192
- name: "project-info",
193
- component: ProjectInfo,
194
- layout: config?.layout ?? { width: "medium" }
195
- };
196
- }
197
- const Root$2 = styled(Flex)`
198
- height: ${rem(33)}; // 33 = PREVIEW_SIZES.default.media.height
199
- box-sizing: content-box;
200
- `;
201
- function ProjectUser({ user, isRobot, roles }) {
202
- const listFormat = useListFormat({ style: "narrow" });
203
- return /* @__PURE__ */ jsx(Root$2, { align: "center", children: /* @__PURE__ */ jsxs(Flex, { align: "center", flex: 1, gap: 2, children: [
204
- /* @__PURE__ */ jsx(Box, { flex: "none", children: isRobot ? /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(RobotIcon, {}) }) : /* @__PURE__ */ jsx(UserAvatar, { user }) }),
205
- /* @__PURE__ */ jsxs(Stack, { flex: 1, space: 2, children: [
206
- /* @__PURE__ */ jsx(Text, { size: 1, style: { color: "inherit" }, textOverflow: "ellipsis", weight: "medium", children: user.displayName }),
207
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, textOverflow: "ellipsis", children: listFormat.format(roles) })
208
- ] })
209
- ] }) });
210
- }
211
- function getInviteUrl(projectId) {
212
- return `https://manage.sanity.io/projects/${projectId}/members`;
213
- }
214
- function ProjectUsers() {
215
- const [project, setProject] = useState(), [users, setUsers] = useState(), [error, setError] = useState(), userStore = useUserStore(), versionedClient = useVersionedClient(), fetchData = useCallback(() => {
216
- const { projectId } = versionedClient.config(), subscription = versionedClient.observable.request({
217
- uri: `/projects/${projectId}`,
218
- tag: "dashboard.project-users"
219
- }).pipe(
220
- switchMap(
221
- (_project) => from(userStore.getUsers(_project.members.map((mem) => mem.id))).pipe(
222
- map((_users) => ({ project: _project, users: _users }))
223
- )
224
- )
225
- ).subscribe({
226
- next: ({ users: _users, project: _project }) => {
227
- setProject(_project), setUsers(
228
- (Array.isArray(_users) ? _users : [_users]).sort(
229
- (userA, userB) => sortUsersByRobotStatus(userA, userB, _project)
230
- )
231
- );
232
- },
233
- error: (e) => setError(e)
234
- });
235
- return () => subscription.unsubscribe();
236
- }, [userStore, versionedClient]);
237
- useEffect(() => fetchData(), [fetchData]);
238
- const handleRetryFetch = useCallback(() => fetchData(), [fetchData]), isLoading = !users || !project;
239
- return error ? /* @__PURE__ */ jsx(DashboardWidgetContainer, { header: "Project users", children: /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsxs(Text, { children: [
240
- "Something went wrong while fetching data. You could",
241
- " ",
242
- /* @__PURE__ */ jsx("a", { onClick: handleRetryFetch, title: "Retry users fetch", style: { cursor: "pointer" }, children: "retry" }),
243
- "..?"
244
- ] }) }) }) : /* @__PURE__ */ jsxs(
245
- DashboardWidgetContainer,
246
- {
247
- header: "Project users",
248
- footer: /* @__PURE__ */ jsx(
249
- Button,
250
- {
251
- style: { width: "100%" },
252
- paddingX: 2,
253
- paddingY: 4,
254
- mode: "bleed",
255
- tone: "primary",
256
- text: "Manage members",
257
- as: "a",
258
- loading: isLoading,
259
- href: isLoading ? void 0 : getInviteUrl(project.id)
260
- }
261
- ),
262
- children: [
263
- isLoading && /* @__PURE__ */ jsx(Box, { paddingY: 5, paddingX: 2, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
264
- /* @__PURE__ */ jsx(Text, { align: "center", muted: !0, size: 1, children: /* @__PURE__ */ jsx(Spinner, {}) }),
265
- /* @__PURE__ */ jsx(Text, { align: "center", size: 1, muted: !0, children: "Loading items\u2026" })
266
- ] }) }),
267
- !isLoading && /* @__PURE__ */ jsx(Stack, { space: 3, padding: 3, children: users?.map((user) => {
268
- const membership = project.members.find((member) => member.id === user.id);
269
- return /* @__PURE__ */ jsx(
270
- ProjectUser,
271
- {
272
- user,
273
- isRobot: membership?.isRobot ?? !1,
274
- roles: membership?.roles.map((role) => role.title) || []
275
- },
276
- user.id
277
- );
278
- }) })
279
- ]
280
- }
281
- );
282
- }
283
- function sortUsersByRobotStatus(userA, userB, project) {
284
- const { members } = project, membershipA = members.find((member) => member.id === userA?.id), membershipB = members.find((member) => member.id === userB?.id);
285
- return membershipA?.isRobot === membershipB?.isRobot ? (membershipA?.createdAt || "") > (membershipB?.createdAt || "") ? 1 : -1 : membershipA?.isRobot ? 1 : -1;
286
- }
287
- function projectUsersWidget(config) {
288
- return {
289
- name: "project-info",
290
- component: ProjectUsers,
291
- layout: config?.layout
292
- };
293
- }
294
- const PlayIconBox = styled(Box)`
295
- position: absolute;
296
- top: 50%;
297
- left: 50%;
298
- transform: translate(-50%, -50%);
299
-
300
- &:before {
301
- content: '';
302
- position: absolute;
303
- top: 50%;
304
- left: 50%;
305
- transform: translate(-50%, -50%);
306
- width: 2.75em;
307
- height: 2.75em;
308
- border-radius: 50%;
309
- background: ${({ theme }) => theme.sanity.color.card.enabled.bg};
310
- opacity: 0.75;
311
- }
312
- `, Root$1 = styled(Flex)`
313
- &:hover {
314
- ${PlayIconBox} {
315
- &:before {
316
- opacity: 1;
317
- }
318
- }
319
- }
320
- `, PosterCard = styled(Card)`
321
- width: 100%;
322
- padding-bottom: calc(9 / 16 * 100%);
323
- position: relative;
324
- `, Poster = styled.img`
325
- position: absolute;
326
- top: 0;
327
- left: 0;
328
- height: 100%;
329
- width: 100%;
330
- object-fit: cover;
331
- display: block;
332
-
333
- &:not([src]) {
334
- display: none;
335
- }
336
- `;
337
- function Tutorial(props) {
338
- const { title, posterURL, showPlayIcon, href, presenterName, presenterSubtitle } = props;
339
- return /* @__PURE__ */ jsx(Root$1, { flex: 1, children: /* @__PURE__ */ jsx(
340
- Card,
341
- {
342
- sizing: "border",
343
- flex: 1,
344
- padding: 2,
345
- radius: 2,
346
- as: "a",
347
- href,
348
- target: "_blank",
349
- rel: "noopener noreferrer",
350
- style: { position: "relative" },
351
- children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
352
- posterURL && /* @__PURE__ */ jsxs(PosterCard, { marginBottom: 1, children: [
353
- /* @__PURE__ */ jsx(Poster, { src: posterURL }),
354
- showPlayIcon && /* @__PURE__ */ jsx(PlayIconBox, { display: "flex", children: /* @__PURE__ */ jsx(Text, { align: "center", children: /* @__PURE__ */ jsx(PlayIcon, {}) }) })
355
- ] }),
356
- /* @__PURE__ */ jsxs(Flex, { direction: "column", justify: "space-between", paddingY: 2, flex: 1, children: [
357
- /* @__PURE__ */ jsx(Heading, { as: "h3", size: 1, children: title }),
358
- /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsxs(Stack, { space: 2, flex: 1, children: [
359
- /* @__PURE__ */ jsx(Text, { size: 1, children: presenterName }),
360
- /* @__PURE__ */ jsx(Text, { size: 0, style: { opacity: 0.7 }, children: presenterSubtitle })
361
- ] }) })
362
- ] })
363
- ] })
364
- }
365
- ) });
366
- }
367
- const tutorialsProjectConfig = {
368
- projectId: "3do82whm",
369
- dataset: "next"
370
- };
371
- function useDataAdapter() {
372
- const versionedClient = useVersionedClient();
373
- return useMemo(
374
- () => ({
375
- getFeed: (templateRepoId) => {
376
- const uri = templateRepoId ? `/addons/dashboard?templateRepoId=${templateRepoId}` : "/addons/dashboard";
377
- return versionedClient.observable.request({
378
- uri,
379
- tag: "dashboard.sanity-tutorials",
380
- withCredentials: !1
381
- });
382
- },
383
- urlBuilder: imageUrlBuilder(tutorialsProjectConfig)
384
- }),
385
- [versionedClient]
386
- );
387
- }
388
- function createUrl(slug, type) {
389
- return type === "tutorial" ? `https://www.sanity.io/docs/tutorials/${slug.current}` : type === "guide" ? `https://www.sanity.io/docs/guides/${slug.current}` : !1;
390
- }
391
- function SanityTutorials(props) {
392
- const { templateRepoId } = props, [feedItems, setFeedItems] = useState([]), { getFeed, urlBuilder } = useDataAdapter();
393
- return useEffect(() => {
394
- const subscription = getFeed(templateRepoId).subscribe((response) => {
395
- setFeedItems(response.items);
396
- });
397
- return () => {
398
- subscription.unsubscribe();
399
- };
400
- }, [setFeedItems, getFeed, templateRepoId]), /* @__PURE__ */ jsx(DashboardWidgetContainer, { header: "Learn about Sanity", children: /* @__PURE__ */ jsx(Flex, { as: "ul", overflow: "auto", align: "stretch", paddingY: 2, children: feedItems?.map((feedItem, index) => {
401
- if (!feedItem.title || !feedItem.guideOrTutorial && !feedItem.externalLink)
402
- return null;
403
- const presenter = feedItem.presenter || feedItem.guideOrTutorial?.presenter || {}, subtitle = feedItem.category, { guideOrTutorial = {} } = feedItem, href = (guideOrTutorial.slug ? createUrl(guideOrTutorial.slug, guideOrTutorial._type) : feedItem.externalLink) || feedItem.externalLink;
404
- return /* @__PURE__ */ jsx(
405
- Flex,
406
- {
407
- as: "li",
408
- paddingRight: index < feedItems?.length - 1 ? 1 : 3,
409
- paddingLeft: index === 0 ? 3 : 0,
410
- align: "stretch",
411
- style: { minWidth: 272, width: "30%" },
412
- children: /* @__PURE__ */ jsx(
413
- Tutorial,
414
- {
415
- title: feedItem.title,
416
- href: href ?? "",
417
- presenterName: presenter.name,
418
- presenterSubtitle: subtitle,
419
- showPlayIcon: feedItem.hasVideo,
420
- posterURL: feedItem.poster ? urlBuilder.image(feedItem.poster).height(360).url() : void 0
421
- }
422
- )
423
- },
424
- feedItem._id
425
- );
426
- }) }) });
427
- }
428
- function sanityTutorialsWidget(config) {
429
- return {
430
- name: "sanity-tutorials",
431
- component: SanityTutorials,
432
- layout: config?.layout ?? { width: "full" }
433
- };
434
- }
435
- function DashboardLayout(props) {
436
- return /* @__PURE__ */ jsx(Container, { width: 4, padding: 4, sizing: "border", style: { height: "100%", overflowY: "auto" }, children: props.children });
437
- }
438
- const media = {
439
- small: (...args) => css`
440
- @media (min-width: ${({ theme }) => theme.sanity.media[0]}px) {
441
- ${css(...args)}
442
- }
443
- `,
444
- medium: (...args) => css`
445
- @media (min-width: ${({ theme }) => theme.sanity.media[2]}px) {
446
- ${css(...args)}
447
- }
448
- `
449
- }, Root = styled(Grid)`
450
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
451
-
452
- & > div {
453
- overflow: hidden;
454
- }
455
-
456
- & > div[data-width='medium'] {
457
- ${media.small`
458
- grid-column: span 2;
459
- `}
460
- }
461
-
462
- & > div[data-width='large'] {
463
- ${media.small`
464
- grid-column: span 2;
465
- `}
466
-
467
- ${media.medium`
468
- grid-column: span 3;
469
- `}
470
- }
471
-
472
- & > div[data-width='full'] {
473
- ${media.small`
474
- grid-column: 1 / -1;
475
- `}
476
- }
477
-
478
- & > div[data-height='medium'] {
479
- ${media.small`
480
- grid-row: span 2;
481
- `}
482
- }
483
-
484
- & > div[data-height='large'] {
485
- ${media.small`
486
- grid-row: span 2;
487
- `}
488
-
489
- ${media.medium`
490
- grid-row: span 3;
491
- `}
492
- }
493
-
494
- & > div[data-height='full'] {
495
- ${media.medium`
496
- grid-row: 1 / -1;
497
- `}
498
- }
499
- `, NO_WIDGETS = [], NO_LAYOUT = {};
500
- function WidgetGroup(props) {
501
- const {
502
- config: { layout = NO_LAYOUT, widgets = NO_WIDGETS }
503
- } = props;
504
- return /* @__PURE__ */ jsxs(
505
- Root,
506
- {
507
- autoFlow: "row dense",
508
- "data-width": layout.width || "auto",
509
- "data-height": layout.height || "auto",
510
- gap: 4,
511
- children: [
512
- widgets.length ? null : /* @__PURE__ */ jsx(Card, { padding: 4, shadow: 1, tone: "primary", children: /* @__PURE__ */ jsx(Text, { align: "center", children: "Add some widgets to populate this space." }) }),
513
- widgets.map((widgetConfig, index) => widgetConfig.type === "__experimental_group" ? /* @__PURE__ */ jsx(WidgetGroup, { config: widgetConfig }, index) : widgetConfig.component ? /* @__PURE__ */ jsx(WidgetContainer, { ...widgetConfig }, index) : /* @__PURE__ */ jsxs(Box, { children: [
514
- widgetConfig.name,
515
- " is missing widget component"
516
- ] }, index))
517
- ]
518
- }
519
- );
520
- }
521
- function Dashboard({ config }) {
522
- return config ? /* @__PURE__ */ jsx(DashboardContext.Provider, { value: config, children: /* @__PURE__ */ jsx(DashboardLayout, { children: /* @__PURE__ */ jsx(WidgetGroup, { config }) }) }) : null;
523
- }
524
- const strokeStyle = {
525
- stroke: "currentColor",
526
- strokeWidth: 1.2
527
- }, DashboardIcon = () => /* @__PURE__ */ jsxs(
528
- "svg",
529
- {
530
- "data-sanity-icon": !0,
531
- viewBox: "0 0 25 25",
532
- fill: "none",
533
- xmlns: "http://www.w3.org/2000/svg",
534
- preserveAspectRatio: "xMidYMid",
535
- width: "1em",
536
- height: "1em",
537
- children: [
538
- /* @__PURE__ */ jsx("path", { d: "M19.5 19.5H5.5V5.5H19.5V19.5Z", style: strokeStyle }),
539
- /* @__PURE__ */ jsx("path", { d: "M5.5 12.5H19.5", style: strokeStyle }),
540
- /* @__PURE__ */ jsx("path", { d: "M14.5 19.5V12.5M10.5 12.5V5.5", style: strokeStyle })
541
- ]
542
- }
543
- ), dashboardTool = definePlugin((config = {}) => {
544
- const pluginConfig = {
545
- layout: config.defaultLayout ?? {},
546
- widgets: config.widgets ?? []
547
- }, title = config.title ?? "Dashboard", name = config.name ?? "dashboard", icon = config.icon ?? DashboardIcon;
548
- return {
549
- name: "dashboard",
550
- tools: (prev, context) => [
551
- ...prev,
552
- {
553
- title,
554
- name,
555
- icon,
556
- component: () => /* @__PURE__ */ jsx(Dashboard, { config: pluginConfig })
557
- }
558
- ]
559
- };
560
- });
561
- export {
562
- DashboardWidgetContainer,
563
- dashboardTool,
564
- projectInfoWidget,
565
- projectUsersWidget,
566
- sanityTutorialsWidget
567
- };
568
- //# sourceMappingURL=index.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/components/DashboardWidgetContainer.tsx","../src/versionedClient.ts","../src/containers/DashboardContext.tsx","../src/containers/WidgetContainer.tsx","../src/widgets/projectInfo/ProjectInfo.tsx","../src/widgets/projectInfo/index.ts","../src/widgets/projectUsers/ProjectUser.tsx","../src/widgets/projectUsers/ProjectUsers.tsx","../src/widgets/projectUsers/index.ts","../src/widgets/sanityTutorials/Tutorial.tsx","../src/widgets/sanityTutorials/dataAdapter.ts","../src/widgets/sanityTutorials/SanityTutorials.tsx","../src/widgets/sanityTutorials/index.ts","../src/components/DashboardLayout.tsx","../src/components/WidgetGroup.tsx","../src/containers/Dashboard.tsx","../src/plugin.tsx"],"sourcesContent":["import React, {forwardRef} from 'react'\nimport {Card, Box, Heading} from '@sanity/ui'\nimport {styled} from 'styled-components'\n\nconst Root = styled(Card)`\n display: flex;\n flex-direction: column;\n justify-content: stretch;\n height: 100%;\n box-sizing: border-box;\n position: relative;\n`\n\nconst Header = styled(Card)`\n position: sticky;\n top: 0;\n z-index: 2;\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n`\n\nconst Footer = styled(Card)`\n position: sticky;\n overflow: hidden;\n bottom: 0;\n z-index: 2;\n border-bottom-right-radius: inherit;\n border-bottom-left-radius: inherit;\n margin-top: auto;\n`\n\nconst Content = styled(Box)`\n position: relative;\n z-index: 1;\n height: stretch;\n min-height: 21.5em;\n\n @media (min-width: ${({theme}) => theme.sanity.media[0]}px) {\n overflow-y: auto;\n outline: none;\n }\n`\n\ninterface DashboardWidgetProps {\n header?: string\n children: React.ReactNode\n footer?: React.ReactNode\n}\n\nexport const DashboardWidgetContainer = forwardRef(function DashboardWidgetContainer(\n props: DashboardWidgetProps,\n ref: React.Ref<HTMLDivElement>,\n) {\n const {header, children, footer} = props\n\n return (\n <Root radius={3} display=\"flex\" ref={ref}>\n {header && (\n <Header borderBottom paddingX={3} paddingY={4}>\n <Heading size={1} textOverflow=\"ellipsis\">\n {header}\n </Heading>\n </Header>\n )}\n {children && <Content>{children}</Content>}\n {footer && <Footer borderTop>{footer}</Footer>}\n </Root>\n )\n})\n","import {useClient} from 'sanity'\n\nexport function useVersionedClient() {\n return useClient({apiVersion: '2024-08-01'})\n}\n","import {createContext, useContext} from 'react'\nimport {DashboardConfig} from '../types'\n\nexport const DashboardContext = createContext<DashboardConfig>({widgets: []})\n\nexport function useDashboardConfig(): DashboardConfig {\n return useContext(DashboardContext)\n}\n","import React, {createElement, useMemo} from 'react'\nimport {useDashboardConfig} from './DashboardContext'\nimport {Card} from '@sanity/ui'\nimport {DashboardWidget} from '../types'\n\nexport function WidgetContainer(props: DashboardWidget) {\n const config = useDashboardConfig()\n const layout = useMemo(\n () => ({\n ...(props.layout || {}),\n ...(config.layout || {}),\n }),\n [props.layout, config.layout],\n )\n\n return (\n <Card shadow={1} data-width={layout.width} data-height={layout.height}>\n {createElement(props.component, {})}\n </Card>\n )\n}\n","import React, {useEffect, useMemo, useState} from 'react'\nimport {Box, Card, Stack, Heading, Grid, Label, Text, Code, Button} from '@sanity/ui'\nimport {useVersionedClient} from '../../versionedClient'\nimport {Subscription} from 'rxjs'\nimport {WidgetContainer} from '../../containers/WidgetContainer'\nimport {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'\nimport {type DashboardWidget} from '../../types'\nimport {type App, type ProjectInfoProps, type ProjectData, UserApplication} from './types'\n\nfunction isUrl(url?: string) {\n return url && /^https?:\\/\\//.test(`${url}`)\n}\n\nfunction getGraphQLUrl(projectId: string, dataset: string) {\n return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`\n}\n\nfunction getGroqUrl(projectId: string, dataset: string) {\n return `https://${projectId}.api.sanity.io/v1/groq/${dataset}`\n}\n\nfunction getManageUrl(projectId: string) {\n return `https://manage.sanity.io/projects/${projectId}`\n}\n\nconst NO_EXPERIMENTAL: DashboardWidget[] = []\nconst NO_DATA: ProjectData[] = []\n\nexport function ProjectInfo(props: ProjectInfoProps) {\n const {__experimental_before = NO_EXPERIMENTAL, data = NO_DATA} = props\n const [studioApps, setStudioApps] = useState<UserApplication[] | {error: string} | undefined>()\n const [graphQLApi, setGraphQLApi] = useState<string | {error: string} | undefined>()\n const versionedClient = useVersionedClient()\n const {projectId = 'unknown', dataset = 'unknown'} = versionedClient.config()\n\n useEffect(() => {\n const subscriptions: Subscription[] = []\n\n subscriptions.push(\n versionedClient.observable\n .request<UserApplication[]>({uri: '/user-applications', tag: 'dashboard.project-info'})\n .subscribe({\n next: (result) => setStudioApps(result.filter((app) => app.type === 'studio')),\n error: (error) => {\n console.error('Error while resolving user applications', error)\n setStudioApps({\n error: 'Something went wrong while resolving user applications. See console.',\n })\n },\n }),\n )\n\n // ping assumed graphql endpoint\n subscriptions.push(\n versionedClient.observable\n .request({\n method: 'HEAD',\n uri: `/graphql/${dataset}/default`,\n tag: 'dashboard.project-info.graphql-api',\n })\n .subscribe({\n next: () => setGraphQLApi(getGraphQLUrl(projectId, dataset)),\n error: (error) => {\n if (error.statusCode === 404) {\n setGraphQLApi(undefined)\n } else {\n console.error('Error while looking for graphQLApi', error)\n setGraphQLApi({\n error: 'Something went wrong while looking up graphQLApi. See console.',\n })\n }\n },\n }),\n )\n\n return () => {\n subscriptions.forEach((s) => s.unsubscribe())\n }\n }, [dataset, projectId, versionedClient, setGraphQLApi])\n\n const assembleTableRows = useMemo(() => {\n let result: App[] = [\n {\n title: 'Sanity project',\n rows: [\n {title: 'Project ID', value: projectId},\n {title: 'Dataset', value: dataset},\n ],\n },\n ]\n\n const apps: App[] = data.filter((item) => item.category === 'apps')\n\n // Handle studios\n ;(Array.isArray(studioApps) ? studioApps : []).forEach((app) => {\n apps.push({\n title: app.title || 'Studio',\n value: app.urlType === 'internal' ? `https://${app.appHost}.sanity.studio` : app.appHost,\n })\n })\n\n if (apps.length > 0) {\n result = result.concat([{title: 'Apps', rows: apps}])\n }\n\n // Handle APIs\n result = result.concat(\n [\n {\n title: 'APIs',\n rows: [\n {title: 'GROQ', value: getGroqUrl(projectId, dataset)},\n {\n title: 'GraphQL',\n value: (typeof graphQLApi === 'object' ? 'Error' : graphQLApi) ?? 'Not deployed',\n },\n ],\n },\n ],\n data.filter((item) => item.category === 'apis'),\n )\n\n // Handle whatever else there might be\n const otherStuff: Record<string, ProjectData[]> = {}\n data.forEach((item) => {\n if (item.category && item.category !== 'apps' && item.category !== 'apis') {\n if (!otherStuff[item.category]) {\n otherStuff[item.category] = []\n }\n otherStuff[item.category].push(item)\n }\n })\n Object.keys(otherStuff).forEach((category) => {\n result.push({title: category, rows: otherStuff[category]})\n })\n\n return result\n }, [graphQLApi, studioApps, projectId, dataset, data])\n\n return (\n <>\n {__experimental_before.map((widgetConfig, idx) => (\n <WidgetContainer key={idx} {...widgetConfig} />\n ))}\n <Box height=\"fill\" marginTop={__experimental_before?.length > 0 ? 4 : 0}>\n <DashboardWidgetContainer\n footer={\n <Button\n style={{width: '100%'}}\n paddingX={2}\n paddingY={4}\n mode=\"bleed\"\n tone=\"primary\"\n text=\"Manage project\"\n as=\"a\"\n href={getManageUrl(projectId)}\n />\n }\n >\n <Card\n paddingY={4}\n radius={2}\n role=\"table\"\n aria-label=\"Project info\"\n aria-describedby=\"project_info_table\"\n >\n <Stack space={4}>\n <Box paddingX={3} as=\"header\">\n <Heading size={1} as=\"h2\" id=\"project_info_table\">\n Project info\n </Heading>\n </Box>\n {assembleTableRows.map((item) => {\n if (!item || !item.rows) {\n return null\n }\n\n return (\n <Stack key={item.title} space={3}>\n <Card borderBottom padding={3}>\n <Label size={0} muted role=\"columnheader\">\n {item.title}\n </Label>\n </Card>\n <Stack space={4} paddingX={3} role=\"rowgroup\">\n {item.rows.map((row) => {\n return (\n <Grid key={`${row.value}-${row.title}`} columns={2} role=\"row\">\n <Text weight=\"medium\" role=\"rowheader\">\n {row.title}\n </Text>\n {typeof row.value === 'object' && (\n <Text size={1}>{row.value?.error}</Text>\n )}\n {typeof row.value === 'string' && (\n <>\n {isUrl(row.value) ? (\n <Text size={1} role=\"cell\" style={{wordBreak: 'break-word'}}>\n <a href={row.value}>{row.value}</a>\n </Text>\n ) : (\n <Code size={1} role=\"cell\" style={{wordBreak: 'break-word'}}>\n {row.value}\n </Code>\n )}\n </>\n )}\n </Grid>\n )\n })}\n </Stack>\n </Stack>\n )\n })}\n </Stack>\n </Card>\n </DashboardWidgetContainer>\n </Box>\n </>\n )\n}\n","import {ProjectInfo} from './ProjectInfo'\nimport {type LayoutConfig, type DashboardWidget} from '../../types'\n\nexport function projectInfoWidget(config?: {layout?: LayoutConfig}): DashboardWidget {\n return {\n name: 'project-info',\n component: ProjectInfo,\n layout: config?.layout ?? {width: 'medium'},\n }\n}\n","import React from 'react'\nimport {Box, Flex, rem, Stack, Text} from '@sanity/ui'\nimport {styled} from 'styled-components'\nimport {useListFormat, type User, UserAvatar} from 'sanity'\nimport {RobotIcon} from '@sanity/icons'\n\nconst Root = styled(Flex)`\n height: ${rem(33)}; // 33 = PREVIEW_SIZES.default.media.height\n box-sizing: content-box;\n`\n\nexport interface ProjectUserProps {\n user: User\n isRobot: boolean\n roles: string[]\n}\n\nexport function ProjectUser({user, isRobot, roles}: ProjectUserProps) {\n const listFormat = useListFormat({style: 'narrow'})\n return (\n <Root align=\"center\">\n <Flex align=\"center\" flex={1} gap={2}>\n <Box flex=\"none\">\n {isRobot ? (\n <Text size={2}>\n <RobotIcon />\n </Text>\n ) : (\n <UserAvatar user={user} />\n )}\n </Box>\n\n <Stack flex={1} space={2}>\n <Text size={1} style={{color: 'inherit'}} textOverflow=\"ellipsis\" weight=\"medium\">\n {user.displayName}\n </Text>\n\n <Text muted size={1} textOverflow=\"ellipsis\">\n {listFormat.format(roles)}\n </Text>\n </Stack>\n </Flex>\n </Root>\n )\n}\n","import React, {useCallback, useEffect, useState} from 'react'\nimport {from} from 'rxjs'\nimport {map, switchMap} from 'rxjs/operators'\nimport {Stack, Spinner, Box, Text, Button} from '@sanity/ui'\nimport {Role, useUserStore} from 'sanity'\nimport {useVersionedClient} from '../../versionedClient'\nimport {User} from 'sanity'\nimport {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'\nimport {ProjectUser} from './ProjectUser'\n\nfunction getInviteUrl(projectId: string) {\n return `https://manage.sanity.io/projects/${projectId}/members`\n}\n\ninterface Member {\n id: string\n roles: Role[]\n isRobot: boolean\n isCurrentUser: boolean\n createdAt: string\n}\n\ninterface Project {\n id: string\n members: Member[]\n}\n\nexport function ProjectUsers() {\n const [project, setProject] = useState<Project | undefined>()\n const [users, setUsers] = useState<User[] | undefined>()\n const [error, setError] = useState<Error | undefined>()\n\n const userStore = useUserStore()\n const versionedClient = useVersionedClient()\n\n const fetchData = useCallback(() => {\n const {projectId} = versionedClient.config()\n const subscription = versionedClient.observable\n .request<Project>({\n uri: `/projects/${projectId}`,\n tag: 'dashboard.project-users',\n })\n .pipe(\n switchMap((_project) =>\n from(userStore.getUsers(_project.members.map((mem) => mem.id))).pipe(\n map((_users) => ({project: _project, users: _users})),\n ),\n ),\n )\n .subscribe({\n next: ({users: _users, project: _project}) => {\n setProject(_project)\n setUsers(\n (Array.isArray(_users) ? _users : [_users]).sort((userA, userB) =>\n sortUsersByRobotStatus(userA, userB, _project),\n ),\n )\n },\n error: (e: Error) => setError(e),\n })\n\n return () => subscription.unsubscribe()\n }, [userStore, versionedClient])\n\n useEffect(() => fetchData(), [fetchData])\n\n const handleRetryFetch = useCallback(() => fetchData(), [fetchData])\n\n const isLoading = !users || !project\n\n if (error) {\n return (\n <DashboardWidgetContainer header=\"Project users\">\n <Box padding={4}>\n <Text>\n Something went wrong while fetching data. You could{' '}\n <a onClick={handleRetryFetch} title=\"Retry users fetch\" style={{cursor: 'pointer'}}>\n retry\n </a>\n ..?\n </Text>\n </Box>\n </DashboardWidgetContainer>\n )\n }\n\n return (\n <DashboardWidgetContainer\n header=\"Project users\"\n footer={\n <Button\n style={{width: '100%'}}\n paddingX={2}\n paddingY={4}\n mode=\"bleed\"\n tone=\"primary\"\n text=\"Manage members\"\n as=\"a\"\n loading={isLoading}\n href={isLoading ? undefined : getInviteUrl(project.id)}\n />\n }\n >\n {isLoading && (\n <Box paddingY={5} paddingX={2}>\n <Stack space={4}>\n <Text align=\"center\" muted size={1}>\n <Spinner />\n </Text>\n <Text align=\"center\" size={1} muted>\n Loading items…\n </Text>\n </Stack>\n </Box>\n )}\n\n {!isLoading && (\n <Stack space={3} padding={3}>\n {users?.map((user) => {\n const membership = project.members.find((member) => member.id === user.id)\n return (\n <ProjectUser\n key={user.id}\n user={user}\n isRobot={membership?.isRobot ?? false}\n roles={membership?.roles.map((role) => role.title) || []}\n />\n )\n })}\n </Stack>\n )}\n </DashboardWidgetContainer>\n )\n}\n\nfunction sortUsersByRobotStatus(userA: User, userB: User, project: Project) {\n const {members} = project\n const membershipA = members.find((member) => member.id === userA?.id)\n const membershipB = members.find((member) => member.id === userB?.id)\n\n // On ties, sort by when the user was added\n if (membershipA?.isRobot === membershipB?.isRobot) {\n return (membershipA?.createdAt || '') > (membershipB?.createdAt || '') ? 1 : -1\n }\n\n // Robots go to the bottom\n return membershipA?.isRobot ? 1 : -1\n}\n","import {ProjectUsers} from './ProjectUsers'\nimport {LayoutConfig, DashboardWidget} from '../../types'\n\nexport function projectUsersWidget(config?: {layout?: LayoutConfig}): DashboardWidget {\n return {\n name: 'project-info',\n component: ProjectUsers,\n layout: config?.layout,\n }\n}\n","import React from 'react'\nimport {Card, Box, Heading, Flex, Text, Stack} from '@sanity/ui'\nimport {PlayIcon} from '@sanity/icons'\nimport {styled} from 'styled-components'\n\nconst PlayIconBox = styled(Box)`\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n\n &:before {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2.75em;\n height: 2.75em;\n border-radius: 50%;\n background: ${({theme}) => theme.sanity.color.card.enabled.bg};\n opacity: 0.75;\n }\n`\n\nconst Root = styled(Flex)`\n &:hover {\n ${PlayIconBox} {\n &:before {\n opacity: 1;\n }\n }\n }\n`\n\nconst PosterCard = styled(Card)`\n width: 100%;\n padding-bottom: calc(9 / 16 * 100%);\n position: relative;\n`\n\nconst Poster = styled.img`\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n object-fit: cover;\n display: block;\n\n &:not([src]) {\n display: none;\n }\n`\n\nexport interface TutorialProps {\n title: string\n posterURL?: string\n href: string\n showPlayIcon?: boolean\n presenterName?: string\n presenterSubtitle?: string\n}\n\nexport function Tutorial(props: TutorialProps) {\n const {title, posterURL, showPlayIcon, href, presenterName, presenterSubtitle} = props\n\n return (\n <Root flex={1}>\n <Card\n sizing=\"border\"\n flex={1}\n padding={2}\n radius={2}\n as=\"a\"\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{position: 'relative'}}\n >\n <Flex direction=\"column\" style={{height: '100%'}}>\n {posterURL && (\n <PosterCard marginBottom={1}>\n <Poster src={posterURL} />\n {showPlayIcon && (\n <PlayIconBox display=\"flex\">\n <Text align=\"center\">\n <PlayIcon />\n </Text>\n </PlayIconBox>\n )}\n </PosterCard>\n )}\n <Flex direction=\"column\" justify=\"space-between\" paddingY={2} flex={1}>\n <Heading as=\"h3\" size={1}>\n {title}\n </Heading>\n <Box marginTop={4}>\n <Stack space={2} flex={1}>\n <Text size={1}>{presenterName}</Text>\n <Text size={0} style={{opacity: 0.7}}>\n {presenterSubtitle}\n </Text>\n </Stack>\n </Box>\n </Flex>\n </Flex>\n </Card>\n </Root>\n )\n}\n","import {useMemo} from 'react'\nimport {useVersionedClient} from '../../versionedClient'\nimport imageUrlBuilder from '@sanity/image-url'\n\nconst tutorialsProjectConfig = {\n projectId: '3do82whm',\n dataset: 'next',\n}\n\nexport interface Guide {\n _type?: string\n slug?: {current: string}\n presenter?: {\n name?: string\n }\n}\n\nexport interface FeedItem {\n _id: string\n title?: string\n poster?: string\n category?: string\n guideOrTutorial?: Guide\n externalLink?: string\n presenter?: {\n name?: string\n }\n hasVideo?: boolean\n}\n\nexport function useDataAdapter() {\n const versionedClient = useVersionedClient()\n return useMemo(\n () => ({\n getFeed: (templateRepoId: string) => {\n const uri = templateRepoId\n ? `/addons/dashboard?templateRepoId=${templateRepoId}`\n : '/addons/dashboard'\n return versionedClient.observable.request<{items: FeedItem[]}>({\n uri,\n tag: 'dashboard.sanity-tutorials',\n withCredentials: false,\n })\n },\n urlBuilder: imageUrlBuilder(tutorialsProjectConfig),\n }),\n [versionedClient],\n )\n}\n","import React, {useEffect, useState} from 'react'\nimport {Flex} from '@sanity/ui'\nimport {Tutorial} from './Tutorial'\nimport {FeedItem, Guide, useDataAdapter} from './dataAdapter'\nimport {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'\n\nfunction createUrl(slug: {current: string}, type?: string) {\n if (type === 'tutorial') {\n return `https://www.sanity.io/docs/tutorials/${slug.current}`\n } else if (type === 'guide') {\n return `https://www.sanity.io/docs/guides/${slug.current}`\n }\n return false\n}\n\nexport interface SanityTutorialsProps {\n templateRepoId: string\n}\n\nexport function SanityTutorials(props: SanityTutorialsProps) {\n const {templateRepoId} = props\n const [feedItems, setFeedItems] = useState<FeedItem[]>([])\n\n const {getFeed, urlBuilder} = useDataAdapter()\n\n useEffect(() => {\n const subscription = getFeed(templateRepoId).subscribe((response) => {\n setFeedItems(response.items)\n })\n return () => {\n subscription.unsubscribe()\n }\n }, [setFeedItems, getFeed, templateRepoId])\n\n const title = 'Learn about Sanity'\n\n return (\n <DashboardWidgetContainer header={title}>\n <Flex as=\"ul\" overflow=\"auto\" align=\"stretch\" paddingY={2}>\n {feedItems?.map((feedItem, index) => {\n if (!feedItem.title || (!feedItem.guideOrTutorial && !feedItem.externalLink)) {\n return null\n }\n const presenter = feedItem.presenter || feedItem.guideOrTutorial?.presenter || {}\n const subtitle = feedItem.category\n const {guideOrTutorial = {} as Guide} = feedItem\n const href =\n (guideOrTutorial.slug\n ? createUrl(guideOrTutorial.slug, guideOrTutorial._type)\n : feedItem.externalLink) || feedItem.externalLink\n\n return (\n <Flex\n as=\"li\"\n key={feedItem._id}\n paddingRight={index < feedItems?.length - 1 ? 1 : 3}\n paddingLeft={index === 0 ? 3 : 0}\n align=\"stretch\"\n style={{minWidth: 272, width: '30%'}}\n >\n <Tutorial\n title={feedItem.title}\n href={href ?? ''}\n presenterName={presenter.name}\n presenterSubtitle={subtitle}\n showPlayIcon={feedItem.hasVideo}\n posterURL={\n feedItem.poster ? urlBuilder.image(feedItem.poster).height(360).url() : undefined\n }\n />\n </Flex>\n )\n })}\n </Flex>\n </DashboardWidgetContainer>\n )\n}\n","import {SanityTutorials} from './SanityTutorials'\nimport {LayoutConfig, DashboardWidget} from '../../types'\n\nexport function sanityTutorialsWidget(config?: {layout?: LayoutConfig}): DashboardWidget {\n return {\n name: 'sanity-tutorials',\n component: SanityTutorials,\n layout: config?.layout ?? {width: 'full'},\n }\n}\n","import React, {PropsWithChildren} from 'react'\nimport {Container} from '@sanity/ui'\n\nexport function DashboardLayout(props: PropsWithChildren<{}>) {\n return (\n <Container width={4} padding={4} sizing=\"border\" style={{height: '100%', overflowY: 'auto'}}>\n {props.children}\n </Container>\n )\n}\n","import React from 'react'\nimport {styled, css} from 'styled-components'\nimport {Box, Card, Grid, Text} from '@sanity/ui'\nimport {WidgetContainer} from '../containers/WidgetContainer'\nimport {DashboardConfig, LayoutConfig, DashboardWidget} from '../types'\n\nconst media = {\n small: (...args: Parameters<typeof css>) => css`\n @media (min-width: ${({theme}) => theme.sanity.media[0]}px) {\n ${css(...args)}\n }\n `,\n medium: (...args: Parameters<typeof css>) => css`\n @media (min-width: ${({theme}) => theme.sanity.media[2]}px) {\n ${css(...args)}\n }\n `,\n}\n\nconst Root = styled(Grid)`\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n\n & > div {\n overflow: hidden;\n }\n\n & > div[data-width='medium'] {\n ${media.small`\n grid-column: span 2;\n `}\n }\n\n & > div[data-width='large'] {\n ${media.small`\n grid-column: span 2;\n `}\n\n ${media.medium`\n grid-column: span 3;\n `}\n }\n\n & > div[data-width='full'] {\n ${media.small`\n grid-column: 1 / -1;\n `}\n }\n\n & > div[data-height='medium'] {\n ${media.small`\n grid-row: span 2;\n `}\n }\n\n & > div[data-height='large'] {\n ${media.small`\n grid-row: span 2;\n `}\n\n ${media.medium`\n grid-row: span 3;\n `}\n }\n\n & > div[data-height='full'] {\n ${media.medium`\n grid-row: 1 / -1;\n `}\n }\n`\n\nexport interface WidgetGroupProps {\n config: Partial<DashboardConfig>\n}\n\nconst NO_WIDGETS: DashboardWidget[] = []\nconst NO_LAYOUT: LayoutConfig = {}\n\nexport function WidgetGroup(props: WidgetGroupProps) {\n const {\n config: {layout = NO_LAYOUT, widgets = NO_WIDGETS},\n } = props\n return (\n <Root\n autoFlow=\"row dense\"\n data-width={layout.width || 'auto'}\n data-height={layout.height || 'auto'}\n gap={4}\n >\n {widgets.length ? null : (\n <Card padding={4} shadow={1} tone=\"primary\">\n <Text align=\"center\">Add some widgets to populate this space.</Text>\n </Card>\n )}\n {widgets.map((widgetConfig, index) => {\n if (widgetConfig.type === '__experimental_group') {\n return <WidgetGroup key={index} config={widgetConfig} />\n }\n if (widgetConfig.component) {\n return <WidgetContainer key={index} {...widgetConfig} />\n }\n return <Box key={index}>{widgetConfig.name} is missing widget component</Box>\n })}\n </Root>\n )\n}\n","import React from 'react'\nimport {DashboardLayout} from '../components/DashboardLayout'\nimport {WidgetGroup} from '../components/WidgetGroup'\nimport {DashboardContext} from './DashboardContext'\nimport {DashboardConfig} from '../types'\n\nexport function Dashboard({config}: {config: DashboardConfig}) {\n if (!config) {\n return null\n }\n\n return (\n <DashboardContext.Provider value={config}>\n <DashboardLayout>\n <WidgetGroup config={config} />\n </DashboardLayout>\n </DashboardContext.Provider>\n )\n}\n","import React, {ComponentType, CSSProperties} from 'react'\nimport {Dashboard} from './containers/Dashboard'\nimport {definePlugin} from 'sanity'\nimport {DashboardConfig, DashboardWidget, LayoutConfig} from './types'\n\nconst strokeStyle: CSSProperties = {\n stroke: 'currentColor',\n strokeWidth: 1.2,\n}\n\nconst DashboardIcon = () => (\n <svg\n data-sanity-icon\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n preserveAspectRatio=\"xMidYMid\"\n width=\"1em\"\n height=\"1em\"\n >\n <path d=\"M19.5 19.5H5.5V5.5H19.5V19.5Z\" style={strokeStyle} />\n <path d=\"M5.5 12.5H19.5\" style={strokeStyle} />\n <path d=\"M14.5 19.5V12.5M10.5 12.5V5.5\" style={strokeStyle} />\n </svg>\n)\n\nexport interface DashboardPluginConfig {\n /**\n * Dashboard tool title\n */\n title?: string\n /**\n * Dashboard tool name (used in url path)\n */\n name?: string\n /**\n * Dashboard tool icon\n */\n icon?: ComponentType\n widgets?: DashboardWidget[]\n\n /**\n * Will be used for widgets that do not define a layout directly.\n */\n defaultLayout?: LayoutConfig\n}\n\nexport const dashboardTool = definePlugin<DashboardPluginConfig>((config = {}) => {\n const pluginConfig: DashboardConfig = {\n layout: config.defaultLayout ?? {},\n widgets: config.widgets ?? [],\n }\n\n const title = config.title ?? 'Dashboard'\n const name = config.name ?? 'dashboard'\n const icon = config.icon ?? DashboardIcon\n\n return {\n name: 'dashboard',\n tools: (prev, context) => {\n return [\n ...prev,\n {\n title,\n name,\n icon,\n component: () => <Dashboard config={pluginConfig} />,\n },\n ]\n },\n }\n})\n"],"names":["Root"],"mappings":"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"}