@sanity/dashboard 3.1.6 → 4.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/dashboard",
3
- "version": "3.1.6",
3
+ "version": "4.1.0",
4
4
  "description": "Tool for rendering dashboard widgets",
5
5
  "keywords": [
6
6
  "sanity",
@@ -22,19 +22,18 @@
22
22
  },
23
23
  "license": "MIT",
24
24
  "author": "Sanity.io <hello@sanity.io>",
25
+ "sideEffects": false,
25
26
  "exports": {
26
27
  ".": {
27
- "types": "./lib/index.d.ts",
28
28
  "source": "./src/index.ts",
29
- "import": "./lib/index.esm.js",
29
+ "import": "./lib/index.mjs",
30
30
  "require": "./lib/index.js",
31
- "default": "./lib/index.esm.js"
31
+ "default": "./lib/index.mjs"
32
32
  },
33
33
  "./package.json": "./package.json"
34
34
  },
35
35
  "main": "./lib/index.js",
36
36
  "module": "./lib/index.esm.js",
37
- "source": "./src/index.ts",
38
37
  "types": "./lib/index.d.ts",
39
38
  "files": [
40
39
  "lib",
@@ -44,60 +43,65 @@
44
43
  ],
45
44
  "scripts": {
46
45
  "prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
47
- "build": "run-s clean && plugin-kit verify-package --silent && pkg-utils build --strict && pkg-utils --strict",
46
+ "build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
48
47
  "clean": "rimraf lib",
49
48
  "compile": "tsc --noEmit",
49
+ "dev": "sanity dev",
50
50
  "format": "prettier --write --cache --ignore-unknown .",
51
51
  "link-watch": "plugin-kit link-watch",
52
52
  "lint": "eslint .",
53
53
  "prepare": "husky install",
54
- "prepublishOnly": "run-s build",
54
+ "prepublishOnly": "npm run build",
55
55
  "watch": "pkg-utils watch --strict"
56
56
  },
57
+ "browserslist": "extends @sanity/browserslist-config",
57
58
  "dependencies": {
58
- "@sanity/icons": "^2.0.0",
59
- "@sanity/image-url": "^1.0.1",
59
+ "@sanity/icons": "^3.3.0",
60
+ "@sanity/image-url": "^1.0.2",
60
61
  "@sanity/incompatible-plugin": "^1.0.4",
61
- "@sanity/ui": "^1.0.0",
62
+ "@sanity/ui": "^2.6.8",
62
63
  "lodash": "^4.17.21",
63
- "rxjs": "^7.0.0"
64
+ "rxjs": "^7.8.1"
64
65
  },
65
66
  "devDependencies": {
66
- "@commitlint/cli": "^17.8.1",
67
- "@commitlint/config-conventional": "^17.8.1",
68
- "@sanity/pkg-utils": "^2.4.10",
69
- "@sanity/plugin-kit": "^3.1.10",
70
- "@sanity/semantic-release-preset": "^2.0.5",
71
- "@types/react": "^18.2.33",
72
- "@types/styled-components": "^5.1.29",
73
- "@typescript-eslint/eslint-plugin": "^5.62.0",
74
- "@typescript-eslint/parser": "^5.62.0",
75
- "eslint": "^8.52.0",
76
- "eslint-config-prettier": "^8.10.0",
67
+ "@commitlint/cli": "^19.5.0",
68
+ "@commitlint/config-conventional": "^19.5.0",
69
+ "@sanity/pkg-utils": "^6.11.2",
70
+ "@sanity/plugin-kit": "^4.0.18",
71
+ "@sanity/semantic-release-preset": "^5.0.0",
72
+ "@types/react": "^18.3.10",
73
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
74
+ "@typescript-eslint/parser": "^7.18.0",
75
+ "eslint": "^8.57.1",
76
+ "eslint-config-prettier": "^9.1.0",
77
77
  "eslint-config-sanity": "^6.0.0",
78
- "eslint-plugin-prettier": "^4.2.1",
79
- "eslint-plugin-react": "^7.33.2",
80
- "eslint-plugin-react-hooks": "^4.6.0",
81
- "husky": "^8.0.1",
82
- "lint-staged": "^13.0.3",
83
- "npm-run-all": "^4.1.5",
84
- "prettier": "^2.8.8",
85
- "prettier-plugin-packagejson": "^2.4.6",
86
- "react": "^18.2.0",
87
- "react-dom": "^18.2.0",
88
- "react-is": "^18.2.0",
89
- "rimraf": "^3.0.2",
90
- "sanity": "^3.19.0",
91
- "styled-components": "^6.1.1",
92
- "typescript": "^4.9.5"
78
+ "eslint-plugin-prettier": "^5.2.1",
79
+ "eslint-plugin-react": "^7.37.1",
80
+ "eslint-plugin-react-hooks": "^4.6.2",
81
+ "husky": "^8.0.3",
82
+ "lint-staged": "^13.3.0",
83
+ "npm-run-all2": "^5.0.0",
84
+ "prettier": "^3.3.3",
85
+ "prettier-plugin-packagejson": "^2.5.2",
86
+ "semantic-release": "^24.1.2",
87
+ "react": "^18.3.1",
88
+ "react-dom": "^18.3.1",
89
+ "react-is": "^18.3.1",
90
+ "rimraf": "^6.0.0",
91
+ "sanity": "^3.59.0",
92
+ "styled-components": "^6.1.13",
93
+ "typescript": "^5.6.2"
93
94
  },
