Skip to content

Commit d00ab79

Browse files
committed
fix: 이미지 업로드 후, 업로드한 이미지로 값을 설정할 수 없던 문제 수정
1 parent d475120 commit d00ab79

File tree

5 files changed

+46
-39
lines changed

5 files changed

+46
-39
lines changed

apps/pyconkr-participant-portal/src/components/dialogs/public_file_upload.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as Common from "@frontend/common";
22
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material";
33
import { enqueueSnackbar, OptionsObject } from "notistack";
44
import * as React from "react";
5+
import * as R from "remeda";
56

67
import { useAppContext } from "../../contexts/app_context";
78

@@ -39,18 +40,17 @@ const SetUploadedFileAsValueConfirmDialog: React.FC<SetUploadedFileAsValueConfir
3940
type PublicFileUploadDialogProps = {
4041
open: boolean;
4142
onClose: () => void;
42-
setFileIdAsValue?: (fileId: string | undefined) => void;
43+
setFileIdAsValue?: (fileId: string | null) => void;
4344
};
4445

4546
type PublicFileUploadDialogState = {
46-
selectedFile?: File | null;
47-
uploadedFileId?: string;
48-
openSetValueDialog?: boolean;
47+
selectedFile: File | null;
48+
uploadedFileId: string | null;
4949
};
5050

