Skip to content

Commit 51d12fe

Browse files
committed
Updates
1 parent 4f7227f commit 51d12fe

File tree

8 files changed

+198
-17
lines changed

8 files changed

+198
-17
lines changed

packages/dashboard/src/components/ComposeEmail.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,17 @@
5959
</button>
6060
<button
6161
type="submit"
62-
class="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-xl hover:from-indigo-700 hover:to-purple-700 font-semibold shadow-lg hover:shadow-xl transition-all duration-200 flex items-center gap-2"
62+
:disabled="isLoading"
63+
class="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-xl hover:from-indigo-700 hover:to-purple-700 font-semibold shadow-lg hover:shadow-xl transition-all duration-200 flex items-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed"
6364
>
64-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
65+
<svg v-if="!isLoading" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
6566
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
6667
</svg>
67-
Send Message
68+
<svg v-else class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
69+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
70+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
71+
</svg>
72+
{{ isLoading ? 'Sending...' : 'Send Message' }}
6873
</button>
6974
</div>
7075
</form>
@@ -93,6 +98,7 @@ const to = ref("");
9398
const subject = ref("");
9499
const body = ref("");
95100
const error = ref<string | null>(null);
101+
const isLoading = ref(false);
96102
97103
const modalTitle = computed(() => {
98104
switch (composeOptions.value.mode) {
@@ -193,6 +199,7 @@ const send = async () => {
193199
error.value = "No mailbox selected.";
194200
return;
195201
}
202+
isLoading.value = true;
196203
try {
197204
const mailboxId = route.params.mailboxId as string;
198205
const emailData = {
@@ -231,6 +238,8 @@ const send = async () => {
231238
closeModal();
232239
} catch (e: any) {
233240
error.value = e.response?.data?.error || "An unexpected error occurred.";
241+
} finally {
242+
isLoading.value = false;
234243
}
235244
};
236245
</script>

packages/dashboard/src/stores/emails.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@ export const useEmailStore = defineStore("emails", {
66
state: () => ({
77
emails: [] as Email[],
88
currentEmail: null as Email | null,
9+
isRefreshing: false,
910
}),
1011
actions: {
1112
async fetchEmails(mailboxId: string, params: any) {
12-
const response = await api.listEmails(mailboxId, params);
13-
this.emails = response.data;
13+
this.isRefreshing = true;
14+
try {
15+
const response = await api.listEmails(mailboxId, params);
16+
this.emails = response.data;
17+
} finally {
18+
this.isRefreshing = false;
19+
}
1420
},
1521
async fetchEmail(mailboxId: string, id: string) {
1622
const response = await api.getEmail(mailboxId, id);

packages/dashboard/src/views/EmailList.vue

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
<template>
22
<div class="bg-white dark:bg-gray-800 shadow-xl rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-700">
3-
<div class="px-6 py-5 border-b border-gray-200 dark:border-gray-700 bg-gradient-to-r from-gray-50 to-white dark:from-gray-800 dark:to-gray-800/50">
3+
<div class="px-6 py-5 border-b border-gray-200 dark:border-gray-700 bg-gradient-to-r from-gray-50 to-white dark:from-gray-800 dark:to-gray-800/50 flex items-center justify-between">
44
<h1 class="text-2xl font-bold text-gray-900 dark:text-white capitalize">{{ folderName }}</h1>
5+
<button
6+
@click="handleRefresh"
7+
:disabled="isRefreshing"
8+
class="p-2 text-gray-600 dark:text-gray-400 hover:text-indigo-600 dark:hover:text-indigo-400 rounded-lg hover:bg-indigo-50 dark:hover:bg-gray-700/50 transition-all duration-200 disabled:opacity-70 disabled:cursor-not-allowed"
9+
:title="isRefreshing ? 'Refreshing...' : 'Refresh emails'"
10+
>
11+
<svg v-if="!isRefreshing" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
13+
</svg>
14+
<svg v-else class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
15+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
16+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
17+
</svg>
18+
</button>
519
</div>
620
<ul v-if="emails.length > 0" class="divide-y divide-gray-100 dark:divide-gray-700/50">
721
<li v-for="email in emails" :key="email.id" class="group relative transition-all duration-200" :class="{ 'bg-indigo-50/30 dark:bg-indigo-900/10': !email.read, 'hover:bg-gradient-to-r hover:from-indigo-50/50 hover:to-purple-50/30 dark:hover:from-indigo-900/10 dark:hover:to-purple-900/10': true }">
@@ -59,29 +73,58 @@
5973

6074
<script setup lang="ts">
6175
import { storeToRefs } from "pinia";
62-
import { computed, onMounted, watch } from "vue";
76+
import { computed, onMounted, onUnmounted, watch, ref } from "vue";
6377
import { useRoute } from "vue-router";
6478
import { useEmailStore } from "@/stores/emails";
6579
import { useFolderStore } from "@/stores/folders";
6680
import type { Email } from "@/types";
6781
6882
const emailStore = useEmailStore();
69-
const { emails } = storeToRefs(emailStore);
83+
const { emails, isRefreshing } = storeToRefs(emailStore);
7084
const folderStore = useFolderStore();
7185
const { folders } = storeToRefs(folderStore);
7286
const route = useRoute();
7387
88+
let refreshInterval: ReturnType<typeof setInterval> | null = null;
89+
7490
const folderId = computed(() => route.params.folder as string);
7591
7692
const folderName = computed(() => {
7793
const foundFolder = folders.value.find((f) => f.id === folderId.value);
7894
return foundFolder ? foundFolder.name : folderId.value;
7995
});
8096
97+
const startAutoRefresh = () => {
98+
if (refreshInterval) clearInterval(refreshInterval);
99+
refreshInterval = setInterval(() => {
100+
emailStore.fetchEmails(route.params.mailboxId as string, {
101+
folder: folderId.value,
102+
});
103+
}, 30000);
104+
};
105+
106+
const stopAutoRefresh = () => {
107+
if (refreshInterval) {
108+
clearInterval(refreshInterval);
109+
refreshInterval = null;
110+
}
111+
};
112+
113+
const handleRefresh = () => {
114+
emailStore.fetchEmails(route.params.mailboxId as string, {
115+
folder: folderId.value,
116+
});
117+
};
118+
81119
onMounted(() => {
82120
emailStore.fetchEmails(route.params.mailboxId as string, {
83121
folder: folderId.value,
84122
});
123+
startAutoRefresh();
124+
});
125+
126+
onUnmounted(() => {
127+
stopAutoRefresh();
85128
});
86129
87130
watch(folderId, (newFolderId) => {

packages/worker/dev/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "email-explorer-dev-worker",
33
"devDependencies": {
4-
"wrangler": "^4.45.0",
5-
"email-explorer": "workspace:*"
4+
"email-explorer": "workspace:*",
5+
"wrangler": "^4.53.0"
66
},
77
"scripts": {
88
"deploy": "wrangler deploy"

packages/worker/dev/wrangler.jsonc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
*/
55
{
66
"$schema": "node_modules/wrangler/config-schema.json",
7-
"compatibility_date": "2025-09-01",
7+
"compatibility_date": "2025-11-28",
88
"main": "index.ts",
99
"name": "email-explorer",
1010
"upload_source_maps": true,
1111
"observability": {
1212
"enabled": true
1313
},
14-
"compatibility_flags": ["nodejs_compat"],
14+
"compatibility_flags": ["nodejs_compat", "enable_email_sending_queuing"],
1515
"assets": {
1616
"directory": "node_modules/email-explorer/dashboard",
1717
"binding": "ASSETS",

0 commit comments

Comments
 (0)