react-saasify
Version:
React components for Saasify web clients.
287 lines (253 loc) • 7.34 kB
JavaScript
import React, { Component, Fragment } from 'react'
import PropTypes from 'prop-types'
import copyTextToClipboard from 'copy-text-to-clipboard'
import { Link } from 'react-router-dom'
import { format } from 'date-fns'
import { observer, inject } from 'mobx-react'
import {
Avatar,
Button,
Divider,
Popconfirm,
Table,
Tooltip,
notification
} from 'lib/antd'
import { Section } from '../Section'
import API from 'lib/api'
import theme from 'lib/theme'
import styles from './styles.module.css'
('auth')
export class ProfileSection extends Component {
static propTypes = {
auth: PropTypes.object.isRequired
}
state = {
copiedTextToClipboard: false,
isLoadingUnsubscribe: false,
isLoadingRefreshAuthToken: false
}
componentWillUnmount() {
if (this._copyTimeout) {
clearTimeout(this._copyTimeout)
this._copyTimeout = null
}
}
render() {
const { auth, ...rest } = this.props
const {
copiedTextToClipboard,
isLoadingUnsubscribe,
isLoadingRefreshAuthToken
} = this.state
const hasSubscription =
auth.consumer &&
auth.consumer.enabled &&
auth.consumer.plan &&
auth.consumer.plan !== 'free'
const columns = [
{
title: 'Avatar',
dataIndex: 'image',
render: (image) =>
image ? (
<Avatar src={image} className={theme(styles, 'avatar')} />
) : (
<Avatar icon='user' className={theme(styles, 'avatar')} />
)
},
{
title: 'Username',
dataIndex: 'username'
},
{
title: 'Email',
dataIndex: 'email'
},
{
title: 'Joined',
dataIndex: 'joined',
render: (date) => format(new Date(date), 'MM/dd/yyyy')
},
{
title: 'Subscription',
dataIndex: 'subscription',
render: (subscription) => <Link to='/pricing'>{subscription}</Link>
},
/*
hasSubscription && {
title: 'Subscribed',
dataIndex: 'subscribed',
render: (date) => (
date ? format(new Date(date), 'MM/dd/yyyy') : ''
)
},
*/
{
title: 'Auth Token',
key: 'token',
render: (token) =>
auth.consumer?.enabled &&
auth.consumer?.token && (
<Tooltip
placement='top'
title={copiedTextToClipboard ? 'Copied!' : 'Copy to clipboard'}
>
<Button type='primary' ghost onClick={this._onClickCopyToken}>
{`${auth.consumer.token.substr(0, 8)} ...`}
</Button>
</Tooltip>
)
},
{
title: 'Actions',
key: 'actions',
render: (token) => (
<Fragment>
{auth.consumer?.enabled && auth.consumer?.token && (
<Fragment>
<Button
type='ghost'
icon='reload'
loading={isLoadingRefreshAuthToken}
onClick={this._onClickRefreshAuthToken}
block
>
Refresh Token
</Button>
<Divider
type='horizontal'
style={{ marginTop: 8, marginBottom: 8 }}
/>
</Fragment>
)}
{hasSubscription ? (
<Popconfirm
placement='top'
title='Are you sure you want to cancel your subscription?'
okText='Yes'
cancelText='No'
onConfirm={this._onConfirmUnsubscribe}
>
<Button type='default' loading={isLoadingUnsubscribe} block>
Cancel
</Button>
</Popconfirm>
) : (
<Button type='primary' href='/pricing' block>
Upgrade
</Button>
)}
</Fragment>
)
}
].filter(Boolean)
const dataSource = [
{
id: auth.user.id,
username: auth.user.username,
email: auth.user.email,
image: auth.user.image,
joined: auth.user.createdAt,
subscription: hasSubscription
? auth.consumer.plan
: auth.consumer && auth.consumer.enabled && auth.consumer.plan
? auth.consumer.plan
: 'none',
subscribed: auth.consumer?.createdAt
}
]
return (
<Section id='profile' title='Profile' {...rest}>
<div className={theme(styles, 'body')}>
<Table
columns={columns}
rowKey={(record) => record.id}
dataSource={dataSource}
pagination={false}
/>
</div>
</Section>
)
}
_onClickCopyToken = () => {
const { token } = this.props.auth.consumer
copyTextToClipboard(token)
this.setState({ copiedTextToClipboard: true })
this._clearCopyTimeout()
this._copyTimeout = setTimeout(this._onCopyTimeout, 3000)
}
_onCopyTimeout = () => {
this._clearCopyTimeout()
this.setState({ copiedTextToClipboard: false })
}
_clearCopyTimeout = () => {
if (this._copyTimeout) {
clearTimeout(this._copyTimeout)
this._copyTimeout = null
}
}
_onConfirmUnsubscribe = () => {
this.setState({ isLoadingUnsubscribe: true })
API.removeConsumer(this.props.auth.consumer).then(
() => {
this.props.auth.consumer = null
notification.success({
message: 'Subscription canceled',
duration: 0,
description: (
<span>
<p>
Your subscription has been canceled. If you have any pending
charges, they will be billed at the end of the current billing
cycle.
</p>
<p>It may take a few minutes for this change to take effect.</p>
</span>
)
})
this.setState({ isLoadingUnsubscribe: false })
},
(err) => {
console.warn(err)
notification.error({
message: 'Error canceling subscription',
description: err?.response?.data?.error || err.error?.message,
duration: 0
})
this.setState({ isLoadingUnsubscribe: false })
}
)
}
_onClickRefreshAuthToken = () => {
this.setState({ isLoadingRefreshAuthToken: true })
API.updateConsumer(this.props.auth.consumer).then(
(consumer) => {
this.props.auth.consumer = consumer
notification.success({
message: 'Auth token refreshed',
description: (
<span>
<p>
Your auth token has been refreshed. Your old token is now
invalid.
</p>
<p>It may take a few minutes for this change to take effect.</p>
</span>
)
})
this.setState({ isLoadingRefreshAuthToken: false })
},
(err) => {
console.warn(err)
notification.error({
message: 'Error refreshing auth token',
description: err?.response?.data?.error || err.error?.message,
duration: 0
})
this.setState({ isLoadingRefreshAuthToken: false })
}
)
}
}