fix(memoro): unify error responses, add offline page, align i18n defaults

Error responses:
- Webhook routes now use consistent { success, error } format
- Meeting bot 402 error uses standard format instead of mixed error/message/details

PWA:
- Add /offline page with shared OfflinePage component

i18n:
- Change default locale from 'en' to 'de' (matching all other ManaCore apps)
- Use app-specific localStorage key 'memoro_locale' (was generic 'locale')

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-01 16:39:11 +02:00
parent 85257212af
commit 11a2db8fcd
5 changed files with 19 additions and 13 deletions

View file

@ -48,14 +48,14 @@ meetingWebhookRoutes.post('/bot-events', async (c) => {
const signature = c.req.header('x-webhook-signature') ?? '';
if (WEBHOOK_SECRET && !verifySignature(rawBody, signature)) {
return c.json({ error: 'Invalid webhook signature' }, 401);
return c.json({ success: false, error: 'Invalid webhook signature' }, 401);
}
let payload: WebhookEvent;
try {
payload = JSON.parse(rawBody) as WebhookEvent;
} catch {
return c.json({ error: 'Invalid JSON payload' }, 400);
return c.json({ success: false, error: 'Invalid JSON payload' }, 400);
}
const key = idempotencyKey(payload);
@ -116,6 +116,6 @@ meetingWebhookRoutes.post('/bot-events', async (c) => {
// Remove from processed set so it can be retried
processedEvents.delete(key);
console.error('[meetings-webhook] Error processing event:', err);
return c.json({ error: 'Failed to process webhook event' }, 500);
return c.json({ success: false, error: 'Failed to process webhook event' }, 500);
}
});

View file

@ -127,7 +127,7 @@ describe('POST /api/v1/meetings/bots', () => {
expect(res.status).toBe(402);
const data = await res.json();
expect(data.error).toBe('InsufficientCredits');
expect(data.error).toContain('Insufficient credits');
});
});

View file

@ -35,12 +35,9 @@ meetingRoutes.post('/bots', async (c) => {
return c.json(
{
success: false,
error: 'InsufficientCredits',
message: `Not enough credits to start recording. Need at least ${MINIMUM_RECORDING_CREDITS} credits.`,
details: {
requiredCredits: MINIMUM_RECORDING_CREDITS,
availableCredits: creditCheck.availableCredits,
},
error: `Insufficient credits: need at least ${MINIMUM_RECORDING_CREDITS}`,
requiredCredits: MINIMUM_RECORDING_CREDITS,
availableCredits: creditCheck.availableCredits,
},
402
);

View file

@ -13,13 +13,13 @@ export const supportedLocales = ['de', 'en', 'fr', 'it', 'es'] as const;
export type SupportedLocale = (typeof supportedLocales)[number];
// Default locale
const defaultLocale = 'en';
const defaultLocale = 'de';
// Get initial locale from browser or localStorage
function getInitialLocale(): SupportedLocale {
if (browser) {
// Check localStorage first
const stored = localStorage.getItem('locale');
const stored = localStorage.getItem('memoro_locale');
if (stored && supportedLocales.includes(stored as SupportedLocale)) {
return stored as SupportedLocale;
}
@ -46,7 +46,7 @@ export function initI18n() {
export function setLocale(newLocale: SupportedLocale) {
locale.set(newLocale);
if (browser) {
localStorage.setItem('locale', newLocale);
localStorage.setItem('memoro_locale', newLocale);
}
}

View file

@ -0,0 +1,9 @@
<script lang="ts">
import { OfflinePage } from '@manacore/shared-ui';
</script>
<OfflinePage
appName="Memoro"
offlineMessage="Deine Memos werden lokal gespeichert und sind offline verfügbar."
accentColor="#f8d62b"
/>