110 lines
4.7 KiB
Vue
110 lines
4.7 KiB
Vue
<script setup lang="ts">
|
||
import { Head, router } from '@inertiajs/vue3';
|
||
import { onMounted, ref, nextTick } from 'vue';
|
||
import CasinoAdminLayout from '@/layouts/admin/CasinoAdminLayout.vue';
|
||
|
||
const props = defineProps<{ aiEnabled?: boolean }>();
|
||
const ai = ref(!!props.aiEnabled);
|
||
|
||
const toggling = ref(false);
|
||
async function toggleAi() {
|
||
toggling.value = true;
|
||
try {
|
||
await router.post('/admin/chat/toggle-ai', { enabled: ai.value }, { preserveScroll: true });
|
||
} finally {
|
||
toggling.value = false;
|
||
}
|
||
}
|
||
|
||
const messages = ref<any[]>([]);
|
||
const loading = ref(false);
|
||
|
||
async function loadMessages() {
|
||
loading.value = true;
|
||
try {
|
||
const res = await fetch('/api/chat?limit=50');
|
||
if (res.ok) {
|
||
const json = await res.json();
|
||
messages.value = json?.data || [];
|
||
}
|
||
} finally {
|
||
loading.value = false;
|
||
nextTick(() => { if ((window as any).lucide) (window as any).lucide.createIcons(); });
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadMessages();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<CasinoAdminLayout>
|
||
<Head title="Admin – Live Chat" />
|
||
<template #title>
|
||
Live Chat Management
|
||
</template>
|
||
<template #actions>
|
||
<label class="switch">
|
||
<input type="checkbox" v-model="ai" @change="toggleAi" :disabled="toggling" />
|
||
<span class="slider"></span>
|
||
<span class="ml-2 text-sm" :class="ai ? 'text-green' : 'text-muted'">AI {{ ai ? 'Enabled' : 'Disabled' }}</span>
|
||
</label>
|
||
</template>
|
||
|
||
<div class="panel">
|
||
<div class="panel-header">
|
||
<h3>Recent Messages</h3>
|
||
<button class="btn-ghost" @click="loadMessages" :disabled="loading">
|
||
<i data-lucide="refresh-ccw"></i> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="chat-list">
|
||
<div v-for="m in messages" :key="m.id" class="chat-item">
|
||
<div class="avatar">{{ m.user?.username?.charAt(0)?.toUpperCase() || '?' }}</div>
|
||
<div class="content">
|
||
<div class="meta">
|
||
<span class="name">{{ m.user?.username || 'Unknown' }}</span>
|
||
<span class="time">{{ new Date(m.created_at).toLocaleString() }}</span>
|
||
</div>
|
||
<div class="text">{{ m.message }}</div>
|
||
</div>
|
||
<form :action="`/admin/chat/${m.id}`" method="post" class="actions" @submit.prevent="$el.submit()">
|
||
<input type="hidden" name="_method" value="DELETE" />
|
||
<button class="btn-danger" title="Delete message"><i data-lucide="trash-2"></i></button>
|
||
</form>
|
||
</div>
|
||
<div v-if="!messages.length && !loading" class="text-center py-8 text-muted">No messages yet.</div>
|
||
</div>
|
||
</div>
|
||
</CasinoAdminLayout>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.panel { background: #111113; border: 1px solid #1f1f22; border-radius: 16px; }
|
||
.panel-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #1f1f22; }
|
||
.chat-list { display: flex; flex-direction: column; }
|
||
.chat-item { display: grid; grid-template-columns: 40px 1fr auto; gap: 12px; padding: 14px 20px; border-bottom: 1px solid #1f1f22; }
|
||
.avatar { width: 40px; height: 40px; border-radius: 50%; background: #27272a; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; }
|
||
.meta { display: flex; gap: 8px; align-items: baseline; }
|
||
.meta .name { font-weight: 700; color: #fff; }
|
||
.meta .time { color: #a1a1aa; font-size: 12px; }
|
||
.text { color: #e4e4e7; }
|
||
.actions { display: flex; align-items: center; gap: 8px; }
|
||
.btn-ghost { background: transparent; border: none; color: #a1a1aa; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
|
||
.btn-ghost:hover { color: #fff; }
|
||
.btn-danger { background: transparent; border: 1px solid #7f1d1d; color: #ef4444; padding: 6px 10px; border-radius: 8px; cursor: pointer; }
|
||
.btn-danger:hover { background: rgba(239, 68, 68, .1); }
|
||
|
||
/* Switch */
|
||
.switch { display: inline-flex; align-items: center; }
|
||
.switch input { display: none; }
|
||
.switch .slider { width: 40px; height: 22px; background: #27272a; border-radius: 9999px; position: relative; transition: .2s; display: inline-block; }
|
||
.switch .slider:after { content: ''; width: 18px; height: 18px; background: #fff; border-radius: 50%; position: absolute; top: 2px; left: 2px; transition: .2s; }
|
||
.switch input:checked + .slider { background: #16a34a; }
|
||
.switch input:checked + .slider:after { transform: translateX(18px); }
|
||
|
||
.text-muted { color: #a1a1aa; }
|
||
.text-green { color: #16a34a; }
|
||
.text-white { color: #fff; }
|
||
</style> |