From 3b694230a7e85d4e461da2a8d5468d1f9600f2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 12 Mar 2026 12:22:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[pmis]=20PmisWorker=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B0=9C=EC=9D=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B4=80=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pmis_workers 전용 모델 생성 (SAM 사원관리와 분리) - 프로필 API 응답 worker 키로 변경 - 직책/소속업체 편집 기능 추가 - React 컴포넌트 data.user → data.worker 전환 --- .../Controllers/Juil/PlanningController.php | 52 ++++++------ app/Models/Juil/PmisWorker.php | 84 +++++++++++++++++++ .../views/juil/construction-pmis.blade.php | 34 ++++++-- 3 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 app/Models/Juil/PmisWorker.php diff --git a/app/Http/Controllers/Juil/PlanningController.php b/app/Http/Controllers/Juil/PlanningController.php index 980ef2df..97baaf3d 100644 --- a/app/Http/Controllers/Juil/PlanningController.php +++ b/app/Http/Controllers/Juil/PlanningController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Juil; use App\Http\Controllers\Controller; +use App\Models\Juil\PmisWorker; use App\Services\WeatherService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -58,24 +59,24 @@ public function pmisProfile(): JsonResponse { $user = auth()->user(); $tenantId = session('current_tenant_id', 1); - - $departments = $user->getDepartmentsForTenant($tenantId); - $roles = $user->getRolesForTenant($tenantId); + $worker = PmisWorker::findOrCreateFromUser($user, $tenantId); return response()->json([ - 'user' => [ - 'name' => $user->name, - 'user_id' => $user->user_id, - 'email' => $user->email, - 'phone' => $user->phone, - 'role' => $user->role, - 'profile_photo_path' => $user->profile_photo_path, - 'department' => $departments->first()?->name ?? '-', - 'position' => $user->getOption('position') ?? '-', - 'gender' => data_get($user->options, 'gender', ''), - 'role_names' => $roles->pluck('name')->join(', ') ?: '-', - 'created_at' => $user->created_at?->format('Y-m-d'), - 'last_login_at' => $user->last_login_at?->format('Y-m-d H:i'), + 'worker' => [ + 'id' => $worker->id, + 'name' => $worker->name, + 'login_id' => $worker->login_id, + 'phone' => $worker->phone, + 'email' => $worker->email, + 'department' => $worker->department ?? '-', + 'position' => $worker->position ?? '-', + 'role_type' => $worker->role_type ?? '-', + 'gender' => $worker->gender ?? '', + 'company' => $worker->company ?? '-', + 'profile_photo_path' => $worker->profile_photo_path, + 'created_at' => $worker->created_at?->format('Y-m-d'), + 'last_login_at' => $worker->last_login_at?->format('Y-m-d H:i') + ?? $user->last_login_at?->format('Y-m-d H:i'), ], ]); } @@ -86,18 +87,21 @@ public function pmisProfileUpdate(Request $request): JsonResponse 'phone' => ['nullable', 'string', 'max:20'], 'email' => ['nullable', 'email', 'max:255'], 'gender' => ['nullable', 'string', 'in:남,여'], + 'position' => ['nullable', 'string', 'max:50'], + 'company' => ['nullable', 'string', 'max:100'], ]); $user = auth()->user(); - $user->phone = $request->input('phone'); - $user->email = $request->input('email'); + $tenantId = session('current_tenant_id', 1); + $worker = PmisWorker::findOrCreateFromUser($user, $tenantId); - $options = $user->options ?? []; - $options['gender'] = $request->input('gender'); - $user->options = $options; - - $user->updated_by = $user->id; - $user->save(); + $worker->update([ + 'phone' => $request->input('phone'), + 'email' => $request->input('email'), + 'gender' => $request->input('gender'), + 'position' => $request->input('position'), + 'company' => $request->input('company'), + ]); return response()->json(['success' => true, 'message' => '개인정보가 저장되었습니다.']); } diff --git a/app/Models/Juil/PmisWorker.php b/app/Models/Juil/PmisWorker.php new file mode 100644 index 00000000..dbac5c71 --- /dev/null +++ b/app/Models/Juil/PmisWorker.php @@ -0,0 +1,84 @@ + 'array', + 'last_login_at' => 'datetime', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * SAM 로그인 사용자로부터 PMIS Worker 자동 생성 (최초 접근 시) + */ + public static function findOrCreateFromUser(User $user, int $tenantId = 1): self + { + $worker = self::where('tenant_id', $tenantId) + ->where('user_id', $user->id) + ->first(); + + if ($worker) { + return $worker; + } + + // 부서명 조회 + $departments = $user->getDepartmentsForTenant($tenantId); + $deptName = $departments->first()?->name ?? null; + + return self::create([ + 'tenant_id' => $tenantId, + 'user_id' => $user->id, + 'name' => $user->name, + 'login_id' => $user->user_id, + 'phone' => $user->phone, + 'email' => $user->email, + 'department' => $deptName, + 'role_type' => $user->role ?? '사용자', + 'profile_photo_path' => $user->profile_photo_path, + ]); + } + + public function getOption(string $key, mixed $default = null): mixed + { + return data_get($this->options, $key, $default); + } + + public function setOption(string $key, mixed $value): self + { + $options = $this->options ?? []; + $options[$key] = $value; + $this->options = $options; + + return $this; + } +} diff --git a/resources/views/juil/construction-pmis.blade.php b/resources/views/juil/construction-pmis.blade.php index bb4dc11d..04b5088c 100644 --- a/resources/views/juil/construction-pmis.blade.php +++ b/resources/views/juil/construction-pmis.blade.php @@ -33,7 +33,7 @@ function PmisSidebar({ onOpenProfile }) { useEffect(() => { fetch('/juil/construction-pmis/profile', { headers: { 'Accept': 'application/json' } }) .then(r => r.json()) - .then(data => setProfile(data.user)) + .then(data => setProfile(data.worker)) .catch(() => {}); }, []); @@ -94,7 +94,7 @@ function ProfileModal({ isOpen, onClose }) { const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); const [editMode, setEditMode] = useState(false); - const [form, setForm] = useState({ phone: '', email: '', gender: '' }); + const [form, setForm] = useState({ phone: '', email: '', gender: '', position: '', company: '' }); const [saving, setSaving] = useState(false); const loadProfile = useCallback(() => { @@ -102,8 +102,8 @@ function ProfileModal({ isOpen, onClose }) { fetch('/juil/construction-pmis/profile', { headers: { 'Accept': 'application/json' } }) .then(r => r.json()) .then(data => { - setProfile(data.user); - setForm({ phone: data.user.phone || '', email: data.user.email || '', gender: data.user.gender || '' }); + setProfile(data.worker); + setForm({ phone: data.worker.phone || '', email: data.worker.email || '', gender: data.worker.gender || '', position: data.worker.position || '', company: data.worker.company || '' }); setLoading(false); }) .catch(() => setLoading(false)); @@ -195,11 +195,18 @@ className="w-full px-2 py-1 border border-gray-300 rounded text-sm" /> 부서 {profile.department} 아이디 - {profile.user_id} + {profile.login_id} 직책 - {profile.position} + + {editMode ? ( + setForm(f => ({...f, position: e.target.value}))} + className="w-full px-2 py-1 border border-gray-300 rounded text-sm" /> + ) : ( + {profile.position || '-'} + )} + 이메일 {editMode ? ( @@ -210,9 +217,9 @@ className="w-full px-2 py-1 border border-gray-300 rounded text-sm" /> )} - + 권한 - {profile.role_names} + {profile.role_type} 성별 {editMode ? ( @@ -227,6 +234,17 @@ className="px-2 py-1 border border-gray-300 rounded text-sm"> )} + + 소속업체 + + {editMode ? ( + setForm(f => ({...f, company: e.target.value}))} + className="px-2 py-1 border border-gray-300 rounded text-sm" style={{ width: '60%' }} /> + ) : ( + {profile.company || '-'} + )} + +