Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,22 @@ const CustomizeLogoSetting = (): JSX.Element => {
const [isImageCropModalShow, setIsImageCropModalShow] = useState<boolean>(false);
const [isDefaultLogoSelected, setIsDefaultLogoSelected] = useState<boolean>(isDefaultLogo ?? true);
const [retrieveError, setRetrieveError] = useState<any>();
const [isImageCropped, setIsImageCropped] = useState<boolean>(false);
const [fileInputKey, setFileInputKey] = useState<string>(Date.now().toString());

const onSelectFile = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files != null && e.target.files.length > 0) {
const files = e.target.files;
const hasFile = files != null && files.length > 0;

setIsImageCropped(false);

if (hasFile) {
const reader = new FileReader();
reader.addEventListener('load', () => setUploadLogoSrc(reader.result));
reader.readAsDataURL(e.target.files[0]);
reader.readAsDataURL(files[0]);
setIsImageCropModalShow(true);
}
}, []);
}, [setUploadLogoSrc, setIsImageCropModalShow, setIsImageCropped]);

const onClickSubmit = useCallback(async () => {
try {
Expand All @@ -48,25 +55,34 @@ const CustomizeLogoSetting = (): JSX.Element => {
}
}, [t, isDefaultLogoSelected]);

const resetFileInput = useCallback(() => {
setFileInputKey(Date.now().toString());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここで呼び出した瞬間のミリ秒時間を引数として入力することによって呼び出したら必ず異なるkeyがinput タグに入力されることになります。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key をリセットするやり方がちょっとトリッキーなので useRef 使って実装してみてください。

const fileInputRef = useRef<HTMLInputElement | null>(null);

const clearFileInput = useCallback(() => {
  if (fileInputRef.current) {
    fileInputRef.current.value = '';
  }
}, []);

}, []);

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' }));
setUploadLogoSrc(null);
setIsImageCropped(false);
setIsImageCropModalShow(false);
resetFileInput();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ロゴを削除するというボタンが押された場合にファイル名を初期化するように実装してあります。
スクリーンショット 2025-12-24 154624

}
catch (err) {
toastError(err);
setRetrieveError(err);
throw new Error('Failed to delete logo');
}
}, [setIsCustomizedLogoUploaded, t]);

}, [setIsCustomizedLogoUploaded, t, setUploadLogoSrc, setIsImageCropped, setIsImageCropModalShow, resetFileInput, setRetrieveError]);

const processImageCompletedHandler = useCallback(async (croppedImage) => {
try {
const formData = new FormData();
formData.append('file', croppedImage);
await apiv3PostForm('/customize-setting/upload-brand-logo', formData);
setIsImageCropModalShow(false);
setIsImageCropped(true);
setIsCustomizedLogoUploaded(true);
toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.current_logo'), ns: 'commons' }));
}
Expand All @@ -75,7 +91,8 @@ const CustomizeLogoSetting = (): JSX.Element => {
setRetrieveError(err);
throw new Error('Failed to upload brand logo');
}
}, [setIsCustomizedLogoUploaded, t]);
}, [setIsCustomizedLogoUploaded, t, setIsImageCropped, setIsImageCropModalShow, setRetrieveError]);


return (
<React.Fragment>
Expand Down Expand Up @@ -142,20 +159,36 @@ const CustomizeLogoSetting = (): JSX.Element => {
{t('admin:customize_settings.upload_new_logo')}
</label>
<div className="col-sm-8 col-12">
<input type="file" onChange={onSelectFile} name="brandLogo" accept="image/*" />
<input
type="file"
key={fileInputKey}
Copy link
Contributor Author

@hikaru-n-cpu hikaru-n-cpu Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この部分でinputタグのkeyに呼び出した現在のミリ秒の文字列を代入しています。この処理により毎回resetFileInput()が呼び出されればinputタグが再構築されることによって空のinputタグが生成され、モーダルのキャンセルボタンを押したとしてもすでに選んでしまったfile名が残らないようにしました。

onChange={onSelectFile}
name="brandLogo"
accept="image/*"
/>
</div>
</div>
</div>
</div>
<AdminUpdateButtonRow onClick={onClickSubmit} disabled={retrieveError != null} />
<AdminUpdateButtonRow
onClick={onClickSubmit}
disabled={retrieveError != null
|| (!isDefaultLogoSelected && uploadLogoSrc == null && !isCustomizedLogoUploaded)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isUpdateButtonDisabled みたいな変数を作ってそれを渡すようにしてください

/>
</div>
</div>
</div>

<ImageCropModal
isShow={isImageCropModalShow}
src={uploadLogoSrc}
onModalClose={() => setIsImageCropModalShow(false)}
onModalClose={() => {
if (!isImageCropped) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onImageProcessCompleted というメソッドが ImageCropModal 側に提供されているみたいですが、ためしてみました?

state が増えると実装が複雑になるので増やすのは慎重にしたいです

resetFileInput();
Copy link
Contributor Author

@hikaru-n-cpu hikaru-n-cpu Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この部分はモーダルでキャンセルボタンを押した時点で「切り抜きボタン(完了ボタン)」が押されていない場合にはファイル名を初期化するように実装してあります。
スクリーンショット 2025-12-24 154802

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsx の部分にロジックがあると見にくいので closeImageCropModalHandler みたいなメソッドを作ってそれを渡すようにしてください

setUploadLogoSrc(null);
}
setIsImageCropModalShow(false);
}}
onImageProcessCompleted={processImageCompletedHandler}
isCircular={false}
showCropOption={false}
Expand Down
3 changes: 1 addition & 2 deletions apps/app/src/client/components/Common/ImageCropModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ const ImageCropModal: FC<Props> = (props: Props) => {
// Save image to database
onImageProcessCompleted(processedImage);
}
onModalCloseHandler();
}, [imageRef, cropOptions, isCropImage, getCroppedImg, convertBase64ToBlob, onImageProcessCompleted, onModalCloseHandler]);
}, [imageRef, cropOptions, isCropImage, getCroppedImg, convertBase64ToBlob, onImageProcessCompleted]);

const toggleCropMode = useCallback(() => setIsCropImage(!isCropImage), [isCropImage]);
const handleCropChange = useCallback((crop: CropOptions) => setCropOtions(crop), []);
Expand Down
Loading