94
95
  "peerDependencies": {
95
96
  "react": "^18",
96
97
  "sanity": "^3",
97
- "styled-components": "^5.2 || ^6.0.0"
98
+ "styled-components": "^6.1"
98
99
  },
99
100
  "engines": {
100
- "node": ">=14"
101
+ "node": ">=18"
102
+ },
103
+ "overrides": {
104
+ "conventional-changelog-conventionalcommits": ">= 8.0.0"
101
105
  },
102
106
  "sanityExchangeUrl": "https://www.sanity.io/exchange/dashboard"
103
107
  }
@@ -1,6 +1,6 @@
1
1
  import React, {forwardRef} from 'react'
2
2
  import {Card, Box, Heading} from '@sanity/ui'
3
- import styled from 'styled-components'
3
+ import {styled} from 'styled-components'
4
4
 
5
5
  const Root = styled(Card)`
6
6
  display: flex;
@@ -49,7 +49,7 @@ interface DashboardWidgetProps {
49
49
 
50
50
  export const DashboardWidgetContainer = forwardRef(function DashboardWidgetContainer(
51
51
  props: DashboardWidgetProps,
52
- ref: React.Ref<HTMLDivElement>
52
+ ref: React.Ref<HTMLDivElement>,
53
53
  ) {
54
54
  const {header, children, footer} = props
55
55
 
@@ -1,6 +1,6 @@
1
1
  import React, {PropsWithChildren, ReactNode} from 'react'
2
2
  import {Card, Stack, Heading, Box} from '@sanity/ui'
3
- import styled from 'styled-components'
3
+ import {styled} from 'styled-components'
4
4
 
5
5
  const Root = styled(Card)`
6
6
  display: flex;
@@ -1,22 +1,20 @@
1
1
  import React from 'react'
2
- import styled, {css} from 'styled-components'
2
+ import {styled, css} from 'styled-components'
3
3
  import {Box, Card, Grid, Text} from '@sanity/ui'
4
4
  import {WidgetContainer} from '../containers/WidgetContainer'
5
5
  import {DashboardConfig, LayoutConfig, DashboardWidget} from '../types'
6
6
 
7
7
  const media = {
8
- small: (...args: Parameters<typeof css>) =>
9
- css`
10
- @media (min-width: ${({theme}) => theme.sanity.media[0]}px) {
11
- ${css(...args)}
12
- }
13
- `,
14
- medium: (...args: Parameters<typeof css>) =>
15
- css`
16
- @media (min-width: ${({theme}) => theme.sanity.media[2]}px) {
17
- ${css(...args)}
18
- }
19
- `,
8
+ small: (...args: Parameters<typeof css>) => css`
9
+ @media (min-width: ${({theme}) => theme.sanity.media[0]}px) {
10
+ ${css(...args)}
11
+ }
12
+ `,
13
+ medium: (...args: Parameters<typeof css>) => css`
14
+ @media (min-width: ${({theme}) => theme.sanity.media[2]}px) {
15
+ ${css(...args)}
16
+ }
17
+ `,
20
18
  }
