-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(campaign): allow to change application state on list page
- Loading branch information
Showing
9 changed files
with
388 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import * as React from 'react' | ||
import { Select, Modal, message } from 'antd' | ||
|
||
import ErrorMessage from '../../ErrorMessage' | ||
|
||
import withSetState, { ChildProps } from './withSetState' | ||
import { GQLCampaignApplicationState } from '../../../definitions' | ||
|
||
type SetStateState = { | ||
applicationState?: GQLCampaignApplicationState | ||
loading: boolean | ||
error: any | ||
} | ||
|
||
const PLACEHOLDER_KEY = 'placeholder' | ||
|
||
const COMMENT_STATES: { | ||
key: GQLCampaignApplicationState | ||
text: string | ||
disabled?: boolean | ||
}[] = [ | ||
{ key: 'succeeded', text: '已通過' }, | ||
{ key: 'pending', text: '審核中' }, | ||
{ key: 'rejected', text: '已拒絕' }, | ||
] | ||
|
||
const ApplicationStateTag = ({ | ||
state, | ||
}: { | ||
state: GQLCampaignApplicationState | ||
}) => { | ||
const stateMap = { | ||
succeeded: '已通過', | ||
pending: '審核中', | ||
rejected: '已拒絕', | ||
} | ||
return <span>{stateMap[state]}</span> | ||
} | ||
|
||
class SetState extends React.Component<ChildProps, SetStateState> { | ||
state: Readonly<SetStateState> = { | ||
applicationState: this.props.applicationState, | ||
loading: false, | ||
error: null, | ||
} | ||
|
||
private _onSelectCampaignState = ( | ||
value: GQLCampaignApplicationState | typeof PLACEHOLDER_KEY | ||
) => { | ||
if (!value || value === PLACEHOLDER_KEY) { | ||
return | ||
} | ||
|
||
this.setState({ applicationState: value }, () => { | ||
if (this.props.applicationState !== value) { | ||
this.preConfirm() | ||
} | ||
}) | ||
} | ||
|
||
private _onConfirmChange = async () => { | ||
this.setState({ loading: true, error: null }) | ||
|
||
const { mutate, user, campaign, onSuccess } = this.props | ||
const { applicationState } = this.state | ||
|
||
if (!applicationState) { | ||
return | ||
} | ||
|
||
try { | ||
await mutate({ | ||
variables: { | ||
user, | ||
campaign, | ||
state: applicationState, | ||
}, | ||
}) | ||
this.setState({ | ||
applicationState, | ||
loading: false, | ||
error: null, | ||
}) | ||
message.success('修改成功', 1, () => { | ||
if (onSuccess) { | ||
onSuccess() | ||
} | ||
}) | ||
} catch (error) { | ||
this.setState({ loading: false, error }) | ||
} | ||
} | ||
|
||
private preConfirm = () => { | ||
Modal.confirm({ | ||
title: `確認修改申請狀態?`, | ||
content: ( | ||
<div style={{ marginTop: 16 }}> | ||
<span> | ||
修改後,申請狀態將從 | ||
{this.props.applicationState && ( | ||
<ApplicationStateTag state={this.props.applicationState} /> | ||
)} | ||
改為 | ||
{this.state.applicationState && ( | ||
<ApplicationStateTag state={this.state.applicationState} /> | ||
)} | ||
</span> | ||
</div> | ||
), | ||
cancelText: '取消', | ||
okText: '確認', | ||
onOk: () => { | ||
this._onConfirmChange() | ||
}, | ||
onCancel: this.revertChange, | ||
}) | ||
} | ||
|
||
private revertChange = () => { | ||
this.setState({ applicationState: this.props.applicationState }) | ||
} | ||
|
||
public render() { | ||
const { disabled } = this.props | ||
const { applicationState, loading, error } = this.state | ||
|
||
if (error) { | ||
return <ErrorMessage error={error} /> | ||
} | ||
|
||
return ( | ||
<span> | ||
<Select | ||
value={applicationState} | ||
onSelect={this._onSelectCampaignState} | ||
style={{ marginRight: 8, width: 100 }} | ||
loading={loading} | ||
disabled={disabled} | ||
> | ||
<Select.Option key={PLACEHOLDER_KEY} disabled></Select.Option> | ||
{COMMENT_STATES.map(({ key, text, disabled }) => ( | ||
<Select.Option key={key} disabled={disabled}> | ||
{text} | ||
</Select.Option> | ||
))} | ||
</Select> | ||
</span> | ||
) | ||
} | ||
} | ||
|
||
export default withSetState(SetState) |
47 changes: 47 additions & 0 deletions
47
src/components/Campaign/SetApplicationState/withSetState.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { graphql, ChildMutateProps } from 'react-apollo' | ||
import gql from 'graphql-tag' | ||
import { GQLCampaignApplicationState } from '../../../definitions' | ||
|
||
const SET_STATE = gql` | ||
mutation updateCampaignApplicationState( | ||
$user: ID! | ||
$campaign: ID! | ||
$state: CampaignApplicationState! | ||
) { | ||
updateCampaignApplicationState( | ||
input: { user: $user, campaign: $campaign, state: $state } | ||
) { | ||
id | ||
} | ||
} | ||
` | ||
|
||
type Response = { | ||
updateCampaignApplicationState: { | ||
user: string | ||
campaign: string | ||
state: string | ||
} | ||
} | ||
|
||
type InputProps = { | ||
user: string | ||
campaign: string | ||
applicationState?: GQLCampaignApplicationState | ||
disabled?: boolean | ||
onSuccess?: () => void | ||
} | ||
|
||
type Variables = { | ||
user: string | ||
campaign: string | ||
state: GQLCampaignApplicationState | ||
} | ||
|
||
export type ChildProps = ChildMutateProps<InputProps, Response, Variables> | ||
|
||
const withSetState = graphql<InputProps, Response, Variables, ChildProps>( | ||
SET_STATE | ||
) | ||
|
||
export default withSetState |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as React from 'react' | ||
import { Table } from 'antd' | ||
import _compact from 'lodash/compact' | ||
|
||
import UserLink from '../../User/Link' | ||
|
||
import { GQLCampaignApplicationState, UserDigest } from '../../../definitions' | ||
import { onPaginationChange, getCurrentPaginationFromUrl } from '../../../utils' | ||
import { PAGE_SIZE } from '../../../constants' | ||
import SetApplicationState from '../SetApplicationState' | ||
|
||
type Datum = { node: UserDigest; applicationState: GQLCampaignApplicationState } | ||
|
||
type CampaignUserDigestListProps = { | ||
campaignId: string | ||
data: Datum[] | ||
loading?: boolean | ||
pagination?: { | ||
totalCount: number | ||
pageSize?: number | ||
fetchMore?: any | ||
variables?: any | ||
} | ||
} | ||
|
||
class CampaignUserDigestList extends React.Component< | ||
CampaignUserDigestListProps | ||
> { | ||
private _renderNameCell(_: any, record: Datum): React.ReactNode { | ||
return ( | ||
<UserLink | ||
id={record.node.id} | ||
userName={record.node.userName} | ||
displayName={record.node.displayName} | ||
/> | ||
) | ||
} | ||
|
||
public render() { | ||
const { data, loading = false, pagination } = this.props | ||
const currentPagination = getCurrentPaginationFromUrl() | ||
|
||
return ( | ||
<Table<Datum> | ||
bordered | ||
loading={loading} | ||
dataSource={_compact(data)} | ||
scroll={{ x: 1200, y: '70vh' }} | ||
pagination={ | ||
pagination | ||
? { | ||
defaultCurrent: currentPagination && currentPagination.page, | ||
pageSize: pagination.pageSize || PAGE_SIZE, | ||
total: pagination.totalCount, | ||
onChange: (page) => onPaginationChange({ pagination, page }), | ||
showTotal: (t) => `共 ${t} 項`, | ||
position: 'both', | ||
} | ||
: false | ||
} | ||
rowKey={(record) => record.node.id} | ||
> | ||
<Table.Column<Datum> | ||
dataIndex="node.info.id" | ||
title="用戶" | ||
width={200} | ||
render={this._renderNameCell} | ||
/> | ||
|
||
<Table.Column<Datum> | ||
dataIndex="applicationState" | ||
title="申請狀態" | ||
width={100} | ||
render={(_: any, record: Datum) => ( | ||
<SetApplicationState | ||
user={record.node.id} | ||
campaign={this.props.campaignId} | ||
applicationState={record.applicationState} | ||
/> | ||
)} | ||
/> | ||
</Table> | ||
) | ||
} | ||
} | ||
|
||
export default CampaignUserDigestList |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.