@sanity/dashboard 2.35.2 → 2.36.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/LICENSE +1 -1
- package/dts/components/dashboardWidget.d.ts +10 -0
- package/dts/legacyParts.d.ts +7 -0
- package/lib/DashboardTool.js +2 -1
- package/lib/DashboardTool.js.map +1 -0
- package/lib/components/DashboardLayout.js +2 -1
- package/lib/components/DashboardLayout.js.map +1 -0
- package/lib/components/NotFoundWidget.js +2 -1
- package/lib/components/NotFoundWidget.js.map +1 -0
- package/lib/components/WidgetGroup.js +2 -1
- package/lib/components/WidgetGroup.js.map +1 -0
- package/lib/components/dashboardWidget.js +2 -1
- package/lib/components/dashboardWidget.js.map +1 -0
- package/lib/containers/Dashboard.js +2 -1
- package/lib/containers/Dashboard.js.map +1 -0
- package/lib/containers/WidgetContainer.js +2 -1
- package/lib/containers/WidgetContainer.js.map +1 -0
- package/lib/dashboardConfig.js +2 -1
- package/lib/dashboardConfig.js.map +1 -0
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -0
- package/lib/legacyParts.js +2 -1
- package/lib/legacyParts.js.map +1 -0
- package/lib/versionedClient.js +2 -1
- package/lib/versionedClient.js.map +1 -0
- package/lib/widgets/projectInfo/ProjectInfo.js +5 -4
- package/lib/widgets/projectInfo/ProjectInfo.js.map +1 -0
- package/lib/widgets/projectInfo/index.js +2 -1
- package/lib/widgets/projectInfo/index.js.map +1 -0
- package/lib/widgets/projectUsers/ProjectUsers.js +2 -1
- package/lib/widgets/projectUsers/ProjectUsers.js.map +1 -0
- package/lib/widgets/projectUsers/index.js +2 -1
- package/lib/widgets/projectUsers/index.js.map +1 -0
- package/lib/widgets/sanityTutorials/SanityTutorials.js +2 -1
- package/lib/widgets/sanityTutorials/SanityTutorials.js.map +1 -0
- package/lib/widgets/sanityTutorials/Tutorial.js +2 -1
- package/lib/widgets/sanityTutorials/Tutorial.js.map +1 -0
- package/lib/widgets/sanityTutorials/dataAdapter.js +2 -1
- package/lib/widgets/sanityTutorials/dataAdapter.js.map +1 -0
- package/lib/widgets/sanityTutorials/index.js +2 -1
- package/lib/widgets/sanityTutorials/index.js.map +1 -0
- package/package.json +52 -20
- package/sanity.json +0 -4
- package/src/DashboardTool.js +30 -0
- package/src/components/DashboardLayout.js +42 -0
- package/src/components/NotFoundWidget.js +41 -0
- package/src/components/WidgetGroup.js +98 -0
- package/src/components/dashboardWidget.tsx +76 -0
- package/src/containers/Dashboard.js +21 -0
- package/src/containers/WidgetContainer.js +57 -0
- package/src/dashboardConfig.js +13 -0
- package/src/index.js +2 -0
- package/src/legacyParts.ts +11 -0
- package/src/versionedClient.js +9 -0
- package/src/widget.css +62 -0
- package/src/widgets/projectInfo/ProjectInfo.js +232 -0
- package/src/widgets/projectInfo/index.js +7 -0
- package/src/widgets/projectUsers/ProjectUsers.js +176 -0
- package/src/widgets/projectUsers/index.js +6 -0
- package/src/widgets/sanityTutorials/SanityTutorials.js +152 -0
- package/src/widgets/sanityTutorials/Tutorial.js +158 -0
- package/src/widgets/sanityTutorials/dataAdapter.js +17 -0
- package/src/widgets/sanityTutorials/index.js +7 -0
- package/.babelrc +0 -4
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {Card, Stack, Heading, Box} from '@sanity/ui'
|
|
4
|
+
import styled from 'styled-components'
|
|
5
|
+
|
|
6
|
+
const Root = styled(Card)`
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
justify-content: stretch;
|
|
10
|
+
height: 100%;
|
|
11
|
+
`
|
|
12
|
+
|
|
13
|
+
function NotFoundWidget(props) {
|
|
14
|
+
const {title, children} = props
|
|
15
|
+
return (
|
|
16
|
+
<Root radius={3} paddingX={3} paddingY={4} tone="critical">
|
|
17
|
+
<Stack space={2}>
|
|
18
|
+
{title && (
|
|
19
|
+
<Heading size={1} as="h2">
|
|
20
|
+
{title}
|
|
21
|
+
</Heading>
|
|
22
|
+
)}
|
|
23
|
+
{children && <Box>{children}</Box>}
|
|
24
|
+
</Stack>
|
|
25
|
+
</Root>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
NotFoundWidget.propTypes = {
|
|
30
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
31
|
+
children: PropTypes.any,
|
|
32
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
33
|
+
title: PropTypes.any,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
NotFoundWidget.defaultProps = {
|
|
37
|
+
children: null,
|
|
38
|
+
title: null,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default NotFoundWidget
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import styled, {css} from 'styled-components'
|
|
4
|
+
import {Grid} from '@sanity/ui'
|
|
5
|
+
import {WidgetContainer} from '../legacyParts'
|
|
6
|
+
|
|
7
|
+
const media = {
|
|
8
|
+
small: (...args) =>
|
|
9
|
+
css`
|
|
10
|
+
@media (min-width: ${({theme}) => theme.sanity.media[0]}px) {
|
|
11
|
+
${css(...args)}
|
|
12
|
+
}
|
|
13
|
+
`,
|
|
14
|
+
medium: (...args) =>
|
|
15
|
+
css`
|
|
16
|
+
@media (min-width: ${({theme}) => theme.sanity.media[2]}px) {
|
|
17
|
+
${css(...args)}
|
|
18
|
+
}
|
|
19
|
+
`,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Root = styled(Grid)`
|
|
23
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
24
|
+
|
|
25
|
+
& > div {
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
& > div[data-width='medium'] {
|
|
30
|
+
${media.small`
|
|
31
|
+
grid-column: span 2;
|
|
32
|
+
`}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
& > div[data-width='large'] {
|
|
36
|
+
${media.small`
|
|
37
|
+
grid-column: span 2;
|
|
38
|
+
`}
|
|
39
|
+
|
|
40
|
+
${media.medium`
|
|
41
|
+
grid-column: span 3;
|
|
42
|
+
`}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
& > div[data-width='full'] {
|
|
46
|
+
${media.small`
|
|
47
|
+
grid-column: 1 / -1;
|
|
48
|
+
`}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
& > div[data-height='medium'] {
|
|
52
|
+
${media.small`
|
|
53
|
+
grid-row: span 2;
|
|
54
|
+
`}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
& > div[data-height='large'] {
|
|
58
|
+
${media.small`
|
|
59
|
+
grid-row: span 2;
|
|
60
|
+
`}
|
|
61
|
+
|
|
62
|
+
${media.medium`
|
|
63
|
+
grid-row: span 3;
|
|
64
|
+
`}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
& > div[data-height='full'] {
|
|
68
|
+
${media.medium`
|
|
69
|
+
grid-row: 1 / -1;
|
|
70
|
+
`}
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
function WidgetGroup(props) {
|
|
75
|
+
const config = props.config || {}
|
|
76
|
+
const widgets = config.widgets || []
|
|
77
|
+
const layout = config.layout || {}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Root
|
|
81
|
+
autoFlow="dense"
|
|
82
|
+
data-width={layout.width || 'auto'}
|
|
83
|
+
data-height={layout.height || 'auto'}
|
|
84
|
+
data-name="sanity-dashboard-widget-group"
|
|
85
|
+
gap={4}
|
|
86
|
+
>
|
|
87
|
+
{widgets.map((widgetConfig, index) => {
|
|
88
|
+
if (widgetConfig.type === '__experimental_group') {
|
|
89
|
+
return <WidgetGroup key={String(index)} config={widgetConfig} />
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return <WidgetContainer key={String(index)} config={widgetConfig} />
|
|
93
|
+
})}
|
|
94
|
+
</Root>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default WidgetGroup
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, {forwardRef} from 'react'
|
|
2
|
+
import {Card, Box, Heading} from '@sanity/ui'
|
|
3
|
+
import styled from 'styled-components'
|
|
4
|
+
|
|
5
|
+
const Root = styled(Card)`
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
justify-content: stretch;
|
|
9
|
+
height: 100%;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
position: relative;
|
|
12
|
+
`
|
|
13
|
+
|
|
14
|
+
const Header = styled(Card)`
|
|
15
|
+
position: sticky;
|
|
16
|
+
top: 0;
|
|
17
|
+
z-index: 2;
|
|
18
|
+
border-top-left-radius: inherit;
|
|
19
|
+
border-top-right-radius: inherit;
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
const Footer = styled(Card)`
|
|
23
|
+
position: sticky;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
z-index: 2;
|
|
27
|
+
border-bottom-right-radius: inherit;
|
|
28
|
+
border-bottom-left-radius: inherit;
|
|
29
|
+
margin-top: auto;
|
|
30
|
+
`
|
|
31
|
+
|
|
32
|
+
const Content = styled(Box)`
|
|
33
|
+
position: relative;
|
|
34
|
+
z-index: 1;
|
|
35
|
+
height: stretch;
|
|
36
|
+
min-height: 21.5em;
|
|
37
|
+
|
|
38
|
+
@media (min-width: ${({theme}) => theme.sanity.media[0]}px) {
|
|
39
|
+
overflow-y: auto;
|
|
40
|
+
outline: none;
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
interface DashboardWidgetProps {
|
|
45
|
+
header?: string
|
|
46
|
+
className?: string
|
|
47
|
+
children: React.ReactNode
|
|
48
|
+
footer?: React.ReactNode
|
|
49
|
+
hideFooterBorder?: boolean
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const DashboardWidget = forwardRef(
|
|
53
|
+
(props: DashboardWidgetProps, ref: React.Ref<HTMLDivElement>) => {
|
|
54
|
+
const {header, children, footer, hideFooterBorder, className} = props
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Root radius={3} display="flex" ref={ref} className={className}>
|
|
58
|
+
{header && (
|
|
59
|
+
<Header borderBottom paddingX={3} paddingY={4}>
|
|
60
|
+
<Heading size={1} textOverflow="ellipsis">
|
|
61
|
+
{header}
|
|
62
|
+
</Heading>
|
|
63
|
+
</Header>
|
|
64
|
+
)}
|
|
65
|
+
{children && <Content data-name="content">{children}</Content>}
|
|
66
|
+
{footer && (
|
|
67
|
+
<Footer sizing="border" borderTop={!hideFooterBorder}>
|
|
68
|
+
{footer}
|
|
69
|
+
</Footer>
|
|
70
|
+
)}
|
|
71
|
+
</Root>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
DashboardWidget.displayName = 'DashboardWidget'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import DashboardLayout from '../components/DashboardLayout'
|
|
3
|
+
import WidgetGroup from '../components/WidgetGroup'
|
|
4
|
+
import {dashboardConfig} from '../legacyParts'
|
|
5
|
+
|
|
6
|
+
function Dashboard() {
|
|
7
|
+
if (!dashboardConfig) {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const widgetConfigs = dashboardConfig.widgets || []
|
|
12
|
+
const layoutWidth = dashboardConfig?.layout?.width || 'large'
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<DashboardLayout width={layoutWidth}>
|
|
16
|
+
<WidgetGroup config={{widgets: widgetConfigs}} />
|
|
17
|
+
</DashboardLayout>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default Dashboard
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import NotFoundWidget from '../components/NotFoundWidget'
|
|
4
|
+
import {definitions} from '../legacyParts'
|
|
5
|
+
|
|
6
|
+
function WidgetContainer(props) {
|
|
7
|
+
const config = props.config || {}
|
|
8
|
+
const definition = Array.isArray(definitions)
|
|
9
|
+
? definitions.find((wid) => wid.name === config.name)
|
|
10
|
+
: null
|
|
11
|
+
|
|
12
|
+
if (definition) {
|
|
13
|
+
const options = {
|
|
14
|
+
...(definition.options || {}),
|
|
15
|
+
...(config.options || {}),
|
|
16
|
+
}
|
|
17
|
+
const layout = {
|
|
18
|
+
...(definition.layout || {}),
|
|
19
|
+
...(config.layout || {}),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
data-width={layout.width}
|
|
25
|
+
data-height={layout.height}
|
|
26
|
+
data-widget-name={config.name}
|
|
27
|
+
data-name="sanity-dashboard-widget-container"
|
|
28
|
+
>
|
|
29
|
+
{React.createElement(definition.component, options)}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const layout = config.layout || {}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div data-width={layout.width} data-height={layout.height}>
|
|
38
|
+
<NotFoundWidget title={<>Not found: "{config.name}"</>}>
|
|
39
|
+
<p>
|
|
40
|
+
Make sure your <code>sanity.json</code> file mentions such a widget and that it’s an
|
|
41
|
+
implementation of <code>part:@sanity/dashboard/widget</code>.
|
|
42
|
+
</p>
|
|
43
|
+
</NotFoundWidget>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
WidgetContainer.propTypes = {
|
|
49
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
50
|
+
config: PropTypes.any,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
WidgetContainer.defaultProps = {
|
|
54
|
+
config: null,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default WidgetContainer
|
package/src/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @todo: remove the following line when part imports has been removed from this file
|
|
2
|
+
///<reference types="@sanity/types/parts" />
|
|
3
|
+
|
|
4
|
+
import WidgetContainer from 'part:@sanity/dashboard/widget-container'
|
|
5
|
+
import dashboardConfig from 'part:@sanity/dashboard/config?'
|
|
6
|
+
import sanityClient from 'part:@sanity/base/client'
|
|
7
|
+
import definitions from 'all:part:@sanity/dashboard/widget?'
|
|
8
|
+
import DefaultPreview from 'part:@sanity/components/previews/default'
|
|
9
|
+
import userStore from 'part:@sanity/base/user'
|
|
10
|
+
|
|
11
|
+
export {WidgetContainer, dashboardConfig, sanityClient, definitions, DefaultPreview, userStore}
|
package/src/widget.css
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@import 'part:@sanity/base/theme/variables-style';
|
|
2
|
+
|
|
3
|
+
.container {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
justify-content: stretch;
|
|
7
|
+
height: 100%;
|
|
8
|
+
border-radius: var(--border-radius-base);
|
|
9
|
+
background-color: var(--component-bg);
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
position: relative;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.containerWithPadding {
|
|
15
|
+
composes: container;
|
|
16
|
+
padding: var(--medium-padding);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.header {
|
|
20
|
+
padding: var(--small-padding) 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.title {
|
|
24
|
+
composes: heading4 from 'part:@sanity/base/theme/typography/headings-style';
|
|
25
|
+
margin: var(--small-padding) var(--medium-padding);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.content {
|
|
29
|
+
display: block;
|
|
30
|
+
margin: 0;
|
|
31
|
+
padding: 0;
|
|
32
|
+
min-height: 21.5em;
|
|
33
|
+
|
|
34
|
+
@media (--screen-medium) {
|
|
35
|
+
height: stretch;
|
|
36
|
+
overflow-y: auto;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.footer {
|
|
41
|
+
display: flex;
|
|
42
|
+
text-align: center;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
min-height: 4em;
|
|
45
|
+
height: 4em;
|
|
46
|
+
margin-top: auto;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
|
|
49
|
+
@nest & > * {
|
|
50
|
+
width: 100%;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* TODO: remove after changing document list plugin */
|
|
55
|
+
.listContainer {
|
|
56
|
+
composes: content;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* TODO: remove after changing document list plugin */
|
|
60
|
+
.bottomButtonContainer {
|
|
61
|
+
composes: footer;
|
|
62
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types, no-console */
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {isPlainObject} from 'lodash'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import {Box, Card, Stack, Heading, Grid, Label, Text, Code, Button} from '@sanity/ui'
|
|
6
|
+
import {versionedClient} from '../../versionedClient'
|
|
7
|
+
import {DashboardWidget} from '../../'
|
|
8
|
+
import {WidgetContainer} from '../../legacyParts'
|
|
9
|
+
|
|
10
|
+
const {projectId, dataset} = versionedClient.config()
|
|
11
|
+
|
|
12
|
+
function isUrl(url) {
|
|
13
|
+
return /^https?:\/\//.test(`${url}`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getGraphQlUrl() {
|
|
17
|
+
return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getGroqUrl() {
|
|
21
|
+
return `https://${projectId}.api.sanity.io/v1/groq/${dataset}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getManageUrl() {
|
|
25
|
+
return `https://manage.sanity.io/projects/${projectId}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ProjectInfo extends React.PureComponent {
|
|
29
|
+
static propTypes = {
|
|
30
|
+
// eslint-disable-next-line camelcase
|
|
31
|
+
__experimental_before: PropTypes.array,
|
|
32
|
+
data: PropTypes.array,
|
|
33
|
+
}
|
|
34
|
+
static defaultProps = {
|
|
35
|
+
// eslint-disable-next-line camelcase
|
|
36
|
+
__experimental_before: [],
|
|
37
|
+
data: [],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
state = {
|
|
41
|
+
studioHost: null,
|
|
42
|
+
graphqlApi: null,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
componentDidMount() {
|
|
46
|
+
// fetch project data
|
|
47
|
+
this.subscriptions = []
|
|
48
|
+
|
|
49
|
+
this.subscriptions.push(
|
|
50
|
+
versionedClient.observable.request({uri: `/projects/${projectId}`}).subscribe({
|
|
51
|
+
next: (result) => {
|
|
52
|
+
const {studioHost} = result
|
|
53
|
+
this.setState({studioHost: studioHost ? `https://${studioHost}.sanity.studio` : null})
|
|
54
|
+
},
|
|
55
|
+
error: (error) => {
|
|
56
|
+
console.log('Error while looking for studioHost', error)
|
|
57
|
+
this.setState({
|
|
58
|
+
studioHost: {
|
|
59
|
+
error: 'Something went wrong while looking up studioHost. See console.',
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// ping assumed graphql endpoint
|
|
67
|
+
this.subscriptions.push(
|
|
68
|
+
versionedClient.observable
|
|
69
|
+
.request({
|
|
70
|
+
method: 'HEAD',
|
|
71
|
+
uri: `/graphql/${dataset}/default`,
|
|
72
|
+
})
|
|
73
|
+
.subscribe({
|
|
74
|
+
next: () => this.setState({graphqlApi: getGraphQlUrl()}),
|
|
75
|
+
error: (error) => {
|
|
76
|
+
if (error.statusCode === 404) {
|
|
77
|
+
this.setState({graphqlApi: null})
|
|
78
|
+
} else {
|
|
79
|
+
console.log('Error while looking for graphqlApi', error)
|
|
80
|
+
this.setState({
|
|
81
|
+
graphqlApi: {
|
|
82
|
+
error: 'Something went wrong while looking up graphqlApi. See console.',
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
componentWillUnmount() {
|
|
92
|
+
this.subscriptions.forEach((sub) => sub.unsubscribe())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
assembleTableRows() {
|
|
96
|
+
const {graphqlApi, studioHost} = this.state
|
|
97
|
+
const propsData = this.props.data
|
|
98
|
+
|
|
99
|
+
let result = [
|
|
100
|
+
{
|
|
101
|
+
title: 'Sanity project',
|
|
102
|
+
rows: [
|
|
103
|
+
{title: 'Project ID', value: projectId},
|
|
104
|
+
{title: 'Dataset', value: dataset},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
// Handle any apps
|
|
110
|
+
const apps = [studioHost ? {title: 'Studio', value: studioHost} : null]
|
|
111
|
+
.concat(propsData.filter((item) => item.category === 'apps'))
|
|
112
|
+
.filter(Boolean)
|
|
113
|
+
if (apps.length > 0) {
|
|
114
|
+
result = result.concat([{title: 'Apps', rows: apps}])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle APIs
|
|
118
|
+
result = result.concat(
|
|
119
|
+
[
|
|
120
|
+
{
|
|
121
|
+
title: 'APIs',
|
|
122
|
+
rows: [
|
|
123
|
+
{title: 'GROQ', value: getGroqUrl()},
|
|
124
|
+
{title: 'GraphQL', value: graphqlApi || 'Not deployed'},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
propsData.filter((item) => item.category === 'apis')
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// Handle whatever else there might be
|
|
132
|
+
const otherStuff = {}
|
|
133
|
+
propsData.forEach((item) => {
|
|
134
|
+
if (item.category !== 'apps' && item.category !== 'apis') {
|
|
135
|
+
if (!otherStuff[item.category]) {
|
|
136
|
+
otherStuff[item.category] = []
|
|
137
|
+
}
|
|
138
|
+
otherStuff[item.category].push(item)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
Object.keys(otherStuff).forEach((category) => {
|
|
142
|
+
result.push({title: category, rows: otherStuff[category]})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return result
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
render() {
|
|
149
|
+
return (
|
|
150
|
+
<>
|
|
151
|
+
{this.props.__experimental_before &&
|
|
152
|
+
this.props.__experimental_before.map((widgetConfig, idx) => (
|
|
153
|
+
<WidgetContainer key={String(idx)} config={widgetConfig} />
|
|
154
|
+
))}
|
|
155
|
+
<Box height="fill" marginTop={this.props.__experimental_before?.length > 0 ? 4 : 0}>
|
|
156
|
+
<DashboardWidget
|
|
157
|
+
footer={
|
|
158
|
+
<Button
|
|
159
|
+
style={{width: '100%'}}
|
|
160
|
+
paddingX={2}
|
|
161
|
+
paddingY={4}
|
|
162
|
+
mode="bleed"
|
|
163
|
+
tone="primary"
|
|
164
|
+
text="Manage project"
|
|
165
|
+
as="a"
|
|
166
|
+
href={getManageUrl()}
|
|
167
|
+
/>
|
|
168
|
+
}
|
|
169
|
+
>
|
|
170
|
+
<Card
|
|
171
|
+
paddingY={4}
|
|
172
|
+
radius={2}
|
|
173
|
+
role="table"
|
|
174
|
+
aria-label="Project info"
|
|
175
|
+
aria-describedby="project_info_table"
|
|
176
|
+
>
|
|
177
|
+
<Stack space={4}>
|
|
178
|
+
<Box paddingX={3} as="header">
|
|
179
|
+
<Heading size={1} as="h2" id="project_info_table">
|
|
180
|
+
Project info
|
|
181
|
+
</Heading>
|
|
182
|
+
</Box>
|
|
183
|
+
{this.assembleTableRows().map((item) => {
|
|
184
|
+
if (!item || !item.rows) {
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<Stack key={item.title} space={3}>
|
|
190
|
+
<Card borderBottom padding={3}>
|
|
191
|
+
<Label size={0} muted role="columnheader">
|
|
192
|
+
{item.title}
|
|
193
|
+
</Label>
|
|
194
|
+
</Card>
|
|
195
|
+
<Stack space={4} paddingX={3} role="rowgroup">
|
|
196
|
+
{item.rows.map((row) => {
|
|
197
|
+
return (
|
|
198
|
+
<Grid key={row.title} columns={2} role="row">
|
|
199
|
+
<Text weight="medium" role="rowheader">
|
|
200
|
+
{row.title}
|
|
201
|
+
</Text>
|
|
202
|
+
{isPlainObject(row.value) && <Text size={1}>{row.value.error}</Text>}
|
|
203
|
+
{!isPlainObject(row.value) && (
|
|
204
|
+
<>
|
|
205
|
+
{isUrl(row.value) ? (
|
|
206
|
+
<Text size={1} role="cell" style={{wordBreak: 'break-word'}}>
|
|
207
|
+
<a href={row.value}>{row.value}</a>
|
|
208
|
+
</Text>
|
|
209
|
+
) : (
|
|
210
|
+
<Code size={1} role="cell" style={{wordBreak: 'break-word'}}>
|
|
211
|
+
{row.value}
|
|
212
|
+
</Code>
|
|
213
|
+
)}
|
|
214
|
+
</>
|
|
215
|
+
)}
|
|
216
|
+
</Grid>
|
|
217
|
+
)
|
|
218
|
+
})}
|
|
219
|
+
</Stack>
|
|
220
|
+
</Stack>
|
|
221
|
+
)
|
|
222
|
+
})}
|
|
223
|
+
</Stack>
|
|
224
|
+
</Card>
|
|
225
|
+
</DashboardWidget>
|
|
226
|
+
</Box>
|
|
227
|
+
</>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default ProjectInfo
|