ShareLinkForm.jsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import dateFnsFormat from 'date-fns/format';
  5. import parse from 'date-fns/parse';
  6. import { withUnstatedContainers } from './UnstatedUtils';
  7. import { toastSuccess, toastError } from '../util/apiNotification';
  8. import AppContainer from '../services/AppContainer';
  9. import PageContainer from '../services/PageContainer';
  10. class ShareLinkForm extends React.Component {
  11. constructor(props) {
  12. super(props);
  13. this.state = {
  14. expirationType: 'unlimited',
  15. numberOfDays: 7,
  16. description: '',
  17. customExpirationDate: dateFnsFormat(new Date(), 'yyyy-MM-dd'),
  18. customExpirationTime: dateFnsFormat(new Date(), 'hh:mm:s'),
  19. };
  20. this.handleChangeExpirationType = this.handleChangeExpirationType.bind(this);
  21. this.handleChangeNumberOfDays = this.handleChangeNumberOfDays.bind(this);
  22. this.handleChangeDescription = this.handleChangeDescription.bind(this);
  23. this.handleIssueShareLink = this.handleIssueShareLink.bind(this);
  24. }
  25. /**
  26. * change expirationType
  27. * @param {string} expirationType
  28. */
  29. handleChangeExpirationType(expirationType) {
  30. this.setState({ expirationType });
  31. }
  32. /**
  33. * change numberOfDays
  34. * @param {number} numberOfDays
  35. */
  36. handleChangeNumberOfDays(numberOfDays) {
  37. this.setState({ numberOfDays });
  38. }
  39. /**
  40. * change description
  41. * @param {string} description
  42. */
  43. handleChangeDescription(description) {
  44. this.setState({ description });
  45. }
  46. /**
  47. * change customExpirationDate
  48. * @param {date} customExpirationDate
  49. */
  50. handleChangeCustomExpirationDate(customExpirationDate) {
  51. this.setState({ customExpirationDate });
  52. }
  53. /**
  54. * change customExpirationTime
  55. * @param {date} customExpirationTime
  56. */
  57. handleChangeCustomExpirationTime(customExpirationTime) {
  58. this.setState({ customExpirationTime });
  59. }
  60. /**
  61. * Generate expiredAt by expirationType
  62. */
  63. generateExpired() {
  64. const { expirationType } = this.state;
  65. let expiredAt;
  66. if (expirationType === 'unlimited') {
  67. return null;
  68. }
  69. if (expirationType === 'numberOfDays') {
  70. const date = new Date();
  71. date.setDate(date.getDate() + this.state.numberOfDays);
  72. expiredAt = date;
  73. }
  74. if (expirationType === 'custom') {
  75. const { customExpirationDate, customExpirationTime } = this.state;
  76. expiredAt = parse(`${customExpirationDate}T${customExpirationTime}`, "yyyy-MM-dd'T'HH:mm:ss", new Date());
  77. }
  78. return expiredAt;
  79. }
  80. closeForm() {
  81. const { onCloseForm } = this.props;
  82. if (onCloseForm == null) {
  83. return;
  84. }
  85. onCloseForm();
  86. }
  87. async handleIssueShareLink() {
  88. const { t, appContainer, pageContainer } = this.props;
  89. const { pageId } = pageContainer.state;
  90. const { description } = this.state;
  91. let expiredAt;
  92. try {
  93. expiredAt = this.generateExpired();
  94. }
  95. catch (err) {
  96. return toastError(err);
  97. }
  98. try {
  99. await appContainer.apiv3Post('/share-links/', { relatedPage: pageId, expiredAt, description });
  100. this.closeForm();
  101. toastSuccess(t('toaster.issue_share_link'));
  102. }
  103. catch (err) {
  104. toastError(err);
  105. }
  106. }
  107. renderExpirationTypeOptions() {
  108. const { expirationType } = this.state;
  109. return (
  110. <div className="form-group">
  111. <div className="custom-control custom-radio offset-4 mb-2">
  112. <input
  113. type="radio"
  114. className="custom-control-input"
  115. id="customRadio1"
  116. name="expirationType"
  117. value="customRadio1"
  118. checked={expirationType === 'unlimited'}
  119. onChange={() => { this.handleChangeExpirationType('unlimited') }}
  120. />
  121. <label className="custom-control-label" htmlFor="customRadio1">Unlimited</label>
  122. </div>
  123. <div className="custom-control custom-radio offset-4 mb-2">
  124. <input
  125. type="radio"
  126. className="custom-control-input"
  127. id="customRadio2"
  128. value="customRadio2"
  129. checked={expirationType === 'numberOfDays'}
  130. onChange={() => { this.handleChangeExpirationType('numberOfDays') }}
  131. name="expirationType"
  132. />
  133. <label className="custom-control-label" htmlFor="customRadio2">
  134. <div className="row align-items-center m-0">
  135. <input
  136. type="number"
  137. min="1"
  138. className="col-4"
  139. name="expirationType"
  140. value={this.state.numberOfDays}
  141. onFocus={() => { this.handleChangeExpirationType('numberOfDays') }}
  142. onChange={e => this.handleChangeNumberOfDays(Number(e.target.value))}
  143. />
  144. <span className="col-auto">Days</span>
  145. </div>
  146. </label>
  147. </div>
  148. <div className="custom-control custom-radio offset-4 mb-2">
  149. <input
  150. type="radio"
  151. className="custom-control-input"
  152. id="customRadio3"
  153. name="expirationType"
  154. value="customRadio3"
  155. checked={expirationType === 'custom'}
  156. onChange={() => { this.handleChangeExpirationType('custom') }}
  157. />
  158. <label className="custom-control-label" htmlFor="customRadio3">
  159. Custom
  160. </label>
  161. <input
  162. type="date"
  163. className="ml-3"
  164. name="customExpirationDate"
  165. value={this.state.customExpirationDate}
  166. onFocus={() => { this.handleChangeExpirationType('custom') }}
  167. onChange={e => this.handleChangeCustomExpirationDate(e.target.value)}
  168. />
  169. <input
  170. type="time"
  171. className="ml-3"
  172. name="customExpiration"
  173. value={this.state.customExpirationTime}
  174. onFocus={() => { this.handleChangeExpirationType('custom') }}
  175. onChange={e => this.handleChangeCustomExpirationTime(e.target.value)}
  176. />
  177. </div>
  178. </div>
  179. );
  180. }
  181. renderDescriptionForm() {
  182. return (
  183. <div className="form-group row">
  184. <label htmlFor="inputDesc" className="col-md-4 col-form-label">Description</label>
  185. <div className="col-md-4">
  186. <input
  187. type="text"
  188. className="form-control"
  189. id="inputDesc"
  190. placeholder="Enter description"
  191. value={this.state.description}
  192. onChange={e => this.handleChangeDescription(e.target.value)}
  193. />
  194. </div>
  195. </div>
  196. );
  197. }
  198. render() {
  199. return (
  200. <div className="share-link-form border p-3">
  201. <h4>Expiration Date</h4>
  202. {this.renderExpirationTypeOptions()}
  203. <hr />
  204. {this.renderDescriptionForm()}
  205. <div className="text-right">
  206. <button type="button" className="btn btn-primary" onClick={this.handleIssueShareLink}>
  207. Issue
  208. </button>
  209. </div>
  210. </div>
  211. );
  212. }
  213. }
  214. const ShareLinkFormWrapper = withUnstatedContainers(ShareLinkForm, [AppContainer, PageContainer]);
  215. ShareLinkForm.propTypes = {
  216. t: PropTypes.func.isRequired, // i18next
  217. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  218. pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
  219. onCloseForm: PropTypes.func,
  220. };
  221. export default withTranslation()(ShareLinkFormWrapper);