21
19
 
22
20
  const Root = styled(Grid)`
@@ -10,7 +10,7 @@ export function WidgetContainer(props: DashboardWidget) {
10
10
  ...(props.layout || {}),
11
11
  ...(config.layout || {}),
12
12
  }),
13
- [props.layout, config.layout]
13
+ [props.layout, config.layout],
14
14
  )
15
15
 
16
16
  return (
@@ -1,5 +1,5 @@
1
1
  import {useClient} from 'sanity'
2
2
 
3
3
  export function useVersionedClient() {
4
- return useClient({apiVersion: '1'})
4
+ return useClient({apiVersion: '2024-08-01'})
5
5
  }
@@ -4,29 +4,14 @@ import {useVersionedClient} from '../../versionedClient'
4
4
  import {Subscription} from 'rxjs'
5
5
  import {WidgetContainer} from '../../containers/WidgetContainer'
6
6
  import {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'
7
- import {DashboardWidget} from '../../types'
8
-
9
- export interface ProjectInfoProps {
10
- __experimental_before?: DashboardWidget[]
11
- data: ProjectData[]
12
- }
13
-
14
- interface App {
15
- title: string
16
- rows?: App[]
17
- value?: string | {error: string}
18
- }
19
-
20
- interface ProjectData {
21
- title: string
22
- category?: string
23
- }
7
+ import {type DashboardWidget} from '../../types'
8
+ import {type App, type ProjectInfoProps, type ProjectData, UserApplication} from './types'
24
9
 
25
10
  function isUrl(url?: string) {
26
11
  return url && /^https?:\/\//.test(`${url}`)
27
12
  }
28
13
 
29
- function getGraphQlUrl(projectId: string, dataset: string) {
14
+ function getGraphQLUrl(projectId: string, dataset: string) {
30
15
  return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`
31
16
  }
32
17
 
@@ -43,8 +28,8 @@ const NO_DATA: ProjectData[] = []
43
28
 
44
29
  export function ProjectInfo(props: ProjectInfoProps) {
45
30
  const {__experimental_before = NO_EXPERIMENTAL, data = NO_DATA} = props
46
- const [studioHost, setStudioHost] = useState<string | {error: string} | undefined>()
47
- const [graphqlApi, setGraphQlApi] = useState<string | {error: string} | undefined>()
31
+ const [studioApps, setStudioApps] = useState<UserApplication[] | {error: string} | undefined>()
32
+ const [graphQLApi, setGraphQLApi] = useState<string | {error: string} | undefined>()
48
33
  const versionedClient = useVersionedClient()
49
34
  const {projectId = 'unknown', dataset = 'unknown'} = versionedClient.config()
50
35
 
@@ -53,19 +38,16 @@ export function ProjectInfo(props: ProjectInfoProps) {
53
38
 
54
39
  subscriptions.push(
55
40
  versionedClient.observable
56
- .request<{studioHost: string}>({uri: `/projects/${projectId}`})
41
+ .request<UserApplication[]>({uri: '/user-applications', tag: 'dashboard.project-info'})
57
42
  .subscribe({
58
- next: (result) => {
59
- const {studioHost: host} = result
60
- setStudioHost(host ? `https://${host}.sanity.studio` : undefined)
61
- },
43
+ next: (result) => setStudioApps(result.filter((app) => app.type === 'studio')),
62
44
  error: (error) => {
63
- console.error('Error while looking for studioHost', error)
64
- setStudioHost({
65
- error: 'Something went wrong while looking up studioHost. See console.',
45
+ console.error('Error while resolving user applications', error)
46
+ setStudioApps({
47
+ error: 'Something went wrong while resolving user applications. See console.',
66
48
  })
67
49
  },
68
- })
50
+ }),
69
51
  )
70
52
 
71
53
  // ping assumed graphql endpoint
@@ -74,26 +56,27 @@ export function ProjectInfo(props: ProjectInfoProps) {
74
56
  .request({
75
57
  method: 'HEAD',
76
58
  uri: `/graphql/${dataset}/default`,
59
+ tag: 'dashboard.project-info.graphql-api',
77
60
  })
78
61
  .subscribe({
79
- next: () => setGraphQlApi(getGraphQlUrl(projectId, dataset)),
62
+ next: () => setGraphQLApi(getGraphQLUrl(projectId, dataset)),
80
63
  error: (error) => {
81
64
  if (error.statusCode === 404) {
82
- setGraphQlApi(undefined)
65
+ setGraphQLApi(undefined)
83
66
  } else {
84
- console.error('Error while looking for graphqlApi', error)
85
- setGraphQlApi({
86
- error: 'Something went wrong while looking up graphqlApi. See console.',
67
+ console.error('Error while looking for graphQLApi', error)
68
+ setGraphQLApi({
69
+ error: 'Something went wrong while looking up graphQLApi. See console.',
87
70
  })
88
71
  }
89
72
  },
90
- })
73
+ }),
91
74
  )
92
75
 
93
76
  return () => {
94
77
  subscriptions.forEach((s) => s.unsubscribe())
95
78
  }
96
- }, [dataset, projectId, versionedClient, setGraphQlApi, setStudioHost])
79
+ }, [dataset, projectId, versionedClient, setGraphQLApi])
97
80
 
