elearning/Frontend-Learner/components/classroom/AnnouncementModal.vue

98 lines
5.3 KiB
Vue

<script setup lang="ts">
/**
* @file AnnouncementModal.vue
* @description Modal component to display course announcements
*/
const props = defineProps<{
modelValue: boolean;
announcements: any[];
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void;
}>();
const { locale, t } = useI18n()
// Helper for localization
const getLocalizedText = (text: any) => {
if (!text) return ''
if (typeof text === 'string') return text
const currentLocale = locale.value as 'th' | 'en'
return text[currentLocale] || text.th || text.en || ''
}
</script>
<template>
<q-dialog :model-value="modelValue" @update:model-value="(val) => emit('update:modelValue', val)" backdrop-filter="blur(4px)">
<q-card class="min-w-[320px] md:min-w-[600px] rounded-3xl overflow-hidden !bg-white dark:!bg-slate-900 shadow-2xl">
<q-card-section class="!bg-white dark:!bg-slate-900 border-b border-gray-100 dark:border-white/5 p-5 flex items-center justify-between sticky top-0 z-10">
<div>
<div class="text-xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<div class="w-10 h-10 rounded-full bg-blue-50 dark:bg-blue-900/30 text-primary flex items-center justify-center">
<q-icon name="campaign" size="22px" />
</div>
{{ $t('classroom.announcements') }}
</div>
<div class="text-xs text-slate-500 dark:text-slate-400 ml-12 mt-1">{{ announcements.length }} {{ $t('common.items') }}</div>
</div>
<q-btn icon="close" flat round dense v-close-popup class="text-slate-400 hover:text-slate-700 dark:hover:text-white bg-slate-50 dark:bg-slate-800" />
</q-card-section>
<q-card-section class="p-0 scroll max-h-[70vh] bg-slate-50 dark:bg-[#0B0F1A]">
<div v-if="announcements.length > 0" class="p-4 space-y-4">
<div
v-for="(ann, index) in announcements"
:key="index"
class="p-5 rounded-2xl bg-white dark:bg-slate-800 shadow-sm border border-gray-200 dark:border-white/5 transition-all hover:shadow-md relative overflow-hidden group"
:class="{'ring-2 ring-orange-200 dark:ring-orange-900/40 !bg-orange-50/50 dark:!bg-orange-900/20': ann.is_pinned}"
>
<!-- Pinned Banner -->
<div v-if="ann.is_pinned" class="absolute top-0 right-0 p-3">
<q-icon name="push_pin" color="orange" size="18px" class="transform rotate-45" />
</div>
<div class="flex items-start gap-4 mb-3">
<q-avatar
:color="ann.is_pinned ? 'orange' : 'primary'"
text-color="white"
size="42px"
font-size="20px"
class="shadow-sm"
>
<span class="font-bold">{{ (getLocalizedText(ann.title) || 'A').charAt(0) }}</span>
</q-avatar>
<div class="flex-1">
<div class="font-bold text-lg text-slate-900 dark:text-white leading-tight pr-8 capitalize font-display">
{{ getLocalizedText(ann.title) || $t('sidebar.announcements') }}
</div>
<div class="text-xs text-slate-500 dark:text-slate-400 flex items-center gap-2 mt-1.5">
<span class="flex items-center gap-1 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-md">
<q-icon name="today" size="12px" />
{{ new Date(ann.created_at || Date.now()).toLocaleDateString(locale === 'th' ? 'th-TH' : 'en-US', { day: 'numeric', month: 'short', year: 'numeric' }) }}
</span>
<span class="flex items-center gap-1 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-md">
<q-icon name="access_time" size="12px" />
{{ new Date(ann.created_at || Date.now()).toLocaleTimeString(locale === 'th' ? 'th-TH' : 'en-US', { hour: '2-digit', minute: '2-digit' }) }}
</span>
</div>
</div>
</div>
<div class="pl-[58px]">
<div class="text-slate-600 dark:text-slate-300 leading-relaxed text-sm whitespace-pre-wrap">
{{ getLocalizedText(ann.content) }}
</div>
</div>
</div>
</div>
<div v-else class="p-10 flex flex-col items-center justify-center text-slate-400">
<q-icon name="campaign" size="40px" class="mb-2 opacity-50" />
<p>{{ $t('classroom.noAnnouncements') }}</p>
</div>
</q-card-section>
</q-card>
</q-dialog>
</template>