5151
export const PublicFileUploadDialog: React.FC<PublicFileUploadDialogProps> = ({ open, onClose, setFileIdAsValue }) => {
5252
const { language } = useAppContext();
53-
const [dialogState, setDialogState] = React.useState<PublicFileUploadDialogState>({});
53+
const [dialogState, setDialogState] = React.useState<PublicFileUploadDialogState>({ selectedFile: null, uploadedFileId: null });
5454
const participantPortalClient = Common.Hooks.BackendParticipantPortalAPI.useParticipantPortalClient();
5555
const uploadPublicFileMutation = Common.Hooks.BackendParticipantPortalAPI.useUploadPublicFileMutation(participantPortalClient);
5656

@@ -64,10 +64,9 @@ export const PublicFileUploadDialog: React.FC<PublicFileUploadDialogProps> = ({
6464
const failedToUploadStr = language === "ko" ? "파일 업로드에 실패했습니다." : "Failed to upload file.";
6565
const loading = uploadPublicFileMutation.isPending;
6666

67-
const openSetValueDialog = () => setDialogState((ps) => ({ ...ps, openSetValueDialog: true }));
68-
const cleanUpDialogState = () => setDialogState({});
69-
const setFile = (selectedFile?: File | null) => setDialogState((ps) => ({ ...ps, selectedFile }));
70-
const setFileId = (uploadedFileId?: string) => setDialogState((ps) => ({ ...ps, uploadedFileId }));
67+
const cleanUpDialogState = () => setDialogState({ selectedFile: null, uploadedFileId: null });
68+
const setFile = (selectedFile: File | null) => setDialogState((ps) => ({ ...ps, selectedFile }));
69+
const setFileId = (uploadedFileId: string | null) => setDialogState((ps) => ({ ...ps, uploadedFileId }));
7170

7271
const uploadFile = async () => {
7372
if (!dialogState.selectedFile) {
@@ -76,10 +75,7 @@ export const PublicFileUploadDialog: React.FC<PublicFileUploadDialogProps> = ({
7675
}
7776

7877
uploadPublicFileMutation.mutate(dialogState.selectedFile, {
79-
onSuccess: (data) => {
80-
setFileId(data.id);
81-
openSetValueDialog();
82-
},
78+
onSuccess: (data) => setFileId(data.id),
8379
onError: (error) => {
8480
console.error("Uploading file failed:", error);
8581

@@ -103,7 +99,7 @@ export const PublicFileUploadDialog: React.FC<PublicFileUploadDialogProps> = ({
10399
<>
104100
<SetUploadedFileAsValueConfirmDialog
105101
language={language}
106-
open={!!dialogState.openSetValueDialog}
102+
open={R.isString(dialogState.uploadedFileId) && !R.isEmpty(dialogState.uploadedFileId)}
107103
closeAll={closeAllDialogs}
108104
setValueAndCloseAll={setValueAndCloseAllDialogs}
109105
/>

apps/pyconkr-participant-portal/src/components/elements/multilang_field.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export const MultiLanguageMarkdownField: React.FC<MultiLanguageMarkdownFieldProp
180180
);
181181
};
182182

183-
type MultiLanguagePublicFileSelect = Omit<SelectProps<string | null | undefined>, "label"> & MultiLanguageCommonProps;
183+
type MultiLanguagePublicFileSelect = Omit<SelectProps<string | null>, "label"> & MultiLanguageCommonProps;
184184

185185
export const MultiLanguagePublicFileSelect: React.FC<MultiLanguagePublicFileSelect> = ({ label, description, ...props }) => {
186186
const { language } = useAppContext();

apps/pyconkr-participant-portal/src/components/elements/public_file_selector.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@ import { Box, Button, CircularProgress, FormControl, InputLabel, MenuItem, Selec
44
import { ErrorBoundary, Suspense } from "@suspensive/react";
55
import * as React from "react";
66

7+
import { Fieldset } from "./fieldset";
78
import { useAppContext } from "../../contexts/app_context";
89
import { PublicFileUploadDialog } from "../dialogs/public_file_upload";
9-
import { Fieldset } from "./fieldset";
1010

11-
type PublicFileSelectorProps = SelectProps<string | null | undefined> & {
12-
setFileIdAsValue?: (fileId?: string | null) => void;
13-
};
11+
type PublicFileSelectorProps = Omit<SelectProps<string | null>, "inputRef">;
1412

1513
const ImageFallback: React.FC<{ language: "ko" | "en" }> = ({ language }) => (
1614
<Box children={language === "ko" ? "이미지가 없습니다." : "No image available."} />
1715
);
1816

1917
type PublicFileSelectorState = {
20-
fileId?: string | null;
18+
value?: string | null;
2119
openUploadDialog?: boolean;
2220
};
2321

@@ -29,35 +27,48 @@ const ScaledFallbackImage = styled(Common.Components.FallbackImage)({
2927

3028
export const PublicFileSelector: React.FC<PublicFileSelectorProps> = ErrorBoundary.with(
3129
{ fallback: Common.Components.ErrorFallback },
32-
Suspense.with({ fallback: <CircularProgress /> }, ({ onChange, setFileIdAsValue, ...props }) => {
33-
const [selectorState, setSelectorState] = React.useState<PublicFileSelectorState>({});
30+
Suspense.with({ fallback: <CircularProgress /> }, ({ value, onChange, ...props }) => {
31+
const selectInputRef = React.useRef<HTMLSelectElement | null>(null);
32+
const [selectorState, setSelectorState] = React.useState<PublicFileSelectorState>({ value });
3433
const { language } = useAppContext();
3534
const participantPortalClient = Common.Hooks.BackendParticipantPortalAPI.useParticipantPortalClient();
3635
const { data } = Common.Hooks.BackendParticipantPortalAPI.usePublicFilesQuery(participantPortalClient);
3736
const isMobile = useMediaQuery((theme) => theme.breakpoints.down("md"));
3837

39-
const setSelectedFile: SelectProps<string | null | undefined>["onChange"] = (event, child) => {
40-
setSelectorState((ps) => ({ ...ps, fileId: event.target.value }));
38+
const setSelectedFile: SelectProps<string | null>["onChange"] = (event, child) => {
39+
setSelectorState((ps) => ({ ...ps, value: event.target.value }));
4140
onChange?.(event, child);
4241
};
42+
const setSelectInputValue = (value: string | null) => {
43+
if (selectInputRef.current) selectInputRef.current.value = value || "";
44+
45+
setSelectorState((ps) => ({ ...ps, value }));
46+
onChange?.({ target: { value } } as React.ChangeEvent<HTMLSelectElement & HTMLInputElement>, null);
47+
};
4348
const openUploadDialog = () => setSelectorState((ps) => ({ ...ps, openUploadDialog: true }));
4449
const closeUploadDialog = () => setSelectorState((ps) => ({ ...ps, openUploadDialog: false }));
4550

4651
const emptyValueStr = language === "ko" ? "선택 안 함" : "Not selected";
4752
const uploadStr = language === "ko" ? "파일 업로드" : "Upload File";
48-
const files = [...(props.required ? [] : [{ id: undefined, file: emptyValueStr, name: emptyValueStr }]), ...data];
49-
const selectedFile = data.find((file) => file.id === props.value);
53+
const files = [...(props.required ? [] : [{ id: "", file: emptyValueStr, name: emptyValueStr }]), ...data];
54+
const selectedFile = data.find((file) => file.id === (selectorState.value || ""));
5055

5156
return (
5257
<>
53-
<PublicFileUploadDialog open={!!selectorState.openUploadDialog} onClose={closeUploadDialog} setFileIdAsValue={setFileIdAsValue} />
58+
<PublicFileUploadDialog open={!!selectorState.openUploadDialog} onClose={closeUploadDialog} setFileIdAsValue={setSelectInputValue} />
5459
<Fieldset legend={props.label?.toString() || ""}>
5560
<Stack direction="column" spacing={2} alignItems="center" justifyContent="center">
56-
<ScaledFallbackImage src={selectedFile?.file} alt="Selected File" loading="lazy" errorFallback={<ImageFallback language={language} />} />
61+
<ScaledFallbackImage
62+
key={selectedFile?.file || ""}
63+
src={selectedFile?.file}
64+
alt="Selected File"
65+
loading="lazy"
66+
errorFallback={<ImageFallback language={language} />}
67+
/>
5768
<Stack direction={isMobile ? "column" : "row"} spacing={2} sx={{ width: "100%" }} alignItems="center">
5869
<FormControl fullWidth>
5970
<InputLabel id="public-file-label">{props.label}</InputLabel>
60-
<Select labelId="public-file-label" onChange={setSelectedFile} {...props}>
71+
<Select labelId="public-file-label" ref={selectInputRef} value={selectorState.value || ""} onChange={setSelectedFile} {...props}>
6172
{files.map((file) => (
6273
<MenuItem key={file.id} value={file.id} children={file.name} />
6374
))}

apps/pyconkr-participant-portal/src/components/pages/profile_editor.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type ProfileType = {
2121
email: string;
2222
nickname_ko: string | null;
2323
nickname_en: string | null;
24-
image?: string | null;
24+
image: string | null;
2525
};
2626

2727
type ProfileEditorState = ProfileType & {
@@ -65,8 +65,8 @@ const InnerProfileEditor: React.FC = () => {
6565
const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) =>
6666
enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } });
6767

68-
const setImageId = (image: string | null | undefined) => setEditorState((ps) => ({ ...ps, image }));
69-
const onImageSelectChange = (e: SelectChangeEvent<string | null | undefined>) => setImageId(e.target.value);
68+
const setImageId = (image: string | null) => setEditorState((ps) => ({ ...ps, image }));
69+
const onImageSelectChange = (e: SelectChangeEvent<string | null>) => setImageId(e.target.value);
7070
const setNickname = (value: string | undefined, lang: "ko" | "en") => setEditorState((ps) => ({ ...ps, [`nickname_${lang}`]: value }));
7171

7272
const updateMe = () => {
@@ -105,7 +105,7 @@ const InnerProfileEditor: React.FC = () => {
105105
{profile?.has_requested_modification_audit && <CurrentlyModAuditInProgress language={language} modificationAuditId={modificationAuditId} />}
106106
<PrimaryTitle variant="h4" children={titleStr} />
107107
<Stack spacing={2} sx={{ width: "100%", flexGrow: 1 }}>
108-
<PublicFileSelector label={speakerImageStr} value={editorState.image} onChange={onImageSelectChange} setFileIdAsValue={setImageId} />
108+
<PublicFileSelector label={speakerImageStr} value={editorState.image} onChange={onImageSelectChange} />
109109
<MultiLanguageField
110110
label={{ ko: "닉네임", en: "Nickname" }}
111111
value={{

apps/pyconkr-participant-portal/src/components/pages/session_editor.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ type SessionUpdateSchema = {
2525
summary_en: string;
2626
description_ko: string;
2727
description_en: string;
28-
image?: string | null;
28+
image: string | null;
2929

3030
speakers: {
3131
id: string; // UUID of the speaker
3232
biography_ko: string; // Biography in Korean
3333
biography_en: string; // Biography in English
34-
image?: string | null; // PK of the speaker's image
34+
image: string | null; // PK of the speaker's image
3535
}[];
3636
};
3737

@@ -118,13 +118,13 @@ const InnerSessionEditor: React.FC = () => {
118118
const setTitle = (value: string | undefined, lang: "ko" | "en") => setEditorState((ps) => ({ ...ps, [`title_${lang}`]: value }));
119119
const setSummary = (value: string | undefined, lang: "ko" | "en") => setEditorState((ps) => ({ ...ps, [`summary_${lang}`]: value }));
120120
const setDescription = (value: string | undefined, lang: "ko" | "en") => setEditorState((ps) => ({ ...ps, [`description_${lang}`]: value }));
121-
const setImage = (image: string | null | undefined) => setEditorState((ps) => ({ ...ps, image }));
122-
const setSpeakerImage = (image: string | null | undefined) => setEditorState((ps) => ({ ...ps, speakers: [{ ...speaker, image }] }));
121+
const setImage = (image: string | null) => setEditorState((ps) => ({ ...ps, image }));
122+
const setSpeakerImage = (image: string | null) => setEditorState((ps) => ({ ...ps, speakers: [{ ...speaker, image }] }));
123123
const setSpeakerBiography = (value: string | undefined, lang: "ko" | "en") =>
124124
setEditorState((ps) => ({ ...ps, speakers: [{ ...speaker, [`biography_${lang}`]: value }] }));
125125

126-
const onImageSelectChange = (e: SelectChangeEvent<string | null | undefined>) => setImage(e.target.value);
127-
const onSpeakerImageSelectChange = (e: SelectChangeEvent<string | null | undefined>) => setSpeakerImage(e.target.value);
126+
const onImageSelectChange = (e: SelectChangeEvent<string | null>) => setImage(e.target.value);
127+
const onSpeakerImageSelectChange = (e: SelectChangeEvent<string | null>) => setSpeakerImage(e.target.value);
128128

129129
const updateSession = () => {
130130
updateSessionMutation.mutate(

0 commit comments

Comments
 (0)