[FEAT] 實作機台廣告管理模組與多語系支援
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m7s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m7s
1. 新增廣告管理列表與機台配置介面,包含多語系 (zh_TW, en, ja) 與完整 CRUD 2. 實作基於 Alpine 的廣告素材預覽輪播功能 3. 優化廣告素材下拉選單,強制綁定所屬公司以達成多租戶資料隔離 4. 重構廣告配置中廣告影片的縮圖渲染邏輯,移除 <video> 標籤以大幅提升頁面載入速度與節省頻寬 5. 放寬個人檔案頭像上傳限制,支援 WebP 格式
This commit is contained in:
85
resources/views/admin/ads/partials/assign-modal.blade.php
Normal file
85
resources/views/admin/ads/partials/assign-modal.blade.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<div x-show="isAssignModalOpen"
|
||||
class="fixed inset-0 z-[100] overflow-y-auto"
|
||||
x-cloak
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0">
|
||||
|
||||
<div class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm"></div>
|
||||
|
||||
<div class="flex items-center justify-center min-h-screen p-4">
|
||||
<div class="relative w-full max-w-lg bg-white dark:bg-slate-900 rounded-[2rem] shadow-2xl border border-slate-200 dark:border-white/10 overflow-visible animate-luxury-in"
|
||||
@click.away="isAssignModalOpen = false">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<div class="bg-slate-50/50 dark:bg-slate-800/50 rounded-t-[2rem] px-8 py-6 border-b border-slate-100 dark:border-white/5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-black text-slate-800 dark:text-white uppercase tracking-tight">{{ __('Assign Advertisement') }}</h3>
|
||||
<p class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mt-1">{{ __('Select a material to play on this machine') }}</p>
|
||||
</div>
|
||||
<button @click="isAssignModalOpen = false" class="p-2 text-slate-400 hover:text-cyan-500 transition-colors">
|
||||
<svg class="size-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="submitAssignment" class="p-8 space-y-6">
|
||||
<!-- Machine & Position Info (Read-only) -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="p-4 bg-slate-50 dark:bg-slate-800/50 rounded-2xl border border-slate-100 dark:border-white/5">
|
||||
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-1">{{ __('Target Position') }}</p>
|
||||
<p class="text-sm font-black text-cyan-600 dark:text-cyan-400 uppercase tracking-tight" x-text="{ vending: '{{ __('vending') }}', visit_gift: '{{ __('visit_gift') }}', standby: '{{ __('standby') }}' }[assignForm.position] || assignForm.position"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ad Material Selection -->
|
||||
<div class="space-y-2">
|
||||
<label class="text-xs font-black text-slate-500 dark:text-slate-400 uppercase tracking-widest ml-1">{{ __('Select Material') }}</label>
|
||||
<div id="assign_ad_select_wrapper">
|
||||
<!-- Select dropdown will be dynamically built by updateAssignSelect() based on machine company context -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Action Footer -->
|
||||
<div class="flex items-center justify-end gap-3 pt-6">
|
||||
<button type="button" @click="isAssignModalOpen = false" class="px-6 py-3 text-sm font-black text-slate-500 hover:text-slate-700 dark:hover:text-slate-300 transition-colors uppercase tracking-widest">
|
||||
{{ __('Cancel') }}
|
||||
</button>
|
||||
<button type="submit" class="btn-luxury-primary px-10 py-3">
|
||||
{{ __('Confirm Assignment') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Note: This logic is added to the main adManager data object in index.blade.php
|
||||
// Here we just define the submit handler
|
||||
async function submitAssignment() {
|
||||
try {
|
||||
const response = await fetch(this.urls.assign, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify(this.assignForm)
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
this.isAssignModalOpen = false;
|
||||
this.fetchMachineAds();
|
||||
window.showToast?.(result.message, 'success');
|
||||
} else {
|
||||
window.showToast?.(result.message || 'Error', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to assign ad', e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user