CustomizeLogoSetting.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { toastError, toastSuccess } from '~/client/util/apiNotification';
  4. import {
  5. apiv3Delete, apiv3Get, apiv3PostForm, apiv3Put,
  6. } from '~/client/util/apiv3-client';
  7. import ImageCropModal from '~/components/Common/ImageCropModal';
  8. import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
  9. const DEFAULT_LOGO = '/images/logo.svg';
  10. const CustomizeLogoSetting = (): JSX.Element => {
  11. const { t } = useTranslation();
  12. const [uploadLogoSrc, setUploadLogoSrc] = useState<ArrayBuffer | string | null>(null);
  13. const [isImageCropModalShow, setIsImageCropModalShow] = useState<boolean>(false);
  14. const [isDefaultLogo, setIsDefaultLogo] = useState<boolean>(true);
  15. const [retrieveError, setRetrieveError] = useState<any>();
  16. const [customizedLogoSrc, setCustomizedLogoSrc] = useState< string | null >(null);
  17. const retrieveData = useCallback(async() => {
  18. try {
  19. const response = await apiv3Get('/customize-setting/customize-logo');
  20. const { isDefaultLogo: _isDefaultLogo, customizedLogoSrc } = response.data;
  21. const isDefaultLogo = _isDefaultLogo ?? true;
  22. setIsDefaultLogo(isDefaultLogo);
  23. setCustomizedLogoSrc(customizedLogoSrc);
  24. }
  25. catch (err) {
  26. setRetrieveError(err);
  27. throw new Error('Failed to fetch data');
  28. }
  29. }, []);
  30. useEffect(() => {
  31. retrieveData();
  32. }, [retrieveData]);
  33. const onSelectFile = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
  34. if (e.target.files != null && e.target.files.length > 0) {
  35. const reader = new FileReader();
  36. reader.addEventListener('load', () => setUploadLogoSrc(reader.result));
  37. reader.readAsDataURL(e.target.files[0]);
  38. setIsImageCropModalShow(true);
  39. }
  40. }, []);
  41. const onClickSubmit = useCallback(async() => {
  42. try {
  43. const response = await apiv3Put('/customize-setting/customize-logo', {
  44. isDefaultLogo,
  45. customizedLogoSrc,
  46. });
  47. const { customizedParams } = response.data;
  48. setIsDefaultLogo(customizedParams.isDefaultLogo);
  49. setCustomizedLogoSrc(customizedParams.customizedLogoSrc);
  50. toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.custom_logo') }));
  51. }
  52. catch (err) {
  53. toastError(err);
  54. }
  55. }, [t, isDefaultLogo, customizedLogoSrc]);
  56. const onClickDeleteBtn = useCallback(async() => {
  57. try {
  58. await apiv3Delete('/customize-setting/delete-brand-logo');
  59. setCustomizedLogoSrc(null);
  60. toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.current_logo') }));
  61. }
  62. catch (err) {
  63. toastError(err);
  64. setRetrieveError(err);
  65. throw new Error('Failed to delete logo');
  66. }
  67. }, [t]);
  68. const processImageCompletedHandler = useCallback(async(croppedImage) => {
  69. try {
  70. const formData = new FormData();
  71. formData.append('file', croppedImage);
  72. const { data } = await apiv3PostForm('/customize-setting/upload-brand-logo', formData);
  73. setCustomizedLogoSrc(data.attachment.filePathProxied);
  74. toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.current_logo') }));
  75. }
  76. catch (err) {
  77. toastError(err);
  78. setRetrieveError(err);
  79. throw new Error('Failed to upload brand logo');
  80. }
  81. }, [t]);
  82. return (
  83. <React.Fragment>
  84. <div className="row">
  85. <div className="col-12">
  86. <div className="mb-5">
  87. <h2 className="border-bottom my-4 admin-setting-header">{t('admin:customize_setting.custom_logo')}</h2>
  88. <div className="row">
  89. <div className="col-md-6 col-12 mb-3 mb-md-0">
  90. <h4>
  91. <div className="custom-control custom-radio radio-primary">
  92. <input
  93. type="radio"
  94. id="radioDefaultLogo"
  95. className="custom-control-input"
  96. form="formImageType"
  97. name="imagetypeForm[isDefaultLogo]"
  98. checked={isDefaultLogo}
  99. onChange={() => { setIsDefaultLogo(true) }}
  100. />
  101. <label className="custom-control-label" htmlFor="radioDefaultLogo">
  102. {t('admin:customize_setting.default_logo')}
  103. </label>
  104. </div>
  105. </h4>
  106. <img src={DEFAULT_LOGO} width="64" />
  107. </div>
  108. <div className="col-md-6 col-12">
  109. <h4>
  110. <div className="custom-control custom-radio radio-primary">
  111. <input
  112. type="radio"
  113. id="radioUploadLogo"
  114. className="custom-control-input"
  115. form="formImageType"
  116. name="imagetypeForm[isDefaultLogo]"
  117. checked={!isDefaultLogo}
  118. onChange={() => { setIsDefaultLogo(false) }}
  119. />
  120. <label className="custom-control-label" htmlFor="radioUploadLogo">
  121. { t('admin:customize_setting.upload_logo') }
  122. </label>
  123. </div>
  124. </h4>
  125. <div className="row mb-3">
  126. <label className="col-sm-4 col-12 col-form-label text-left">
  127. { t('admin:customize_setting.current_logo') }
  128. </label>
  129. <div className="col-sm-8 col-12">
  130. <p><img src={customizedLogoSrc || DEFAULT_LOGO} className="picture picture-lg " id="settingBrandLogo" width="64" /></p>
  131. {(customizedLogoSrc != null) && (
  132. <button type="button" className="btn btn-danger" onClick={onClickDeleteBtn}>
  133. { t('admin:customize_setting.delete_logo') }
  134. </button>
  135. )}
  136. </div>
  137. </div>
  138. <div className="row">
  139. <label className="col-sm-4 col-12 col-form-label text-left">
  140. { t('admin:customize_setting.upload_new_logo') }
  141. </label>
  142. <div className="col-sm-8 col-12">
  143. <input type="file" onChange={onSelectFile} name="brandLogo" accept="image/*" />
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. <AdminUpdateButtonRow onClick={onClickSubmit} disabled={retrieveError != null} />
  149. </div>
  150. </div>
  151. </div>
  152. <ImageCropModal
  153. isShow={isImageCropModalShow}
  154. src={uploadLogoSrc}
  155. onModalClose={() => setIsImageCropModalShow(false)}
  156. onImageProcessCompleted={processImageCompletedHandler}
  157. isCircular={false}
  158. showCropOption={false}
  159. />
  160. </React.Fragment>
  161. );
  162. };
  163. export default CustomizeLogoSetting;