Files
HeuJung d17ea28da5 fix(core,board,ecommerce,page,kginicis): 정책값 하드코딩·계약 분리 결함 일괄 수정
관리자 환경설정이 값을 저장은 하지만 실제 동작에는 닿지 않던 결함군을 정리했다.
설정 화면(min/max)·저장 검증(FormRequest)·런타임 적용(Service) 세 계층이 각자
리터럴을 들고 있어 서로 조용히 갈라지던 것이 공통 원인이다.

경계값 SSoT 도입:
- config/core.php `settings_limits` 를 단일 출처로 두고, FormRequest 는 이 값으로
 규칙을 만들고 화면은 설정 응답의 `_meta.limits` 로 같은 값을 받아 바인딩한다.
- 게시판·이커머스도 각 모듈 config 의 limits 를 동일 구조로 노출한다.

적용되지 않던 정책을 실제로 적용:
- 비밀번호 최소 길이·특수문자 필수(신규 지정 7개 경로. 로그인은 제외 — 정책을
 올렸을 때 기존 회원이 자기 계정에 접근조차 못 하는 데드락을 막기 위함)
- 계정 잠금 시간 0 = 무한대(전용 컬럼 분리. timestamp 상한과 NULL 규약 때문에
 기존 컬럼 재사용이 불가능하다) + 관리자 수동 해제 경로
- 업로드 최대 크기·허용 확장자, 게시판 첨부 개수·댓글 깊이, 페이지 첨부 정책,
 장바구니 수량·구매 한도, 마일리지 사용 한도, 주문 부가세

파생 결함:
- 큐 Job(sync 드라이버)이 진행 중인 요청의 Request 바인딩을 덮어써, 뒤이어 실행되는
 훅 리스너가 헤더를 잃고 조용히 건너뛰었다. Job 이 원 바인딩을 복원하도록 고치고,
 전송 헤더가 필요한 리스너는 동기 실행으로 선언한다.
- 확장 업데이트 실패 시 상태만 되돌리고 상태 캐시를 비우지 않아, 그 확장의 관리자
 화면만 캐시 TTL(기본 하루) 동안 404 로 남았다. 실패 경로에도 무효화를 대칭으로
 두고, 빈 목록 자체를 캐시하지 않는다.
- 글 작성 시 첨부 파일이 Service 로 전달되지 않아 검증·권한은 통과하고 첨부만 사라졌다.
- 환경설정 저장 실패 안내가 내부 설정 키를 그대로 노출했다(105개 항목에 라벨 부여).

재발 방지:
- audit 룰 3종(레이아웃 경계 리터럴 / FormRequest 경계 불일치 / Service 재클램프)
- 테스트 실행 범위 강제 훅(check-test-scope)
- 트러블슈팅 사례 2건(큐 Job 요청 오염 / 확장 상태 캐시 고착)

공개 이슈: gnuboard/g7
2026-07-30 07:52:24 +09:00

1376 lines
53 KiB
JSON

