@sanity/dashboard 2.35.0 → 2.36.0-v2-studio.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 (65) hide show
  1. package/LICENSE +1 -1
  2. package/dts/components/dashboardWidget.d.ts +10 -0
  3. package/dts/legacyParts.d.ts +7 -0
  4. package/lib/DashboardTool.js +2 -1
  5. package/lib/DashboardTool.js.map +1 -0
  6. package/lib/components/DashboardLayout.js +2 -1
  7. package/lib/components/DashboardLayout.js.map +1 -0
  8. package/lib/components/NotFoundWidget.js +2 -1
  9. package/lib/components/NotFoundWidget.js.map +1 -0
  10. package/lib/components/WidgetGroup.js +2 -1
  11. package/lib/components/WidgetGroup.js.map +1 -0
  12. package/lib/components/dashboardWidget.js +2 -1
  13. package/lib/components/dashboardWidget.js.map +1 -0
  14. package/lib/containers/Dashboard.js +2 -1
  15. package/lib/containers/Dashboard.js.map +1 -0
  16. package/lib/containers/WidgetContainer.js +2 -1
  17. package/lib/containers/WidgetContainer.js.map +1 -0
  18. package/lib/dashboardConfig.js +2 -1
  19. package/lib/dashboardConfig.js.map +1 -0
  20. package/lib/index.js +2 -1
  21. package/lib/index.js.map +1 -0
  22. package/lib/legacyParts.js +2 -1
  23. package/lib/legacyParts.js.map +1 -0
  24. package/lib/versionedClient.js +2 -1
  25. package/lib/versionedClient.js.map +1 -0
  26. package/lib/widgets/projectInfo/ProjectInfo.js +5 -4
  27. package/lib/widgets/projectInfo/ProjectInfo.js.map +1 -0
  28. package/lib/widgets/projectInfo/index.js +2 -1
  29. package/lib/widgets/projectInfo/index.js.map +1 -0
  30. package/lib/widgets/projectUsers/ProjectUsers.js +2 -1
  31. package/lib/widgets/projectUsers/ProjectUsers.js.map +1 -0
  32. package/lib/widgets/projectUsers/index.js +2 -1
  33. package/lib/widgets/projectUsers/index.js.map +1 -0
  34. package/lib/widgets/sanityTutorials/SanityTutorials.js +2 -1
  35. package/lib/widgets/sanityTutorials/SanityTutorials.js.map +1 -0
  36. package/lib/widgets/sanityTutorials/Tutorial.js +2 -1
  37. package/lib/widgets/sanityTutorials/Tutorial.js.map +1 -0
  38. package/lib/widgets/sanityTutorials/dataAdapter.js +2 -1
  39. package/lib/widgets/sanityTutorials/dataAdapter.js.map +1 -0
  40. package/lib/widgets/sanityTutorials/index.js +2 -1
  41. package/lib/widgets/sanityTutorials/index.js.map +1 -0
  42. package/package.json +53 -21
  43. package/sanity.json +0 -4
  44. package/src/DashboardTool.js +30 -0
  45. package/src/components/DashboardLayout.js +42 -0
  46. package/src/components/NotFoundWidget.js +41 -0
  47. package/src/components/WidgetGroup.js +98 -0
  48. package/src/components/dashboardWidget.tsx +76 -0
  49. package/src/containers/Dashboard.js +21 -0
  50. package/src/containers/WidgetContainer.js +57 -0
  51. package/src/dashboardConfig.js +13 -0
  52. package/src/index.js +2 -0
  53. package/src/legacyParts.ts +11 -0
  54. package/src/versionedClient.js +9 -0
  55. package/src/widget.css +62 -0
  56. package/src/widgets/projectInfo/ProjectInfo.js +232 -0
  57. package/src/widgets/projectInfo/index.js +7 -0
  58. package/src/widgets/projectUsers/ProjectUsers.js +176 -0
  59. package/src/widgets/projectUsers/index.js +6 -0
  60. package/src/widgets/sanityTutorials/SanityTutorials.js +152 -0
  61. package/src/widgets/sanityTutorials/Tutorial.js +158 -0
  62. package/src/widgets/sanityTutorials/dataAdapter.js +17 -0
  63. package/src/widgets/sanityTutorials/index.js +7 -0
  64. package/.babelrc +0 -4
  65. package/tsconfig.json +0 -17
