Skip to content

Commit 9d7d394

Browse files
committed
feat: 계정 생성 시 랜덤 비밀번호를 사용하도록 수정
1 parent 2f45ef3 commit 9d7d394

File tree

3 files changed

+97
-57
lines changed

3 files changed

+97
-57
lines changed

apps/pyconkr-admin/src/components/layouts/admin_editor.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type AppResourceIdType = AppResourceType & { id?: string };
4545
type AdminEditorPropsType = React.PropsWithChildren<{
4646
hidingFields?: string[];
4747
context?: Record<string, string>;
48+
onCreated?: (data: Record<string, string>) => void;
4849
onClose?: () => void;
4950
beforeSubmit?: onSubmitType;
5051
afterSubmit?: onSubmitType;
@@ -253,7 +254,21 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> = Err
253254
{ fallback: Common.Components.ErrorFallback },
254255
Suspense.with(
255256
{ fallback: <CircularProgress /> },
256-
({ app, resource, id, hidingFields, context, onClose, beforeSubmit, afterSubmit, extraActions, notModifiable, notDeletable, children }) => {
257+
({
258+
app,
259+
resource,
260+
id,
261+
hidingFields,
262+
context,
263+
onCreated,
264+
onClose,
265+
beforeSubmit,
266+
afterSubmit,
267+
extraActions,
268+
notModifiable,
269+
notDeletable,
270+
children,
271+
}) => {
257272
const navigate = useNavigate();
258273
const formRef = React.useRef<Form<Record<string, string>, RJSFSchema, { [k in string]: unknown }> | null>(null);
259274
const [editorState, setEditorState] = React.useState<InnerAdminEditorStateType>({
@@ -297,10 +312,13 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> = Err
297312
beforeSubmit?.(newFormData, event);
298313
submitMutation.mutate(newFormData, {
299314
onSuccess: (newFormData) => {
300-
addSnackbar(id ? "저장했습니다." : "페이지를 생성했습니다.", "success");
301-
afterSubmit?.(newFormData, event);
302-
303-
if (!id && newFormData.id) navigate(`/${app}/${resource}/${newFormData.id}`);
315+
if (!id && onCreated) {
316+
onCreated(newFormData);
317+
} else {
318+
addSnackbar(id ? "저장했습니다." : "페이지를 생성했습니다.", "success");
319+
afterSubmit?.(newFormData, event);
320+
if (!id && newFormData.id) navigate(`/${app}/${resource}/${newFormData.id}`);
321+
}
304322
},
305323
onError: addErrorSnackbar,
306324
});

apps/pyconkr-admin/src/components/pages/user/editor.tsx

Lines changed: 22 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,39 @@
11
import * as Common from "@frontend/common";
2-
import { ContentCopy, KeyOff } from "@mui/icons-material";
3-
import {
4-
Button,
5-
ButtonProps,
6-
CircularProgress,
7-
Dialog,
8-
DialogActions,
9-
DialogContent,
10-
DialogContentText,
11-
DialogTitle,
12-
IconButton,
13-
InputAdornment,
14-
TextField,
15-
} from "@mui/material";
2+
import { KeyOff } from "@mui/icons-material";
3+
import { Button, ButtonProps, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material";
164
import { ErrorBoundary, Suspense } from "@suspensive/react";
175
import * as React from "react";
18-
import { useParams } from "react-router-dom";
6+
import { useNavigate, useParams } from "react-router-dom";
197

20-
import { addErrorSnackbar, addSnackbar } from "../../../utils/snackbar";
8+
import { PasswordResultDialog } from "./password_result_dialog";
9+
import { addErrorSnackbar } from "../../../utils/snackbar";
2110
import { AdminEditor } from "../../layouts/admin_editor";
2211

2312
type PageStateType = {
2413
isConfirmDialogOpen: boolean;
2514
isResultDialogOpen: boolean;
2615
newPassword: string | null;
16+
createdUserId: string | null;
2717
};
2818

2919
export const AdminUserExtEditor: React.FC = ErrorBoundary.with(
3020
{ fallback: Common.Components.ErrorFallback },
3121
Suspense.with({ fallback: <CircularProgress /> }, () => {
3222
const { id } = useParams<{ id?: string }>();
23+
const navigate = useNavigate();
3324
const [pageState, setPageState] = React.useState<PageStateType>({
3425
isConfirmDialogOpen: false,
3526
isResultDialogOpen: false,
3627
newPassword: null,
28+
createdUserId: null,
3729
});
3830
const openConfirmDialog = () => setPageState((ps) => ({ ...ps, isConfirmDialogOpen: true }));
3931
const closeConfirmDialog = () => setPageState((ps) => ({ ...ps, isConfirmDialogOpen: false }));
40-
const closeResultDialog = () => setPageState((ps) => ({ ...ps, isResultDialogOpen: false, newPassword: null }));
32+
const closeResultDialog = () => {
33+
const userId = pageState.createdUserId;
34+
setPageState((ps) => ({ ...ps, isResultDialogOpen: false, newPassword: null, createdUserId: null }));
35+
if (userId) navigate(`/user/userext/${userId}`);
36+
};
4137

4238
const backendAdminClient = Common.Hooks.BackendAdminAPI.useBackendAdminClient();
4339
const useResetPasswordMutation = Common.Hooks.BackendAdminAPI.useResetUserPasswordMutation(backendAdminClient, id || "");
@@ -58,13 +54,13 @@ export const AdminUserExtEditor: React.FC = ErrorBoundary.with(
5854
}
5955
};
6056

61-
const copyPasswordToClipboard = () => {
62-
if (pageState.newPassword) {
63-
navigator.clipboard.writeText(pageState.newPassword).then(
64-
() => addSnackbar("비밀번호가 클립보드에 복사되었습니다.", "success"),
65-
() => addSnackbar("클립보드 복사에 실패했습니다.", "error")
66-
);
67-
}
57+
const onCreated = (data: Record<string, string>) => {
58+
setPageState((ps) => ({
59+
...ps,
60+
isResultDialogOpen: true,
61+
newPassword: data.password,
62+
createdUserId: data.id,
63+
}));
6864
};
6965

7066
const resetUserPasswordButton: ButtonProps = {
@@ -91,35 +87,9 @@ export const AdminUserExtEditor: React.FC = ErrorBoundary.with(
9187
</DialogActions>
9288
</Dialog>
9389

94-
<Dialog open={pageState.isResultDialogOpen} maxWidth="sm" fullWidth>
95-
<DialogTitle>비밀번호가 초기화되었습니다</DialogTitle>
96-
<DialogContent>
97-
<DialogContentText sx={{ mb: 2 }}>
98-
새로운 비밀번호가 생성되었습니다. 이 비밀번호는 다시 확인할 수 없으니 반드시 복사해 두세요.
99-
</DialogContentText>
100-
<TextField
101-
fullWidth
102-
value={pageState.newPassword || ""}
103-
slotProps={{
104-
input: {
105-
readOnly: true,
106-
endAdornment: (
107-
<InputAdornment position="end">
108-
<IconButton onClick={copyPasswordToClipboard} edge="end">
109-
<ContentCopy />
110-
</IconButton>
111-
</InputAdornment>
112-
),
113-
},
114-
}}
115-
/>
116-
</DialogContent>
117-
<DialogActions>
118-
<Button onClick={closeResultDialog}>닫기</Button>
119-
</DialogActions>
120-
</Dialog>
90+
<PasswordResultDialog open={pageState.isResultDialogOpen} password={pageState.newPassword} onClose={closeResultDialog} />
12191

122-
<AdminEditor app="user" resource="userext" id={id} extraActions={[resetUserPasswordButton]} />
92+
<AdminEditor app="user" resource="userext" id={id} extraActions={[resetUserPasswordButton]} onCreated={onCreated} />
12393
</>
12494
);
12595
})
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { ContentCopy } from "@mui/icons-material";
2+
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, InputAdornment, TextField } from "@mui/material";
3+
import * as React from "react";
4+
5+
import { addSnackbar } from "../../../utils/snackbar";
6+
7+
type PasswordResultDialogProps = {
8+
open: boolean;
9+
password: string | null;
10+
onClose: () => void;
11+
};
12+
13+
export const PasswordResultDialog: React.FC<PasswordResultDialogProps> = ({ open, password, onClose }) => {
14+
const copyPasswordToClipboard = () => {
15+
if (password) {
16+
navigator.clipboard.writeText(password).then(
17+
() => addSnackbar("비밀번호가 클립보드에 복사되었습니다.", "success"),
18+
() => addSnackbar("클립보드 복사에 실패했습니다.", "error")
19+
);
20+
}
21+
};
22+
23+
return (
24+
<Dialog open={open} maxWidth="sm" fullWidth>
25+
<DialogTitle>비밀번호가 설정되었습니다</DialogTitle>
26+
<DialogContent>
27+
<DialogContentText sx={{ mb: 2 }}>
28+
새로운 비밀번호가 생성되었습니다. 이 비밀번호는 다시 확인할 수 없으니 반드시 복사해 두세요.
29+
</DialogContentText>
30+
<TextField
31+
fullWidth
32+
value={password || ""}
33+
slotProps={{
34+
input: {
35+
readOnly: true,
36+
endAdornment: (
37+
<InputAdornment position="end">
38+
<IconButton onClick={copyPasswordToClipboard} edge="end">
39+
<ContentCopy />
40+
</IconButton>
41+
</InputAdornment>
42+
),
43+
},
44+
}}
45+
/>
46+
</DialogContent>
47+
<DialogActions>
48+
<Button onClick={onClose}>닫기</Button>
49+
</DialogActions>
50+
</Dialog>
51+
);
52+
};

0 commit comments

Comments
 (0)