Merge branch 'main' into copilot/fix-mailbox-parameter-editing

This commit is contained in:
Christian Krakau-Louis
2026-03-26 11:47:37 +01:00
committed by GitHub
9 changed files with 302 additions and 37 deletions
+6 -6
View File
@@ -5625,9 +5625,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6432,9 +6432,9 @@
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
+66 -26
View File
@@ -15,6 +15,7 @@ import {
Server,
AlertTriangle,
XCircle,
Bug,
} from 'lucide-react';
export default function SettingsPage() {
@@ -104,6 +105,19 @@ function SettingsContent() {
},
});
const [debugEmailResult, setDebugEmailResult] = useState<string | null>(null);
const sendDebugEmailMutation = useMutation({
mutationFn: gmailApi.sendDebugEmail,
onSuccess: () => {
setDebugEmailResult('success');
setTimeout(() => setDebugEmailResult(null), 5000);
},
onError: () => {
setDebugEmailResult('error');
setTimeout(() => setDebugEmailResult(null), 5000);
},
});
const saveSmtpMutation = useMutation({
mutationFn: smtpApi.save,
onSuccess: () => {
@@ -302,35 +316,61 @@ function SettingsContent() {
)}
{!gmailLoading && gmailConnected && (
<div className="flex items-center justify-between flex-wrap gap-4">
<div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-500" />
<div>
<p className="text-sm font-medium text-gray-900">
Connected as <span className="font-semibold">{gmailCredential.gmail_email}</span>
</p>
{gmailCredential.last_verified_at && (
<p className="text-xs text-gray-500">
Last verified: {new Date(gmailCredential.last_verified_at).toLocaleString()}
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between flex-wrap gap-4">
<div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-500" />
<div>
<p className="text-sm font-medium text-gray-900">
Connected as <span className="font-semibold">{gmailCredential.gmail_email}</span>
</p>
)}
{gmailCredential.last_verified_at && (
<p className="text-xs text-gray-500">
Last verified: {new Date(gmailCredential.last_verified_at).toLocaleString()}
</p>
)}
</div>
</div>
<div className="flex gap-2 flex-wrap">
<button
onClick={() => sendDebugEmailMutation.mutate()}
disabled={sendDebugEmailMutation.isPending}
title="Inject a test email into your Gmail inbox"
className="flex items-center gap-1.5 px-4 py-2 text-sm bg-amber-50 text-amber-700 rounded-md hover:bg-amber-100 transition-colors disabled:opacity-50"
>
{sendDebugEmailMutation.isPending ? (
<><Loader2 className="h-4 w-4 animate-spin" />Sending</>
) : (
<><Bug className="h-4 w-4" />Send Debug Email</>
)}
</button>
<button
onClick={handleConnectGmail}
className="px-4 py-2 text-sm bg-blue-50 text-blue-700 rounded-md hover:bg-blue-100 transition-colors"
>
Re-authorise
</button>
<button
onClick={handleDisconnectGmail}
disabled={disconnectGmailMutation.isPending}
className="px-4 py-2 text-sm bg-red-50 text-red-700 rounded-md hover:bg-red-100 transition-colors disabled:opacity-50"
>
Disconnect
</button>
</div>
</div>
<div className="flex gap-2">
<button
onClick={handleConnectGmail}
className="px-4 py-2 text-sm bg-blue-50 text-blue-700 rounded-md hover:bg-blue-100 transition-colors"
>
Re-authorise
</button>
<button
onClick={handleDisconnectGmail}
disabled={disconnectGmailMutation.isPending}
className="px-4 py-2 text-sm bg-red-50 text-red-700 rounded-md hover:bg-red-100 transition-colors disabled:opacity-50"
>
Disconnect
</button>
</div>
{debugEmailResult === 'success' && (
<div className="flex items-center gap-2 text-sm text-green-700 bg-green-50 border border-green-200 rounded-md px-3 py-2">
<CheckCircle className="h-4 w-4 flex-shrink-0" />
Debug email injected successfully. Check your Gmail inbox it should be labelled <strong className="mx-1">test</strong> and <strong className="mx-1">imported</strong>.
</div>
)}
{debugEmailResult === 'error' && (
<div className="flex items-center gap-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded-md px-3 py-2">
<AlertTriangle className="h-4 w-4 flex-shrink-0" />
Failed to inject debug email. Check that Gmail API access is still valid.
</div>
)}
</div>
)}
+13
View File
@@ -145,6 +145,13 @@ export interface GmailCredential {
updated_at: string;
}
export interface GmailDebugEmailResponse {
message: string;
message_id: string | null;
thread_id: string | null;
label_ids: string[];
}
export interface UserSmtpConfig {
id: number;
user_id: number;
@@ -322,6 +329,12 @@ export const gmailApi = {
async disconnect(): Promise<void> {
await api.delete('/providers/gmail-credential');
},
/** Inject a debug test email into the user's Gmail inbox. */
async sendDebugEmail(): Promise<GmailDebugEmailResponse> {
const response = await api.post<GmailDebugEmailResponse>('/providers/gmail/debug-email');
return response.data;
},
};
// ── SMTP Config API ─────────────────────────────────────────────────────