Skip to content

Commit cf23ee5

Browse files
committed
Merge remote-tracking branch 'origin/feature/admin-editor-language-select-tab'
2 parents 71d1fe4 + ae6ffac commit cf23ee5

File tree

4 files changed

+147
-52
lines changed

4 files changed

+147
-52
lines changed

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

Lines changed: 110 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import {
66
ButtonProps,
77
CircularProgress,
88
Stack,
9+
Tab,
910
Table,
1011
TableBody,
1112
TableCell,
1213
TableHead,
1314
TableRow,
15+
Tabs,
1416
Typography,
1517
} from "@mui/material";
1618
import Form, { IChangeEvent } from "@rjsf/core";
@@ -88,6 +90,11 @@ const ReadOnlyValueField: React.FC<{
8890
return value as string;
8991
};
9092

93+
type InnerAdminEditorStateType = {
94+
tab: number;
95+
formData: Record<string, string> | undefined;
96+
};
97+
9198
const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> =
9299
ErrorBoundary.with(
93100
{ fallback: Common.Components.ErrorFallback },
@@ -110,9 +117,11 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> =
110117
RJSFSchema,
111118
{ [k in string]: unknown }
112119
> | null>(null);
113-
const [formDataState, setFormDataState] = React.useState<
114-
Record<string, string> | undefined
115-
>(undefined);
120+
const [editorState, setEditorState] =
121+
React.useState<InnerAdminEditorStateType>({
122+
tab: 0,
123+
formData: undefined,
124+
});
116125
const backendAdminClient =
117126
Common.Hooks.BackendAdminAPI.useBackendAdminClient();
118127
const { data: schemaInfo } =
@@ -122,6 +131,13 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> =
122131
resource
123132
);
124133

134+
const setTab = (_: React.SyntheticEvent, selectedTab: number) =>
135+
setEditorState((ps) => ({ ...ps, tab: selectedTab }));
136+
const setFormDataState = (formData?: Record<string, string>) =>
137+
setEditorState((ps) => ({ ...ps, formData }));
138+
const selectedLanguage = editorState.tab === 0 ? "ko" : "en";
139+
const notSelectedLanguage = editorState.tab === 0 ? "en" : "ko";
140+
125141
const createMutation = Common.Hooks.BackendAdminAPI.useCreateMutation<
126142
Record<string, string>
127143
>(backendAdminClient, app, resource);
@@ -195,16 +211,40 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> =
195211
const goToCreateNew = () => navigate(`/${app}/${resource}/create`);
196212

197213
const writableSchema =
198-
Common.Utils.filterWritablePropertiesInJsonSchema(schemaInfo.schema);
214+
Common.Utils.filterPropertiesByLanguageInJsonSchema(
215+
Common.Utils.filterWritablePropertiesInJsonSchema(
216+
schemaInfo.schema
217+
),
218+
schemaInfo.translation_fields,
219+
selectedLanguage
220+
);
199221
const readOnlySchema =
200-
Common.Utils.filterReadOnlyPropertiesInJsonSchema(schemaInfo.schema);
222+
Common.Utils.filterPropertiesByLanguageInJsonSchema(
223+
Common.Utils.filterReadOnlyPropertiesInJsonSchema(
224+
schemaInfo.schema
225+
),
226+
schemaInfo.translation_fields,
227+
selectedLanguage
228+
);
201229
const uiSchema: UiSchema = schemaInfo.ui_schema;
202230
const disabled =
203231
createMutation.isPending ||
204232
modifyMutation.isPending ||
205233
deleteMutation.isPending;
206234
const title = `${app.toUpperCase()} > ${resource.toUpperCase()} > ${id ? "편집: " + id : "새 객체 추가"}`;
207235

236+
const notSelectedLangFields = schemaInfo.translation_fields.map(
237+
(f) => `${f}_${notSelectedLanguage}`
238+
);
239+
const languageFilteredFormData = editorState.formData
240+
? Object.entries(editorState.formData)
241+
.filter(([k]) => !notSelectedLangFields.includes(k))
242+
.reduce(
243+
(acc, [k, v]) => ({ ...acc, [k]: v }),
244+
{} as Record<string, string>
245+
)
246+
: undefined;
247+
208248
const handleCtrlSAction: (
209249
this: GlobalEventHandlers,
210250
ev: KeyboardEvent
@@ -225,56 +265,75 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> =
225265
};
226266
}, []);
227267

228-
if (formDataState === undefined) return <CircularProgress />;
268+
if (editorState.formData === undefined) return <CircularProgress />;
229269

