Skip to content

Commit

Permalink
fix(releases): improvements to archive release detail (#8597)
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanl17 authored Feb 13, 2025
1 parent 0324381 commit 73d6b0e
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {type Mock, type Mocked} from 'vitest'

import {useProjectSubscriptions} from '../useProjectSubscriptions'

export const useProjectSubscriptionsMockReturn: Mocked<ReturnType<typeof useProjectSubscriptions>> =
{
error: null,
isLoading: false,
projectSubscriptions: {
id: 'sub_123',
projectId: 'proj_456',
productType: 'premium',
productId: 'prod_789',
customerId: 'cust_101',
planId: 'plan_202',
previousSubscriptionId: null,
status: 'active',
startedAt: '2024-02-01T00:00:00Z',
startedBy: 'user_303',
endedAt: null,
endedBy: null,
trialUntil: '2024-02-15T00:00:00Z',
plan: {
id: 'plan_202',
planTypeId: 'type_404',
variantId: null,
productType: 'premium',
variantOfPlanId: null,
name: 'Premium Plan',
variantName: null,
price: 49.99,
trialDays: 14,
createdAt: '2024-01-01T00:00:00Z',
supersededAt: null,
default: false,
public: true,
orderable: true,
isBasePlan: true,
pricingModel: 'flat-rate',
resources: {},
featureTypes: {},
},
resources: {},
featureTypes: {
retention: {
features: [
{
attributes: {
maxRetentionDays: 123,
},
id: '',
variantId: null,
name: '',
price: 0,
included: false,
custom: false,
startedAt: null,
startedBy: null,
endedAt: null,
endedBy: null,
},
],
id: '',
name: '',
singular: false,
},
},
},
}

export const mockUseProjectSubscriptions = useProjectSubscriptions as Mock<
typeof useProjectSubscriptions
>
156 changes: 156 additions & 0 deletions packages/sanity/src/core/hooks/useProjectSubscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import {type SanityClient} from '@sanity/client'
import {useMemo} from 'react'
import {useObservable} from 'react-rx'
import {type Observable, of} from 'rxjs'
import {catchError, map, shareReplay, startWith} from 'rxjs/operators'

import {useSource} from '../studio'
import {DEFAULT_STUDIO_CLIENT_OPTIONS} from '../studioClient'
import {useClient} from './useClient'

type FeatureAttributes = Record<string, string | number | boolean | null>

type Feature = {
id: string
variantId: string | null
name: string
price: number
included: boolean
attributes: FeatureAttributes
custom: boolean
startedAt: string | null
startedBy: string | null
endedAt: string | null
endedBy: string | null
}

type FeatureType = {
id: string
name: string
singular: boolean
features: Feature[]
}

type Resource = {
id: string
name: string
unit: string
type: string
quota: number | null
custom: boolean
overageAllowed: boolean
overageChunkSize: number
overageChunkPrice: number
maxOverageQuota: number | null
}

type Plan = {
id: string
planTypeId: string
variantId: string | null
productType: string
variantOfPlanId: string | null
name: string
variantName: string | null
price: number
trialDays: number
createdAt: string
supersededAt: string | null
default: boolean
public: boolean
orderable: boolean
isBasePlan: boolean
pricingModel: string
resources: Record<string, Resource>
featureTypes: Record<string, FeatureType>
}

export type ProjectSubscriptionsResponse = {
id: string
projectId: string
productType: string
productId: string
customerId: string | null
planId: string
previousSubscriptionId: string | null
status: string
startedAt: string
startedBy: string | null
endedAt: string | null
endedBy: string | null
trialUntil: string | null
plan: Plan
resources: Record<string, Resource>
featureTypes: Record<string, FeatureType>
}

type ProjectSubscriptions = {
error: Error | null
projectSubscriptions: ProjectSubscriptionsResponse | null
isLoading: boolean
}

const INITIAL_LOADING_STATE: ProjectSubscriptions = {
error: null,
projectSubscriptions: null,
isLoading: true,
}

/**
* fetches subscriptions for this project
*/
function fetchProjectSubscriptions({
versionedClient,
}: {
versionedClient: SanityClient
}): Observable<ProjectSubscriptionsResponse> {
return versionedClient.observable.request<ProjectSubscriptionsResponse>({
uri: `/subscriptions/project/${versionedClient.config().projectId}`,
tag: 'project-subscriptions',
})
}

const cachedProjectSubscriptionsRequest = new Map<
string,
Observable<ProjectSubscriptionsResponse>
>()

/** @internal */
export function useProjectSubscriptions(): ProjectSubscriptions {
const versionedClient = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)
const {projectId} = useSource()

if (!cachedProjectSubscriptionsRequest.get(projectId)) {
const projectSubscriptions = fetchProjectSubscriptions({versionedClient}).pipe(shareReplay())
cachedProjectSubscriptionsRequest.set(projectId, projectSubscriptions)
}

const projectSubscriptionsObservable = useMemo(() => {
const projectSubscriptions$ = cachedProjectSubscriptionsRequest.get(projectId)

if (!projectSubscriptions$)
return of<ProjectSubscriptions>({
isLoading: false,
error: null,
projectSubscriptions: null,
})

return projectSubscriptions$.pipe(
map((cachedSubscriptions) => ({
isLoading: false,
projectSubscriptions: cachedSubscriptions,
error: null,
})),
startWith(INITIAL_LOADING_STATE),
catchError((error: Error) =>
of<ProjectSubscriptions>({
isLoading: false,
projectSubscriptions: null,
error,
}),
),
)
}, [projectId])

return useObservable(projectSubscriptionsObservable, INITIAL_LOADING_STATE)
}
2 changes: 1 addition & 1 deletion packages/sanity/src/core/i18n/bundles/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1232,7 +1232,7 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
/** Title for tooltip to explain release time */
'release.dialog.tooltip.title': 'Approximate time of release',
/** The placeholder text when the release doesn't have a description */
'release.form.placeholer-describe-release': 'Describe the release…',
'release.form.placeholder-describe-release': 'Describe the release…',
/** Tooltip for button to hide release visibility */
'release.layer.hide': 'Hide release',
/** Label for draft perspective in navbar */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export function TitleDescriptionForm({
release: EditableReleaseDocument
onChange: (changedValue: EditableReleaseDocument) => void
}): React.JSX.Element {
const isReleaseOpen = release.state !== 'archived' && release.state !== 'published'
const descriptionRef = useRef<HTMLTextAreaElement | null>(null)

const [scrollHeight, setScrollHeight] = useState(46)
Expand Down Expand Up @@ -145,26 +146,31 @@ export function TitleDescriptionForm({
[onChange, release.metadata, value],
)

const shouldShowDescription = isReleaseOpen || value.metadata.description

return (
<Stack space={4}>
<TitleInput
onChange={handleTitleChange}
value={value.metadata.title}
placeholder={t('release.placeholder-untitled-release')}
data-testid="release-form-title"
readOnly={!isReleaseOpen}
/>
<DescriptionTextArea
ref={descriptionRef}
autoFocus={!value}
value={value.metadata.description}
placeholder={t('release.form.placeholer-describe-release')}
onChange={handleDescriptionChange}
style={{
height: `${scrollHeight}px`,
maxHeight: MAX_DESCRIPTION_HEIGHT,
}}
data-testid="release-form-description"
/>
{shouldShowDescription && (
<DescriptionTextArea
ref={descriptionRef}
autoFocus={!value}
value={value.metadata.description}
placeholder={t('release.form.placeholder-describe-release')}
onChange={handleDescriptionChange}
style={{
height: `${scrollHeight}px`,
maxHeight: MAX_DESCRIPTION_HEIGHT,
}}
data-testid="release-form-description"
/>
)}
</Stack>
)
}
9 changes: 9 additions & 0 deletions packages/sanity/src/core/releases/i18n/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ const releasesLocaleStrings = {
/** Label for the button to proceed with archiving a release */
'archive-dialog.confirm-archive-button': 'Yes, archive now',

/** Title for information card on a archived release */
'archive-info.title': 'This release is archived',
/** Description for information card on a published or archived release to description retention effects */
'archive-info.description':
'Your plan supports a {{retentionDays}}-day retention period. After this period this release will be removed.',

/** Title for changes to published documents */
'changes-published-docs.title': 'Changes to published documents',
/** Text for when a release / document was created */
Expand Down Expand Up @@ -203,6 +209,9 @@ const releasesLocaleStrings = {
/** Label for when documents in release have validation errors */
'publish-dialog.validation.error': 'Some documents have validation errors',

/** Title for information card on a published release */
'publish-info.title': 'This release is published',

/** Description for the review changes button in release tool */
'review.description': 'Add documents to this release to review changes',
/** Text for when a document is edited */
Expand Down
Loading

0 comments on commit 73d6b0e

Please sign in to comment.