Skip to content

Commit

Permalink
feat: mutation mode (#27)
Browse files Browse the repository at this point in the history
* wip

* fix: ToMaybeRefs types

* wip

* chore: add toast

* wip

* chore(stroybook-vue): ignore show exception with AbortDefer

* wip

* feat: form add redirect when successed

* feat: add crud pages to form.stories

* feat: function navigateTo
  • Loading branch information
aa900031 authored Sep 17, 2024
1 parent d377552 commit 88bd0d4
Show file tree
Hide file tree
Showing 42 changed files with 2,224 additions and 708 deletions.
142 changes: 114 additions & 28 deletions packages/core/src/controller/form.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import type { SetOptional, SetRequired } from 'type-fest'
import type { BaseRecord, CreateResult, GetOneResult, Meta, UpdateResult } from '../query'
import { getFetcherName } from '../query'
import { MutationMode, getFetcherName } from '../query'
import type { MutateFn as CreateMutateFn, MutationOptionsFromProps as CreateMutationOptionsFromProps, MutationProps as CreateMutationProps } from '../query/create'
import type { MutateFn as UpdateMutateFn, MutationOptionsFromProps as UpdateMutationOptionsFromProps, MutationProps as UpdateMutationProps } from '../query/update'
import type { QueryOptionsFromProps } from '../query/get-one'
import type { ResolvedResource, ResourceActionForForm } from '../resource'
import { getResourceIdentifier } from '../resource'
import type { ResolvedResource, ResourceActionForForm, ResourceActionTypeValues } from '../resource'
import { ResourceActionType, getResourceIdentifier } from '../resource'
import type { NavigateToFn, NavigateToProps } from '../router'

export type RedirectTo<
TResultData,
> =
| ResourceActionTypeValues
| NavigateToProps
| (
(data: TResultData) =>
| ResourceActionTypeValues
| NavigateToProps
)

export type CreateProps<
TMutationParams,
TMutationData extends BaseRecord,
TMutationError,
> = SetOptional<
& Omit<
CreateMutationProps<TMutationData, TMutationError, TMutationParams>,
| 'params'
| 'meta'
>
CreateMutationProps<TMutationData, TMutationError, TMutationParams>,
| 'params'
| 'meta'
>
& {
action: 'create'
redirect?: RedirectTo<CreateResult<TMutationData>>
mutationMeta?: Meta
mutationOptions?: CreateMutationOptionsFromProps<TMutationData, TMutationError, TMutationParams>
},
Expand All @@ -35,12 +48,13 @@ export type UpdateProps<
TMutationError,
> = SetOptional<
& Omit<
UpdateMutationProps<TMutationData, TMutationError, TMutationParams>,
| 'params'
| 'meta'
>
UpdateMutationProps<TMutationData, TMutationError, TMutationParams>,
| 'params'
| 'meta'
>
& {
action: 'edit'
redirect?: RedirectTo<UpdateResult<TMutationData>>
queryMeta?: Meta
queryOptions?: QueryOptionsFromProps<TQueryData, TQueryError, TQueryResultData>
mutationMeta?: Meta
Expand Down Expand Up @@ -135,8 +149,8 @@ export function resolveProps<
action,
resource: resourceName,
id: ('id' in props ? props.id : undefined)
?? (resource && resource.action === 'edit' ? resource.id : undefined)
?? '', // TODO: maybe use undeined?
?? (resource && resource.action === 'edit' ? resource.id : undefined)
?? '', // TODO: maybe use undeined?
fetcherName,
}
}
Expand Down Expand Up @@ -209,6 +223,7 @@ export interface CreateSaveFnParams<
TMutationData extends BaseRecord,
TMutationError,
> {
navigateTo: NavigateToFn
getProps: () => ResolvedProps<TQueryData, TMutationParams, TQueryError, TQueryResultData, TMutationData, TMutationError>
mutateFnForCreate: CreateMutateFn<TMutationData, TMutationError, TMutationParams>
mutateFnForUpdate: UpdateMutateFn<TMutationData, TMutationError, TMutationParams>
Expand All @@ -223,34 +238,62 @@ export function createSaveFn<
TMutationError,
>(
{
navigateTo,
getProps,
mutateFnForCreate,
mutateFnForUpdate,
}: CreateSaveFnParams<TQueryData, TMutationParams, TQueryError, TQueryResultData, TMutationData, TMutationError>,
): SaveFn<TMutationParams, TMutationData> {
return async function saveFn(params) {
const {
action,
...rest
} = getProps()
const props = getProps()

switch (action) {
case 'create':
// TODO: redirect
switch (props.action) {
case 'create': {
const resolvedProps: ResolvedCreateProps<TMutationParams, TMutationData, TMutationError> = props
return mutateFnForCreate({
...rest as ResolvedCreateProps<TMutationParams, TMutationData, TMutationError>,
...resolvedProps,
params,
meta: rest.mutationMeta,
meta: props.mutationMeta,
}, {
onSuccess: (data) => {
dispatchRedirect(
navigateTo,
resolvedProps,
data,
)
},
})
case 'edit':
// TODO: mutationMode
// TODO: redirect
}
case 'edit': {
const resolvedProps: ResolvedUpdateProps<TQueryData, TMutationParams, TQueryError, TQueryResultData, TMutationData, TMutationError> = props
const isPessimistic = resolvedProps.mutationMode == null || resolvedProps.mutationMode === MutationMode.Pessimistic

if (!isPessimistic) {
setTimeout(() => {
dispatchRedirect(
navigateTo,
resolvedProps,
{ data: { id: props.id, ...params as any } },
)
}, 0)
}

return mutateFnForUpdate({
...rest as ResolvedUpdateProps<TQueryData, TMutationParams, TQueryError, TQueryResultData, TMutationData, TMutationError>,
...resolvedProps,
params,
meta: rest.mutationMeta,
meta: props.mutationMeta,
}, {
onSuccess: (data) => {
if (isPessimistic) {
dispatchRedirect(
navigateTo,
resolvedProps,
data,
)
}
},
})

}
default:
throw new Error('No')
}
Expand All @@ -277,3 +320,46 @@ export function getRecord<

return queryResultData?.data
}

function dispatchRedirect<
TMutationData extends BaseRecord,
TMutationParams,
>(
navigateTo: NavigateToFn,
props: ResolvedProps<any, TMutationParams, any, any, TMutationData, any>,
data: UpdateResult<TMutationData> | CreateResult<TMutationData>,
) {
const resolvedRedirectTo = (
typeof props.redirect === 'function'
? props.redirect(data)
: props.redirect
) ?? (
props.action === 'create'
? ResourceActionType.List
: props.action === 'edit'
? ResourceActionType.Show
: false
)

const resolvedNavigateProps: NavigateToProps = (() => {
switch (resolvedRedirectTo) {
case ResourceActionType.List:
case ResourceActionType.Create:
return {
resource: props.resource,
action: resolvedRedirectTo,
}
case ResourceActionType.Edit:
case ResourceActionType.Show:
return {
resource: props.resource,
action: resolvedRedirectTo,
id: data.data.id!,
}
default:
return resolvedRedirectTo
}
})()

return navigateTo(resolvedNavigateProps)
}
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './query'
export * from './resource'
export * from './router'
export * from './controller'
export { AbortDefer } from './utils/defer'
21 changes: 15 additions & 6 deletions packages/core/src/notification/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,29 @@ import type { ValueOf } from 'type-fest'
export const NotificationType = {
Success: 'success',
Error: 'error',
// Progress: 'progress'
Progress: 'progress',
} as const

export type NotificationTypeValues = ValueOf<typeof NotificationType>

export interface OpenNotificationParams {
type: NotificationTypeValues
export interface NormalNotificationParams {
type: typeof NotificationType.Success | typeof NotificationType.Error
message: string
key?: string
description?: string
cancelMutation?: () => void
undoableTimeout?: number
key?: string
}

export interface ProgressNotificationParams extends Omit<NormalNotificationParams, 'type'> {
type: typeof NotificationType.Progress
timeout: number
onFinish: () => void
onCancel: () => void
}

export type OpenNotificationParams =
| NormalNotificationParams
| ProgressNotificationParams

export type NotificationOpenFn = (params: OpenNotificationParams) => void

export type NotificationCloseFn = (key: string) => void
Expand Down
Loading

0 comments on commit 88bd0d4

Please sign in to comment.