98
81
  const assembleTableRows = useMemo(() => {
99
82
  let result: App[] = [
@@ -106,11 +89,16 @@ export function ProjectInfo(props: ProjectInfoProps) {
106
89
  },
107
90
  ]
108
91
 
109
- // Handle any apps
110
- const apps: App[] = [
111
- studioHost ? {title: 'Studio', value: studioHost} : null,
112
- ...data.filter((item) => item.category === 'apps'),
113
- ].filter((a): a is App => !!a)
92
+ const apps: App[] = data.filter((item) => item.category === 'apps')
93
+
94
+ // Handle studios
95
+ ;(Array.isArray(studioApps) ? studioApps : []).forEach((app) => {
96
+ apps.push({
97
+ title: app.title || 'Studio',
98
+ value: app.urlType === 'internal' ? `https://${app.appHost}.sanity.studio` : app.appHost,
99
+ })
100
+ })
101
+
114
102
  if (apps.length > 0) {
115
103
  result = result.concat([{title: 'Apps', rows: apps}])
116
104
  }
@@ -124,12 +112,12 @@ export function ProjectInfo(props: ProjectInfoProps) {
124
112
  {title: 'GROQ', value: getGroqUrl(projectId, dataset)},
125
113
  {
126
114
  title: 'GraphQL',
127
- value: (typeof graphqlApi === 'object' ? 'Error' : graphqlApi) ?? 'Not deployed',
115
+ value: (typeof graphQLApi === 'object' ? 'Error' : graphQLApi) ?? 'Not deployed',
128
116
  },
129
117
  ],
130
118
  },
131
119
  ],
132
- data.filter((item) => item.category === 'apis')
120
+ data.filter((item) => item.category === 'apis'),
133
121
  )
134
122
 
135
123
  // Handle whatever else there might be
@@ -147,7 +135,7 @@ export function ProjectInfo(props: ProjectInfoProps) {
147
135
  })
148
136
 
149
137
  return result
150
- }, [graphqlApi, studioHost, projectId, dataset, data])
138
+ }, [graphQLApi, studioApps, projectId, dataset, data])
151
139
 