@@ -0,0 +1,176 @@
1
+ // @todo: remove the following line when part imports has been removed from this file
2
+ ///<reference types="@sanity/types/parts" />
3
+
4
+ import React from 'react'
5
+ import {map, switchMap} from 'rxjs/operators'
6
+ import {Stack, Card, Box, Text, Button} from '@sanity/ui'
7
+ import {RobotIcon} from '@sanity/icons'
8
+ import styled from 'styled-components'
9
+ import {DefaultPreview} from '@sanity/base/components'
10
+ import Spinner from 'part:@sanity/components/loading/spinner'
11
+ import {versionedClient} from '../../versionedClient'
12
+ import {DashboardWidget} from '../../'
13
+ import {userStore} from '../../legacyParts'
14
+
15
+ const AvatarWrapper = styled(Card)`
16
+ box-sizing: border-box;
17
+ border-radius: 50%;
18
+ border-color: transparent;
19
+ overflow: hidden;
20
+ width: 100%;
21
+ height: 100%;
22
+
23
+ & > img {
24
+ width: 100%;
25
+ height: auto;
26
+ }
27
+ `
28
+
29
+ function getInviteUrl(projectId) {
30
+ return `https://manage.sanity.io/projects/${projectId}/team/invite`
31
+ }
32
+
33
+ function sortUsersByRobotStatus(userA, userB, project) {
34
+ const {members} = project
35
+ const membershipA = members.find((member) => member.id === userA.id)
36
+ const membershipB = members.find((member) => member.id === userB.id)
37
+ if (membershipA.isRobot) {
38
+ return 1
39
+ }
40
+ if (membershipB.isRobot) {
41
+ return -1
42
+ }
43
+ return 0
44
+ }
45
+
46
+ class ProjectUsers extends React.PureComponent {
47
+ static propTypes = {}
48
+ static defaultProps = {}
49
+
50
+ state = {
51
+ project: null,
52
+ users: null,
53
+ error: null,
54
+ }
55
+
56
+ componentDidMount() {
57
+ this.fetchData()
58
+ }
59
+
60
+ componentWillUnmount() {
61
+ if (this.subscription) {
62
+ this.subscription.unsubscribe()
63
+ }
64
+ }
65
+
66
+ fetchData() {
67
+ if (this.subscription) {
68
+ this.subscription.unsubscribe()
69
+ }
70
+
71
+ const {projectId} = versionedClient.config()
72
+ this.subscription = versionedClient.observable
73
+ .request({
74
+ uri: `/projects/${projectId}`,
75
+ })
76
+ .pipe(
77
+ switchMap((project) =>
78
+ userStore.observable
79
+ .getUsers(project.members.map((mem) => mem.id))
80
+ .pipe(map((users) => ({project, users})))
81
+ )
82
+ )
83
+ .subscribe({
84
+ next: ({users, project}) =>
85
+ this.setState({
86
+ project,
87
+ users: (Array.isArray(users) ? users : [users]).sort((userA, userB) =>
88
+ sortUsersByRobotStatus(userA, userB, project)
89
+ ),
90
+ }),
91
+ error: (error) => this.setState({error}),
92
+ })
93
+ }
94
+
95
+ handleRetryFetch = () => {
96
+ this.fetchData()
97
+ }
98
+
99
+ render() {
100
+ const {error, project, users} = this.state
101
+ const isLoading = !users || !project
102
+
103
+ if (error) {
104
+ return (
105
+ <DashboardWidget header="Project users">
106
+ <Box padding={4}>
107
+ <Text>
108
+ Something went wrong while fetching data. You could{' '}
109
+ <a
110
+ onClick={this.handleRetryFetch}
111
+ title="Retry users fetch"
112
+ style={{cursor: 'pointer'}}
113
+ >
114
+ retry
115
+ </a>
116
+ ..?
117
+ </Text>
118
+ </Box>
119
+ </DashboardWidget>
120
+ )
121
+ }
122
+
123
+ return (
124
+ <DashboardWidget
125
+ header="Project users"
126
+ footer={
127
+ <Button
128
+ style={{width: '100%'}}
129
+ paddingX={2}
130
+ paddingY={4}
131
+ mode="bleed"
132
+ tone="primary"
133
+ text="Invite members"
134
+ as="a"
135
+ loading={isLoading}
136
+ href={isLoading ? undefined : getInviteUrl(project.id)}
137
+ />
138
+ }
139
+ >
140
+ {isLoading && (
141
+ <Card padding={4}>
142
+ <Spinner center message="Loading..." />
143
+ </Card>
144
+ )}
145
+
146
+ {!isLoading && (
147
+ <Stack space={3} padding={3}>
148
+ {users.map((user) => {
149
+ const membership = project.members.find((member) => member.id === user.id)
150
+ const media = membership.isRobot ? (
151
+ <Text size={3}>
152
+ <RobotIcon />
153
+ </Text>
154
+ ) : (
155
+ <AvatarWrapper tone="transparent">
156
+ {user?.imageUrl && <img src={user.imageUrl} alt={user?.displayName} />}
157
+ </AvatarWrapper>
158
+ )
159
+ return (
160
+ <Box key={user.id}>
161
+ <DefaultPreview
162
+ title={user.displayName}
163
+ subtitle={membership.role}
164
+ media={media}
165
+ />
166
+ </Box>
167
+ )
168
+ })}
169
+ </Stack>
170
+ )}
171
+ </DashboardWidget>
172
+ )
173
+ }
174
+ }
175
+
176
+ export default ProjectUsers
@@ -0,0 +1,6 @@
1
+ import ProjectUsers from './ProjectUsers'
2
+
3
+ export default {
4
+ name: 'project-users',
5
+ component: ProjectUsers,
6
+ }
@@ -0,0 +1,152 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import {Flex, Grid, Stack, Heading, Container, Card, Text, Button} from '@sanity/ui'
4
+ import Tutorial from './Tutorial'
5
+ import dataAdapter from './dataAdapter'
6
+
7
+ const FeedItem = ({feedItem}) => {
8
+ // Check to see if the feed item has the content needed to render an item with a link and poster image
9
+ const isEmpty =
10
+ !feedItem.title || (!feedItem.guideOrTutorial && !feedItem.externalLink && !feedItem.feedItems)
11
+
12
+ if (isEmpty) {
13
+ return null
14
+ }
15
+ const subtitle = feedItem.description
16
+ const {guideOrTutorial = {}} = feedItem
17
+ return (
18
+ <Tutorial
19
+ title={feedItem.title}
20
+ href={createUrl(guideOrTutorial.slug, guideOrTutorial._type) || feedItem.externalLink}
21
+ presenterSubtitle={subtitle}
22
+ showPlayIcon={feedItem.hasVideo}
23
+ posterURL={feedItem.poster ? urlBuilder.image(feedItem.poster).height(360).url() : undefined}
24
+ />
25
+ )
26
+ }
27
+
28
+ const {urlBuilder, getFeed} = dataAdapter
29
+
30
+ function createUrl(slug, type) {
31
+ if (type === 'tutorial') {
32
+ return `https://www.sanity.io/docs/tutorials/${slug.current}`
33
+ } else if (type === 'guide') {
34
+ return `https://www.sanity.io/docs/guides/${slug.current}`
35
+ }
36
+ return false
37
+ }
38
+
39
+ class SanityTutorials extends React.Component {
40
+ static propTypes = {
41
+ templateRepoId: PropTypes.string,
42
+ }
43
+
44
+ static defaultProps = {
45
+ templateRepoId: null,
46
+ }
47
+
48
+ state = {
49
+ feedItems: [],
50
+ }
51
+
52
+ componentDidMount() {
53
+ const {templateRepoId} = this.props
54
+ this.subscription = getFeed(templateRepoId).subscribe((response) => {
55
+ this.setState({
56
+ title: response.title,
57
+ feedItems: response.items,
58
+ })
59
+ })
60
+ }
61
+
62
+ componentWillUnmount() {
63
+ if (this.subscription) {
64
+ this.subscription.unsubscribe()
65
+ }
66
+ }
67
+
68
+ render() {
69
+ const {title = 'Learn about Sanity', feedItems} = this.state
70
+
71
+ // Filter out items and sections for layout purposes
72
+ const sections = feedItems.filter((i) => i._type === 'feedSection')
73
+ const items = feedItems.filter((i) => i._type === 'feedItem')
74
+
75
+ const columns = (length) => (length < 4 ? [1, 2, 3] : [1, 2, 3, 4])
76
+
77
+ return (
78
+ <Container width={4}>
79
+ <Stack space={6} paddingBottom={4}>
80
+ <Card
81
+ tone="primary"
82
+ padding={4}
83
+ radius={2}
84
+ border
85
+ marginTop={4}
86
+ data-name="sanity-tutorials-widget-docs-link"
87
+ >
88
+ <Flex direction={['column', 'column', 'row']}>
89
+ <Stack space={4} flex={1} paddingRight={[0, 0, 4]}>
90
+ <Heading as="h2">Getting started guide</Heading>
91
+ <Text as="p">
92
+ {`It's time to learn how to build schemas, create content, and connect it to other
93
+ applications.`}
94
+ </Text>
95
+ </Stack>
96
+ <Flex paddingTop={[4, 4, 0]} align="center">
97
+ <Stack flex={1}>
98
+ <Button
99
+ paddingY={3}
100
+ paddingX={5}
101
+ tone="primary"
102
+ as="a"
103
+ target="_blank"
104
+ href="https://www.sanity.io/docs?ref=studio-dashboard"
105
+ text="Go to docs"
106
+ />
107
+ </Stack>
108
+ </Flex>
109
+ </Flex>
110
+ </Card>
111
+ {sections &&
112
+ sections?.length > 0 &&
113
+ sections.map((section) => {
114
+ return (
115
+ section?.sectionItems && (
116
+ <Stack space={4} key={section._id}>
117
+ <Heading>{section.title}</Heading>
118
+ <Grid
119
+ as="ul"
120
+ columns={columns(section?.sectionItems?.length)}
121
+ gap={4}
122
+ data-name="sanity-dashboard-widget-tutorials-section"
123
+ >
124
+ {section?.sectionItems.map((item) => (
125
+ <Flex as="li" key={item._id}>
126
+ <FeedItem feedItem={item} />
127
+ </Flex>
128
+ ))}
129
+ </Grid>
130
+ </Stack>
131
+ )
132
+ )
133
+ })}
134
+ {items && items.length > 0 && (
135
+ <Stack space={4}>
136
+ <Heading>{title}</Heading>
137
+ <Grid as="ul" columns={columns(items?.length)} gap={4}>
138
+ {items.map((feedItem) => (
139
+ <Flex as="li" key={feedItem._id}>
140
+ <FeedItem feedItem={feedItem} />
141
+ </Flex>
142
+ ))}
143
+ </Grid>
144
+ </Stack>
145
+ )}
146
+ </Stack>
147
+ </Container>
148
+ )
149
+ }
150
+ }
151
+
152
+ export default SanityTutorials
@@ -0,0 +1,158 @@
1
+ import React from 'react'
2
+ import {Card, Box, Flex, Text, Stack} from '@sanity/ui'
3
+ import styled from 'styled-components'
4
+
5
+ // eslint-disable-next-line no-useless-escape
6
+ const youtubeRegex = /youtu(?:.*\/v\/|.*v\=|\.be\/)([A-Za-z0-9_-]{11})/
7
+
8
+ const PlayIconBox = styled(Box)`
9
+ position: absolute;
10
+ top: 50%;
11
+ left: 50%;
12
+ transform: translate(-50%, -50%);
13
+
14
+ &:before {
15
+ content: '';
16
+ position: absolute;
17
+ top: 50%;
18
+ left: 50%;
19
+ transform: translate(-50%, -50%);
20
+ width: 2.75em;
21
+ height: 2.75em;
22
+ border-radius: 50%;
23
+ background: ${({theme}) => theme.sanity.color.card.enabled.bg};
24
+ opacity: 0.75;
25
+ }
26
+ `
27
+
28
+ const Root = styled(Flex)`
29
+ &:hover {
30
+ ${PlayIconBox} {
31
+ &:before {
32
+ opacity: 1;
33
+ }
34
+ }
35
+ }
36
+ `
37
+
38
+ const PosterCard = styled(Card)`
39
+ width: 100%;
40
+ padding-bottom: calc(9 / 16 * 100%);
41
+ position: relative;
42
+ border-bottom-left-radius: 0;
43
+ border-bottom-right-radius: 0;
44
+ `
45
+
46
+ const Poster = styled.img`
47
+ position: absolute;
48
+ top: 0;
49
+ left: 0;
50
+ height: 100%;
51
+ width: 100%;
52
+ object-fit: cover;
53
+ display: block;
54
+ border-radius: inherit;
55
+
56
+ &:not([src]) {
57
+ display: none;
58
+ }
59
+ `
60
+ const YoutubeContainer = styled(Card)`
61
+ position: relative;
62
+ padding-bottom: 56.25%;
63
+ overflow: hidden;
64
+ border-bottom-left-radius: 0;
65
+ border-bottom-right-radius: 0;
66
+
67
+ iframe {
68
+ position: absolute;
69
+ top: 0;
70
+ left: 0;
71
+ width: 100%;
72
+ height: 100%;
73
+ }
74
+ `
75
+ const YoutubeEmbed = ({embedId}) => (
76
+ <YoutubeContainer radius={3}>
77
+ <iframe
78
+ width="853"
79
+ height="480"
80
+ src={`https://www.youtube.com/embed/${embedId}`}
81
+ frameBorder="0"
82
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
83
+ allowFullScreen
84
+ />
85
+ </YoutubeContainer>
86
+ )
87
+ class Tutorial extends React.PureComponent {
88
+ static defaultProps = {
89
+ posterURL: null,
90
+ showPlayIcon: false,
91
+ }
92
+
93
+ render() {
94
+ const {title, posterURL, showPlayIcon, href, presenterSubtitle} = this.props
95
+
96
+ const isYoutube = showPlayIcon && href && href.match(youtubeRegex)
97
+
98
+ return isYoutube ? (
99
+ <Card
100
+ space={2}
101
+ sizing="border"
102
+ flex={1}
103
+ radius={3}
104
+ style={{position: 'relative'}}
105
+ border
106
+ paddingBottom={2}
107
+ >
108
+ <Stack space={2} height="fill">
109
+ <YoutubeEmbed embedId={href.match(youtubeRegex)[1]} />
110
+ <Stack space={3} flex={1} padding={2}>
111
+ <Text as="h3" size={1} weight="bold">
112
+ {title}
113
+ </Text>
114
+ {presenterSubtitle && (
115
+ <Text size={1} muted>
116
+ {presenterSubtitle}
117
+ </Text>
118
+ )}
119
+ </Stack>
120
+ </Stack>
121
+ </Card>
122
+ ) : (
123
+ <Root flex={1}>
124
+ <Card
125
+ sizing="border"
126
+ flex={1}
127
+ radius={3}
128
+ as="a"
129
+ href={href}
130
+ target="_blank"
131
+ style={{position: 'relative'}}
132
+ border
133
+ paddingBottom={2}
134
+ >
135
+ <Stack space={2} height="fill">
136
+ {posterURL && (
137
+ <PosterCard radius={3}>
138
+ <Poster src={posterURL} />
139
+ </PosterCard>
140
+ )}
141
+ <Stack space={3} flex={1} padding={2}>
142
+ <Text as="h3" size={1} weight="bold">
143
+ {title}
144
+ </Text>
145
+ {presenterSubtitle && (
146
+ <Text size={1} muted>
147
+ {presenterSubtitle}
148
+ </Text>
149
+ )}
150
+ </Stack>
151
+ </Stack>
152
+ </Card>
153
+ </Root>
154
+ )
155
+ }
156
+ }
157
+
158
+ export default Tutorial
@@ -0,0 +1,17 @@
1
+ import imageUrlBuilder from '@sanity/image-url'
2
+ import {versionedClient} from '../../versionedClient'
3
+
4
+ const tutorialsProjectConfig = {
5
+ projectId: '3do82whm',
6
+ dataset: 'next',
7
+ }
8
+
9
+ export default {
10
+ getFeed: (templateRepoId) => {
11
+ const uri = templateRepoId
12
+ ? `/addons/dashboard?templateRepoId=${templateRepoId}`
13
+ : '/addons/dashboard'
14
+ return versionedClient.observable.request({uri, withCredentials: false})
15
+ },
16
+ urlBuilder: imageUrlBuilder(tutorialsProjectConfig),
17
+ }
@@ -0,0 +1,7 @@
1
+ import SanityTutorials from './SanityTutorials'
2
+
3
+ export default {
4
+ name: 'sanity-tutorials',
5
+ component: SanityTutorials,
6
+ layout: {width: 'full'},
7
+ }
package/.babelrc DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "../../../.babelrc",
3
- "presets": ["@babel/react", "@babel/typescript"]
4
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig",
3
- "include": ["src"],
4
- "compilerOptions": {
5
- "composite": true,
6
- "outDir": "./dist/dts",
7
- "rootDir": "./src",
8
- "jsx": "react",
9
- "noImplicitAny": false
10
- },
11
- "references": [
12
- {
13
- "path": "../base"
14
- },
15
- ]
16
- }
17
-