feat: filter digest tiles by user permissions and add import progress status
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s

- Store UserGroups from OIDC in UserSettings entity, sync on each request
- Filter daily digest tiles based on user's permission groups
- Add in-memory job status tracking to EmailImportService
- Poll import job status in MailImportWizard and show progress in Spin tip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 16:29:56 +02:00
parent 2747b0046a
commit 4c75a1ded2
9 changed files with 116 additions and 78 deletions
+11 -2
View File
@@ -62,10 +62,19 @@ export const emailImportApi = {
return res.data.isDuplicate;
},
executeImport: async (emailDate: string, attachments: AttachmentImportData[]): Promise<{ success: boolean; results: any[] }> => {
const res = await api.post('/api/email-import/execute', { emailDate, attachments });
executeImport: async (emailDate: string, attachments: AttachmentImportData[], jobId?: string): Promise<{ success: boolean; results: any[] }> => {
const res = await api.post('/api/email-import/execute', { emailDate, attachments, jobId }, { timeout: 300_000 });
return res.data;
},
getJobStatus: async (jobId: string): Promise<{ message: string; done: boolean } | null> => {
try {
const res = await api.get(`/api/email-import/jobs/${jobId}/status`);
return res.data;
} catch {
return null;
}
},
ensurePreviews: async (emailId: number): Promise<void> => {
await api.post(`/api/email-import/emails/${emailId}/ensure-previews`);
@@ -44,6 +44,7 @@ export default function MailImportWizard({ visible, onClose, onSuccess, email, a
// Step 3 specific state
const [importSuccess, setImportSuccess] = useState(false);
const [importStatus, setImportStatus] = useState('');
useEffect(() => {
if (visible && attachments.length > 0) {
@@ -315,6 +316,14 @@ export default function MailImportWizard({ visible, onClose, onSuccess, email, a
const executeImport = async () => {
setLoading(true);
setImportStatus('Import wird gestartet...');
const jobId = crypto.randomUUID ? crypto.randomUUID() : `job-${Date.now()}`;
const statusPoll = setInterval(async () => {
try {
const status = await emailImportApi.getJobStatus(jobId);
if (status?.message) setImportStatus(status.message);
} catch {}
}, 1500);
try {
const finalData = [];
@@ -358,12 +367,14 @@ export default function MailImportWizard({ visible, onClose, onSuccess, email, a
});
}
await emailImportApi.executeImport(email.Date, finalData);
await emailImportApi.executeImport(email.Date, finalData, jobId);
setImportSuccess(true);
setCurrentStep(2);
} catch (e: any) {
message.error(`Fehler beim Import: ${e.message}`);
} finally {
clearInterval(statusPoll);
setImportStatus('');
setLoading(false);
}
};
@@ -747,7 +758,7 @@ export default function MailImportWizard({ visible, onClose, onSuccess, email, a
style={{ marginBottom: 24 }}
/>
<Spin spinning={loading}>
<Spin spinning={loading} tip={importStatus || undefined}>
<div style={{ minHeight: 300 }}>
{currentStep === 0 && renderStep1()}
{currentStep === 1 && renderStep2()}