diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..853540d --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,8 @@ +# Project Guidelines & Automated Checks + +## Formatting and Linting +When asked to fix or check formatting/linting issues, or before committing code changes, always run the automated fix commands directly instead of manually inspecting and fixing errors one by one: + +```bash +npm run format && composer lint && npm run lint +``` diff --git a/app/Http/Controllers/Admin/EmailController.php b/app/Http/Controllers/Admin/EmailController.php index 651c0a3..c26e45d 100644 --- a/app/Http/Controllers/Admin/EmailController.php +++ b/app/Http/Controllers/Admin/EmailController.php @@ -85,7 +85,7 @@ public function store(Request $request): RedirectResponse foreach ($rawLines as $line) { $email = strtolower(trim($line)); - if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) { + if (! empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) { $cleanedEmails[$email] = true; } } diff --git a/resources/js/components/NotificationDropdown.vue b/resources/js/components/NotificationDropdown.vue index 7ecb300..aa434bd 100644 --- a/resources/js/components/NotificationDropdown.vue +++ b/resources/js/components/NotificationDropdown.vue @@ -179,13 +179,14 @@ const handleScroll = (e: Event) => { return; } - // Ignore scroll events originating from inside the dropdown itself (e.g. scrolling the notification items) - if ( - dropdownRef.value && - e.target instanceof Node && - dropdownRef.value.contains(e.target) - ) { - return; + // Ignore scroll events originating from inside the dropdown or panel itself (e.g. scrolling the notification items) + if (e.target instanceof Node) { + if ( + (dropdownRef.value && dropdownRef.value.contains(e.target)) || + (panelRef.value && panelRef.value.contains(e.target)) + ) { + return; + } } closeDropdown(); diff --git a/resources/js/pages/admin/EmailSend.vue b/resources/js/pages/admin/EmailSend.vue index abd66ee..432b8e1 100644 --- a/resources/js/pages/admin/EmailSend.vue +++ b/resources/js/pages/admin/EmailSend.vue @@ -20,7 +20,7 @@ import { import { computed, onBeforeUnmount, onMounted, ref } from 'vue'; import HTMLEditor from '@/components/HTMLEditor.vue'; -const props = defineProps({ +defineProps({ recipientCount: { type: Number, default: 0, @@ -87,6 +87,7 @@ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const recipientStats = computed(() => { const raw = form.recipients.trim(); + if (!raw) { return { validEmails: [] as string[], @@ -107,6 +108,7 @@ const recipientStats = computed(() => { for (const token of tokens) { const lower = token.toLowerCase(); + if (emailRegex.test(lower)) { if (seen.has(lower)) { duplicateCount++; @@ -148,7 +150,10 @@ const handleRemoveImage = () => { // Import helper const importSubscribers = async (type: 'all' | 'students' | 'staff') => { - if (isImporting.value) return; + if (isImporting.value) { + return; + } + isImporting.value = true; isImportDropdownOpen.value = false; @@ -191,11 +196,13 @@ const handleSendClick = () => { 'recipients', 'Please provide at least one valid recipient email.', ); + return; } if (!form.subject.trim() || !form.body.trim()) { form.validate(); + return; } @@ -333,7 +340,9 @@ const submitForm = () => { class="flex w-full items-center justify-between rounded-xl px-3 py-2 text-left text-xs font-semibold text-slate-700 transition hover:bg-indigo-50 hover:text-indigo-600 disabled:cursor-not-allowed disabled:opacity-50 dark:text-gray-200 dark:hover:bg-indigo-950/40 dark:hover:text-indigo-400" >