Files
sam-manage/resources/views/app-versions/index.blade.php
권혁성 78e67eb928 feat: 앱 버전 관리 페이지 구현
- AppVersion 모델, Service, Controller
- 버전 등록 폼 (APK 업로드, 강제 업데이트 설정)
- 버전 목록 테이블 (활성 토글, 다운로드 수, 삭제)
- /app-versions 라우트 추가
- app_releases 스토리지 디스크 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 19:53:09 +09:00

170 lines
10 KiB
PHP

@extends('layouts.app')
@section('title', '앱 버전 관리')
@section('content')
<div class="max-w-6xl mx-auto">
<h1 class="text-2xl font-bold mb-6"> 버전 관리</h1>
{{-- 성공 메시지 --}}
@if(session('success'))
<div class="mb-4 rounded-lg bg-green-50 p-4 text-green-800 border border-green-200">
{{ session('success') }}
</div>
@endif
{{-- 에러 메시지 --}}
@if($errors->any())
<div class="mb-4 rounded-lg bg-red-50 p-4 text-red-800 border border-red-200">
<ul class="list-disc list-inside">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{-- 등록 --}}
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-4"> 버전 등록</h2>
<form action="{{ route('app-versions.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">버전 코드 (정수) *</label>
<input type="number" name="version_code" value="{{ old('version_code') }}" required min="1"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm"
placeholder="예: 2">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">버전명 *</label>
<input type="text" name="version_name" value="{{ old('version_name') }}" required maxlength="20"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm"
placeholder="예: 0.2">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">플랫폼</label>
<select name="platform" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm">
<option value="android" {{ old('platform') === 'ios' ? '' : 'selected' }}>Android</option>
<option value="ios" {{ old('platform') === 'ios' ? 'selected' : '' }}>iOS</option>
</select>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">변경사항</label>
<textarea name="release_notes" rows="3"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm"
placeholder="- 기능 추가&#10;- 버그 수정">{{ old('release_notes') }}</textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">APK 파일</label>
<input type="file" name="apk_file" accept=".apk"
class="w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
</div>
<div class="flex items-end">
<label class="inline-flex items-center">
<input type="hidden" name="force_update" value="0">
<input type="checkbox" name="force_update" value="1" {{ old('force_update') ? 'checked' : '' }}
class="rounded border-gray-300 text-blue-600 shadow-sm focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">강제 업데이트</span>
</label>
</div>
</div>
<div class="mt-4">
<button type="submit" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
등록
</button>
</div>
</form>
</div>
{{-- 버전 목록 --}}
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">버전</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">플랫폼</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">변경사항</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">강제</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">활성</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">다운로드</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">APK</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">배포일</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">관리</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse($versions as $version)
<tr class="{{ $version->is_active ? '' : 'bg-gray-50 opacity-60' }}">
<td class="px-4 py-3 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">v{{ $version->version_name }}</div>
<div class="text-xs text-gray-500">code: {{ $version->version_code }}</div>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
{{ strtoupper($version->platform) }}
</td>
<td class="px-4 py-3 text-sm text-gray-500 max-w-xs truncate">
{{ $version->release_notes ? \Illuminate\Support\Str::limit($version->release_notes, 60) : '-' }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-center">
@if($version->force_update)
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">필수</span>
@else
<span class="text-gray-400 text-xs">-</span>
@endif
</td>
<td class="px-4 py-3 whitespace-nowrap text-center">
<form action="{{ route('app-versions.toggle', $version->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-sm {{ $version->is_active ? 'text-green-600 hover:text-green-800' : 'text-gray-400 hover:text-gray-600' }}">
{{ $version->is_active ? 'ON' : 'OFF' }}
</button>
</form>
</td>
<td class="px-4 py-3 whitespace-nowrap text-center text-sm text-gray-500">
{{ number_format($version->download_count) }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
@if($version->apk_path)
<span class="text-green-600" title="{{ $version->apk_original_name }}">
{{ $version->apk_original_name ? \Illuminate\Support\Str::limit($version->apk_original_name, 20) : 'APK' }}
</span>
@if($version->apk_size)
<span class="text-xs text-gray-400">({{ number_format($version->apk_size / 1024 / 1024, 1) }}MB)</span>
@endif
@else
<span class="text-gray-400">-</span>
@endif
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
{{ $version->published_at?->format('Y-m-d') ?? '-' }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-center">
<form action="{{ route('app-versions.destroy', $version->id) }}" method="POST" class="inline"
onsubmit="return confirm('v{{ $version->version_name }}을 삭제하시겠습니까?')">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-800 text-sm">삭제</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-8 text-center text-gray-500">등록된 버전이 없습니다.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- 페이지네이션 --}}
@if($versions->hasPages())
<div class="px-4 py-3 border-t border-gray-200">
{{ $versions->links() }}
</div>
@endif
</div>
</div>
@endsection