{
"version": "1.0.0",
"layout_name": "admin_user_detail",
"permissions": [
"core.users.read"
],
"extends": "_admin_base",
"meta": {
"title": "$t:admin.users.detail.page_title",
"description": "$t:admin.users.detail.page_description"
},
"data_sources": [
{
"id": "user",
"label_key": "$t:editor.data_source.user",
"type": "api",
"endpoint": "/api/admin/users/{{route?.id}}",
"method": "GET",
"auto_fetch": true,
"auth_required": true
}
],
"errorHandling": {
"403": {
"handler": "showErrorPage",
"params": {
"target": "content"
}
},
"404": {
"handler": "showErrorPage",
"params": {
"target": "content"
}
}
},
"state": {
"activeTab": "basic_info"
},
"slots": {
"content": [
{
"id": "user_detail_content",
"type": "basic",
"name": "Div",
"props": {
"className": "admin-page-content"
},
"children": [
{
"id": "page_header",
"type": "basic",
"name": "Div",
"props": {
"className": "page-header-fullbleed"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between"
},
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "H1",
"props": {
"className": "page-title"
},
"text": "$t:admin.users.detail.page_title"
},
{
"type": "basic",
"name": "P",
"props": {
"className": "page-description"
},
"text": "$t:admin.users.detail.page_description_with_info|name={{user?.data?.name ?? ''}}|email={{user?.data?.email ?? ''}}"
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2"
},
"children": [
{
"id": "header_back_button",
"type": "basic",
"name": "Button",
"props": {
"className": "flex-center gap-2 btn btn-secondary"
},
"actions": [
{
"type": "click",
"handler": "navigateBack"
}
],
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "arrow-left",
"className": "text-base"
}
},
{
"type": "basic",
"name": "Span",
"text": "$t:common.back"
}
]
},
{
"id": "header_edit_button",
"type": "basic",
"name": "Button",
"props": {
"disabled": "{{user?.data?.abilities?.can_update !== true}}",
"className": "flex-center gap-2 px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
},
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/users/{{route?.id}}/edit",
"mergeQuery": true,
"query": {}
}
}
],
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "pencil",
"className": "text-base"
}
},
{
"type": "basic",
"name": "Span",
"text": "$t:admin.users.detail.edit"
}
]
},
{
"id": "header_unlock_button",
"type": "basic",
"name": "Button",
"if": "{{user?.data?.is_locked === true}}",
"props": {
"disabled": "{{user?.data?.abilities?.can_update !== true}}",
"className": "flex-center gap-2 btn btn-warning"
},
"actions": [
{
"type": "click",
"handler": "apiCall",
"auth_required": true,
"target": "/api/admin/users/{{route?.id}}/unlock",
"params": {
"method": "POST"
},
"onSuccess": [
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "user"
}
},
{
"handler": "toast",
"params": {
"type": "success",
"message": "$t:admin.users.detail.unlock_success"
}
}
],
"onError": [
{
"handler": "toast",
"params": {
"type": "error",
"message": "{{error.message}}"
}
}
]
}
],
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "unlock",
"className": "text-base"
}
},
{
"type": "basic",
"name": "Span",
"text": "$t:admin.users.detail.unlock"
}
]
}
]
}
]
}
]
},
{
"id": "user_detail_tabs",
"type": "composite",
"name": "TabNavigationScroll",
"props": {
"className": "sticky-tab-nav tab-nav-default",
"activeTabId": "{{_local.activeTab ?? 'basic_info'}}",
"enableScrollSpy": true,
"scrollSpyOffset": 56,
"sectionIdPrefix": "",
"tabs": [
{
"id": "basic_info",
"label": "$t:admin.users.detail.tabs.basic_info"
},
{
"id": "contact_info",
"label": "$t:admin.users.detail.tabs.contact_info"
},
{
"id": "location_info",
"label": "$t:admin.users.detail.tabs.location_info"
},
{
"id": "profile_memo",
"label": "$t:admin.users.detail.tabs.profile_memo"
},
{
"id": "activity_info",
"label": "$t:admin.users.detail.tabs.activity_info"
},
{
"id": "consent_info",
"label": "$t:admin.users.detail.tabs.consent_info"
}
]
},
"actions": [
{
"type": "change",
"handler": "setState",
"params": {
"target": "local",
"activeTab": "{{$event.value}}"
}
}
]
},
{
"id": "user_api_error",
"type": "basic",
"name": "Div",
"if": "{{_dataSourceErrors?.user}}",
"props": {
"className": "alert-danger"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-3"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "triangle-exclamation",
"className": "text-xl text-red-600 dark:text-red-400 flex-shrink-0"
}
},
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "H3",
"props": {
"className": "text-error-strong"
},
"text": "$t:admin.users.detail.error_loading_user"
},
{
"type": "basic",
"name": "P",
"props": {
"className": "text-error-soft mt-1"
},
"text": "{{_dataSourceErrors?.user?.message ?? '$t:common.error_occurred'}}"
}
]
}
]
}
]
},
{
"id": "section_basic_info",
"type": "basic",
"name": "Div",
"props": {
"id": "basic_info",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "user",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.form.sections.basic_info"
}
]
},
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "row-stack"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_uuid",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "UUID"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm font-mono"
},
"text": "{{user?.data?.uuid ?? '-'}}"
}
]
},
{
"id": "field_name",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.name"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.name ?? '-'}}"
}
]
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_nickname",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.nickname"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.nickname ?? '-'}}"
}
]
},
{
"id": "field_roles",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.role"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex flex-wrap gap-1"
},
"children": [
{
"type": "basic",
"name": "Span",
"iteration": {
"source": "user?.data?.roles ?? []",
"item_var": "role"
},
"props": {
"className": "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
},
"text": "{{role.name}}"
}
]
}
]
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_status",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.status"
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "{{user?.data?.status_variant === 'success' ? 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : user?.data?.status_variant === 'secondary' ? 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200' : user?.data?.status_variant === 'danger' ? 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' : 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'}}"
},
"text": "{{user?.data?.status_label ?? '-'}}"
}
]
},
{
"id": "field_email_verified",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.detail.email_verification"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-1"
},
"children": [
{
"type": "basic",
"name": "Icon",
"condition": "{{user?.data?.email_verified_at}}",
"props": {
"name": "check-circle",
"className": "text-base text-green-500"
}
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.email_verified_at ? '$t:admin.users.form.fields.email_verified' : '$t:admin.users.detail.not_verified'}}"
}
]
}
]
}
]
}
]
}
]
}
]
},
{
"id": "section_contact_info",
"type": "basic",
"name": "Div",
"props": {
"id": "contact_info",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "envelope",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.form.sections.contact_info"
}
]
},
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "row-stack"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_email",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.email"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.email ?? '-'}}"
}
]
},
{
"id": "field_homepage",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.homepage"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.homepage ?? '-'}}"
}
]
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_mobile",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.mobile"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.mobile ?? '-'}}"
}
]
},
{
"id": "field_phone",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.phone"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.phone ?? '-'}}"
}
]
}
]
}
]
}
]
}
]
},
{
"id": "section_location_info",
"type": "basic",
"name": "Div",
"props": {
"id": "location_info",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "map-pin",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.form.sections.location_info"
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "row-stack"
},
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_country",
"if": false,
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.country"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.country_name ?? user?.data?.country ?? '-'}}"
}
]
},
{
"id": "field_language",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.language"
},
{
"type": "basic",
"name": "Span",
"if": "{{user?.data?.language}}",
"props": {
"className": "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200"
},
"text": "{{user?.data?.language_label ?? user?.data?.language ?? '-'}}"
},
{
"type": "basic",
"name": "Span",
"if": "{{!user?.data?.language}}",
"props": {
"className": "text-primary text-sm"
},
"text": "-"
}
]
}
]
}
]
}
]
},
{
"id": "field_timezone",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.timezone"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.timezone ?? '-'}}"
}
]
},
{
"id": "field_address",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.address"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.zipcode || user?.data?.address || user?.data?.address_detail ? ((user?.data?.zipcode ? '(' + user.data.zipcode + ') ' : '') + (user?.data?.address ?? '') + (user?.data?.address_detail ? ' ' + user.data.address_detail : '')) : '-'}}"
}
]
}
]
}
]
},
{
"id": "section_profile_memo",
"type": "basic",
"name": "Div",
"props": {
"id": "profile_memo",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "user-circle",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.form.sections.profile_memo"
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "row-stack"
},
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_signature",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.signature"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.signature ?? '-'}}"
}
]
},
{
"id": "field_bio",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.bio"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm whitespace-pre-line"
},
"text": "{{user?.data?.bio ?? '-'}}"
}
]
}
]
}
]
}
]
},
{
"id": "field_admin_memo",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.admin_memo"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm whitespace-pre-line"
},
"text": "{{user?.data?.admin_memo ?? '-'}}"
}
]
}
]
}
]
},
{
"id": "section_activity_info",
"type": "basic",
"name": "Div",
"props": {
"id": "activity_info",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "clock",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.form.sections.activity_info"
}
]
},
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "row-stack"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_created_at",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.created_at"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.created_at ?? '-'}}"
}
]
},
{
"id": "field_last_login_at",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.last_login_at"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.last_login_at ?? '-'}}"
}
]
},
{
"id": "field_lock_status",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.lock_status"
},
{
"type": "basic",
"name": "Div",
"if": "{{user?.data?.locked_permanently === true}}",
"props": {
"className": "text-error-soft text-sm"
},
"text": "$t:admin.users.detail.locked_permanently_value"
},
{
"type": "basic",
"name": "Div",
"if": "{{user?.data?.locked_permanently !== true && user?.data?.is_locked === true}}",
"props": {
"className": "text-error-soft text-sm"
},
"text": "$t:admin.users.detail.locked_until_value|until={{user?.data?.locked_until ?? ''}}"
},
{
"type": "basic",
"name": "Div",
"if": "{{user?.data?.is_locked !== true}}",
"props": {
"className": "text-primary text-sm"
},
"text": "$t:admin.users.detail.not_locked_value"
}
]
}
]
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_ip_address",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.form.fields.ip_address"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.ip_address ?? '-'}}"
}
]
},
{
"id": "field_withdrawn_at",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.detail.withdrawn_at"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.withdrawn_at ?? '-'}}"
}
]
}
]
},
{
"id": "field_blocked_at",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.detail.blocked_at"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.blocked_at ?? '-'}}"
}
]
}
]
}
]
}
]
},
{
"id": "section_consent_info",
"type": "basic",
"name": "Div",
"props": {
"id": "consent_info",
"className": "admin-card"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mb-4"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "file-contract",
"className": "text-secondary text-xl"
}
},
{
"type": "basic",
"name": "H3",
"props": {
"className": "section-heading"
},
"text": "$t:admin.users.detail.consent_history"
}
]
},
{
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "grid-2col-responsive"
},
"children": [
{
"id": "field_terms_consent",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.detail.consent_type.terms"
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.terms_consent?.agreed_at ?? '-'}}"
}
]
},
{
"id": "field_privacy_consent",
"type": "basic",
"name": "Div",
"children": [
{
"type": "basic",
"name": "Label",
"props": {
"className": "text-label-subtle block font-medium mb-1"
},
"text": "$t:admin.users.detail.consent_type.privacy"
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-primary text-sm"
},
"text": "{{user?.data?.privacy_consent?.agreed_at ?? '-'}}"
}
]
}
]
}
]
}
]
},
{
"id": "extension_tab_content",
"type": "basic",
"name": "Div",
"props": {
"className": "tab-content mt-6"
},
"comment": "확장(모듈/플러그인)이 inject_props로 탭을 추가하고 append_child로 섹션 콘텐츠를 주입하는 영역"
}
]
}
]
},
"transition_overlay": {
"wait_for": [
"user"
]
}
}