@sanity/dashboard 5.0.0 → 6.0.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/README.md +4 -50
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +605 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -79
- package/lib/index.d.mts +0 -65
- package/lib/index.d.ts +0 -65
- package/lib/index.esm.js +0 -568
- package/lib/index.esm.js.map +0 -1
- package/lib/index.js +0 -564
- package/lib/index.js.map +0 -1
- package/lib/index.mjs +0 -568
- package/lib/index.mjs.map +0 -1
- package/sanity.json +0 -8
- package/src/components/DashboardLayout.tsx +0 -10
- package/src/components/DashboardWidgetContainer.tsx +0 -69
- package/src/components/NotFoundWidget.tsx +0 -30
- package/src/components/WidgetGroup.tsx +0 -106
- package/src/containers/Dashboard.tsx +0 -19
- package/src/containers/DashboardContext.tsx +0 -8
- package/src/containers/WidgetContainer.tsx +0 -21
- package/src/index.ts +0 -7
- package/src/plugin.tsx +0 -72
- package/src/types.ts +0 -21
- package/src/versionedClient.ts +0 -5
- package/src/widgets/projectInfo/ProjectInfo.tsx +0 -221
- package/src/widgets/projectInfo/index.ts +0 -10
- package/src/widgets/projectInfo/types.ts +0 -28
- package/src/widgets/projectUsers/ProjectUser.tsx +0 -45
- package/src/widgets/projectUsers/ProjectUsers.tsx +0 -148
- package/src/widgets/projectUsers/index.ts +0 -10
- package/src/widgets/sanityTutorials/SanityTutorials.tsx +0 -77
- package/src/widgets/sanityTutorials/Tutorial.tsx +0 -111
- package/src/widgets/sanityTutorials/dataAdapter.ts +0 -49
- package/src/widgets/sanityTutorials/index.ts +0 -10
- package/v2-incompatible.js +0 -11
package/sanity.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React, {PropsWithChildren} from 'react'
|
|
2
|
-
import {Container} from '@sanity/ui'
|
|
3
|
-
|
|
4
|
-
export function DashboardLayout(props: PropsWithChildren<{}>) {
|
|
5
|
-
return (
|
|
6
|
-
<Container width={4} padding={4} sizing="border" style={{height: '100%', overflowY: 'auto'}}>
|
|
7
|
-
{props.children}
|
|
8
|
-
</Container>
|
|
9
|
-
)
|
|
10
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
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
|
-
children: React.ReactNode
|
|
47
|
-
footer?: React.ReactNode
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const DashboardWidgetContainer = forwardRef(function DashboardWidgetContainer(
|
|
51
|
-
props: DashboardWidgetProps,
|
|
52
|
-
ref: React.Ref<HTMLDivElement>,
|
|
53
|
-
) {
|
|
54
|
-
const {header, children, footer} = props
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Root radius={3} display="flex" ref={ref}>
|
|
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>{children}</Content>}
|
|
66
|
-
{footer && <Footer borderTop>{footer}</Footer>}
|
|
67
|
-
</Root>
|
|
68
|
-
)
|
|
69
|
-
})
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React, {PropsWithChildren, ReactNode} from 'react'
|
|
2
|
-
import {Card, Stack, Heading, Box} 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
|
-
`
|
|
11
|
-
|
|
12
|
-
export type NotFoundWidgetProps = PropsWithChildren<{
|
|
13
|
-
title?: ReactNode
|
|
14
|
-
}>
|
|
15
|
-
|
|
16
|
-
export function NotFoundWidget(props: NotFoundWidgetProps) {
|
|
17
|
-
const {title, children} = props
|
|
18
|
-
return (
|
|
19
|
-
<Root radius={3} paddingX={3} paddingY={4} tone="critical">
|
|
20
|
-
<Stack space={2}>
|
|
21
|
-
{title && (
|
|
22
|
-
<Heading size={1} as="h2">
|
|
23
|
-
{title}
|
|
24
|
-
</Heading>
|
|
25
|
-
)}
|
|
26
|
-
{children && <Box>{children}</Box>}
|
|
27
|
-
</Stack>
|
|
28
|
-
</Root>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {styled, css} from 'styled-components'
|
|
3
|
-
import {Box, Card, Grid, Text} from '@sanity/ui'
|
|
4
|
-
import {WidgetContainer} from '../containers/WidgetContainer'
|
|
5
|
-
import {DashboardConfig, LayoutConfig, DashboardWidget} from '../types'
|
|
6
|
-
|
|
7
|
-
const media = {
|
|
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
|
-
`,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const Root = styled(Grid)`
|
|
21
|
-
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
22
|
-
|
|
23
|
-
& > div {
|
|
24
|
-
overflow: hidden;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
& > div[data-width='medium'] {
|
|
28
|
-
${media.small`
|
|
29
|
-
grid-column: span 2;
|
|
30
|
-
`}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
& > div[data-width='large'] {
|
|
34
|
-
${media.small`
|
|
35
|
-
grid-column: span 2;
|
|
36
|
-
`}
|
|
37
|
-
|
|
38
|
-
${media.medium`
|
|
39
|
-
grid-column: span 3;
|
|
40
|
-
`}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
& > div[data-width='full'] {
|
|
44
|
-
${media.small`
|
|
45
|
-
grid-column: 1 / -1;
|
|
46
|
-
`}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
& > div[data-height='medium'] {
|
|
50
|
-
${media.small`
|
|
51
|
-
grid-row: span 2;
|
|
52
|
-
`}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
& > div[data-height='large'] {
|
|
56
|
-
${media.small`
|
|
57
|
-
grid-row: span 2;
|
|
58
|
-
`}
|
|
59
|
-
|
|
60
|
-
${media.medium`
|
|
61
|
-
grid-row: span 3;
|
|
62
|
-
`}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
& > div[data-height='full'] {
|
|
66
|
-
${media.medium`
|
|
67
|
-
grid-row: 1 / -1;
|
|
68
|
-
`}
|
|
69
|
-
}
|
|
70
|
-
`
|
|
71
|
-
|
|
72
|
-
export interface WidgetGroupProps {
|
|
73
|
-
config: Partial<DashboardConfig>
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const NO_WIDGETS: DashboardWidget[] = []
|
|
77
|
-
const NO_LAYOUT: LayoutConfig = {}
|
|
78
|
-
|
|
79
|
-
export function WidgetGroup(props: WidgetGroupProps) {
|
|
80
|
-
const {
|
|
81
|
-
config: {layout = NO_LAYOUT, widgets = NO_WIDGETS},
|
|
82
|
-
} = props
|
|
83
|
-
return (
|
|
84
|
-
<Root
|
|
85
|
-
autoFlow="row dense"
|
|
86
|
-
data-width={layout.width || 'auto'}
|
|
87
|
-
data-height={layout.height || 'auto'}
|
|
88
|
-
gap={4}
|
|
89
|
-
>
|
|
90
|
-
{widgets.length ? null : (
|
|
91
|
-
<Card padding={4} shadow={1} tone="primary">
|
|
92
|
-
<Text align="center">Add some widgets to populate this space.</Text>
|
|
93
|
-
</Card>
|
|
94
|
-
)}
|
|
95
|
-
{widgets.map((widgetConfig, index) => {
|
|
96
|
-
if (widgetConfig.type === '__experimental_group') {
|
|
97
|
-
return <WidgetGroup key={index} config={widgetConfig} />
|
|
98
|
-
}
|
|
99
|
-
if (widgetConfig.component) {
|
|
100
|
-
return <WidgetContainer key={index} {...widgetConfig} />
|
|
101
|
-
}
|
|
102
|
-
return <Box key={index}>{widgetConfig.name} is missing widget component</Box>
|
|
103
|
-
})}
|
|
104
|
-
</Root>
|
|
105
|
-
)
|
|
106
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {DashboardLayout} from '../components/DashboardLayout'
|
|
3
|
-
import {WidgetGroup} from '../components/WidgetGroup'
|
|
4
|
-
import {DashboardContext} from './DashboardContext'
|
|
5
|
-
import {DashboardConfig} from '../types'
|
|
6
|
-
|
|
7
|
-
export function Dashboard({config}: {config: DashboardConfig}) {
|
|
8
|
-
if (!config) {
|
|
9
|
-
return null
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<DashboardContext.Provider value={config}>
|
|
14
|
-
<DashboardLayout>
|
|
15
|
-
<WidgetGroup config={config} />
|
|
16
|
-
</DashboardLayout>
|
|
17
|
-
</DashboardContext.Provider>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import {createContext, useContext} from 'react'
|
|
2
|
-
import {DashboardConfig} from '../types'
|
|
3
|
-
|
|
4
|
-
export const DashboardContext = createContext<DashboardConfig>({widgets: []})
|
|
5
|
-
|
|
6
|
-
export function useDashboardConfig(): DashboardConfig {
|
|
7
|
-
return useContext(DashboardContext)
|
|
8
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React, {createElement, useMemo} from 'react'
|
|
2
|
-
import {useDashboardConfig} from './DashboardContext'
|
|
3
|
-
import {Card} from '@sanity/ui'
|
|
4
|
-
import {DashboardWidget} from '../types'
|
|
5
|
-
|
|
6
|
-
export function WidgetContainer(props: DashboardWidget) {
|
|
7
|
-
const config = useDashboardConfig()
|
|
8
|
-
const layout = useMemo(
|
|
9
|
-
() => ({
|
|
10
|
-
...(props.layout || {}),
|
|
11
|
-
...(config.layout || {}),
|
|
12
|
-
}),
|
|
13
|
-
[props.layout, config.layout],
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Card shadow={1} data-width={layout.width} data-height={layout.height}>
|
|
18
|
-
{createElement(props.component, {})}
|
|
19
|
-
</Card>
|
|
20
|
-
)
|
|
21
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export * from './types'
|
|
2
|
-
export * from './components/DashboardWidgetContainer'
|
|
3
|
-
export * from './widgets/projectInfo'
|
|
4
|
-
export * from './widgets/projectUsers'
|
|
5
|
-
export * from './widgets/sanityTutorials'
|
|
6
|
-
|
|
7
|
-
export {type DashboardPluginConfig, dashboardTool} from './plugin'
|
package/src/plugin.tsx
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React, {ComponentType, CSSProperties} from 'react'
|
|
2
|
-
import {Dashboard} from './containers/Dashboard'
|
|
3
|
-
import {definePlugin} from 'sanity'
|
|
4
|
-
import {DashboardConfig, DashboardWidget, LayoutConfig} from './types'
|
|
5
|
-
|
|
6
|
-
const strokeStyle: CSSProperties = {
|
|
7
|
-
stroke: 'currentColor',
|
|
8
|
-
strokeWidth: 1.2,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const DashboardIcon = () => (
|
|
12
|
-
<svg
|
|
13
|
-
data-sanity-icon
|
|
14
|
-
viewBox="0 0 25 25"
|
|
15
|
-
fill="none"
|
|
16
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
-
preserveAspectRatio="xMidYMid"
|
|
18
|
-
width="1em"
|
|
19
|
-
height="1em"
|
|
20
|
-
>
|
|
21
|
-
<path d="M19.5 19.5H5.5V5.5H19.5V19.5Z" style={strokeStyle} />
|
|
22
|
-
<path d="M5.5 12.5H19.5" style={strokeStyle} />
|
|
23
|
-
<path d="M14.5 19.5V12.5M10.5 12.5V5.5" style={strokeStyle} />
|
|
24
|
-
</svg>
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
export interface DashboardPluginConfig {
|
|
28
|
-
/**
|
|
29
|
-
* Dashboard tool title
|
|
30
|
-
*/
|
|
31
|
-
title?: string
|
|
32
|
-
/**
|
|
33
|
-
* Dashboard tool name (used in url path)
|
|
34
|
-
*/
|
|
35
|
-
name?: string
|
|
36
|
-
/**
|
|
37
|
-
* Dashboard tool icon
|
|
38
|
-
*/
|
|
39
|
-
icon?: ComponentType
|
|
40
|
-
widgets?: DashboardWidget[]
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Will be used for widgets that do not define a layout directly.
|
|
44
|
-
*/
|
|
45
|
-
defaultLayout?: LayoutConfig
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const dashboardTool = definePlugin<DashboardPluginConfig>((config = {}) => {
|
|
49
|
-
const pluginConfig: DashboardConfig = {
|
|
50
|
-
layout: config.defaultLayout ?? {},
|
|
51
|
-
widgets: config.widgets ?? [],
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const title = config.title ?? 'Dashboard'
|
|
55
|
-
const name = config.name ?? 'dashboard'
|
|
56
|
-
const icon = config.icon ?? DashboardIcon
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
name: 'dashboard',
|
|
60
|
-
tools: (prev, context) => {
|
|
61
|
-
return [
|
|
62
|
-
...prev,
|
|
63
|
-
{
|
|
64
|
-
title,
|
|
65
|
-
name,
|
|
66
|
-
icon,
|
|
67
|
-
component: () => <Dashboard config={pluginConfig} />,
|
|
68
|
-
},
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
}
|
|
72
|
-
})
|
package/src/types.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {ComponentClass, FunctionComponent} from 'react'
|
|
2
|
-
|
|
3
|
-
export interface DashboardWidget {
|
|
4
|
-
name: string
|
|
5
|
-
type?: '__experimental_group'
|
|
6
|
-
component: FunctionComponent<any> | ComponentClass<any>
|
|
7
|
-
layout?: LayoutConfig
|
|
8
|
-
widgets?: DashboardWidget[]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type LayoutSize = 'auto' | 'small' | 'medium' | 'large' | 'full'
|
|
12
|
-
|
|
13
|
-
export interface LayoutConfig {
|
|
14
|
-
width?: LayoutSize
|
|
15
|
-
height?: LayoutSize
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface DashboardConfig {
|
|
19
|
-
widgets: DashboardWidget[]
|
|
20
|
-
layout?: LayoutConfig
|
|
21
|
-
}
|
package/src/versionedClient.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import React, {useEffect, useMemo, useState} from 'react'
|
|
2
|
-
import {Box, Card, Stack, Heading, Grid, Label, Text, Code, Button} from '@sanity/ui'
|
|
3
|
-
import {useVersionedClient} from '../../versionedClient'
|
|
4
|
-
import {Subscription} from 'rxjs'
|
|
5
|
-
import {WidgetContainer} from '../../containers/WidgetContainer'
|
|
6
|
-
import {DashboardWidgetContainer} from '../../components/DashboardWidgetContainer'
|
|
7
|
-
import {type DashboardWidget} from '../../types'
|
|
8
|
-
import {type App, type ProjectInfoProps, type ProjectData, UserApplication} from './types'
|
|
9
|
-
|
|
10
|
-
function isUrl(url?: string) {
|
|
11
|
-
return url && /^https?:\/\//.test(`${url}`)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getGraphQLUrl(projectId: string, dataset: string) {
|
|
15
|
-
return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getGroqUrl(projectId: string, dataset: string) {
|
|
19
|
-
return `https://${projectId}.api.sanity.io/v1/groq/${dataset}`
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getManageUrl(projectId: string) {
|
|
23
|
-
return `https://manage.sanity.io/projects/${projectId}`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const NO_EXPERIMENTAL: DashboardWidget[] = []
|
|
27
|
-
const NO_DATA: ProjectData[] = []
|
|
28
|
-
|
|
29
|
-
export function ProjectInfo(props: ProjectInfoProps) {
|
|
30
|
-
const {__experimental_before = NO_EXPERIMENTAL, data = NO_DATA} = props
|
|
31
|
-
const [studioApps, setStudioApps] = useState<UserApplication[] | {error: string} | undefined>()
|
|
32
|
-
const [graphQLApi, setGraphQLApi] = useState<string | {error: string} | undefined>()
|
|
33
|
-
const versionedClient = useVersionedClient()
|
|
34
|
-
const {projectId = 'unknown', dataset = 'unknown'} = versionedClient.config()
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const subscriptions: Subscription[] = []
|
|
38
|
-
|
|
39
|
-
subscriptions.push(
|
|
40
|
-
versionedClient.observable
|
|
41
|
-
.request<UserApplication[]>({uri: '/user-applications', tag: 'dashboard.project-info'})
|
|
42
|
-
.subscribe({
|
|
43
|
-
next: (result) => setStudioApps(result.filter((app) => app.type === 'studio')),
|
|
44
|
-
error: (error) => {
|
|
45
|
-
console.error('Error while resolving user applications', error)
|
|
46
|
-
setStudioApps({
|
|
47
|
-
error: 'Something went wrong while resolving user applications. See console.',
|
|
48
|
-
})
|
|
49
|
-
},
|
|
50
|
-
}),
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
// ping assumed graphql endpoint
|
|
54
|
-
subscriptions.push(
|
|
55
|
-
versionedClient.observable
|
|
56
|
-
.request({
|
|
57
|
-
method: 'HEAD',
|
|
58
|
-
uri: `/graphql/${dataset}/default`,
|
|
59
|
-
tag: 'dashboard.project-info.graphql-api',
|
|
60
|
-
})
|
|
61
|
-
.subscribe({
|
|
62
|
-
next: () => setGraphQLApi(getGraphQLUrl(projectId, dataset)),
|
|
63
|
-
error: (error) => {
|
|
64
|
-
if (error.statusCode === 404) {
|
|
65
|
-
setGraphQLApi(undefined)
|
|
66
|
-
} else {
|
|
67
|
-
console.error('Error while looking for graphQLApi', error)
|
|
68
|
-
setGraphQLApi({
|
|
69
|
-
error: 'Something went wrong while looking up graphQLApi. See console.',
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
return () => {
|
|
77
|
-
subscriptions.forEach((s) => s.unsubscribe())
|
|
78
|
-
}
|
|
79
|
-
}, [dataset, projectId, versionedClient, setGraphQLApi])
|
|
80
|
-
|
|
81
|
-
const assembleTableRows = useMemo(() => {
|
|
82
|
-
let result: App[] = [
|
|
83
|
-
{
|
|
84
|
-
title: 'Sanity project',
|
|
85
|
-
rows: [
|
|
86
|
-
{title: 'Project ID', value: projectId},
|
|
87
|
-
{title: 'Dataset', value: dataset},
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
]
|
|
91
|
-
|
|
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
|
-
|
|
102
|
-
if (apps.length > 0) {
|
|
103
|
-
result = result.concat([{title: 'Apps', rows: apps}])
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Handle APIs
|
|
107
|
-
result = result.concat(
|
|
108
|
-
[
|
|
109
|
-
{
|
|
110
|
-
title: 'APIs',
|
|
111
|
-
rows: [
|
|
112
|
-
{title: 'GROQ', value: getGroqUrl(projectId, dataset)},
|
|
113
|
-
{
|
|
114
|
-
title: 'GraphQL',
|
|
115
|
-
value: (typeof graphQLApi === 'object' ? 'Error' : graphQLApi) ?? 'Not deployed',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
data.filter((item) => item.category === 'apis'),
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
// Handle whatever else there might be
|
|
124
|
-
const otherStuff: Record<string, ProjectData[]> = {}
|
|
125
|
-
data.forEach((item) => {
|
|
126
|
-
if (item.category && item.category !== 'apps' && item.category !== 'apis') {
|
|
127
|
-
if (!otherStuff[item.category]) {
|
|
128
|
-
otherStuff[item.category] = []
|
|
129
|
-
}
|
|
130
|
-
otherStuff[item.category].push(item)
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
Object.keys(otherStuff).forEach((category) => {
|
|
134
|
-
result.push({title: category, rows: otherStuff[category]})
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
return result
|
|
138
|
-
}, [graphQLApi, studioApps, projectId, dataset, data])
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<>
|
|
142
|
-
{__experimental_before.map((widgetConfig, idx) => (
|
|
143
|
-
<WidgetContainer key={idx} {...widgetConfig} />
|
|
144
|
-
))}
|
|
145
|
-
<Box height="fill" marginTop={__experimental_before?.length > 0 ? 4 : 0}>
|
|
146
|
-
<DashboardWidgetContainer
|
|
147
|
-
footer={
|
|
148
|
-
<Button
|
|
149
|
-
style={{width: '100%'}}
|
|
150
|
-
paddingX={2}
|
|
151
|
-
paddingY={4}
|
|
152
|
-
mode="bleed"
|
|
153
|
-
tone="primary"
|
|
154
|
-
text="Manage project"
|
|
155
|
-
as="a"
|
|
156
|
-
href={getManageUrl(projectId)}
|
|
157
|
-
/>
|
|
158
|
-
}
|
|
159
|
-
>
|
|
160
|
-
<Card
|
|
161
|
-
paddingY={4}
|
|
162
|
-
radius={2}
|
|
163
|
-
role="table"
|
|
164
|
-
aria-label="Project info"
|
|
165
|
-
aria-describedby="project_info_table"
|
|
166
|
-
>
|
|
167
|
-
<Stack space={4}>
|
|
168
|
-
<Box paddingX={3} as="header">
|
|
169
|
-
<Heading size={1} as="h2" id="project_info_table">
|
|
170
|
-
Project info
|
|
171
|
-
</Heading>
|
|
172
|
-
</Box>
|
|
173
|
-
{assembleTableRows.map((item) => {
|
|
174
|
-
if (!item || !item.rows) {
|
|
175
|
-
return null
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<Stack key={item.title} space={3}>
|
|
180
|
-
<Card borderBottom padding={3}>
|
|
181
|
-
<Label size={0} muted role="columnheader">
|
|
182
|
-
{item.title}
|
|
183
|
-
</Label>
|
|
184
|
-
</Card>
|
|
185
|
-
<Stack space={4} paddingX={3} role="rowgroup">
|
|
186
|
-
{item.rows.map((row) => {
|
|
187
|
-
return (
|
|
188
|
-
<Grid key={`${row.value}-${row.title}`} columns={2} role="row">
|
|
189
|
-
<Text weight="medium" role="rowheader">
|
|
190
|
-
{row.title}
|
|
191
|
-
</Text>
|
|
192
|
-
{typeof row.value === 'object' && (
|
|
193
|
-
<Text size={1}>{row.value?.error}</Text>
|
|
194
|
-
)}
|
|
195
|
-
{typeof row.value === 'string' && (
|
|
196
|
-
<>
|
|
197
|
-
{isUrl(row.value) ? (
|
|
198
|
-
<Text size={1} role="cell" style={{wordBreak: 'break-word'}}>
|
|
199
|
-
<a href={row.value}>{row.value}</a>
|
|
200
|
-
</Text>
|
|
201
|
-
) : (
|
|
202
|
-
<Code size={1} role="cell" style={{wordBreak: 'break-word'}}>
|
|
203
|
-
{row.value}
|
|
204
|
-
</Code>
|
|
205
|
-
)}
|
|
206
|
-
</>
|
|
207
|
-
)}
|
|
208
|
-
</Grid>
|
|
209
|
-
)
|
|
210
|
-
})}
|
|
211
|
-
</Stack>
|
|
212
|
-
</Stack>
|
|
213
|
-
)
|
|
214
|
-
})}
|
|
215
|
-
</Stack>
|
|
216
|
-
</Card>
|
|
217
|
-
</DashboardWidgetContainer>
|
|
218
|
-
</Box>
|
|
219
|
-
</>
|
|
220
|
-
)
|
|
221
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import {ProjectInfo} from './ProjectInfo'
|
|
2
|
-
import {type LayoutConfig, type DashboardWidget} from '../../types'
|
|
3
|
-
|
|
4
|
-
export function projectInfoWidget(config?: {layout?: LayoutConfig}): DashboardWidget {
|
|
5
|
-
return {
|
|
6
|
-
name: 'project-info',
|
|
7
|
-
component: ProjectInfo,
|
|
8
|
-
layout: config?.layout ?? {width: 'medium'},
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
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
|
-
}
|