152
140
  return (
153
141
  <>
@@ -197,7 +185,7 @@ export function ProjectInfo(props: ProjectInfoProps) {
197
185
  <Stack space={4} paddingX={3} role="rowgroup">
198
186
  {item.rows.map((row) => {
199
187
  return (
200
- <Grid key={row.title} columns={2} role="row">
188
+ <Grid key={`${row.value}-${row.title}`} columns={2} role="row">
201
189
  <Text weight="medium" role="rowheader">
202
190
  {row.title}
203
191
  </Text>
@@ -1,5 +1,5 @@
1
1
  import {ProjectInfo} from './ProjectInfo'
2
- import {LayoutConfig, DashboardWidget} from '../../types'
2
+ import {type LayoutConfig, type DashboardWidget} from '../../types'
3
3
 
4
4
  export function projectInfoWidget(config?: {layout?: LayoutConfig}): DashboardWidget {
5
5
  return {
@@ -0,0 +1,28 @@
1
+ import {type DashboardWidget} from '../../types'
2
+
3
+ export interface ProjectInfoProps {
4
+ __experimental_before?: DashboardWidget[]
5
+ data: ProjectData[]
6
+ }
7
+
8
+ export interface App {
9
+ title: string
10
+ rows?: App[]
11
+ value?: string | {error: string}
12
+ }
13
+
14
+ export interface ProjectData {
15
+ title: string
16
+ category?: string
17
+ }
18
+
19
+ export interface UserApplication {
20
+ id: string
21
+ projectId: string
22
+ title: string | null
23
+ type: string
24
+ urlType: 'internal' | 'external'
25
+ appHost: string
26
+
27
+ // … there are other props here, but we don't really care about them for our use case
28
+ }
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+ import {Box, Flex, rem, Stack, Text} from '@sanity/ui'
3
+ import {styled} from 'styled-components'
4
+ import {useListFormat, type User, UserAvatar} from 'sanity'
5
+ import {RobotIcon} from '@sanity/icons'
6
+
7
+ const Root = styled(Flex)`
8
+ height: ${rem(33)}; // 33 = PREVIEW_SIZES.default.media.height
9
+ box-sizing: content-box;
10
+ `
11
+
12
+ export interface ProjectUserProps {
13
+ user: User
14
+ isRobot: boolean
15
+ roles: string[]
16
+ }
17
+
18
+ export function ProjectUser({user, isRobot, roles}: ProjectUserProps) {
19
+ const listFormat = useListFormat({style: 'narrow'})
20
+ return (
21
+ <Root align="center">
22
+ <Flex align="center" flex={1} gap={2}>
23
+ <Box flex="none">
24
+ {isRobot ? (
25
+ <Text size={2}>
26
+ <RobotIcon />
27
+ </Text>
28
+ ) : (
29
+ <UserAvatar user={user} />
30
+ )}
31
+ </Box>
32
+
33
+ <Stack flex={1} space={2}>
34
+ <Text size={1} style={{color: 'inherit'}} textOverflow="ellipsis" weight="medium">
35
+ {user.displayName}
36
+ </Text>
37
+
38
+ <Text muted size={1} textOverflow="ellipsis">
39
+ {listFormat.format(roles)}
40
+ </Text>
41
+ </Stack>
42
+ </Flex>
43
+ </Root>
44
+ )
45
+ }
@@ -1,36 +1,23 @@
1
1
  import React, {useCallback, useEffect, useState} from 'react'
2
2
  import {from} from 'rxjs'
3
3
  import {map, switchMap} from 'rxjs/operators'
4
- import {Stack, Spinner, Card, Box, Text, Button} from '@sanity/ui'
5
- import {RobotIcon} from '@sanity/icons'
6
- import styled from 'styled-components'
7
- import {DefaultPreview, useUserStore} from 'sanity'
4
+ import {Stack, Spinner, Box, Text, Button} from '@sanity/ui'
5
+ import {Role, useUserStore} from 'sanity'
8
6
  import {useVersionedClient} from '../../versionedClient'
9
7
  import {User} from 'sanity'
10
8
  import {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'
11
-
12
- const AvatarWrapper = styled(Card)`
13
- box-sizing: border-box;
14
- border-radius: 50%;
15
- border-color: transparent;
16
- overflow: hidden;
17
- width: 100%;
18
- height: 100%;
19
-
20
- & > img {
21
- width: 100%;
22
- height: auto;
23
- }
24
- `
9
+ import {ProjectUser} from './ProjectUser'
25
10
 
26
11
  function getInviteUrl(projectId: string) {
27
- return `https://manage.sanity.io/projects/${projectId}/team/invite`
12
+ return `https://manage.sanity.io/projects/${projectId}/members`
28
13
  }
29
14
 
30
15
  interface Member {
31
16
  id: string
32
- role: string
17
+ roles: Role[]
33
18
  isRobot: boolean
19
+ isCurrentUser: boolean
20
+ createdAt: string
34
21
  }
35
22
 
36
23
  interface Project {
@@ -51,21 +38,22 @@ export function ProjectUsers() {
51
38
  const subscription = versionedClient.observable
52
39
  .request<Project>({
53
40
  uri: `/projects/${projectId}`,
41
+ tag: 'dashboard.project-users',
54
42
  })
55
43
  .pipe(
56
44
  switchMap((_project) =>
57
45
  from(userStore.getUsers(_project.members.map((mem) => mem.id))).pipe(
58
- map((_users) => ({project: _project, users: _users}))
59
- )
60
- )
46
+ map((_users) => ({project: _project, users: _users})),
47
+ ),
48
+ ),
61
49
  )
62
50
  .subscribe({
63
51
  next: ({users: _users, project: _project}) => {
64
52
  setProject(_project)
65
53
  setUsers(
66
54
  (Array.isArray(_users) ? _users : [_users]).sort((userA, userB) =>
67
- sortUsersByRobotStatus(userA, userB, _project)
68
- )
55
+ sortUsersByRobotStatus(userA, userB, _project),
56
+ ),
69
57
  )
70
58
  },
71
59
  error: (e: Error) => setError(e),
@@ -106,7 +94,7 @@ export function ProjectUsers() {
106
94
  paddingY={4}
107
95
  mode="bleed"
108
96
  tone="primary"
109
- text="Invite members"
97
+ text="Manage members"
110
98
  as="a"
111
99
  loading={isLoading}
112
100
  href={isLoading ? undefined : getInviteUrl(project.id)}
@@ -120,7 +108,7 @@ export function ProjectUsers() {
120
108
  <Spinner />
121
109
  </Text>
122
110
  <Text align="center" size={1} muted>
123
- Loading items...
111
+ Loading items
124
112
  </Text>
125
113
  </Stack>
126
114
  </Box>
@@ -130,23 +118,13 @@ export function ProjectUsers() {
130
118
  <Stack space={3} padding={3}>
131
119
  {users?.map((user) => {
132
120
  const membership = project.members.find((member) => member.id === user.id)
133
- const media = membership?.isRobot ? (
134
- <Text size={3}>
135
- <RobotIcon />
136
- </Text>
137
- ) : (
138
- <AvatarWrapper tone="transparent">
139
- {user?.imageUrl && <img src={user.imageUrl} alt={user?.displayName} />}
140
- </AvatarWrapper>
141
- )
142
121
  return (
143
- <Box key={user.id}>
144
- <DefaultPreview
145
- title={user.displayName}
146
- subtitle={membership?.role}
147
- media={media}
148
- />
149
- </Box>
122
+ <ProjectUser
123
+ key={user.id}
124
+ user={user}
125
+ isRobot={membership?.isRobot ?? false}
126
+ roles={membership?.roles.map((role) => role.title) || []}
127
+ />
150
128
  )
151
129
  })}
152
130
  </Stack>
@@ -159,11 +137,12 @@ function sortUsersByRobotStatus(userA: User, userB: User, project: Project) {
159
137
  const {members} = project
160
138
  const membershipA = members.find((member) => member.id === userA?.id)
161
139
  const membershipB = members.find((member) => member.id === userB?.id)
162
- if (membershipA?.isRobot) {
163
- return 1
164
- }
165
- if (membershipB?.isRobot) {
166
- return -1
140
+
141
+ // On ties, sort by when the user was added
142
+ if (membershipA?.isRobot === membershipB?.isRobot) {
143
+ return (membershipA?.createdAt || '') > (membershipB?.createdAt || '') ? 1 : -1
167
144
  }
168
- return 0
145
+
146
+ // Robots go to the bottom
147
+ return membershipA?.isRobot ? 1 : -1
169
148
  }
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import {Card, Box, Heading, Flex, Text, Stack} from '@sanity/ui'
3
3
  import {PlayIcon} from '@sanity/icons'
4
- import styled from 'styled-components'
4
+ import {styled} from 'styled-components'
5
5
 
6
6
  const PlayIconBox = styled(Box)`
7
7
  position: absolute;
@@ -38,11 +38,12 @@ export function useDataAdapter() {
38
38
  : '/addons/dashboard'
39
39
  return versionedClient.observable.request<{items: FeedItem[]}>({
40
40
  uri,
41
+ tag: 'dashboard.sanity-tutorials',
41
42
  withCredentials: false,
42
43
  })
43
44
  },
44
45
  urlBuilder: imageUrlBuilder(tutorialsProjectConfig),
45
46
  }),
46
- [versionedClient]
47
+ [versionedClient],
47
48
  )
48
49
  }