Skip to content

Commit d7ef946

Browse files
committed
feat: 필터에 선택이 가능하도록 수정
1 parent 63a205b commit d7ef946

6 files changed

Lines changed: 62 additions & 4 deletions

File tree

apps/pyconkr-admin/src/components/elements/admin_list_filter.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import * as React from "react";
55
import BackendAdminAPISchemas from "../../../../../packages/common/src/schemas/backendAdminAPI";
66

77
type OpenAPIParameterSchema = BackendAdminAPISchemas.OpenAPIParameterSchema;
8+
type ChoicesResponse = BackendAdminAPISchemas.ChoicesResponse;
89

910
type AdminListFilterProps = {
1011
parameters: OpenAPIParameterSchema[];
1112
values: Record<string, string>;
13+
choices?: ChoicesResponse;
1214
onApply: (values: Record<string, string>) => void;
1315
};
1416

15-
export const AdminListFilter: React.FC<AdminListFilterProps> = ({ parameters, values, onApply }) => {
17+
export const AdminListFilter: React.FC<AdminListFilterProps> = ({ parameters, values, choices, onApply }) => {
1618
const [localValues, setLocalValues] = React.useState<Record<string, string>>(values);
1719

1820
React.useEffect(() => {
@@ -44,7 +46,13 @@ export const AdminListFilter: React.FC<AdminListFilterProps> = ({ parameters, va
4446
</Stack>
4547
<Stack direction="row" spacing={2} sx={{ flexWrap: "wrap", alignItems: "flex-start" }}>
4648
{parameters.map((param) => (
47-
<FilterField key={param.name} param={param} value={localValues[param.name] || ""} onChange={handleChange} />
49+
<FilterField
50+
key={param.name}
51+
param={param}
52+
value={localValues[param.name] || ""}
53+
choices={choices?.[param.name]}
54+
onChange={handleChange}
55+
/>
4856
))}
4957
</Stack>
5058
<Stack direction="row" spacing={1}>
@@ -60,18 +68,39 @@ export const AdminListFilter: React.FC<AdminListFilterProps> = ({ parameters, va
6068
);
6169
};
6270

71+
type ChoiceItem = { const: string | null; title: string };
72+
6373
type FilterFieldProps = {
6474
param: OpenAPIParameterSchema;
6575
value: string;
76+
choices?: ChoiceItem[];
6677
onChange: (name: string, value: string) => void;
6778
};
6879

69-
const FilterField: React.FC<FilterFieldProps> = ({ param, value, onChange }) => {
80+
const FilterField: React.FC<FilterFieldProps> = ({ param, value, choices, onChange }) => {
7081
const { name, schema, description } = param;
7182

7283
if (schema?.type === "array") return <ArrayFilterField name={name} items={schema.items} value={value} onChange={onChange} />;
7384
if (schema?.enum) return <EnumFilterField name={name} options={schema.enum} value={value} onChange={onChange} />;
7485

86+
if (choices && choices.length > 0) {
87+
return (
88+
<FormControl size="small" sx={{ minWidth: 200 }}>
89+
<InputLabel>{name}</InputLabel>
90+
<Select value={value} label={name} onChange={(e) => onChange(name, e.target.value as string)}>
91+
<MenuItem value="">
92+
<em>전체</em>
93+
</MenuItem>
94+
{choices.map((choice) => (
95+
<MenuItem key={choice.const ?? "__null__"} value={choice.const ?? ""}>
96+
{choice.title}
97+
</MenuItem>
98+
))}
99+
</Select>
100+
</FormControl>
101+
);
102+
}
103+
75104
const inputType = schema?.type === "integer" || schema?.type === "number" ? "number" : "text";
76105
const helperText = schema?.format === "uuid" ? "UUID" : description || undefined;
77106

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,21 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> = Err
277277
});
278278
const backendAdminClient = Common.Hooks.BackendAdminAPI.useBackendAdminClient();
279279
const { data: schemaInfo } = Common.Hooks.BackendAdminAPI.useSchemaQuery(backendAdminClient, app, resource);
280+
const { data: choicesData } = Common.Hooks.BackendAdminAPI.useChoicesQuery(backendAdminClient, app, resource);
281+
282+
// Merge choices into schema for FK/M2M fields
283+
React.useMemo(() => {
284+
if (!choicesData || !schemaInfo.schema.properties) return;
285+
for (const [fieldName, items] of Object.entries(choicesData)) {
286+
const prop = (schemaInfo.schema.properties as Record<string, RJSFSchema>)[fieldName];
287+
if (!prop) continue;
288+
if (prop.type === "array" && prop.items) {
289+
(prop.items as RJSFSchema).oneOf = items;
290+
} else {
291+
prop.oneOf = items;
292+
}
293+
}
294+
}, [choicesData, schemaInfo.schema]);
280295

281296
const setTab = (_: React.SyntheticEvent, tab: number) => setEditorState((ps) => ({ ...ps, tab }));
282297
const setFormData = (formData?: Record<string, string>) => setEditorState((ps) => ({ ...ps, formData }));

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const InnerAdminList: React.FC<AdminListProps> = ErrorBoundary.with(
3939
[openApiSchemaQuery.data, app, resource]
4040
);
4141

42+
const choicesQuery = Common.Hooks.BackendAdminAPI.useChoicesQuery(backendAdminClient, app, resource);
43+
4244
const handleFilterApply = (newParams: Record<string, string>) => setSearchParams(newParams, { replace: true });
4345

4446
return (
@@ -47,7 +49,7 @@ const InnerAdminList: React.FC<AdminListProps> = ErrorBoundary.with(
4749
{app.toUpperCase()} &gt; {resource.toUpperCase()} &gt; 목록
4850
</Typography>
4951
<br />
50-
<AdminListFilter parameters={queryParameters} values={filterParams} onApply={handleFilterApply} />
52+
<AdminListFilter parameters={queryParameters} values={filterParams} choices={choicesQuery.data} onApply={handleFilterApply} />
5153
<Box>
5254
{!hideCreateNew && (
5355
<Button variant="contained" onClick={() => navigate(`/${app}/${resource}/create`)} startIcon={<Add />}>

packages/common/src/apis/admin_api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ namespace BackendAdminAPIs {
5858
export const schema = (client: BackendAPIClient, app: string, resource: string) => () =>
5959
client.get<BackendAdminAPISchemas.AdminSchemaDefinition>(`v1/admin-api/${app}/${resource}/json-schema/`);
6060

61+
export const choices = (client: BackendAPIClient, app: string, resource: string) => () =>
62+
client.get<BackendAdminAPISchemas.ChoicesResponse>(`v1/admin-api/${app}/${resource}/choices/`);
63+
6164
export const openApiSchema = (client: BackendAPIClient) => () =>
6265
client.get<BackendAdminAPISchemas.OpenAPISchema>("api/schema/v1/", { params: { format: "json" } });
6366

packages/common/src/hooks/useAdminAPI.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const QUERY_KEYS = {
1010
ADMIN_LIST: ["query", "admin", "list"],
1111
ADMIN_RETRIEVE: ["query", "admin", "retrieve"],
1212
ADMIN_SCHEMA: ["query", "admin", "schema"],
13+
ADMIN_CHOICES: ["query", "admin", "choices"],
1314
ADMIN_OPENAPI_SCHEMA: ["query", "admin", "openapi-schema"],
1415
ADMIN_PREVIEW_MODIFICATION_AUDIT: ["query", "admin", "retrieve", "modification-audit"],
1516
};
@@ -68,6 +69,12 @@ namespace BackendAdminAPIHooks {
6869
queryFn: BackendAdminAPIs.schema(client, app, resource),
6970
});
7071

72+
export const useChoicesQuery = (client: BackendAPIClient, app: string, resource: string) =>
73+
useSuspenseQuery({
74+
queryKey: [...QUERY_KEYS.ADMIN_CHOICES, app, resource],
75+
queryFn: BackendAdminAPIs.choices(client, app, resource),
76+
});
77+
7178
export const useOpenApiSchemaQuery = (client: BackendAPIClient) =>
7279
useSuspenseQuery({
7380
queryKey: QUERY_KEYS.ADMIN_OPENAPI_SCHEMA,

packages/common/src/schemas/backendAdminAPI.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace BackendAdminAPISchemas {
1818
translation_fields: string[];
1919
};
2020

21+
export type ChoicesResponse = Record<string, { const: string | null; title: string }[]>;
22+
2123
export type UserSchema = {
2224
id: number;
2325
username: string;

0 commit comments

Comments
 (0)