import React, { type JSX, useCallback, useRef, useState } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; import ImageCropModal from '~/client/components/Common/ImageCropModal'; import { apiv3Delete, apiv3PostForm, apiv3Put, } from '~/client/util/apiv3-client'; import { toastError, toastSuccess } from '~/client/util/toastr'; import { useIsDefaultLogo } from '~/states/global'; import { isCustomizedLogoUploadedAtom } from '~/states/server-configurations'; import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow'; const DEFAULT_LOGO = '/images/logo.svg'; const CUSTOMIZED_LOGO = '/attachment/brand-logo'; const CustomizeLogoSetting = (): JSX.Element => { const { t } = useTranslation(); const isDefaultLogo = useIsDefaultLogo(); const isCustomizedLogoUploaded = useAtomValue(isCustomizedLogoUploadedAtom); const setIsCustomizedLogoUploaded = useSetAtom(isCustomizedLogoUploadedAtom); const [uploadLogoSrc, setUploadLogoSrc] = useState< ArrayBuffer | string | null >(null); const [isImageCropModalShow, setIsImageCropModalShow] = useState(false); const [isDefaultLogoSelected, setIsDefaultLogoSelected] = useState( isDefaultLogo ?? true, ); const [retrieveError, setRetrieveError] = useState(); const fileInputRef = useRef(null); const isSystemError: boolean = retrieveError != null; const isLogoSettingIncomplete: boolean = !isDefaultLogoSelected && uploadLogoSrc == null && !isCustomizedLogoUploaded; const isUpdateButtonDisabled: boolean = isSystemError || isLogoSettingIncomplete; const onSelectFile = useCallback((e: React.ChangeEvent) => { if (e.target.files != null && e.target.files.length > 0) { const reader = new FileReader(); reader.addEventListener('load', () => setUploadLogoSrc(reader.result)); reader.readAsDataURL(e.target.files[0]); setIsImageCropModalShow(true); } }, []); const onClickSubmit = useCallback(async () => { try { await apiv3Put('/customize-setting/customize-logo', { isDefaultLogo: isDefaultLogoSelected, }); toastSuccess( t('toaster.update_successed', { target: t('admin:customize_settings.custom_logo'), ns: 'commons', }), ); } catch (err) { toastError(err); } }, [t, isDefaultLogoSelected]); const clearFileInput = useCallback(() => { if (fileInputRef.current) { fileInputRef.current.value = ''; } }, []); const resetFileSelectionState = useCallback(() => { clearFileInput(); setUploadLogoSrc(null); }, [clearFileInput]); const onCloseCropModal = useCallback(() => { resetFileSelectionState(); setIsImageCropModalShow(false); }, [resetFileSelectionState]); const onClickDeleteBtn = useCallback(async () => { try { await apiv3Delete('/customize-setting/delete-brand-logo'); setIsCustomizedLogoUploaded(false); toastSuccess( t('toaster.update_successed', { target: t('admin:customize_settings.current_logo'), ns: 'commons', }), ); resetFileSelectionState(); } catch (err) { toastError(err); setRetrieveError(err); throw new Error('Failed to delete logo'); } }, [setIsCustomizedLogoUploaded, t, resetFileSelectionState]); const processImageCompletedHandler = useCallback( async (croppedImage) => { try { const formData = new FormData(); formData.append('file', croppedImage); await apiv3PostForm('/customize-setting/upload-brand-logo', formData); setIsCustomizedLogoUploaded(true); toastSuccess( t('toaster.update_successed', { target: t('admin:customize_settings.current_logo'), ns: 'commons', }), ); } catch (err) { toastError(err); setRetrieveError(err); throw new Error('Failed to upload brand logo'); } }, [setIsCustomizedLogoUploaded, t], ); return (

{t('admin:customize_settings.custom_logo')}

{ setIsDefaultLogoSelected(true); }} />

{t('admin:customize_settings.default_logo')}

{ setIsDefaultLogoSelected(false); }} />

{t('admin:customize_settings.current_logo')}
{isCustomizedLogoUploaded && ( <>

)}
); }; export default CustomizeLogoSetting;