diff --git a/packages/sanity/src/core/releases/i18n/resources.ts b/packages/sanity/src/core/releases/i18n/resources.ts
index 91082df8d3e..f0680247452 100644
--- a/packages/sanity/src/core/releases/i18n/resources.ts
+++ b/packages/sanity/src/core/releases/i18n/resources.ts
@@ -34,6 +34,14 @@ const releasesLocaleStrings = {
/** Text for when a release / document was created */
'created': 'Created ',
+ /** Text for the releases detail screen when a release was published */
+ 'dashboard.details.published-on': 'Published on {{date}}',
+
+ /** Text for the releases detail screen in the pin release button. */
+ 'dashboard.details.pin-release': 'Pin release',
+
+ /** Activity inspector button text */
+ 'dashboard.details.activity': 'Activity',
/** Warning for deleting a release that it will delete one document version */
'delete.warning_one': 'This will also delete one document version.',
/** Warning for deleting a release that it will delete multiple document version */
@@ -60,6 +68,14 @@ const releasesLocaleStrings = {
/** Title text when error during release update */
'failed-edit-title': 'Failed to save changes',
+ /**The text that will be shown in the footer to indicate the time the release was archived */
+ 'footer.status.archived': 'Archived',
+ /**The text that will be shown in the footer to indicate the time the release was created */
+ 'footer.status.created': 'Created',
+ /**The text that will be shown in the footer to indicate the time the release was created */
+ 'footer.status.edited': 'Edited',
+ /**The text that will be shown in the footer to indicate the time the release was published */
+ 'footer.status.published': 'Published',
/** Label text for the loading state whilst release is being loaded */
'loading-release': 'Loading release',
@@ -118,10 +134,19 @@ const releasesLocaleStrings = {
/** Text for when the release is composed of multiple documents */
'summary.document-count_other': '{{count}} documents',
+ /** add action type that will be shown in the table*/
+ 'table-body.action.add': 'Add',
+ /** Change action type that will be shown in the table*/
+ 'table-body.action.change': 'Change',
+
/** Header for the document table in the release tool - contributors */
'table-header.contributors': 'Contributors',
/** Header for the document table in the release tool - created */
'table-header.created': 'Created',
+ /** Header for the document table in the release tool - type */
+ 'table-header.type': 'Type',
+ /** Header for the document table in the release tool - action */
+ 'table-header.action': 'Action',
/** Header for the document table in the release tool - title */
'table-header.documents': 'Documents',
/** Header for the document table in the release tool - edited */
diff --git a/packages/sanity/src/core/releases/tool/components/BadgeIcon.tsx b/packages/sanity/src/core/releases/tool/components/BadgeIcon.tsx
new file mode 100644
index 00000000000..7145cca65b9
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/components/BadgeIcon.tsx
@@ -0,0 +1,13 @@
+import {type IconComponent} from '@sanity/icons'
+import {type BadgeTone} from '@sanity/ui'
+import {createElement, type CSSProperties} from 'react'
+
+export function BadgeIcon(props: {icon: IconComponent; tone: BadgeTone}) {
+ const {icon, tone} = props
+
+ return createElement(icon, {
+ style: {
+ '--card-icon-color': `var(--card-badge-${tone}-icon-color)`,
+ } as CSSProperties,
+ })
+}
diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseAvatar.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseAvatar.tsx
new file mode 100644
index 00000000000..3256b634819
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/components/ReleaseAvatar.tsx
@@ -0,0 +1,24 @@
+import {DotIcon} from '@sanity/icons'
+import {type BundleDocument} from 'sanity'
+
+import {getReleaseTone} from '../../util/getReleaseTone'
+import {VersionAvatar} from './VersionAvatar'
+
+export function ReleaseAvatar({
+ fontSize,
+ padding,
+ release,
+}: {
+ fontSize?: number
+ padding?: number
+ release: BundleDocument
+}) {
+ return (
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/components/StatusItem.tsx b/packages/sanity/src/core/releases/tool/components/StatusItem.tsx
new file mode 100644
index 00000000000..d715a4a1e53
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/components/StatusItem.tsx
@@ -0,0 +1,23 @@
+import {Box, Card, Flex, Text} from '@sanity/ui'
+import {type ReactNode} from 'react'
+
+export function StatusItem(props: {avatar?: ReactNode; text: ReactNode}) {
+ const {avatar, text} = props
+
+ return (
+
+
+ {avatar && (
+
+ {avatar}
+
+ )}
+
+
+ {text}
+
+
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
index 0d65198931d..7c70227e6c4 100644
--- a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
+++ b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
@@ -1,4 +1,4 @@
-import {Box, Card, type CardProps, Flex, Stack, Text} from '@sanity/ui'
+import {Box, Card, type CardProps, Container, Flex, Stack, Text} from '@sanity/ui'
import {
defaultRangeExtractor,
type Range,
@@ -52,19 +52,17 @@ export interface TableProps {
scrollContainerRef: MutableRefObject
}
-const RowStack = styled(Stack)({
- '& > *:not([first-child])': {
- borderTopLeftRadius: 0,
- borderTopRightRadius: 0,
- marginTop: -1,
- },
-
- '& > *:not([last-child])': {
- borderBottomLeftRadius: 0,
- borderBottomRightRadius: 0,
- },
-})
-
+const CustomCard = styled(Card)`
+ display: flex;
+ max-width: 1200px;
+ margin: 0 auto;
+ ::before {
+ content: '';
+ display: block;
+ border: 1px solid red;
+ position: absolute;
+ }
+`
const ITEM_HEIGHT = 59
/**
@@ -112,6 +110,7 @@ const TableInner = ({
if (!sort) return filteredResult
return [...filteredResult].sort((a, b) => {
+ // TODO: Update this tos support sorting not only by date but also by string
const parseDate = (dateString: unknown) =>
typeof dateString === 'string' ? Date.parse(dateString) : 0
@@ -178,12 +177,8 @@ const TableInner = ({
key={String(get(datum, rowId))}
data-testid="table-row"
as="tr"
- border
- radius={3}
+ borderBottom
display="flex"
- first-child={datum.isFirst ? '' : undefined}
- last-child={datum.isLast ? '' : undefined}
- margin={-1}
style={{
height: `${datum.virtualRow.size}px`,
transform: `translateY(${datum.virtualRow.start - datum.index * datum.virtualRow.size}px)`,
@@ -251,23 +246,25 @@ const TableInner = ({
position: 'relative',
}}
>
-
-
-
- {filteredData.length === 0
- ? emptyContent
- : rowVirtualizer.getVirtualItems().map((virtualRow, index) => {
- const datum = filteredData[virtualRow.index]
- return renderRow({
- ...datum,
- virtualRow,
- index,
- isFirst: virtualRow.index === 0,
- isLast: virtualRow.index === filteredData.length - 1,
- })
- })}
-
-
+
+
+
+
+ {filteredData.length === 0
+ ? emptyContent
+ : rowVirtualizer.getVirtualItems().map((virtualRow, index) => {
+ const datum = filteredData[virtualRow.index]
+ return renderRow({
+ ...datum,
+ virtualRow,
+ index,
+ isFirst: virtualRow.index === 0,
+ isLast: virtualRow.index === filteredData.length - 1,
+ })
+ })}
+
+
+
)
diff --git a/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx b/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx
index ef73a358fec..3a9084d6a34 100644
--- a/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx
+++ b/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx
@@ -57,7 +57,7 @@ const TableHeaderSearch = ({
*/
export const TableHeader = ({headers, searchDisabled}: TableHeaderProps) => {
return (
-
+
{headers.map(({header: Header, width, id, sorting}) => (
+
+
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardActivityPanel.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardActivityPanel.tsx
new file mode 100644
index 00000000000..846f23bf1d4
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardActivityPanel.tsx
@@ -0,0 +1,14 @@
+import {Stack, Text} from '@sanity/ui'
+
+export function ReleaseDashboardActivityPanel() {
+ return (
+
+
+ {'Activity'}
+
+
+ {'🚧 Under construction 🚧'}
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx
new file mode 100644
index 00000000000..52f3b98f49e
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardDetails.tsx
@@ -0,0 +1,77 @@
+import {PinFilledIcon, PinIcon} from '@sanity/icons'
+import {
+ Box,
+ // Custom button with full radius used here
+ // eslint-disable-next-line no-restricted-imports
+ Button,
+ Card,
+ Container,
+ Flex,
+ Stack,
+ Text,
+} from '@sanity/ui'
+import {format} from 'date-fns'
+import {useCallback} from 'react'
+import {type BundleDocument, usePerspective, useTranslation} from 'sanity'
+
+import {releasesLocaleNamespace} from '../../i18n'
+import {getReleaseTone} from '../../util/getReleaseTone'
+import {ReleaseAvatar} from '../components/ReleaseAvatar'
+import {ReleaseDetailsEditor} from './ReleaseDetailsEditor'
+
+export function ReleaseDashboardDetails({release}: {release: BundleDocument}) {
+ const {archived, _id, publishedAt} = release
+ const {t} = useTranslation(releasesLocaleNamespace)
+
+ const {currentGlobalBundle, setPerspective} = usePerspective()
+
+ const handlePinRelease = useCallback(() => {
+ if (_id === currentGlobalBundle._id) {
+ setPerspective('drafts')
+ } else {
+ setPerspective(_id)
+ }
+ }, [_id, currentGlobalBundle._id, setPerspective])
+
+ return (
+
+
+
+
+
+ {
+ publishedAt ? (
+
+
+
+
+ {t('dashboard.details.published-on', {
+ date: format(new Date(publishedAt), `MMM d, yyyy`),
+ })}
+
+
+
+ ) : null
+ // TODO: Add the release time field here
+ //
+ }
+
+
+
+
+
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardFooter.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardFooter.tsx
new file mode 100644
index 00000000000..9187a624371
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardFooter.tsx
@@ -0,0 +1,39 @@
+import {Card, Flex} from '@sanity/ui'
+import {type BundleDocument} from 'sanity'
+
+import {ReleaseMenuButton} from '../components/ReleaseMenuButton/ReleaseMenuButton'
+import {ReleasePublishAllButton} from '../components/ReleasePublishAllButton/ReleasePublishAllButton'
+import {ReleaseStatusItems} from './ReleaseStatusItems'
+import {type DocumentInBundleResult} from './useBundleDocuments'
+
+export function ReleaseDashboardFooter(props: {
+ documents: DocumentInBundleResult[]
+ release: BundleDocument
+ isBundleDeleted: boolean
+}) {
+ const {documents, release, isBundleDeleted} = props
+
+ return (
+
+
+
+
+
+
+
+
+
+ {/* TODO: Replace this with the real actions. */}
+ {!isBundleDeleted && !release.publishedAt && (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardHeader.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardHeader.tsx
new file mode 100644
index 00000000000..94a7c55a8de
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDashboardHeader.tsx
@@ -0,0 +1,81 @@
+import {ChevronRightIcon, RestoreIcon} from '@sanity/icons'
+import {
+ Box,
+ Breadcrumbs,
+ // eslint-disable-next-line no-restricted-imports
+ Button, // Custom button with a different textWeight, consider adding textWeight to the shared
+ Flex,
+ Text,
+} from '@sanity/ui'
+import {type Dispatch, type SetStateAction, useCallback} from 'react'
+import {type BundleDocument, useTranslation} from 'sanity'
+import {useRouter} from 'sanity/router'
+
+import {releasesLocaleNamespace} from '../../i18n'
+import {type ReleaseInspector} from './ReleaseDetail'
+
+export function ReleaseDashboardHeader(props: {
+ inspector: ReleaseInspector | undefined
+ release: BundleDocument
+ setInspector: Dispatch>
+}) {
+ const {inspector, release, setInspector} = props
+ const {title} = release
+ const {t} = useTranslation(releasesLocaleNamespace)
+ const router = useRouter()
+ const handleNavigateToReleasesList = useCallback(() => {
+ router.navigate({})
+ }, [router])
+
+ const handleActivityClick = useCallback(() => {
+ setInspector((prev) => (prev === 'activity' ? undefined : 'activity'))
+ }, [setInspector])
+
+ const handleTitleClick = useCallback(() => {
+ // TODO: Focus on the title when clicked once it's editable
+ }, [])
+
+ return (
+
+
+
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
index 64568bcebdb..53eccc8b4eb 100644
--- a/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
@@ -1,58 +1,51 @@
-import {ArrowLeftIcon} from '@sanity/icons'
-import {Box, Card, Container, Flex, Heading, Stack, Text} from '@sanity/ui'
-import {useCallback, useEffect, useMemo, useRef} from 'react'
+import {Card, Container, Flex, Heading, Stack} from '@sanity/ui'
+import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {LoadingBlock} from 'sanity'
import {type RouterContextValue, useRouter} from 'sanity/router'
-import {Button} from '../../../../ui-components'
import {Translate, useTranslation} from '../../../i18n'
-import {useBundles} from '../../../store/bundles'
+import {type BundleDocument, useBundles} from '../../../store/bundles'
import {releasesLocaleNamespace} from '../../i18n'
import {type ReleasesRouterState} from '../../types/router'
-import {ReleaseMenuButton} from '../components/ReleaseMenuButton/ReleaseMenuButton'
-import {ReleasePublishAllButton} from '../components/ReleasePublishAllButton/ReleasePublishAllButton'
import {useReleaseHistory} from './documentTable/useReleaseHistory'
+import {ReleaseDashboardActivityPanel} from './ReleaseDashboardActivityPanel'
+import {ReleaseDashboardDetails} from './ReleaseDashboardDetails'
+import {ReleaseDashboardFooter} from './ReleaseDashboardFooter'
+import {ReleaseDashboardHeader} from './ReleaseDashboardHeader'
import {ReleaseReview} from './ReleaseReview'
import {ReleaseSummary} from './ReleaseSummary'
import {useBundleDocuments} from './useBundleDocuments'
+export type ReleaseInspector = 'activity'
+
const SUPPORTED_SCREENS = ['summary', 'review'] as const
-type Screen = (typeof SUPPORTED_SCREENS)[number]
-
-const getActiveScreen = (router: RouterContextValue): Screen => {
- const activeScreen = Object.fromEntries(router.state._searchParams || []).screen as Screen
- if (
- typeof activeScreen !== 'string' ||
- !activeScreen ||
- !SUPPORTED_SCREENS.includes(activeScreen)
- ) {
+export type ReleaseView = (typeof SUPPORTED_SCREENS)[number]
+
+const getActiveView = (router: RouterContextValue): ReleaseView => {
+ const activeView = Object.fromEntries(router.state._searchParams || []).screen as ReleaseView
+ if (typeof activeView !== 'string' || !activeView || !SUPPORTED_SCREENS.includes(activeView)) {
return 'summary'
}
- return activeScreen
+ return activeView
}
export const ReleaseDetail = () => {
const router = useRouter()
-
- const activeScreen = getActiveScreen(router)
+ const [inspector, setInspector] = useState(undefined)
+ const {t} = useTranslation(releasesLocaleNamespace)
+ const activeView = getActiveView(router)
const {releaseId: releaseIdRaw}: ReleasesRouterState = router.state
const releaseId = decodeURIComponent(releaseIdRaw || '')
const {data, loading, deletedBundles} = useBundles()
- const deletedBundle = deletedBundles[releaseId]
+ const deletedBundle = deletedBundles[releaseId] as BundleDocument | undefined
const {loading: documentsLoading, results} = useBundleDocuments(releaseId)
const documentIds = results.map((result) => result.document?._id)
const history = useReleaseHistory(documentIds, releaseId)
-
const bundle = data?.find((storeBundle) => storeBundle._id === releaseId)
- const bundleHasDocuments = !!results.length
- const showPublishButton = loading || !bundle?.publishedAt
- const isPublishButtonDisabled = loading || !bundle || !bundleHasDocuments
-
- const {t} = useTranslation(releasesLocaleNamespace)
const navigateToReview = useCallback(() => {
router.navigate({
@@ -71,95 +64,10 @@ export const ReleaseDetail = () => {
// review screen will not be available once published
// so redirect to summary screen
useEffect(() => {
- if (activeScreen === 'review' && bundle?.publishedAt) {
+ if (activeView === 'review' && bundle?.publishedAt) {
navigateToSummary()
}
- }, [activeScreen, bundle?.publishedAt, navigateToSummary])
-
- const header = useMemo(() => {
- const headerBundle = bundle || deletedBundle
- const isBundleDeleted = !!deletedBundle
-
- return (
-
-
-
-
- router.navigate({})}
- data-testid="back-to-releases-button"
- icon={ArrowLeftIcon}
- mode="bleed"
- tooltipProps={{content: 'Back to releases'}}
- />
-
-
- {headerBundle?.title}
-
-
-
-
-
-
- {/* StudioButton supports tooltip when button is disabled */}
- {!headerBundle?.publishedAt && (
-
- )}
-
-
-
-
- {!isBundleDeleted && showPublishButton && bundle && (
-
- )}
-
-
-
-
- )
- }, [
- activeScreen,
- bundle,
- bundleHasDocuments,
- deletedBundle,
- isPublishButtonDisabled,
- navigateToReview,
- navigateToSummary,
- results,
- router,
- showPublishButton,
- t,
- ])
+ }, [activeView, bundle?.publishedAt, navigateToSummary])
const scrollContainerRef = useRef(null)
@@ -177,20 +85,23 @@ export const ReleaseDetail = () => {
)
}
+ if (documentsLoading) {
+ return
+ }
if (!bundle) return null
- if (activeScreen === 'summary') {
+ if (activeView === 'summary') {
return (
)
}
- if (activeScreen === 'review') {
+ if (activeView === 'review') {
+ // This screen needs to be confirmed, is not part of the prototype yet, maybe it could be removed...
return (
{
)
}
return null
- }, [
- activeScreen,
- bundle,
- deletedBundle,
- history.collaborators,
- history.documentsHistory,
- results,
- t,
- ])
+ }, [activeView, bundle, deletedBundle, documentsLoading, history.documentsHistory, results, t])
if (loading) {
return (
@@ -217,26 +120,53 @@ export const ReleaseDetail = () => {
)
}
- if (!bundle && !deletedBundle) {
+ const bundleInDetail = (bundle || deletedBundle) as BundleDocument
+ if (bundleInDetail) {
return (
-
-
-
- {t('not-found', {releaseId})}
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {detailContent}
+
+
+
+
+
+ {inspector === 'activity' && (
+ <>
+
+
+
+
+ >
+ )}
+
+
)
}
return (
-
- {header}
-
-
- {documentsLoading ? : detailContent}
-
-
-
+
+
+
+ {t('not-found', {releaseId})}
+
+
+
)
}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDetailsEditor.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDetailsEditor.tsx
new file mode 100644
index 00000000000..4dfccf74be6
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDetailsEditor.tsx
@@ -0,0 +1,26 @@
+import {Box, Heading, Text} from '@sanity/ui'
+import {type BundleDocument} from 'sanity'
+
+// TODO: This is not a working example, it's just a placeholder. A proper editor should be implemented.
+export function ReleaseDetailsEditor({
+ description,
+ title,
+}: {
+ description: BundleDocument['description']
+ title: BundleDocument['title']
+}) {
+ return (
+ <>
+
+
+ {title}
+
+
+
+
+ {description || 'Describe the release...'}
+
+
+ >
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseStatusItems.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseStatusItems.tsx
new file mode 100644
index 00000000000..906f703fccb
--- /dev/null
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseStatusItems.tsx
@@ -0,0 +1,72 @@
+import {Flex} from '@sanity/ui'
+import {type BundleDocument, RelativeTime, UserAvatar, useTranslation} from 'sanity'
+
+import {releasesLocaleNamespace} from '../../i18n'
+import {StatusItem} from '../components/StatusItem'
+
+interface LastEdit {
+ author: string
+ date: string
+}
+
+function getLastEdit(): LastEdit | null {
+ /* TODO: Hold until release activity is ready, we will need to use that data to show the last edit done to the release. */
+ return null
+}
+
+export function ReleaseStatusItems({release}: {release: BundleDocument}) {
+ const {t} = useTranslation(releasesLocaleNamespace)
+
+ const lastEdit = getLastEdit()
+ return (
+
+ {/* Created */}
+ {!release.archivedAt && !release.publishedAt && !lastEdit && (
+ }
+ text={
+ <>
+ {t('footer.status.created')}
+ >
+ }
+ />
+ )}
+
+ {/* Edited */}
+ {lastEdit && !release.publishedAt && !release.archived && (
+ : null}
+ text={
+ <>
+ {t('footer.status.edited')}
+ >
+ }
+ />
+ )}
+
+ {/* Published */}
+ {release.publishedAt && (
+ : null}
+ text={
+ <>
+ {t('footer.status.published')}
+ >
+ }
+ />
+ )}
+
+ {/* Archived */}
+ {release.archived && release.archivedAt && (
+ : null}
+ text={
+ <>
+ {t('footer.status.archived')} `
+ >
+ }
+ />
+ )}
+
+ )
+}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
index 06ededa46ee..80ae93081fd 100644
--- a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
@@ -1,18 +1,8 @@
-import {DocumentsIcon} from '@sanity/icons'
-import {AvatarStack, Box, Flex, Heading, Stack, Text, useToast} from '@sanity/ui'
-import {type RefObject, useCallback, useEffect, useMemo, useState} from 'react'
+import {type RefObject, useCallback, useMemo} from 'react'
-import {RelativeTime} from '../../../components/RelativeTime'
-import {UserAvatar} from '../../../components/userAvatar/UserAvatar'
-import {Translate, useTranslation} from '../../../i18n'
+import {useTranslation} from '../../../i18n'
import {type BundleDocument} from '../../../store/bundles/types'
-import {useBundleOperations} from '../../../store/bundles/useBundleOperations'
-import {
- ReleaseIconEditorPicker,
- type ReleaseIconEditorPickerValue,
-} from '../../components/dialog/ReleaseIconEditorPicker'
import {releasesLocaleNamespace} from '../../i18n'
-import {Chip} from '../components/Chip'
import {Table, type TableProps} from '../components/Table/Table'
import {DocumentActions} from './documentTable/DocumentActions'
import {getDocumentTableColumnDefs} from './documentTable/DocumentTableColumnDefs'
@@ -21,49 +11,25 @@ import {type DocumentInBundleResult} from './useBundleDocuments'
export type DocumentWithHistory = DocumentInBundleResult & {
history: DocumentHistory | undefined
+ // TODO: Get this value from the document, it can be calculated by checking if there is a corresponding document with no version attached
+ isAdded?: boolean
}
export type BundleDocumentRow = DocumentWithHistory
export interface ReleaseSummaryProps {
documents: DocumentInBundleResult[]
documentsHistory: Record
- collaborators: string[]
scrollContainerRef: RefObject
release: BundleDocument
}
-const setIconHue = ({hue, icon}: {hue: BundleDocument['hue']; icon: BundleDocument['icon']}) => ({
- hue: hue ?? 'gray',
- icon: icon ?? 'documents',
-})
-
const getRowProps: TableProps['rowProps'] = (datum) =>
datum?.validation?.hasError ? {tone: 'critical'} : {}
export function ReleaseSummary(props: ReleaseSummaryProps) {
- const {documents, documentsHistory, release, collaborators, scrollContainerRef} = props
- const {hue, icon} = release
+ const {documents, documentsHistory, release, scrollContainerRef} = props
const {t} = useTranslation(releasesLocaleNamespace)
- const {updateBundle} = useBundleOperations()
-
- const [iconValue, setIconValue] = useState(setIconHue({hue, icon}))
- const toast = useToast()
- const handleIconValueChange = useCallback(
- async (value: {hue: BundleDocument['hue']; icon: BundleDocument['icon']}) => {
- setIconValue(value)
- try {
- await updateBundle({...value, _id: release._id})
- } catch (e) {
- toast.push({
- closable: true,
- status: 'error',
- title: t('failed-edit-title'),
- })
- }
- },
- [release._id, t, toast, updateBundle],
- )
const aggregatedData = useMemo(
() =>
@@ -88,8 +54,6 @@ export function ReleaseSummary(props: ReleaseSummaryProps) {
() => getDocumentTableColumnDefs(release._id, t),
[release._id, t],
)
- // update hue and icon when release changes
- useEffect(() => setIconValue(setIconHue({hue, icon})), [hue, icon])
const filterRows = useCallback(
(data: DocumentWithHistory[], searchTerm: string) =>
@@ -103,90 +67,6 @@ export function ReleaseSummary(props: ReleaseSummaryProps) {
return (
<>
-
-
-
-
-
-
- {release.title}
-
-
- {release.description && (
-
- {release.description}
-
- )}
-
-
-
- {t('summary.document-count', {count: documents.length})}>}
- icon={
-
-
-
- }
- />
-
- {/* Created */}
- }
- data-testid="release-created"
- text={
-
- (
-
- ),
- }}
- />
-
- }
- />
-
- {/* Published */}
- {
- : null
- }
- text={
- release.publishedAt ? (
-
-
- release.publishedAt && (
-
- ),
- }}
- />
-
- ) : (
- t('summary.not-published')
- )
- }
- />
- }
-
- {/* Contributors */}
-
- {collaborators?.length > 0 && (
-
- {collaborators?.map((userId) => )}
-
- )}
-
-
-
-
-
data={aggregatedData}
emptyState={t('summary.no-documents')}
diff --git a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
index 6b0abd90b73..ad3ba059263 100644
--- a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
@@ -1,6 +1,6 @@
-import {AvatarStack, Box, Flex, Text} from '@sanity/ui'
+import {Badge, Box, Flex, Text} from '@sanity/ui'
import {memo} from 'react'
-import {type TFunction, UserAvatar} from 'sanity'
+import {type TFunction, UserAvatar, useSchema} from 'sanity'
import {RelativeTime} from '../../../../components/RelativeTime'
import {ReleaseDocumentPreview} from '../../components/ReleaseDocumentPreview'
@@ -31,44 +31,73 @@ const MemoReleaseDocumentPreview = memo(
(prev, next) => prev.item.memoKey === next.item.memoKey && prev.releaseSlug === next.releaseSlug,
)
+const MemoDocumentType = memo(
+ function DocumentType({type}: {type: string}) {
+ const schema = useSchema()
+ const schemaType = schema.get(type)
+ return {schemaType?.title || 'Not found'}
+ },
+ (prev, next) => prev.type === next.type,
+)
+
export const getDocumentTableColumnDefs: (
releaseId: string,
t: TFunction<'releases', undefined>,
) => Column[] = (releaseSlug, t) => [
{
- id: 'search',
- width: null,
+ id: 'action',
+ width: 100,
header: (props) => (
-
+
+
+
),
cell: ({cellProps, datum}) => (
-
-
-
+
+
+ {/* TODO: Determine if the document was added or not, check for the existance of the document in it's non version type. */}
+ {datum.isAdded ? (
+
+ {t('table-body.action.add')}
+
+ ) : (
+
+ {t('table-body.action.change')}
+
+ )}
+
+
),
},
{
- id: 'document._createdAt',
+ id: 'document._type',
+ width: 100,
sorting: true,
- width: 130,
header: (props) => (
-
+
),
- cell: ({cellProps, datum: {document, history}}) => (
-
- {document._createdAt && (
-
- {history?.createdBy && }
-
-
-
-
- )}
+ cell: ({cellProps, datum}) => (
+
+
+
+
),
},
+ {
+ id: 'search',
+ width: null,
+ header: (props) => (
+
+ ),
+ cell: ({cellProps, datum}) => (
+
+
+
+ ),
+ },
{
id: 'document._updatedAt',
sorting: true,
@@ -91,58 +120,4 @@ export const getDocumentTableColumnDefs: (
),
},
- {
- id: 'document._publishedAt',
- sorting: true,
- width: 130,
- header: (props) => (
-
-
-
- ),
- cell: ({cellProps, datum: {document}}) => (
-
- {/* TODO: How to get the publishedAt date from the document, consider history API */}
- {/* {document._publishedAt && (
-
-
-
-
-
-
- )} */}
-
- {!document._publishedAt && (
-
-
-
- )}
-
- ),
- },
- {
- id: 'contributors',
- sorting: false,
- width: 100,
- header: ({headerProps}) => (
-
-
-
- {t('table-header.contributors')}
-
-
-
- ),
- cell: ({datum: document, cellProps}) => (
-
- {document.history?.editors && (
-
- {document.history.editors.map((userId) => (
-
- ))}
-
- )}
-
- ),
- },
]
diff --git a/packages/sanity/src/core/releases/util/getReleaseTone.ts b/packages/sanity/src/core/releases/util/getReleaseTone.ts
new file mode 100644
index 00000000000..9b3dfe9fe4b
--- /dev/null
+++ b/packages/sanity/src/core/releases/util/getReleaseTone.ts
@@ -0,0 +1,21 @@
+import {type BadgeTone} from '@sanity/ui'
+import {type BundleDocument} from 'sanity'
+
+export function getReleaseTone(release: BundleDocument): BadgeTone | undefined {
+ if (release.publishedAt !== undefined) {
+ return 'positive'
+ }
+
+ if (release.archived) {
+ return undefined
+ }
+
+ if (release.releaseType === 'immediately') {
+ return 'critical'
+ }
+
+ if (release.releaseType === 'never') {
+ return 'explore'
+ }
+ return 'prospect'
+}
diff --git a/packages/sanity/src/core/store/bundles/types.ts b/packages/sanity/src/core/store/bundles/types.ts
index 519a4c3073d..ea2941a8308 100644
--- a/packages/sanity/src/core/store/bundles/types.ts
+++ b/packages/sanity/src/core/store/bundles/types.ts
@@ -15,13 +15,17 @@ export interface BundleDocument
extends Pick {
_type: 'release'
title: string
+ archived?: boolean
+ releaseType: 'immediately' | 'never' | 'scheduled'
description?: string
hue: ColorHueKey
icon: IconSymbol
authorId: string
publishedAt?: string
- archivedAt?: string
publishedBy?: string
+
+ archivedAt?: string
+ archivedBy?: string
}
/**