230270
return (
231271
<Box sx={{ flexGrow: 1, width: "100%", minHeight: "100%" }}>
232272
<Typography variant="h5">{title}</Typography>
233-
{id && (
234-
<>
235-
<Table>
236-
<TableHead>
237-
<TableRow>
238-
<TableCell>필드</TableCell>
239-
<TableCell></TableCell>
240-
</TableRow>
241-
</TableHead>
242-
<TableBody>
243-
{Object.keys(readOnlySchema.properties || {}).map((key) => (
244-
<TableRow key={key}>
245-
<TableCell>{key}</TableCell>
246-
<TableCell>
247-
<ReadOnlyValueField
248-
name={key}
249-
value={formDataState?.[key]}
250-
uiSchema={uiSchema}
251-
/>
252-
</TableCell>
253-
</TableRow>
254-
))}
255-
</TableBody>
256-
</Table>
257-
<br />
258-
</>
259-
)}
260-
<MuiForm
261-
ref={formRef}
262-
schema={writableSchema}
263-
uiSchema={{
264-
...uiSchema,
265-
"ui:submitButtonOptions": { norender: true },
266-
}}
267-
validator={customizeValidator({ AjvClass: AjvDraft04 })}
268-
formData={formDataState}
269-
liveValidate
270-
focusOnFirstError
271-
formContext={{ readonlyAsDisabled: true }}
272-
onChange={({ formData }) => setFormDataState(formData)}
273-
onSubmit={onSubmitFunc}
274-
disabled={disabled}
275-
showErrorList={false}
276-
fields={{ file: FileField }}
277-
/>
273+
<Stack
274+
direction="row"
275+
spacing={2}
276+
sx={{ width: "100%", height: "100%", maxWidth: "100%" }}
277+
>
278+
<Tabs
279+
orientation="vertical"
280+
value={editorState.tab}
281+
onChange={setTab}
282+
scrollButtons={false}
283+
>
284+
<Tab wrapped label="한국어" />
285+
<Tab wrapped label="영어" />
286+
</Tabs>
287+
<Box sx={{ flexGrow: 1 }}>
288+
{id && (
289+
<>
290+
<Table>
291+
<TableHead>
292+
<TableRow>
293+
<TableCell>필드</TableCell>
294+
<TableCell></TableCell>
295+
</TableRow>
296+
</TableHead>
297+
<TableBody>
298+
{Object.keys(readOnlySchema.properties || {}).map(
299+
(key) => (
300+
<TableRow key={key}>
301+
<TableCell>{key}</TableCell>
302+
<TableCell>
303+
<ReadOnlyValueField
304+
name={key}
305+
value={languageFilteredFormData?.[key]}
306+
uiSchema={uiSchema}
307+
/>
308+
</TableCell>
309+
</TableRow>
310+
)
311+
)}
312+
</TableBody>
313+
</Table>
314+
<br />
315+
</>
316+
)}
317+
<MuiForm
318+
ref={formRef}
319+
schema={writableSchema}
320+
uiSchema={{
321+
...uiSchema,
322+
"ui:submitButtonOptions": { norender: true },
323+
}}
324+
validator={customizeValidator({ AjvClass: AjvDraft04 })}
325+
formData={languageFilteredFormData}
326+
liveValidate
327+
focusOnFirstError
328+
formContext={{ readonlyAsDisabled: true }}
329+
onChange={({ formData }) => setFormDataState(formData)}
330+
onSubmit={onSubmitFunc}
331+
disabled={disabled}
332+
showErrorList={false}
333+
fields={{ file: FileField }}
334+
/>
335+
</Box>
336+
</Stack>
278337
{children}
279338
<Stack
280339
direction="row"

packages/common/src/schemas/backendAdminAPI.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace BackendAdminAPISchemas {
1515
export type AdminSchemaDefinition = {
1616
schema: RJSFSchema;
1717
ui_schema: UiSchema;
18-
translation_fields: { [key: string]: string };
18+
translation_fields: string[];
1919
};
2020

2121
export type UserSchema = {

packages/common/src/utils/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
isFormValid as _isFormValid,
1212
} from "./form";
1313
import {
14+
filterPropertiesByLanguageInJsonSchema as _filterPropertiesByLanguageInJsonSchema,
1415
filterReadOnlyPropertiesInJsonSchema as _filterReadOnlyPropertiesInJsonSchema,
1516
filterWritablePropertiesInJsonSchema as _filterWritablePropertiesInJsonSchema,
1617
} from "./json_schema";
@@ -28,6 +29,8 @@ namespace Utils {
2829
_filterWritablePropertiesInJsonSchema;
2930
export const filterReadOnlyPropertiesInJsonSchema =
3031
_filterReadOnlyPropertiesInJsonSchema;
32+
export const filterPropertiesByLanguageInJsonSchema =
33+
_filterPropertiesByLanguageInJsonSchema;
3134
}
3235

3336
export default Utils;

packages/common/src/utils/json_schema.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,36 @@ export const filterReadOnlyPropertiesInJsonSchema = (schema: JSONSchema7) => {
4040

4141
return readOnlySchema;
4242
};
43+
44+
type SupportedLanguage = "ko" | "en";
45+
46+
export const filterPropertiesByLanguageInJsonSchema = (
47+
schema: JSONSchema7,
48+
translation_fields: string[] = [],
49+
language: SupportedLanguage
50+
) => {
51+
const filteredSchema: JSONSchema7 = { ...schema };
52+
if (translation_fields.length === 0) return filteredSchema;
53+
54+
const notSelectedLanguage = language === "ko" ? "en" : "ko";
55+
const notSelectedLangFields = translation_fields.map(
56+
(f) => `${f}_${notSelectedLanguage}`
57+
);
58+
if (filteredSchema.properties) {
59+
filteredSchema.properties = Object.entries(filteredSchema.properties)
60+
.filter(([key]) => !notSelectedLangFields.includes(key))
61+
.reduce(
62+
(acc, [propKey, propDef]) => ({
63+
...acc,
64+
[propKey]: filterPropertiesByLanguageInJsonSchema(
65+
propDef as JSONSchema7,
66+
translation_fields,
67+
language
68+
),
69+
}),
70+
{} as JSONSchema7["properties"]
71+
);
72+
}
73+
74+
return filteredSchema;
75+
};

0 commit comments

Comments
 (0)