Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
2026-02-11 16:03:32 +09:00
4 changed files with 46 additions and 4 deletions

View File

@@ -76,6 +76,26 @@ public function deleteToken(int $id): Response
->header('HX-Trigger', 'tokenDeleted');
}
/**
* 에러 토큰 전체 삭제
*/
public function deleteErrorTokens(Request $request): Response
{
$query = PushDeviceToken::withoutGlobalScopes()->whereNotNull('last_error');
if ($tenantId = $request->get('tenant_id')) {
$query->where('tenant_id', $tenantId);
}
$count = $query->count();
$query->toBase()->delete();
\Log::info("에러 토큰 전체삭제: {$count}건 영구 삭제");
return response('', 200)
->header('HX-Refresh', 'true');
}
/**
* FCM 테스트 발송 페이지
*/

View File

@@ -1024,6 +1024,7 @@ className="flex-1 py-2 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700
const [form, setForm] = useState({});
const [saving, setSaving] = useState(false);
const [baseBalance, setBaseBalance] = useState(0);
const [focusedField, setFocusedField] = useState(null);
const isEditMode = !!editData;
@@ -1300,9 +1301,11 @@ className="text-red-600"
<label className="block text-sm font-medium text-stone-700 mb-1">금액 <span className="text-red-500">*</span></label>
<input
type="text"
value={formatAmount(form.amount)}
value={focusedField === 'amount' && form.amount === 0 ? '' : formatAmount(form.amount)}
onChange={(e) => handleAmountChange('amount', e.target.value)}
placeholder="0"
onFocus={() => setFocusedField('amount')}
onBlur={() => setFocusedField(null)}
placeholder="금액 입력"
className="w-full px-3 py-2 border border-stone-200 rounded-lg text-sm focus:ring-2 focus:ring-emerald-500 outline-none text-right"
/>
</div>
@@ -1312,9 +1315,11 @@ className="w-full px-3 py-2 border border-stone-200 rounded-lg text-sm focus:rin
</label>
<input
type="text"
value={formatAmount(form.balance)}
value={focusedField === 'balance' && form.balance === 0 ? '' : formatAmount(form.balance)}
onChange={(e) => handleAmountChange('balance', e.target.value)}
placeholder="0"
onFocus={() => setFocusedField('balance')}
onBlur={() => setFocusedField(null)}
placeholder="잔액"
className="w-full px-3 py-2 border border-stone-200 rounded-lg text-sm focus:ring-2 focus:ring-emerald-500 outline-none text-right bg-stone-50"
/>
{baseBalance !== 0 && (

View File

@@ -87,6 +87,22 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
</form>
</div>
<!-- 에러 토큰 전체삭제 -->
@if($stats['has_error'] > 0)
<div class="flex items-center justify-between bg-red-50 border border-red-200 rounded-lg px-4 py-3 mb-4">
<span class="text-sm text-red-700">
에러 토큰 <strong>{{ $stats['has_error'] }}</strong> 있습니다.
</span>
<button
hx-delete="{{ route('fcm.tokens.deleteError') }}"
hx-confirm="에러 토큰 {{ $stats['has_error'] }}건을 모두 삭제하시겠습니까?"
class="bg-red-600 hover:bg-red-700 text-white text-sm px-4 py-1.5 rounded-lg transition font-medium"
>
에러 토큰 전체삭제
</button>
</div>
@endif
<!-- 테이블 -->
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div id="table-container">

View File

@@ -680,6 +680,7 @@
Route::get('/tokens/stats', [FcmController::class, 'tokenStats'])->name('tokens.stats');
Route::post('/tokens/{id}/toggle', [FcmController::class, 'toggleToken'])->name('tokens.toggle');
Route::delete('/tokens/{id}', [FcmController::class, 'deleteToken'])->name('tokens.delete');
Route::delete('/tokens-error/bulk', [FcmController::class, 'deleteErrorTokens'])->name('tokens.deleteError');
// 테스트 발송
Route::get('/send', [FcmController::class, 'send'])->name('send');