332 lines
16 KiB
PHP
332 lines
16 KiB
PHP
<!-- SAM RULES: include=../inc/header.php; base=/tenant; width=1280; js=jQuery+BS5 -->
|
|
<?php
|
|
// 섹션은 임시로 'member' 사용 (상단 2뎁스에 노출만 필요)
|
|
$CURRENT_SECTION = 'member';
|
|
include '../inc/header.php';
|
|
|
|
// ====== 샘플 데이터 (실구현은 DB 조인) ======
|
|
// setting_field_defs
|
|
$FIELD_DEFS = [
|
|
['field_key' => 'name', 'label' => '이름', 'data_type' => 'string', 'input_type' => 'text', 'option_source' => null],
|
|
['field_key' => 'email', 'label' => '이메일', 'data_type' => 'string', 'input_type' => 'text', 'option_source' => null],
|
|
['field_key' => 'phone', 'label' => '전화번호', 'data_type' => 'string', 'input_type' => 'text', 'option_source' => null],
|
|
['field_key' => 'department', 'label' => '부서', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'position', 'label' => '직급', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'job_title', 'label' => '직책', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'manager_id', 'label' => '직속 상급자', 'data_type' => 'bigint', 'input_type' => 'select_user', 'option_source' => null],
|
|
['field_key' => 'hire_date', 'label' => '입사일', 'data_type' => 'date', 'input_type' => 'date', 'option_source' => null],
|
|
['field_key' => 'employment_type', 'label' => '고용형태', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'work_location', 'label' => '근무지', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'work_type', 'label' => '근무형태', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'gender', 'label' => '성별', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'bank_name', 'label' => '은행', 'data_type' => 'string', 'input_type' => 'select', 'option_source' => 'tenant_list'],
|
|
['field_key' => 'bank_account', 'label' => '계좌번호', 'data_type' => 'string', 'input_type' => 'text', 'option_source' => null],
|
|
['field_key' => 'employee_no', 'label' => '사번', 'data_type' => 'string', 'input_type' => 'text', 'option_source' => null],
|
|
['field_key' => 'profile_photo', 'label' => '프로필 사진', 'data_type' => 'file', 'input_type' => 'file', 'option_source' => null],
|
|
];
|
|
|
|
// tenant_option_groups (id, name) — select 항목에서 선택할 그룹
|
|
$OPTION_GROUPS = [
|
|
['id' => 101, 'group_key' => 'department', 'name' => '부서 그룹'],
|
|
['id' => 102, 'group_key' => 'position', 'name' => '직급 그룹'],
|
|
['id' => 103, 'group_key' => 'job_title', 'name' => '직책 그룹'],
|
|
['id' => 104, 'group_key' => 'employment_type', 'name' => '고용형태 그룹'],
|
|
['id' => 105, 'group_key' => 'work_location', 'name' => '근무지 그룹'],
|
|
['id' => 106, 'group_key' => 'work_type', 'name' => '근무형태 그룹'],
|
|
['id' => 107, 'group_key' => 'gender', 'name' => '성별 그룹'],
|
|
['id' => 108, 'group_key' => 'bank_name', 'name' => '은행 그룹'],
|
|
];
|
|
|
|
// tenant_field_settings (샘플: enabled/required/sort/order/group)
|
|
// key = field_key
|
|
$SETTINGS = [
|
|
'name' => ['enabled' => 1, 'required' => 1, 'sort_order' => 10, 'option_group_id' => null],
|
|
'email' => ['enabled' => 1, 'required' => 1, 'sort_order' => 20, 'option_group_id' => null],
|
|
'phone' => ['enabled' => 1, 'required' => 0, 'sort_order' => 30, 'option_group_id' => null],
|
|
'department' => ['enabled' => 1, 'required' => 1, 'sort_order' => 40, 'option_group_id' => 101],
|
|
'position' => ['enabled' => 1, 'required' => 0, 'sort_order' => 50, 'option_group_id' => 102],
|
|
'job_title' => ['enabled' => 0, 'required' => 0, 'sort_order' => 60, 'option_group_id' => 103],
|
|
'manager_id' => ['enabled' => 0, 'required' => 0, 'sort_order' => 70, 'option_group_id' => null],
|
|
'hire_date' => ['enabled' => 1, 'required' => 0, 'sort_order' => 80, 'option_group_id' => null],
|
|
'employment_type' => ['enabled' => 1, 'required' => 0, 'sort_order' => 90, 'option_group_id' => 104],
|
|
'work_location' => ['enabled' => 1, 'required' => 0, 'sort_order' => 100, 'option_group_id' => 105],
|
|
'work_type' => ['enabled' => 0, 'required' => 0, 'sort_order' => 110, 'option_group_id' => 106],
|
|
'gender' => ['enabled' => 0, 'required' => 0, 'sort_order' => 120, 'option_group_id' => 107],
|
|
'bank_name' => ['enabled' => 0, 'required' => 0, 'sort_order' => 130, 'option_group_id' => 108],
|
|
'bank_account' => ['enabled' => 0, 'required' => 0, 'sort_order' => 140, 'option_group_id' => null],
|
|
'employee_no' => ['enabled' => 1, 'required' => 0, 'sort_order' => 150, 'option_group_id' => null],
|
|
'profile_photo' => ['enabled' => 0, 'required' => 0, 'sort_order' => 160, 'option_group_id' => null],
|
|
];
|
|
|
|
// helper: 옵션그룹 셀렉트 HTML
|
|
function render_group_select($field_key, $sel, $groups, $def)
|
|
{
|
|
if (! ($def['input_type'] === 'select' && $def['option_source'] === 'tenant_list')) {
|
|
return '<span class="text-muted">—</span>';
|
|
}
|
|
$html = '<select class="form-select form-select-sm opt-group" data-field="'.$field_key.'" style="min-width:160px;">';
|
|
$html .= '<option value="">(그룹 선택)</option>';
|
|
foreach ($groups as $g) {
|
|
$s = ($sel == $g['id']) ? 'selected' : '';
|
|
$html .= '<option value="'.$g['id'].'" '.$s.'>'.htmlspecialchars($g['name']).'</option>';
|
|
}
|
|
$html .= '</select>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
// 기존 render_group_select() 제거하고 ↓로 교체
|
|
function group_name_by_id($id, $groups)
|
|
{
|
|
foreach ($groups as $g) {
|
|
if ((string) $g['id'] === (string) $id) {
|
|
return $g['name'];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function render_group_label($field_key, $sel_id, $groups, $def)
|
|
{
|
|
// select + tenant_list 타입만 노출, 그 외는 대시
|
|
if (! ($def['input_type'] === 'select' && $def['option_source'] === 'tenant_list')) {
|
|
return '<span class="text-muted">—</span>';
|
|
}
|
|
if (! $sel_id) {
|
|
return '<span class="badge bg-light text-muted">미지정</span>';
|
|
}
|
|
$name = htmlspecialchars(group_name_by_id($sel_id, $groups) ?: '알 수 없음');
|
|
|
|
// 필요하면 관리 링크 표시
|
|
return '<span class="badge bg-secondary-subtle text-dark">'.$name.'</span>'
|
|
.' <a href="/tenant/settings/option_groups.php?selected='.$sel_id.'" class="btn btn-sm btn-outline-secondary ms-1">관리</a>';
|
|
}
|
|
|
|
?>
|
|
<div class="container" style="max-width:1280px; margin-top:18px;">
|
|
|
|
<!-- 상단 툴바 -->
|
|
<div class="card mb-3">
|
|
<div class="sam-toolbar">
|
|
<div class="sam-title">회원가입 필드 설정</div>
|
|
<div class="sam-actions">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnAllOn">모두 사용</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnAllOff">모두 미사용</button>
|
|
<button type="button" class="btn btn-primary btn-sm" id="btnSaveAll">일괄 저장</button>
|
|
</div>
|
|
</div>
|
|
<div class="p-3">
|
|
<div class="small text-muted mb-1">미리보기 · 회원가입 화면 노출 순서</div>
|
|
<div id="preview" class="d-flex flex-wrap gap-2"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 설정 테이블 -->
|
|
<div class="card p-3">
|
|
<div class="table-responsive">
|
|
<table class="table table-sam align-middle" id="fieldsTable">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:150px;">순서</th>
|
|
<th class="text-start">필드</th>
|
|
<th style="width:120px;">입력유형</th>
|
|
<th style="width:180px;">옵션 그룹</th>
|
|
<th style="width:100px;">필수</th>
|
|
<th style="width:100px;">사용</th>
|
|
<th style="width:140px;">관리</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php
|
|
// sort_order 순으로 정렬된 것처럼 표시
|
|
usort($FIELD_DEFS, function ($a, $b) use ($SETTINGS) {
|
|
$sa = $SETTINGS[$a['field_key']]['sort_order'] ?? 9999;
|
|
$sb = $SETTINGS[$b['field_key']]['sort_order'] ?? 9999;
|
|
|
|
return $sa <=> $sb;
|
|
});
|
|
|
|
foreach ($FIELD_DEFS as $def) {
|
|
$key = $def['field_key'];
|
|
$set = $SETTINGS[$key] ?? ['enabled' => 0, 'required' => 0, 'sort_order' => 9999, 'option_group_id' => null];
|
|
$enabled = (int) $set['enabled'];
|
|
$required = (int) $set['required'];
|
|
$order = (int) $set['sort_order'];
|
|
$groupId = $set['option_group_id'];
|
|
?>
|
|
<tr data-field="<?= htmlspecialchars($key) ?>">
|
|
<td>
|
|
<div class="input-group input-group-sm">
|
|
<button class="btn btn-outline-secondary btn-order" data-dir="-10" type="button">▲</button>
|
|
<input type="number" class="form-control form-control-sm sort-order text-center" value="<?= $order ?>">
|
|
<button class="btn btn-outline-secondary btn-order" data-dir="10" type="button">▼</button>
|
|
</div>
|
|
</td>
|
|
<td class="text-start">
|
|
<div class="fw-semibold"><?= htmlspecialchars($def['label']) ?></div>
|
|
<div class="text-muted small"><?= htmlspecialchars($key) ?> · <?= htmlspecialchars($def['data_type']) ?></div>
|
|
</td>
|
|
<td><span class="badge rounded-pill bg-light text-dark"><?= htmlspecialchars($def['input_type']) ?></span></td>
|
|
<td><?= render_group_label($key, $groupId, $OPTION_GROUPS, $def) ?></td>
|
|
<td>
|
|
<div class="form-check form-switch d-flex justify-content-center m-0">
|
|
<input class="form-check-input toggle-req" type="checkbox" <?= $required ? 'checked' : '' ?> <?= $enabled ? '' : 'disabled' ?>>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="form-check form-switch d-flex justify-content-center m-0">
|
|
<input class="form-check-input toggle-on" type="checkbox" <?= $enabled ? 'checked' : '' ?>>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary btn-apply" type="button">저장</button>
|
|
<button class="btn btn-sm btn-outline-secondary btn-reset" type="button">되돌리기</button>
|
|
</td>
|
|
</tr>
|
|
<?php } ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
#preview .pill{
|
|
display:inline-flex; align-items:center; gap:6px;
|
|
background:#eef3ff; color:#2c4a85; border:1px solid #d9e4ff;
|
|
padding:.25rem .5rem; border-radius:999px; font-size:.85rem;
|
|
}
|
|
#preview .pill .req{ color:#c1121f; font-weight:600; }
|
|
#fieldsTable td .input-group-sm .form-control { max-width:70px; }
|
|
#fieldsTable .btn-order { width:34px; }
|
|
</style>
|
|
|
|
<script>
|
|
$(function(){
|
|
// —— 메모리 상태(실제 구현은 서버 조인 결과로 초기화)
|
|
const defs = <?= json_encode($FIELD_DEFS, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
|
const initSettings = <?= json_encode($SETTINGS, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
|
|
|
let state = JSON.parse(JSON.stringify(initSettings)); // deep copy
|
|
|
|
// 프리뷰 갱신
|
|
function renderPreview(){
|
|
const list = Object.keys(state)
|
|
.map(k => ({k, ...state[k], label: findDef(k)?.label || k}))
|
|
.filter(x => x.enabled)
|
|
.sort((a,b)=> a.sort_order - b.sort_order);
|
|
|
|
const html = list.map(x => `
|
|
<span class="pill" data-k="${x.k}">
|
|
${escapeHtml(x.label)}
|
|
${x.required ? '<span class="req">*</span>' : ''}
|
|
<small class="text-muted ms-1">(${x.sort_order})</small>
|
|
</span>
|
|
`).join('');
|
|
$('#preview').html(html || '<span class="text-muted">사용 중인 필드가 없습니다.</span>');
|
|
}
|
|
|
|
function findDef(key){ return defs.find(d => d.field_key===key); }
|
|
function escapeHtml(s){ return String(s??'').replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); }
|
|
|
|
// 테이블 → state 동기화(행 하나)
|
|
function pullRow($tr){
|
|
const key = $tr.data('field');
|
|
const enabled = $tr.find('.toggle-on').prop('checked') ? 1:0;
|
|
const required = $tr.find('.toggle-req').prop('checked') ? 1:0;
|
|
const sort_order = parseInt($tr.find('.sort-order').val(),10) || 0;
|
|
const groupSel = $tr.find('.opt-group');
|
|
const option_group_id = groupSel.length ? (groupSel.val()? parseInt(groupSel.val(),10): null) : null;
|
|
state[key] = { enabled, required, sort_order, option_group_id };
|
|
}
|
|
|
|
// state → 테이블(행 하나) 재적용
|
|
function pushRow($tr){
|
|
const key = $tr.data('field');
|
|
const s = state[key] || {};
|
|
$tr.find('.sort-order').val(s.sort_order ?? 0);
|
|
$tr.find('.toggle-on').prop('checked', !!s.enabled);
|
|
$tr.find('.toggle-req').prop('checked', !!s.required).prop('disabled', !s.enabled);
|
|
const groupSel = $tr.find('.opt-group');
|
|
if (groupSel.length){
|
|
groupSel.val(s.option_group_id ? String(s.option_group_id) : '');
|
|
}
|
|
}
|
|
|
|
// 개별 저장 (모의)
|
|
$(document).on('click', '.btn-apply', function(){
|
|
const $tr = $(this).closest('tr');
|
|
pullRow($tr);
|
|
renderPreview();
|
|
// 실제:
|
|
// $.post('/tenant/api/settings/member_field_save.php', { field_key:..., ...state[field_key] })
|
|
alert('저장(모의): '+$tr.data('field'));
|
|
});
|
|
|
|
// 되돌리기
|
|
$(document).on('click', '.btn-reset', function(){
|
|
const key = $(this).closest('tr').data('field');
|
|
state[key] = JSON.parse(JSON.stringify(initSettings[key] || {enabled:0,required:0,sort_order:9999,option_group_id:null}));
|
|
pushRow($(this).closest('tr'));
|
|
renderPreview();
|
|
});
|
|
|
|
// 사용 스위치
|
|
$(document).on('change', '.toggle-on', function(){
|
|
const $tr = $(this).closest('tr');
|
|
const on = $(this).prop('checked');
|
|
$tr.find('.toggle-req').prop('disabled', !on);
|
|
pullRow($tr); renderPreview();
|
|
});
|
|
|
|
// 필수 스위치
|
|
$(document).on('change', '.toggle-req', function(){
|
|
const $tr = $(this).closest('tr');
|
|
pullRow($tr); renderPreview();
|
|
});
|
|
|
|
// 옵션그룹 변경
|
|
$(document).on('change', '.opt-group', function(){
|
|
pullRow($(this).closest('tr')); renderPreview();
|
|
});
|
|
|
|
// 순서 숫자 직접 수정
|
|
$(document).on('input', '.sort-order', function(){
|
|
pullRow($(this).closest('tr')); renderPreview();
|
|
});
|
|
|
|
// 순서 버튼 ▲▼
|
|
$(document).on('click', '.btn-order', function(){
|
|
const $tr = $(this).closest('tr');
|
|
const dir = parseInt($(this).data('dir'),10);
|
|
const $inp = $tr.find('.sort-order');
|
|
const cur = parseInt($inp.val(),10) || 0;
|
|
$inp.val(cur + dir).trigger('input');
|
|
});
|
|
|
|
// 모두 사용/미사용
|
|
$('#btnAllOn').on('click', function(){
|
|
$('#fieldsTable tbody tr').each(function(){
|
|
$(this).find('.toggle-on').prop('checked', true).trigger('change');
|
|
});
|
|
});
|
|
$('#btnAllOff').on('click', function(){
|
|
$('#fieldsTable tbody tr').each(function(){
|
|
$(this).find('.toggle-on').prop('checked', false).trigger('change');
|
|
});
|
|
});
|
|
|
|
// 일괄 저장(모의)
|
|
$('#btnSaveAll').on('click', function(){
|
|
// 실제: $.post('/tenant/api/settings/member_fields_bulk_save.php', { payload: JSON.stringify(state) })
|
|
console.log('[BULK SAVE]', state);
|
|
alert('일괄 저장(모의). 콘솔을 확인하세요.');
|
|
});
|
|
|
|
// 초기 렌더
|
|
renderPreview();
|
|
});
|
|
</script>
|
|
<?php include '../inc/footer.php'; ?>
|