From 8bc5b5a49baf778fea32928a8383ae140c3f66ab Mon Sep 17 00:00:00 2001 From: chan Date: Tue, 17 Mar 2026 10:17:25 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A6=B0=ED=8A=B8=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/features/users/UserCreatePage.tsx | 6 +- .../src/features/users/UserDetailPage.tsx | 4 +- .../src/features/users/utils/csvParser.ts | 28 ++-- locales/en.toml | 147 ++++++++++++++++-- locales/ko.toml | 44 +++++- locales/template.toml | 40 ++++- .../services/login_challenge_loop_guard.dart | 1 - 7 files changed, 237 insertions(+), 33 deletions(-) diff --git a/adminfront/src/features/users/UserCreatePage.tsx b/adminfront/src/features/users/UserCreatePage.tsx index b657a4cd..451d92f9 100644 --- a/adminfront/src/features/users/UserCreatePage.tsx +++ b/adminfront/src/features/users/UserCreatePage.tsx @@ -477,8 +477,10 @@ function UserCreatePage() { /> {errors.metadata?.[field.key] && (

- {(errors.metadata[field.key] as { message?: string }) - ?.message} + { + (errors.metadata[field.key] as { message?: string }) + ?.message + }

)} diff --git a/adminfront/src/features/users/UserDetailPage.tsx b/adminfront/src/features/users/UserDetailPage.tsx index 3b69b6cb..6278c917 100644 --- a/adminfront/src/features/users/UserDetailPage.tsx +++ b/adminfront/src/features/users/UserDetailPage.tsx @@ -45,7 +45,9 @@ type UserSchemaField = { validation?: string; }; -type UserFormValues = UserUpdateRequest & { metadata: Record> }; +type UserFormValues = UserUpdateRequest & { + metadata: Record>; +}; // [New] Component for per-tenant profile/schema management function TenantProfileCard({ diff --git a/adminfront/src/features/users/utils/csvParser.ts b/adminfront/src/features/users/utils/csvParser.ts index 7be80adb..f4ce6543 100644 --- a/adminfront/src/features/users/utils/csvParser.ts +++ b/adminfront/src/features/users/utils/csvParser.ts @@ -13,25 +13,27 @@ export function parseUserCSV(text: string): BulkUserItem[] { if (!lines[i].trim()) continue; const values = lines[i].split(",").map((v) => v.trim()); - const item: Record = { metadata: {} }; + const item: Partial & { metadata: Record } = { + metadata: {}, + }; for (let index = 0; index < headers.length; index++) { const header = headers[index]; const value = values[index]; if (value === undefined || value === "") continue; - if ( - [ - "email", - "name", - "phone", - "role", - "companycode", - "department", - ].includes(header) - ) { - const key = header === "companycode" ? "companyCode" : header; - item[key] = value; + if (header === "email") { + item.email = value; + } else if (header === "name") { + item.name = value; + } else if (header === "phone") { + item.phone = value; + } else if (header === "role") { + item.role = value; + } else if (header === "companycode") { + item.companyCode = value; + } else if (header === "department") { + item.department = value; } else { item.metadata[header] = value; } diff --git a/locales/en.toml b/locales/en.toml index 0ef6eb11..7d9ca1f5 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -116,6 +116,10 @@ count = "Count" [msg.admin.groups] +[msg.admin.groups.create] +description = "Adds a new organization unit such as a department or team." +title = "Create New Organization Unit" + [msg.admin.groups.list] create_error = "Create Failed" create_success = "Create Success" @@ -388,7 +392,7 @@ docs_body = "Includes PKCE, client_secret_basic, redirect URI validation tips." subtitle = "Developer guides for Confidential/Public clients, redirect URIs, and auth methods." [msg.dev.clients.registry] -description = "Description" +description = "OIDC 앱, 인증 방식, 리다이렉트 URI, 비밀키 재발행을 감사 로그와 함께 관리합니다." [msg.dev.clients.scopes] email = "Email" @@ -588,15 +592,102 @@ organization = "Organization" security = "Security" [msg.userfront.qr] -rescan = "Rescan" -result_success = "Result Success" -title = "Scan QR Code" +camera_error = "Camera Error" +permission_error = "Permission Error" +permission_required = "Permission Required" [msg.userfront.reset] -confirm_password = "Confirm Password" -new_password = "New Password" -submit = "Submit" -subtitle = "Subtitle" +invalid_body = "Invalid Body" +invalid_link = "Invalid Link" +invalid_title = "Invalid Title" +policy_loading = "Policy Loading" +success = "Success" + +[msg.userfront.reset.error] +empty_password = "Please enter Password." +generic = "Generic" +lowercase = "Lowercase" +min_length = "Min Length" +min_types = "Min Types" +mismatch = "Mismatch" +number = "Number" +symbol = "Symbol" +uppercase = "Uppercase" + +[msg.userfront.reset.policy] +lowercase = "Lowercase" +min_length = "Min Length" +min_types = "Min Types" +number = "Number" +symbol = "Symbol" +uppercase = "Uppercase" + +[msg.userfront.sections] +apps_subtitle = "Apps Subtitle" +audit_subtitle = "Audit Subtitle" + +[msg.userfront.settings] +disabled = "Disabled" + +[msg.userfront.signup] +failed = "Failed" +privacy_full = "Privacy Full" +tos_full = "Tos Full" + +[msg.userfront.signup.agreement] +title = "Agreement Title" + +[msg.userfront.signup.auth] +affiliate_notice = "Affiliate Notice" +title = "Auth Title" + +[msg.userfront.signup.email] +code_mismatch = "Code Mismatch" +duplicate = "Duplicate" +invalid = "Invalid" +send_failed = "Send Failed" +verified = "Verified" +verify_failed = "Verify Failed" + +[msg.userfront.signup.password] +length_required = "Length Required" +lowercase_required = "Lowercase Required" +mismatch = "Mismatch" +number_required = "Number Required" +symbol_required = "Symbol Required" +title = "Password Title" +uppercase_required = "Uppercase Required" + +[msg.userfront.signup.password.rule] +lowercase = "Lowercase" +min_length = "Min Length" +min_types = "Min Types" +number = "Number" +symbol = "Symbol" +uppercase = "Uppercase" + +[msg.userfront.signup.phone] +code_mismatch = "Code Mismatch" +send_failed = "Send Failed" +verified = "Verified" +verify_failed = "Verify Failed" + +[msg.userfront.signup.policy] +loading = "Loading" +lowercase = "Lowercase" +min_length = "Min Length" +min_types = "Min Types" +number = "Number" +summary = "Summary" +symbol = "Symbol" +uppercase = "Uppercase" + +[msg.userfront.signup.profile] +affiliate_hint = "Affiliate Hint" +title = "Profile Title" + +[msg.userfront.signup.success] +body = "Body" title = "Title" [ui] @@ -688,6 +779,7 @@ time = "TIME" import_csv = "Import Csv" [ui.admin.groups.create] +description = "Adds a new organization unit such as a department or team." title = "Title" [ui.admin.groups.detail] @@ -704,6 +796,7 @@ desc_label = "Description" desc_placeholder = "Desc Placeholder" name_label = "Group Name" name_placeholder = "Name Placeholder" +parent_label = "Parent Unit" submit = "Submit" unit_level_label = "Unit Level Label" unit_level_placeholder = "Unit Level Placeholder" @@ -859,6 +952,18 @@ name = "NAME" role = "ROLE" status = "STATUS" +[ui.admin.tenants.profile] +allowed_domains = "Allowed Domains" +allowed_domains_help = "Users with these email domains will be automatically assigned to this tenant." +approve_button = "Approve Tenant" +description = "Description" +name = "Tenant Name" +slug = "Slug" +status = "Status" +subtitle = "Slug and status changes are applied immediately." +title = "Tenant Profile" +type = "Type" + [ui.admin.tenants.registry] title = "Tenant registry" @@ -942,12 +1047,16 @@ department = "Department" department_placeholder = "Department Placeholder" email = "Email" email_placeholder = "user@example.com" +job_title = "Job Title" +job_title_placeholder = "e.g. Frontend Developer" name = "Name" name_placeholder = "Name Placeholder" password = "Password" password_placeholder = "********" phone = "Phone number" phone_placeholder = "010-1234-5678" +position = "Position" +position_placeholder = "e.g. Senior" role = "Role" tenant = "Tenant" tenant_global = "Tenant Global" @@ -967,7 +1076,10 @@ section = "Users" multi_title = "Per-tenant Profile Management" [ui.admin.users.detail.form] -name_required = "Name is required." +department = "Department" +department_placeholder = "Department Placeholder" +name = "Name" +name_placeholder = "Name Placeholder" phone = "Phone number" phone_placeholder = "010-1234-5678" role = "Role" @@ -992,6 +1104,7 @@ empty = "Empty" fetch_error = "Fetch Error" search_placeholder = "Search Placeholder" subtitle = "Subtitle" +title = "User Manage" [ui.admin.users.list.breadcrumb] list = "List" @@ -1005,6 +1118,7 @@ tenant = "Tenant Filter" [ui.admin.users.list.registry] count = "Count" +title = "User Registry" [ui.admin.users.list.table] actions = "ACTIONS" @@ -1148,6 +1262,7 @@ revoke = "Revoke" revoked_at = "Revoked: " scope_label = "Scope:" search_placeholder = "Search Placeholder" +status_all = "All Statuses" status_label = "Status:" status_revoked = "Revoked" subject = "Subject" @@ -1155,6 +1270,7 @@ title = "User Consent Grants" [ui.dev.clients.consents.breadcrumb] clients = "Clients" +current = "User Consent Grants" home = "Home" [ui.dev.clients.consents.filters] @@ -1248,7 +1364,9 @@ pkce = "PKCE" title = "Security Settings" [ui.dev.clients.help] +docs_body = "Includes PKCE, client_secret_basic, redirect URI validation tips." docs_title = "Docs & Examples" +subtitle = "Developer guides for Confidential/Public clients, redirect URIs, and auth methods." title = "Need help with OIDC configuration?" view_guides = "View guides" @@ -1265,9 +1383,15 @@ subtitle = "Tenant admin on-call" title = "Owner" [ui.dev.clients.registry] +description = "OIDC 앱, 인증 방식, 리다이렉트 URI, 비밀키 재발행을 감사 로그와 함께 관리합니다." subtitle = "Applications" title = "RP registry" +[ui.dev.clients.scopes] +email = "Email" +openid = "Openid" +profile = "Profile" + [ui.dev.clients.table] actions = "Actions" application = "Application" @@ -1277,8 +1401,8 @@ status = "Status" type = "Type" [ui.dev.clients.type] -private = "Server side App" pkce = "PKCE" +private = "Server side App" [ui.dev.dashboard] ready_badge = "devfront ready" @@ -1470,6 +1594,9 @@ organization = "Organization" security = "Security" [ui.userfront.qr] +camera_error = "Camera Error" +permission_error = "Permission Error" +permission_required = "Permission Required" rescan = "Rescan" result_success = "Result Success" title = "Scan QR Code" diff --git a/locales/ko.toml b/locales/ko.toml index a6b05cc7..fe4f867c 100644 --- a/locales/ko.toml +++ b/locales/ko.toml @@ -116,6 +116,10 @@ count = "로드된 로그 {{count}}건" [msg.admin.groups] +[msg.admin.groups.create] +description = "부서나 팀과 같은 새로운 조직 단위를 추가합니다." +title = "새 조직 단위 생성" + [msg.admin.groups.list] create_error = "생성 실패" create_success = "조직 단위가 생성되었습니다." @@ -775,6 +779,7 @@ time = "TIME" import_csv = "CSV 임포트" [ui.admin.groups.create] +description = "부서나 팀과 같은 새로운 조직 단위를 추가합니다." title = "새 그룹 생성" [ui.admin.groups.detail] @@ -791,6 +796,7 @@ desc_label = "설명" desc_placeholder = "그룹 용도 설명" name_label = "그룹 이름" name_placeholder = "예: 개발팀, 인사팀" +parent_label = "상위 조직" submit = "생성하기" unit_level_label = "조직 레벨" unit_level_placeholder = "예: 본부, 팀" @@ -849,7 +855,7 @@ view_audit_logs = "감사 로그 보기" audit_events_24h = "24시간 이벤트" oidc_clients = "OIDC 클라이언트" policy_gate = "정책 게이트" -total_tenants = "전체 테넌트" +total_tenants = "전체 테넌트 수" [ui.admin.profile] manageable_tenants = "관리 가능한 테넌트" @@ -946,6 +952,18 @@ name = "NAME" role = "ROLE" status = "STATUS" +[ui.admin.tenants.profile] +allowed_domains = "허용된 도메인 (콤마로 구분)" +allowed_domains_help = "이 도메인을 가진 이메일로 가입한 사용자는 자동으로 이 테넌트에 배정됩니다." +approve_button = "테넌트 승인" +description = "설명" +name = "테넌트 이름" +slug = "슬러그 (Slug)" +status = "상태" +subtitle = "슬러그 및 상태 변경은 즉시 적용됩니다." +title = "테넌트 프로필" +type = "테넌트 유형" + [ui.admin.tenants.registry] title = "Tenant registry" @@ -1029,12 +1047,16 @@ department = "부서" department_placeholder = "개발팀" email = "이메일" email_placeholder = "user@example.com" +job_title = "직무" +job_title_placeholder = "프론트엔드 개발" name = "이름" name_placeholder = "홍길동" password = "비밀번호" password_placeholder = "********" phone = "전화번호" phone_placeholder = "010-1234-5678" +position = "직급" +position_placeholder = "수석/책임/선임" role = "역할" tenant = "테넌트" tenant_global = "시스템 전역" @@ -1054,7 +1076,10 @@ section = "Users" multi_title = "테넌트별 프로필 관리" [ui.admin.users.detail.form] -name_required = "이름은 필수입니다." +department = "부서" +department_placeholder = "개발팀" +name = "이름" +name_placeholder = "홍길동" phone = "전화번호" phone_placeholder = "010-1234-5678" role = "역할" @@ -1079,6 +1104,7 @@ empty = "검색 결과가 없습니다." fetch_error = "사용자 목록 조회에 실패했습니다." search_placeholder = "이름 또는 이메일 검색..." subtitle = "시스템 사용자를 조회하고 관리합니다." +title = "사용자 관리" [ui.admin.users.list.breadcrumb] list = "List" @@ -1092,6 +1118,7 @@ tenant = "테넌트 필터" [ui.admin.users.list.registry] count = "총 {{count}}명의 사용자가 등록되어 있습니다." +title = "사용자 레지스트리" [ui.admin.users.list.table] actions = "ACTIONS" @@ -1242,6 +1269,7 @@ title = "User Consent Grants" [ui.dev.clients.consents.breadcrumb] clients = "Clients" +current = "User Consent Grants" home = "Home" [ui.dev.clients.consents.filters] @@ -1335,7 +1363,9 @@ pkce = "PKCE" title = "보안 설정" [ui.dev.clients.help] +docs_body = "Includes PKCE, client_secret_basic, redirect URI validation tips." docs_title = "Docs & Examples" +subtitle = "Developer guides for Confidential/Public clients, redirect URIs, and auth methods." title = "Need help with OIDC configuration?" view_guides = "View guides" @@ -1352,9 +1382,15 @@ subtitle = "Tenant admin on-call" title = "Owner" [ui.dev.clients.registry] +description = "OIDC 앱, 인증 방식, 리다이렉트 URI, 비밀키 재발행을 감사 로그와 함께 관리합니다." subtitle = "연동 앱" title = "RP registry" +[ui.dev.clients.scopes] +email = "이메일 주소 접근" +openid = "OIDC 인증 필수 스코프" +profile = "기본 프로필 정보 접근" + [ui.dev.clients.table] actions = "액션" application = "애플리케이션" @@ -1402,8 +1438,8 @@ plane = "Dev Plane" subtitle = "Manage your applications" [ui.dev.session] -active = "만료 시간 확인 중..." -unknown = "확인 불가" +active = "세션 활성" +unknown = "알 수 없음" expired = "세션 만료" expiring = "만료 임박: {{minutes}}분 {{seconds}}초 남음" remaining = "만료 예정: {{minutes}}분 {{seconds}}초 남음" diff --git a/locales/template.toml b/locales/template.toml index 38372e8d..c60e787d 100644 --- a/locales/template.toml +++ b/locales/template.toml @@ -116,6 +116,10 @@ count = "" [msg.admin.groups] +[msg.admin.groups.create] +description = "" +title = "" + [msg.admin.groups.list] create_error = "" create_success = "" @@ -775,6 +779,7 @@ time = "" import_csv = "" [ui.admin.groups.create] +description = "" title = "" [ui.admin.groups.detail] @@ -791,6 +796,7 @@ desc_label = "" desc_placeholder = "" name_label = "" name_placeholder = "" +parent_label = "" submit = "" unit_level_label = "" unit_level_placeholder = "" @@ -946,6 +952,18 @@ name = "" role = "" status = "" +[ui.admin.tenants.profile] +allowed_domains = "" +allowed_domains_help = "" +approve_button = "" +description = "" +name = "" +slug = "" +status = "" +subtitle = "" +title = "" +type = "" + [ui.admin.tenants.registry] title = "" @@ -1029,12 +1047,16 @@ department = "" department_placeholder = "" email = "" email_placeholder = "" +job_title = "" +job_title_placeholder = "" name = "" name_placeholder = "" password = "" password_placeholder = "" phone = "" phone_placeholder = "" +position = "" +position_placeholder = "" role = "" tenant = "" tenant_global = "" @@ -1054,7 +1076,10 @@ section = "" multi_title = "" [ui.admin.users.detail.form] -name_required = "" +department = "" +department_placeholder = "" +name = "" +name_placeholder = "" phone = "" phone_placeholder = "" role = "" @@ -1079,6 +1104,7 @@ empty = "" fetch_error = "" search_placeholder = "" subtitle = "" +title = "" [ui.admin.users.list.breadcrumb] list = "" @@ -1092,6 +1118,7 @@ tenant = "" [ui.admin.users.list.registry] count = "" +title = "" [ui.admin.users.list.table] actions = "" @@ -1242,6 +1269,7 @@ title = "" [ui.dev.clients.consents.breadcrumb] clients = "" +current = "" home = "" [ui.dev.clients.consents.filters] @@ -1335,7 +1363,9 @@ pkce = "" title = "" [ui.dev.clients.help] +docs_body = "" docs_title = "" +subtitle = "" title = "" view_guides = "" @@ -1352,9 +1382,15 @@ subtitle = "" title = "" [ui.dev.clients.registry] +description = "" subtitle = "" title = "" +[ui.dev.clients.scopes] +email = "" +openid = "" +profile = "" + [ui.dev.clients.table] actions = "" application = "" @@ -1364,8 +1400,8 @@ status = "" type = "" [ui.dev.clients.type] -private = "" pkce = "" +private = "" [ui.dev.dashboard] ready_badge = "" diff --git a/userfront/lib/core/services/login_challenge_loop_guard.dart b/userfront/lib/core/services/login_challenge_loop_guard.dart index d3a6e3d0..a6999d98 100644 --- a/userfront/lib/core/services/login_challenge_loop_guard.dart +++ b/userfront/lib/core/services/login_challenge_loop_guard.dart @@ -1,4 +1,3 @@ -import 'login_challenge_loop_guard_base.dart'; import 'login_challenge_loop_guard_stub.dart' if (dart.library.js_interop) 'login_challenge_loop_guard_web.dart';