codeapp-js 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI/codeapp.agent.md +105 -0
- package/AI/skills/connections/SKILL.md +47 -0
- package/AI/skills/dataverse/SKILL.md +99 -0
- package/AI/skills/environment-variables/SKILL.md +89 -0
- package/AI/skills/frontend-design/SKILL.md +34 -0
- package/AI/skills/jira/SKILL.md +81 -0
- package/AI/skills/office365-groups/SKILL.md +61 -0
- package/AI/skills/office365-outlook/SKILL.md +52 -0
- package/AI/skills/office365-users/SKILL.md +78 -0
- package/AI/skills/sharepoint/SKILL.md +77 -0
- package/AI/skills/sql/SKILL.md +85 -0
- package/AI/skills/start/SKILL.md +46 -0
- package/AI/skills/teams/SKILL.md +55 -0
- package/{examples/combined demo/.power/schemas/office365groups/office365groups.Schema.json → codeApp/.power/schemas/office365groups/office365groups.Schema.json} +2203 -2203
- package/codeApp/dist/codeapp.js +95 -1792
- package/codeApp/dist/connectors/azureKeyvault.js +459 -0
- package/codeApp/dist/connectors/jira.js +1247 -0
- package/codeApp/dist/connectors/office365groups.js +642 -0
- package/codeApp/dist/connectors/office365users.js +513 -0
- package/codeApp/dist/connectors/outlook.js +1393 -0
- package/codeApp/dist/connectors/sharepoint.js +466 -0
- package/codeApp/dist/connectors/sql.js +149 -0
- package/codeApp/dist/connectors/teams.js +280 -0
- package/codeApp/dist/power-apps-data.js +16 -2
- package/examples/{kanban → apps/kanban}/dist/dataverse.js +94 -94
- package/examples/{kanban → apps/kanban}/dist/environmentVar.js +55 -55
- package/examples/{kanban → apps/kanban}/dist/index.css +605 -605
- package/examples/{kanban → apps/kanban}/dist/index.html +21 -21
- package/examples/{kanban → apps/kanban}/dist/index.js +860 -860
- package/examples/{kanban → apps/kanban}/dist/office365groups.js +97 -97
- package/examples/apps/kanban/dist/office365users.js +451 -0
- package/examples/{kanban → apps/kanban}/dist/outlook.js +162 -162
- package/examples/{planning Poker/dist/power-apps-data.js → apps/kanban/dist/power-apps-data.js} +2953 -2953
- package/examples/{kanban → apps/kanban}/dist/sharepoint.js +435 -339
- package/examples/{kanban → apps/kanban}/power.config.json +35 -35
- package/examples/{planning Poker → apps/planning Poker}/additional files/customizations (tables).xml +6428 -6428
- package/examples/{planning Poker → apps/planning Poker}/additional files/dataverse-tables.json +165 -165
- package/examples/{planning Poker → apps/planning Poker}/additional files/readme.md +122 -122
- package/examples/{planning Poker → apps/planning Poker}/dist/dataverse.js +78 -78
- package/examples/{planning Poker → apps/planning Poker}/dist/index.html +198 -198
- package/examples/{planning Poker → apps/planning Poker}/dist/index.js +954 -954
- package/examples/{todo/dist/power-apps-data.js → apps/planning Poker/dist/power-apps-data.js } +2953 -2953
- package/examples/{planning Poker → apps/planning Poker}/dist/styles.css +815 -815
- package/examples/{planning Poker → apps/planning Poker}/power.config.json +50 -50
- package/examples/{outlook Demo2 → apps/solution explorer}/dist/codeapp.js +9 -245
- package/examples/apps/solution explorer/dist/index.html +80 -0
- package/examples/apps/solution explorer/dist/index.js +735 -0
- package/examples/apps/solution explorer/dist/styles.css +571 -0
- package/examples/apps/solution explorer/power.config.json +151 -0
- package/examples/{todo → apps/todo}/dist/dataverse.js +64 -64
- package/examples/{todo → apps/todo}/dist/index.html +75 -75
- package/examples/{todo → apps/todo}/dist/index.js +8 -8
- package/examples/{kanban → apps/todo}/dist/power-apps-data.js +2953 -2953
- package/examples/{todo → apps/todo}/dist/renderer.js +375 -375
- package/examples/{todo → apps/todo}/dist/styles.css +691 -691
- package/examples/{todo → apps/todo}/power.config.json +34 -34
- package/examples/combined demo/.power/schemas/appschemas/dataSourcesInfo.ts +6275 -7830
- package/examples/combined demo/.power/schemas/jira/jira.Schema.json +6903 -0
- package/examples/combined demo/.power/schemas/keyvault/keyvault.Schema.json +1600 -0
- package/examples/combined demo/.power/schemas/teams/teams.Schema.json +11112 -0
- package/examples/combined demo/dist/codeapp.js +394 -1098
- package/examples/{outlook Demo2/OutlookDemo_1_0_0_1.zip → combined demo/dist/icon-512.png} +0 -0
- package/examples/combined demo/dist/index.html +29 -511
- package/examples/combined demo/dist/index.js +490 -470
- package/examples/combined demo/dist/office365users.js +513 -0
- package/examples/combined demo/dist/outlook.js +1393 -0
- package/examples/combined demo/dist/power-apps-data.js +3079 -3006
- package/examples/combined demo/dist/styles.css +483 -0
- package/examples/combined demo/power.config.json +33 -42
- package/examples/combined demo/src/generated/index.ts +12 -14
- package/examples/combined demo/src/generated/models/AzureKeyVaultModel.ts +107 -0
- package/examples/combined demo/src/generated/models/JiraModel.ts +501 -0
- package/examples/combined demo/src/generated/services/AzureKeyVaultService.ts +257 -0
- package/examples/combined demo/src/generated/services/JiraService.ts +1124 -0
- package/examples/dataverse Demo/dist/codeapp.js +394 -1085
- package/examples/dataverse Demo/dist/icon-512.png +0 -0
- package/examples/dataverse Demo/dist/index.html +146 -54
- package/examples/dataverse Demo/dist/index.js +693 -83
- package/examples/dataverse Demo/dist/power-apps-data.js +3079 -2911
- package/examples/dataverse Demo/dist/styles.css +528 -0
- package/examples/dataverse Demo/power.config.json +41 -35
- package/examples/dataverse Demo/readme.md +79 -79
- package/examples/groups Demo/dist/codeapp.js +394 -1085
- package/examples/groups Demo/dist/icon-512.png +0 -0
- package/examples/groups Demo/dist/index.html +21 -25
- package/examples/groups Demo/dist/index.js +304 -113
- package/examples/groups Demo/dist/office365groups.js +642 -0
- package/examples/groups Demo/dist/power-apps-data.js +3079 -2911
- package/examples/groups Demo/dist/styles.css +509 -0
- package/examples/groups Demo/power.config.json +25 -25
- package/examples/myProfile/dist/codeapp.js +398 -0
- package/examples/myProfile/dist/index.html +21 -184
- package/examples/myProfile/dist/index.js +324 -141
- package/examples/myProfile/dist/office365users.js +517 -169
- package/examples/myProfile/dist/power-apps-data.js +3080 -2953
- package/examples/myProfile/dist/styles.css +458 -0
- package/examples/myProfile/power.config.json +24 -23
- package/examples/outlook Demo/dist/codeapp.js +394 -1085
- package/examples/outlook Demo/dist/index.html +150 -35
- package/examples/outlook Demo/dist/index.js +516 -170
- package/examples/outlook Demo/dist/outlook.js +1393 -121
- package/examples/outlook Demo/dist/power-apps-data.js +3079 -2911
- package/examples/outlook Demo/dist/styles.css +408 -84
- package/examples/outlook Demo/power.config.json +24 -23
- package/examples/outlook Demo/readme.md +92 -82
- package/examples/sharePoint Demo/dist/codeapp.js +394 -1085
- package/examples/sharePoint Demo/dist/icon-512.png +0 -0
- package/examples/sharePoint Demo/dist/index.html +22 -255
- package/examples/sharePoint Demo/dist/index.js +899 -262
- package/examples/sharePoint Demo/dist/power-apps-data.js +3079 -2911
- package/{dev files → examples/sharePoint Demo/dist}/sharepoint.js +239 -112
- package/examples/sharePoint Demo/dist/styles.css +587 -0
- package/examples/sharePoint Demo/power.config.json +23 -22
- package/package.json +1 -1
- package/readme.md +465 -76
- package/.vscode/settings.json +0 -6
- package/dev files/customConnector.js +0 -98
- package/dev files/dataverse.js +0 -120
- package/dev files/environmentVar.js +0 -55
- package/dev files/office365groups.js +0 -65
- package/dev files/office365users.js +0 -169
- package/dev files/outlook.js +0 -330
- package/dev files/power-apps-data.js +0 -2952
- package/examples/combined demo/.power/schemas/office365/office365.Schema.json +0 -21098
- package/examples/combined demo/.power/schemas/office365users/office365users.Schema.json +0 -2094
- package/examples/kanban/agent/decision-log.md +0 -9
- package/examples/kanban/agent/mockup-01-editorial-glass.html +0 -159
- package/examples/kanban/agent/mockup-02-dark-rail.html +0 -147
- package/examples/kanban/agent/mockup-03-paper-grid.html +0 -114
- package/examples/kanban/agent/mockup-04-neon-minimal.html +0 -141
- package/examples/kanban/agent/mockup-05-mono-architect.html +0 -119
- package/examples/kanban/dist/office365users.js +0 -169
- package/examples/kanban/src/generated/index.ts +0 -14
- package/examples/kanban/src/generated/models/Office365GroupsModel.ts +0 -363
- package/examples/kanban/src/generated/models/Office365OutlookModel.ts +0 -2046
- package/examples/kanban/src/generated/models/Office365UsersModel.ts +0 -254
- package/examples/kanban/src/generated/services/Office365GroupsService.ts +0 -326
- package/examples/kanban/src/generated/services/Office365OutlookService.ts +0 -2476
- package/examples/kanban/src/generated/services/Office365UsersService.ts +0 -358
- package/examples/outlook Demo2/agent/decision-log.md +0 -7
- package/examples/outlook Demo2/dist/index.html +0 -98
- package/examples/outlook Demo2/dist/index.js +0 -272
- package/examples/outlook Demo2/dist/styles.css +0 -639
- package/examples/outlook Demo2/power.config.json +0 -23
- package/examples/planning Poker/.vscode/settings.json +0 -5
- package/examples/sharePoint Demo/agent/decision-log.md +0 -17
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/index.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365GroupsModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365OutlookModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365UsersModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365GroupsService.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365OutlookService.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365UsersService.ts +0 -0
- /package/examples/{planning Poker → apps/planning Poker}/additional files/AgilePoker_1_0_0_1.zip +0 -0
- /package/examples/{planning Poker → apps/planning Poker}/additional files/PokerTables_1_0_0_1.zip +0 -0
- /package/examples/{outlook Demo2 → apps/solution explorer}/dist/icon-512.png +0 -0
- /package/examples/{outlook Demo2 → apps/solution explorer}/dist/power-apps-data.js +0 -0
- /package/examples/{todo → apps/todo}/dist/icon192.png +0 -0
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { listEmails, sendEmail } from './codeapp.js';
|
|
2
|
-
|
|
3
|
-
// ===== STATE =====
|
|
4
|
-
let emails = [];
|
|
5
|
-
let selectedEmailId = null;
|
|
6
|
-
|
|
7
|
-
// ===== DOM REFS =====
|
|
8
|
-
const $ = (sel) => document.querySelector(sel);
|
|
9
|
-
const emailListEl = () => $('#emailList');
|
|
10
|
-
const emailCountEl = () => $('#emailCount');
|
|
11
|
-
const loadingEl = () => $('#loadingSkeleton');
|
|
12
|
-
const detailEmpty = () => $('#detailEmpty');
|
|
13
|
-
const detailContent = () => $('#detailContent');
|
|
14
|
-
const statusBar = () => $('#statusBar');
|
|
15
|
-
const statusText = () => $('#statusText');
|
|
16
|
-
const composeOverlay = () => $('#composeOverlay');
|
|
17
|
-
|
|
18
|
-
// ===== STATUS HELPERS =====
|
|
19
|
-
const setStatus = (text, type = 'ok') => {
|
|
20
|
-
const bar = statusBar();
|
|
21
|
-
const txt = statusText();
|
|
22
|
-
if (!bar || !txt) return;
|
|
23
|
-
bar.className = 'status-bar' + (type === 'error' ? ' status-bar--error' : type === 'loading' ? ' status-bar--loading' : '');
|
|
24
|
-
txt.textContent = text;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// ===== FORMAT HELPERS =====
|
|
28
|
-
const getInitials = (name) => {
|
|
29
|
-
if (!name) return '?';
|
|
30
|
-
const parts = name.trim().split(/\s+/);
|
|
31
|
-
if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
32
|
-
return name.slice(0, 2).toUpperCase();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const formatTime = (dateStr) => {
|
|
36
|
-
if (!dateStr) return '';
|
|
37
|
-
try {
|
|
38
|
-
const d = new Date(dateStr);
|
|
39
|
-
const now = new Date();
|
|
40
|
-
const diff = now - d;
|
|
41
|
-
const oneDay = 86400000;
|
|
42
|
-
if (diff < oneDay && d.getDate() === now.getDate()) {
|
|
43
|
-
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
44
|
-
}
|
|
45
|
-
if (diff < 7 * oneDay) {
|
|
46
|
-
return d.toLocaleDateString([], { weekday: 'short' });
|
|
47
|
-
}
|
|
48
|
-
return d.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
49
|
-
} catch {
|
|
50
|
-
return '';
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const formatFullDate = (dateStr) => {
|
|
55
|
-
if (!dateStr) return '';
|
|
56
|
-
try {
|
|
57
|
-
return new Date(dateStr).toLocaleString([], {
|
|
58
|
-
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
|
|
59
|
-
hour: '2-digit', minute: '2-digit'
|
|
60
|
-
});
|
|
61
|
-
} catch {
|
|
62
|
-
return '';
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const stripHtml = (html) => {
|
|
67
|
-
if (!html) return '';
|
|
68
|
-
const tmp = document.createElement('div');
|
|
69
|
-
tmp.innerHTML = html;
|
|
70
|
-
return tmp.textContent || tmp.innerText || '';
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// ===== RENDER INBOX =====
|
|
74
|
-
const renderEmailList = () => {
|
|
75
|
-
const list = emailListEl();
|
|
76
|
-
if (!list) return;
|
|
77
|
-
|
|
78
|
-
const skeleton = loadingEl();
|
|
79
|
-
if (skeleton) skeleton.remove();
|
|
80
|
-
|
|
81
|
-
if (emails.length === 0) {
|
|
82
|
-
list.innerHTML = '<div style="padding:40px 20px;text-align:center;color:var(--ink-muted);font-size:14px;">No emails found</div>';
|
|
83
|
-
emailCountEl().textContent = '0';
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
emailCountEl().textContent = `${emails.length}`;
|
|
88
|
-
|
|
89
|
-
list.innerHTML = emails.map((email) => {
|
|
90
|
-
const id = email.Id || email.id || '';
|
|
91
|
-
const from = email.From || email.from;
|
|
92
|
-
const senderName = from?.EmailAddress?.Name || from?.emailAddress?.name || from?.Name || 'Unknown';
|
|
93
|
-
const subject = email.Subject || email.subject || '(No subject)';
|
|
94
|
-
const preview = stripHtml(email.BodyPreview || email.bodyPreview || email.Body?.Content || '');
|
|
95
|
-
const dateStr = email.DateTimeReceived || email.receivedDateTime || email.ReceivedDateTime || '';
|
|
96
|
-
const isRead = email.IsRead ?? email.isRead ?? true;
|
|
97
|
-
const isActive = id === selectedEmailId;
|
|
98
|
-
|
|
99
|
-
return `
|
|
100
|
-
<div class="email-item ${!isRead ? 'email-item--unread' : ''} ${isActive ? 'email-item--active' : ''}"
|
|
101
|
-
data-id="${id}" role="button" tabindex="0">
|
|
102
|
-
<div class="email-item__avatar">${getInitials(senderName)}</div>
|
|
103
|
-
<div class="email-item__content">
|
|
104
|
-
<div class="email-item__row">
|
|
105
|
-
<span class="email-item__sender">${escapeHtml(senderName)}</span>
|
|
106
|
-
<span class="email-item__time">${formatTime(dateStr)}</span>
|
|
107
|
-
</div>
|
|
108
|
-
<div class="email-item__subject">${escapeHtml(subject)}</div>
|
|
109
|
-
<div class="email-item__preview">${escapeHtml(preview.slice(0, 100))}</div>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
`;
|
|
113
|
-
}).join('');
|
|
114
|
-
|
|
115
|
-
// Attach click handlers
|
|
116
|
-
list.querySelectorAll('.email-item').forEach((el) => {
|
|
117
|
-
el.addEventListener('click', () => selectEmail(el.dataset.id));
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const escapeHtml = (str) => {
|
|
122
|
-
const div = document.createElement('div');
|
|
123
|
-
div.appendChild(document.createTextNode(str || ''));
|
|
124
|
-
return div.innerHTML;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// ===== SELECT EMAIL =====
|
|
128
|
-
const selectEmail = (id) => {
|
|
129
|
-
selectedEmailId = id;
|
|
130
|
-
const email = emails.find((e) => (e.Id || e.id) === id);
|
|
131
|
-
if (!email) return;
|
|
132
|
-
|
|
133
|
-
// Re-render list to update active state
|
|
134
|
-
renderEmailList();
|
|
135
|
-
|
|
136
|
-
// Show detail
|
|
137
|
-
const empty = detailEmpty();
|
|
138
|
-
const content = detailContent();
|
|
139
|
-
if (empty) empty.style.display = 'none';
|
|
140
|
-
if (!content) return;
|
|
141
|
-
content.style.display = 'block';
|
|
142
|
-
|
|
143
|
-
const from = email.From || email.from;
|
|
144
|
-
const senderName = from?.EmailAddress?.Name || from?.emailAddress?.name || from?.Name || 'Unknown';
|
|
145
|
-
const senderEmail = from?.EmailAddress?.Address || from?.emailAddress?.address || from?.Address || '';
|
|
146
|
-
const subject = email.Subject || email.subject || '(No subject)';
|
|
147
|
-
const dateStr = email.DateTimeReceived || email.receivedDateTime || email.ReceivedDateTime || '';
|
|
148
|
-
const body = email.Body?.Content || email.body?.content || email.BodyPreview || email.bodyPreview || '';
|
|
149
|
-
|
|
150
|
-
content.innerHTML = `
|
|
151
|
-
<h2 class="detail-panel__subject">${escapeHtml(subject)}</h2>
|
|
152
|
-
<div class="detail-panel__meta">
|
|
153
|
-
<div class="detail-panel__meta-avatar">${getInitials(senderName)}</div>
|
|
154
|
-
<div class="detail-panel__meta-info">
|
|
155
|
-
<div class="detail-panel__meta-sender">${escapeHtml(senderName)}</div>
|
|
156
|
-
<div class="detail-panel__meta-email">${escapeHtml(senderEmail)}</div>
|
|
157
|
-
<div class="detail-panel__meta-date">${formatFullDate(dateStr)}</div>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
<div class="detail-panel__body">${body}</div>
|
|
161
|
-
`;
|
|
162
|
-
|
|
163
|
-
// Mobile: show detail
|
|
164
|
-
const appBody = $('#appBody');
|
|
165
|
-
if (appBody) appBody.classList.add('app-body--detail-open');
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// ===== FETCH INBOX =====
|
|
169
|
-
const fetchInbox = async () => {
|
|
170
|
-
setStatus('Loading inbox…', 'loading');
|
|
171
|
-
try {
|
|
172
|
-
const result = await listEmails({ folderId: 'Inbox', top: 25 });
|
|
173
|
-
emails = Array.isArray(result) ? result : [];
|
|
174
|
-
if (result && Array.isArray(result.value)) emails = result.value;
|
|
175
|
-
renderEmailList();
|
|
176
|
-
setStatus(`${emails.length} email${emails.length !== 1 ? 's' : ''} loaded`);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
console.error('Failed to fetch inbox:', err);
|
|
179
|
-
setStatus(`Error: ${err.message}`, 'error');
|
|
180
|
-
const list = emailListEl();
|
|
181
|
-
const skeleton = loadingEl();
|
|
182
|
-
if (skeleton) skeleton.remove();
|
|
183
|
-
if (list) {
|
|
184
|
-
list.innerHTML = `<div style="padding:40px 20px;text-align:center;color:var(--accent);font-size:14px;">
|
|
185
|
-
Failed to load emails.<br><span style="color:var(--ink-muted);font-size:12px;">${escapeHtml(err.message)}</span>
|
|
186
|
-
</div>`;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// ===== SEND EMAIL =====
|
|
192
|
-
const doSendEmail = async () => {
|
|
193
|
-
const toEl = $('#composeTo');
|
|
194
|
-
const subjectEl = $('#composeSubject');
|
|
195
|
-
const bodyEl = $('#composeBody');
|
|
196
|
-
const sendBtn = $('#btnSendEmail');
|
|
197
|
-
|
|
198
|
-
const to = toEl?.value?.trim();
|
|
199
|
-
const subject = subjectEl?.value?.trim();
|
|
200
|
-
const body = bodyEl?.value?.trim();
|
|
201
|
-
|
|
202
|
-
if (!to) {
|
|
203
|
-
toEl?.focus();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
sendBtn.disabled = true;
|
|
208
|
-
sendBtn.textContent = 'Sending…';
|
|
209
|
-
setStatus('Sending email…', 'loading');
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
await sendEmail({
|
|
213
|
-
to: to,
|
|
214
|
-
subject: subject || '(No subject)',
|
|
215
|
-
body: '<p>' + escapeHtml(body || '') + '</p>',
|
|
216
|
-
importance: 'Normal'
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
setStatus('Email sent successfully');
|
|
220
|
-
closeCompose();
|
|
221
|
-
|
|
222
|
-
// Clear form
|
|
223
|
-
if (toEl) toEl.value = '';
|
|
224
|
-
if (subjectEl) subjectEl.value = '';
|
|
225
|
-
if (bodyEl) bodyEl.value = '';
|
|
226
|
-
} catch (err) {
|
|
227
|
-
console.error('Failed to send email:', err);
|
|
228
|
-
setStatus(`Send failed: ${err.message}`, 'error');
|
|
229
|
-
} finally {
|
|
230
|
-
sendBtn.disabled = false;
|
|
231
|
-
sendBtn.textContent = 'Send ➤';
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
// ===== COMPOSE MODAL =====
|
|
236
|
-
const openCompose = () => {
|
|
237
|
-
const overlay = composeOverlay();
|
|
238
|
-
if (overlay) overlay.classList.add('modal-overlay--visible');
|
|
239
|
-
setTimeout(() => $('#composeTo')?.focus(), 200);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const closeCompose = () => {
|
|
243
|
-
const overlay = composeOverlay();
|
|
244
|
-
if (overlay) overlay.classList.remove('modal-overlay--visible');
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// ===== EVENT BINDINGS =====
|
|
248
|
-
const bindEvents = () => {
|
|
249
|
-
$('#btnCompose')?.addEventListener('click', openCompose);
|
|
250
|
-
$('#btnCloseCompose')?.addEventListener('click', closeCompose);
|
|
251
|
-
$('#btnDiscardCompose')?.addEventListener('click', closeCompose);
|
|
252
|
-
$('#btnSendEmail')?.addEventListener('click', doSendEmail);
|
|
253
|
-
$('#btnRefresh')?.addEventListener('click', fetchInbox);
|
|
254
|
-
|
|
255
|
-
// Close modal on overlay click
|
|
256
|
-
composeOverlay()?.addEventListener('click', (e) => {
|
|
257
|
-
if (e.target === composeOverlay()) closeCompose();
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Escape key closes modal
|
|
261
|
-
document.addEventListener('keydown', (e) => {
|
|
262
|
-
if (e.key === 'Escape') closeCompose();
|
|
263
|
-
});
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// ===== BOOT =====
|
|
267
|
-
async function boot() {
|
|
268
|
-
bindEvents();
|
|
269
|
-
await fetchInbox();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
boot();
|