feat: [pmis] PMIS 자료실/안전관리/품질관리 기능 추가 및 개선
- 자료실 하위 3개 메뉴: 자료보관함, 매뉴얼, 공지사항 - 자료보관함: 폴더 트리 + 파일 업로드/다운로드/삭제 - 매뉴얼/공지사항: 게시판형 CRUD + 첨부파일 - 안전관리: 안전보건교육, TBM현황, 위험성평가, 재해예방조치 - 품질관리: 시정조치 UI 페이지 - 대시보드: 슈퍼관리자 전용 레거시 사이트 참고 카드 - 작업일보/출면일보 오류 수정 및 기능 개선 - 설비 사진 업로드, 근로계약서 종료일 수정
This commit is contained in:
@@ -113,6 +113,80 @@ public function pmisDailyReport(Request $request): View|Response
|
||||
return view('juil.pmis-daily-report');
|
||||
}
|
||||
|
||||
public function pmisCorrectiveAction(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.corrective-action'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-corrective-action');
|
||||
}
|
||||
|
||||
// ── 안전관리 ──
|
||||
|
||||
public function pmisSafetyEducation(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.safety-education'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-safety-education');
|
||||
}
|
||||
|
||||
public function pmisTbm(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.tbm'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-tbm');
|
||||
}
|
||||
|
||||
public function pmisRiskAssessment(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.risk-assessment'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-risk-assessment');
|
||||
}
|
||||
|
||||
public function pmisDisasterPrevention(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.disaster-prevention'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-disaster-prevention');
|
||||
}
|
||||
|
||||
public function pmisArchiveFiles(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.archive-files'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-archive-files');
|
||||
}
|
||||
|
||||
public function pmisArchiveManual(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.archive-manual'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-archive-manual');
|
||||
}
|
||||
|
||||
public function pmisArchiveNotice(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('juil.construction-pmis.archive-notice'));
|
||||
}
|
||||
|
||||
return view('juil.pmis-archive-notice');
|
||||
}
|
||||
|
||||
public function pmisWeather(WeatherService $weatherService): JsonResponse
|
||||
{
|
||||
$forecasts = $weatherService->getWeeklyForecast();
|
||||
@@ -142,6 +216,8 @@ public function pmisProfile(): JsonResponse
|
||||
'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'),
|
||||
'is_admin' => $user->isAdmin(),
|
||||
'is_super_admin' => $user->isSuperAdmin(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
201
app/Http/Controllers/Juil/PmisArchiveController.php
Normal file
201
app/Http/Controllers/Juil/PmisArchiveController.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Juil;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Juil\PmisArchiveFile;
|
||||
use App\Models\Juil\PmisArchiveFolder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PmisArchiveController extends Controller
|
||||
{
|
||||
private function tenantId(): int
|
||||
{
|
||||
return (int) session('current_tenant_id', 1);
|
||||
}
|
||||
|
||||
/* ─── 폴더 트리 ─── */
|
||||
|
||||
public function folderTree(): JsonResponse
|
||||
{
|
||||
$folders = PmisArchiveFolder::tenant($this->tenantId())
|
||||
->whereNull('parent_id')
|
||||
->with('children.children.children')
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
|
||||
return response()->json(['folders' => $folders]);
|
||||
}
|
||||
|
||||
public function folderStore(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:200',
|
||||
'parent_id' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
$folder = PmisArchiveFolder::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'parent_id' => $request->parent_id,
|
||||
'name' => $request->name,
|
||||
'sort_order' => PmisArchiveFolder::tenant($this->tenantId())
|
||||
->where('parent_id', $request->parent_id)
|
||||
->count(),
|
||||
]);
|
||||
|
||||
return response()->json(['folder' => $folder], 201);
|
||||
}
|
||||
|
||||
public function folderUpdate(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$folder = PmisArchiveFolder::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
$request->validate(['name' => 'required|string|max:200']);
|
||||
$folder->update(['name' => $request->name]);
|
||||
|
||||
return response()->json(['folder' => $folder]);
|
||||
}
|
||||
|
||||
public function folderDestroy(int $id): JsonResponse
|
||||
{
|
||||
$folder = PmisArchiveFolder::tenant($this->tenantId())->findOrFail($id);
|
||||
$descendantIds = $folder->allDescendantIds();
|
||||
|
||||
PmisArchiveFile::tenant($this->tenantId())
|
||||
->whereIn('folder_id', $descendantIds)
|
||||
->each(function ($file) {
|
||||
Storage::disk('public')->delete($file->file_path);
|
||||
$file->delete();
|
||||
});
|
||||
|
||||
PmisArchiveFolder::tenant($this->tenantId())
|
||||
->whereIn('id', $descendantIds)
|
||||
->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
/* ─── 파일 목록 ─── */
|
||||
|
||||
public function fileList(Request $request): JsonResponse
|
||||
{
|
||||
$folderId = $request->query('folder_id');
|
||||
$tab = $request->query('tab', '전체');
|
||||
$search = $request->query('search', '');
|
||||
$dateFrom = $request->query('date_from');
|
||||
$dateTo = $request->query('date_to');
|
||||
$includeSubfolder = $request->boolean('include_subfolder', true);
|
||||
|
||||
$query = PmisArchiveFile::tenant($this->tenantId());
|
||||
|
||||
if ($folderId) {
|
||||
if ($includeSubfolder) {
|
||||
$folder = PmisArchiveFolder::tenant($this->tenantId())->find($folderId);
|
||||
if ($folder) {
|
||||
$folderIds = $folder->allDescendantIds();
|
||||
$query->whereIn('folder_id', $folderIds);
|
||||
}
|
||||
} else {
|
||||
$query->where('folder_id', $folderId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($tab !== '전체') {
|
||||
$query->where('file_type', $tab);
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('title', 'like', "%{$search}%")
|
||||
->orWhere('original_name', 'like', "%{$search}%")
|
||||
->orWhereHas('folder', fn ($fq) => $fq->where('name', 'like', "%{$search}%"));
|
||||
});
|
||||
}
|
||||
|
||||
if ($dateFrom) {
|
||||
$query->whereDate('created_at', '>=', $dateFrom);
|
||||
}
|
||||
if ($dateTo) {
|
||||
$query->whereDate('created_at', '<=', $dateTo);
|
||||
}
|
||||
|
||||
$files = $query->orderByDesc('created_at')->get();
|
||||
|
||||
$result = $files->map(fn ($f) => [
|
||||
'id' => $f->id,
|
||||
'title' => $f->title ?: $f->original_name,
|
||||
'fileName' => $f->original_name,
|
||||
'filePath' => Storage::disk('public')->url($f->file_path),
|
||||
'fileType' => $f->file_type,
|
||||
'siteName' => $f->site_name,
|
||||
'size' => PmisArchiveFile::formatSize($f->file_size),
|
||||
'sizeRaw' => $f->file_size,
|
||||
'registrant' => $f->registeredByUser?->name ?? '-',
|
||||
'registeredAt' => $f->created_at->format('Y-m-d'),
|
||||
'folderId' => $f->folder_id,
|
||||
]);
|
||||
|
||||
return response()->json(['files' => $result]);
|
||||
}
|
||||
|
||||
/* ─── 파일 업로드 ─── */
|
||||
|
||||
public function fileStore(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'folder_id' => 'required|integer|exists:pmis_archive_folders,id',
|
||||
'files' => 'required|array|min:1',
|
||||
'files.*' => 'file|max:51200',
|
||||
'title' => 'nullable|string|max:300',
|
||||
'site_name' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
$user = auth()->user();
|
||||
$uploaded = [];
|
||||
|
||||
foreach ($request->file('files') as $file) {
|
||||
$ext = $file->getClientOriginalExtension();
|
||||
$path = $file->store('pmis/archive', 'public');
|
||||
|
||||
$record = PmisArchiveFile::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'folder_id' => $request->folder_id,
|
||||
'title' => $request->title ?: '',
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'file_type' => PmisArchiveFile::detectFileType($ext),
|
||||
'file_size' => $file->getSize(),
|
||||
'site_name' => $request->site_name ?: '',
|
||||
'registered_by' => $user?->id,
|
||||
]);
|
||||
|
||||
$uploaded[] = $record;
|
||||
}
|
||||
|
||||
return response()->json(['files' => $uploaded, 'message' => count($uploaded).'개 파일이 업로드되었습니다.'], 201);
|
||||
}
|
||||
|
||||
/* ─── 파일 삭제 ─── */
|
||||
|
||||
public function fileDestroy(int $id): JsonResponse
|
||||
{
|
||||
$file = PmisArchiveFile::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
Storage::disk('public')->delete($file->file_path);
|
||||
$file->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
/* ─── 파일 다운로드 ─── */
|
||||
|
||||
public function fileDownload(int $id)
|
||||
{
|
||||
$file = PmisArchiveFile::tenant($this->tenantId())->findOrFail($id);
|
||||
$fullPath = Storage::disk('public')->path($file->file_path);
|
||||
|
||||
return response()->download($fullPath, $file->original_name);
|
||||
}
|
||||
}
|
||||
322
app/Http/Controllers/Juil/PmisDailyWorkReportController.php
Normal file
322
app/Http/Controllers/Juil/PmisDailyWorkReportController.php
Normal file
@@ -0,0 +1,322 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Juil;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Juil\PmisDailyWorkReport;
|
||||
use App\Models\Juil\PmisWorkReportEquipment;
|
||||
use App\Models\Juil\PmisWorkReportMaterial;
|
||||
use App\Models\Juil\PmisWorkReportPhoto;
|
||||
use App\Models\Juil\PmisWorkReportVolume;
|
||||
use App\Models\Juil\PmisWorkReportWorker;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PmisDailyWorkReportController extends Controller
|
||||
{
|
||||
private function tenantId(): int
|
||||
{
|
||||
return (int) session('current_tenant_id', 1);
|
||||
}
|
||||
|
||||
public function show(Request $request): JsonResponse
|
||||
{
|
||||
$date = $request->input('date', now()->toDateString());
|
||||
$company = $request->input('company') ?? '';
|
||||
|
||||
$report = PmisDailyWorkReport::tenant($this->tenantId())
|
||||
->where('date', $date)
|
||||
->when($company, fn ($q) => $q->where('company_name', $company))
|
||||
->first();
|
||||
|
||||
if (! $report) {
|
||||
$report = PmisDailyWorkReport::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'date' => $date,
|
||||
'company_name' => $company,
|
||||
'weather' => '맑음',
|
||||
'status' => 'draft',
|
||||
]);
|
||||
}
|
||||
|
||||
$report->load(['workers', 'equipments', 'materials', 'volumes', 'photos']);
|
||||
|
||||
return response()->json($report);
|
||||
}
|
||||
|
||||
public function monthStatus(Request $request): JsonResponse
|
||||
{
|
||||
$year = $request->integer('year', now()->year);
|
||||
$month = $request->integer('month', now()->month);
|
||||
$company = $request->input('company') ?? '';
|
||||
|
||||
$reports = PmisDailyWorkReport::tenant($this->tenantId())
|
||||
->whereYear('date', $year)
|
||||
->whereMonth('date', $month)
|
||||
->when($company, fn ($q) => $q->where('company_name', $company))
|
||||
->withCount(['workers', 'equipments', 'materials', 'volumes', 'photos'])
|
||||
->get();
|
||||
|
||||
$result = [];
|
||||
foreach ($reports as $r) {
|
||||
$day = (int) $r->date->format('d');
|
||||
$hasData = $r->workers_count > 0 || $r->equipments_count > 0
|
||||
|| $r->materials_count > 0 || $r->volumes_count > 0
|
||||
|| $r->photos_count > 0
|
||||
|| $r->work_content_today;
|
||||
if ($hasData) {
|
||||
$result[$day] = $r->status;
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$report = PmisDailyWorkReport::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'weather' => 'sometimes|string|max:50',
|
||||
'temp_low' => 'sometimes|nullable|numeric',
|
||||
'temp_high' => 'sometimes|nullable|numeric',
|
||||
'precipitation' => 'sometimes|nullable|numeric',
|
||||
'snowfall' => 'sometimes|nullable|numeric',
|
||||
'fine_dust' => 'sometimes|nullable|string|max:50',
|
||||
'ultra_fine_dust' => 'sometimes|nullable|string|max:50',
|
||||
'work_content_today' => 'sometimes|nullable|string',
|
||||
'work_content_tomorrow' => 'sometimes|nullable|string',
|
||||
'notes' => 'sometimes|nullable|string',
|
||||
'status' => 'sometimes|in:draft,review,approved',
|
||||
'options' => 'sometimes|nullable|array',
|
||||
]);
|
||||
|
||||
// NOT NULL string 컬럼의 null → 빈 문자열 변환
|
||||
foreach (['fine_dust', 'ultra_fine_dust', 'weather'] as $col) {
|
||||
if (array_key_exists($col, $validated) && $validated[$col] === null) {
|
||||
$validated[$col] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$report->update($validated);
|
||||
$report->load(['workers', 'equipments', 'materials', 'volumes', 'photos']);
|
||||
|
||||
return response()->json($report);
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$report = PmisDailyWorkReport::tenant($this->tenantId())->findOrFail($id);
|
||||
$report->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
public function saveReviewers(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$report = PmisDailyWorkReport::tenant($this->tenantId())->findOrFail($id);
|
||||
$reviewers = $request->input('reviewers', []);
|
||||
$options = $report->options ?? [];
|
||||
$options['reviewers'] = $reviewers;
|
||||
$report->update(['options' => $options]);
|
||||
|
||||
return response()->json(['message' => '검토자가 저장되었습니다.']);
|
||||
}
|
||||
|
||||
// ─── Worker CRUD ───
|
||||
|
||||
public function workerStore(Request $request): JsonResponse
|
||||
{
|
||||
$v = $request->validate([
|
||||
'report_id' => 'required|integer|exists:pmis_daily_work_reports,id',
|
||||
'work_type' => 'required|string|max:200',
|
||||
'job_type' => 'required|string|max:200',
|
||||
'prev_cumulative' => 'nullable|integer|min:0',
|
||||
'today_count' => 'nullable|integer|min:0',
|
||||
]);
|
||||
$v['tenant_id'] = $this->tenantId();
|
||||
$v['prev_cumulative'] = $v['prev_cumulative'] ?? 0;
|
||||
$v['today_count'] = $v['today_count'] ?? 0;
|
||||
$v['sort_order'] = (PmisWorkReportWorker::where('report_id', $v['report_id'])->max('sort_order') ?? 0) + 1;
|
||||
|
||||
return response()->json(PmisWorkReportWorker::create($v), 201);
|
||||
}
|
||||
|
||||
public function workerUpdate(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$w = PmisWorkReportWorker::where('tenant_id', $this->tenantId())->findOrFail($id);
|
||||
$w->update($request->validate([
|
||||
'work_type' => 'sometimes|string|max:200',
|
||||
'job_type' => 'sometimes|string|max:200',
|
||||
'prev_cumulative' => 'nullable|integer|min:0',
|
||||
'today_count' => 'nullable|integer|min:0',
|
||||
]));
|
||||
|
||||
return response()->json($w);
|
||||
}
|
||||
|
||||
public function workerDestroy(int $id): JsonResponse
|
||||
{
|
||||
PmisWorkReportWorker::where('tenant_id', $this->tenantId())->findOrFail($id)->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
// ─── Equipment CRUD ───
|
||||
|
||||
public function equipmentStore(Request $request): JsonResponse
|
||||
{
|
||||
$v = $request->validate([
|
||||
'report_id' => 'required|integer|exists:pmis_daily_work_reports,id',
|
||||
'equipment_name' => 'required|string|max:200',
|
||||
'specification' => 'nullable|string|max:300',
|
||||
'prev_cumulative' => 'nullable|integer|min:0',
|
||||
'today_count' => 'nullable|integer|min:0',
|
||||
]);
|
||||
$v['tenant_id'] = $this->tenantId();
|
||||
$v['specification'] = $v['specification'] ?? '';
|
||||
$v['prev_cumulative'] = $v['prev_cumulative'] ?? 0;
|
||||
$v['today_count'] = $v['today_count'] ?? 0;
|
||||
$v['sort_order'] = (PmisWorkReportEquipment::where('report_id', $v['report_id'])->max('sort_order') ?? 0) + 1;
|
||||
|
||||
return response()->json(PmisWorkReportEquipment::create($v), 201);
|
||||
}
|
||||
|
||||
public function equipmentUpdate(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$e = PmisWorkReportEquipment::where('tenant_id', $this->tenantId())->findOrFail($id);
|
||||
$e->update($request->validate([
|
||||
'equipment_name' => 'sometimes|string|max:200',
|
||||
'specification' => 'nullable|string|max:300',
|
||||
'prev_cumulative' => 'nullable|integer|min:0',
|
||||
'today_count' => 'nullable|integer|min:0',
|
||||
]));
|
||||
|
||||
return response()->json($e);
|
||||
}
|
||||
|
||||
public function equipmentDestroy(int $id): JsonResponse
|
||||
{
|
||||
PmisWorkReportEquipment::where('tenant_id', $this->tenantId())->findOrFail($id)->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
// ─── Material CRUD ───
|
||||
|
||||
public function materialStore(Request $request): JsonResponse
|
||||
{
|
||||
$v = $request->validate([
|
||||
'report_id' => 'required|integer|exists:pmis_daily_work_reports,id',
|
||||
'material_name' => 'required|string|max:200',
|
||||
'specification' => 'nullable|string|max:300',
|
||||
'unit' => 'nullable|string|max:50',
|
||||
'design_qty' => 'nullable|numeric|min:0',
|
||||
'prev_cumulative' => 'nullable|numeric|min:0',
|
||||
'today_count' => 'nullable|numeric|min:0',
|
||||
]);
|
||||
$v['tenant_id'] = $this->tenantId();
|
||||
$v['sort_order'] = (PmisWorkReportMaterial::where('report_id', $v['report_id'])->max('sort_order') ?? 0) + 1;
|
||||
|
||||
return response()->json(PmisWorkReportMaterial::create($v), 201);
|
||||
}
|
||||
|
||||
public function materialUpdate(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$m = PmisWorkReportMaterial::where('tenant_id', $this->tenantId())->findOrFail($id);
|
||||
$m->update($request->validate([
|
||||
'material_name' => 'sometimes|string|max:200',
|
||||
'specification' => 'nullable|string|max:300',
|
||||
'unit' => 'nullable|string|max:50',
|
||||
'design_qty' => 'nullable|numeric|min:0',
|
||||
'prev_cumulative' => 'nullable|numeric|min:0',
|
||||
'today_count' => 'nullable|numeric|min:0',
|
||||
]));
|
||||
|
||||
return response()->json($m);
|
||||
}
|
||||
|
||||
public function materialDestroy(int $id): JsonResponse
|
||||
{
|
||||
PmisWorkReportMaterial::where('tenant_id', $this->tenantId())->findOrFail($id)->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
// ─── Volume CRUD ───
|
||||
|
||||
public function volumeStore(Request $request): JsonResponse
|
||||
{
|
||||
$v = $request->validate([
|
||||
'report_id' => 'required|integer|exists:pmis_daily_work_reports,id',
|
||||
'work_type' => 'required|string|max:200',
|
||||
'sub_work_type' => 'nullable|string|max:200',
|
||||
'unit' => 'nullable|string|max:50',
|
||||
'design_qty' => 'nullable|numeric|min:0',
|
||||
'prev_cumulative' => 'nullable|numeric|min:0',
|
||||
'today_count' => 'nullable|numeric|min:0',
|
||||
]);
|
||||
$v['tenant_id'] = $this->tenantId();
|
||||
$v['sort_order'] = (PmisWorkReportVolume::where('report_id', $v['report_id'])->max('sort_order') ?? 0) + 1;
|
||||
|
||||
return response()->json(PmisWorkReportVolume::create($v), 201);
|
||||
}
|
||||
|
||||
public function volumeUpdate(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$vol = PmisWorkReportVolume::where('tenant_id', $this->tenantId())->findOrFail($id);
|
||||
$vol->update($request->validate([
|
||||
'work_type' => 'sometimes|string|max:200',
|
||||
'sub_work_type' => 'nullable|string|max:200',
|
||||
'unit' => 'nullable|string|max:50',
|
||||
'design_qty' => 'nullable|numeric|min:0',
|
||||
'prev_cumulative' => 'nullable|numeric|min:0',
|
||||
'today_count' => 'nullable|numeric|min:0',
|
||||
]));
|
||||
|
||||
return response()->json($vol);
|
||||
}
|
||||
|
||||
public function volumeDestroy(int $id): JsonResponse
|
||||
{
|
||||
PmisWorkReportVolume::where('tenant_id', $this->tenantId())->findOrFail($id)->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
// ─── Photo CRUD ───
|
||||
|
||||
public function photoStore(Request $request): JsonResponse
|
||||
{
|
||||
$v = $request->validate([
|
||||
'report_id' => 'required|integer|exists:pmis_daily_work_reports,id',
|
||||
'location' => 'nullable|string|max:200',
|
||||
'content' => 'nullable|string|max:500',
|
||||
'photo' => 'nullable|image|max:10240',
|
||||
]);
|
||||
|
||||
$path = '';
|
||||
if ($request->hasFile('photo')) {
|
||||
$path = $request->file('photo')->store('pmis/work-report-photos', 'public');
|
||||
}
|
||||
|
||||
$photo = PmisWorkReportPhoto::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'report_id' => $v['report_id'],
|
||||
'photo_path' => $path,
|
||||
'location' => $v['location'] ?? '',
|
||||
'content' => $v['content'] ?? '',
|
||||
'photo_date' => now()->toDateString(),
|
||||
'sort_order' => (PmisWorkReportPhoto::where('report_id', $v['report_id'])->max('sort_order') ?? 0) + 1,
|
||||
]);
|
||||
|
||||
return response()->json($photo, 201);
|
||||
}
|
||||
|
||||
public function photoDestroy(int $id): JsonResponse
|
||||
{
|
||||
PmisWorkReportPhoto::where('tenant_id', $this->tenantId())->findOrFail($id)->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
}
|
||||
187
app/Http/Controllers/Juil/PmisManualController.php
Normal file
187
app/Http/Controllers/Juil/PmisManualController.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Juil;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Juil\PmisManual;
|
||||
use App\Models\Juil\PmisManualAttachment;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PmisManualController extends Controller
|
||||
{
|
||||
private function tenantId(): int
|
||||
{
|
||||
return (int) session('current_tenant_id', 1);
|
||||
}
|
||||
|
||||
public function list(Request $request): JsonResponse
|
||||
{
|
||||
$search = $request->query('search', '');
|
||||
|
||||
$query = PmisManual::tenant($this->tenantId())
|
||||
->with('author:id,name')
|
||||
->withCount('attachments')
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($search) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('title', 'like', "%{$search}%")
|
||||
->orWhere('tags', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
$manuals = $query->get()->map(fn ($m) => [
|
||||
'id' => $m->id,
|
||||
'title' => $m->title,
|
||||
'author' => $m->author?->name ?? '관리자',
|
||||
'createdAt' => $m->created_at->format('Y-m-d'),
|
||||
'views' => $m->views,
|
||||
'hasAttachment' => $m->attachments_count > 0,
|
||||
]);
|
||||
|
||||
return response()->json(['manuals' => $manuals]);
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
$manual = PmisManual::tenant($this->tenantId())
|
||||
->with(['author:id,name', 'attachments'])
|
||||
->findOrFail($id);
|
||||
|
||||
$manual->increment('views');
|
||||
|
||||
return response()->json([
|
||||
'manual' => [
|
||||
'id' => $manual->id,
|
||||
'title' => $manual->title,
|
||||
'content' => $manual->content,
|
||||
'tags' => $manual->tags,
|
||||
'author' => $manual->author?->name ?? '관리자',
|
||||
'createdAt' => $manual->created_at->format('Y-m-d'),
|
||||
'views' => $manual->views,
|
||||
'attachments' => $manual->attachments->map(fn ($a) => [
|
||||
'id' => $a->id,
|
||||
'fileName' => $a->original_name,
|
||||
'size' => PmisManualAttachment::formatSize($a->file_size),
|
||||
'downloadUrl' => "/juil/construction-pmis/api/manuals/attachments/{$a->id}/download",
|
||||
]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'title' => 'required|string|max:300',
|
||||
'content' => 'nullable|string',
|
||||
'tags' => 'nullable|string|max:500',
|
||||
'files' => 'nullable|array',
|
||||
'files.*' => 'file|max:51200',
|
||||
]);
|
||||
|
||||
$manual = PmisManual::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'title' => $request->title,
|
||||
'content' => $request->content ?? '',
|
||||
'tags' => $request->tags ?? '',
|
||||
'author_id' => $user->id,
|
||||
]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
foreach ($request->file('files') as $file) {
|
||||
$path = $file->store('pmis/manuals', 'public');
|
||||
$manual->attachments()->create([
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'file_size' => $file->getSize(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['manual' => $manual->load('attachments'), 'message' => '등록되었습니다.'], 201);
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$manual = PmisManual::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'title' => 'required|string|max:300',
|
||||
'content' => 'nullable|string',
|
||||
'tags' => 'nullable|string|max:500',
|
||||
'files' => 'nullable|array',
|
||||
'files.*' => 'file|max:51200',
|
||||
]);
|
||||
|
||||
$manual->update([
|
||||
'title' => $request->title,
|
||||
'content' => $request->content ?? '',
|
||||
'tags' => $request->tags ?? '',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
foreach ($request->file('files') as $file) {
|
||||
$path = $file->store('pmis/manuals', 'public');
|
||||
$manual->attachments()->create([
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'file_size' => $file->getSize(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['manual' => $manual->load('attachments'), 'message' => '수정되었습니다.']);
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$manual = PmisManual::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
foreach ($manual->attachments as $att) {
|
||||
Storage::disk('public')->delete($att->file_path);
|
||||
}
|
||||
|
||||
$manual->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
public function attachmentDownload(int $id)
|
||||
{
|
||||
$att = PmisManualAttachment::findOrFail($id);
|
||||
$fullPath = Storage::disk('public')->path($att->file_path);
|
||||
|
||||
return response()->download($fullPath, $att->original_name);
|
||||
}
|
||||
|
||||
public function attachmentDestroy(int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$att = PmisManualAttachment::findOrFail($id);
|
||||
Storage::disk('public')->delete($att->file_path);
|
||||
$att->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
}
|
||||
185
app/Http/Controllers/Juil/PmisNoticeController.php
Normal file
185
app/Http/Controllers/Juil/PmisNoticeController.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Juil;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Juil\PmisNotice;
|
||||
use App\Models\Juil\PmisNoticeAttachment;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PmisNoticeController extends Controller
|
||||
{
|
||||
private function tenantId(): int
|
||||
{
|
||||
return (int) session('current_tenant_id', 1);
|
||||
}
|
||||
|
||||
public function list(Request $request): JsonResponse
|
||||
{
|
||||
$search = $request->query('search', '');
|
||||
$withTrashed = $request->boolean('with_trashed', false);
|
||||
|
||||
$query = PmisNotice::tenant($this->tenantId())
|
||||
->with('author:id,name')
|
||||
->withCount('attachments')
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($withTrashed && auth()->user()->isAdmin()) {
|
||||
$query->withTrashed();
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query->where('title', 'like', "%{$search}%");
|
||||
}
|
||||
|
||||
$notices = $query->get()->map(fn ($n) => [
|
||||
'id' => $n->id,
|
||||
'title' => $n->title,
|
||||
'author' => $n->author?->name ?? '관리자',
|
||||
'createdAt' => $n->created_at->format('Y-m-d H:i:s'),
|
||||
'views' => $n->views,
|
||||
'hasAttachment' => $n->attachments_count > 0,
|
||||
'isDeleted' => $n->trashed(),
|
||||
]);
|
||||
|
||||
return response()->json(['notices' => $notices]);
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
$notice = PmisNotice::tenant($this->tenantId())
|
||||
->with(['author:id,name', 'attachments'])
|
||||
->findOrFail($id);
|
||||
|
||||
$notice->increment('views');
|
||||
|
||||
return response()->json([
|
||||
'notice' => [
|
||||
'id' => $notice->id,
|
||||
'title' => $notice->title,
|
||||
'content' => $notice->content,
|
||||
'author' => $notice->author?->name ?? '관리자',
|
||||
'createdAt' => $notice->created_at->format('Y-m-d H:i:s'),
|
||||
'views' => $notice->views,
|
||||
'attachments' => $notice->attachments->map(fn ($a) => [
|
||||
'id' => $a->id,
|
||||
'fileName' => $a->original_name,
|
||||
'size' => PmisNoticeAttachment::formatSize($a->file_size),
|
||||
'downloadUrl' => "/juil/construction-pmis/api/notices/attachments/{$a->id}/download",
|
||||
]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'title' => 'required|string|max:300',
|
||||
'content' => 'nullable|string',
|
||||
'files' => 'nullable|array',
|
||||
'files.*' => 'file|max:51200',
|
||||
]);
|
||||
|
||||
$notice = PmisNotice::create([
|
||||
'tenant_id' => $this->tenantId(),
|
||||
'title' => $request->title,
|
||||
'content' => $request->content ?? '',
|
||||
'author_id' => $user->id,
|
||||
]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
foreach ($request->file('files') as $file) {
|
||||
$path = $file->store('pmis/notices', 'public');
|
||||
$notice->attachments()->create([
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'file_size' => $file->getSize(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['notice' => $notice->load('attachments'), 'message' => '등록되었습니다.'], 201);
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$notice = PmisNotice::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'title' => 'required|string|max:300',
|
||||
'content' => 'nullable|string',
|
||||
'files' => 'nullable|array',
|
||||
'files.*' => 'file|max:51200',
|
||||
]);
|
||||
|
||||
$notice->update([
|
||||
'title' => $request->title,
|
||||
'content' => $request->content ?? '',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('files')) {
|
||||
foreach ($request->file('files') as $file) {
|
||||
$path = $file->store('pmis/notices', 'public');
|
||||
$notice->attachments()->create([
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'file_path' => $path,
|
||||
'file_size' => $file->getSize(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['notice' => $notice->load('attachments'), 'message' => '수정되었습니다.']);
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$notice = PmisNotice::tenant($this->tenantId())->findOrFail($id);
|
||||
|
||||
foreach ($notice->attachments as $att) {
|
||||
Storage::disk('public')->delete($att->file_path);
|
||||
}
|
||||
|
||||
$notice->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
|
||||
public function attachmentDownload(int $id)
|
||||
{
|
||||
$att = PmisNoticeAttachment::findOrFail($id);
|
||||
$fullPath = Storage::disk('public')->path($att->file_path);
|
||||
|
||||
return response()->download($fullPath, $att->original_name);
|
||||
}
|
||||
|
||||
public function attachmentDestroy(int $id): JsonResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user->isAdmin()) {
|
||||
return response()->json(['message' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
$att = PmisNoticeAttachment::findOrFail($id);
|
||||
Storage::disk('public')->delete($att->file_path);
|
||||
$att->delete();
|
||||
|
||||
return response()->json(['message' => '삭제되었습니다.']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user