fix: push notification toggle, textarea resize, mobile viewport, card alignment

1. Fix push toggle error: VAPID key field name mismatch (public_key → vapid_public_key)
2. Fix textarea auto-resize: proper height reset, overflow handling, mobile Enter
   inserts newline instead of sending (send via button on mobile)
3. Fix mobile viewport overflow: add overflow-x hidden to html/body, overflow-x
   hidden on content container
4. Fix dashboard cards: always 4 columns with responsive text sizing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Algis Dumbris
2026-03-17 14:23:19 +02:00
co-authored by Claude Opus 4.6
parent 15e7877ea0
commit 71288f64d8
6 changed files with 36 additions and 23 deletions
+4
View File
@@ -20,6 +20,10 @@
}
@layer base {
html, body {
overflow-x: hidden;
max-width: 100vw;
}
body {
font-family: 'DM Sans', sans-serif;
background-color: var(--bg-primary);
+1 -1
View File
@@ -223,7 +223,7 @@ export const push = {
request<{ id: number; message: string }>('POST', '/api/push/subscribe', subscription),
unsubscribe: (endpoint: string) =>
request<{ message: string }>('DELETE', '/api/push/subscribe', { endpoint }),
vapidKey: () => request<{ public_key: string }>('GET', '/api/push/vapid-key')
vapidKey: () => request<{ vapid_public_key: string }>('GET', '/api/push/vapid-key')
};
// User profile
+15 -6
View File
@@ -55,20 +55,29 @@
function autoResize() {
if (!textareaEl) return;
textareaEl.style.height = 'auto';
// Reset to measure true scrollHeight
textareaEl.style.height = '80px';
const maxHeight = 240; // ~12 lines
const scrollHeight = textareaEl.scrollHeight;
if (scrollHeight > maxHeight) {
textareaEl.style.height = maxHeight + 'px';
textareaEl.style.overflowY = 'auto';
textareaEl.classList.remove('overflow-hidden');
} else {
textareaEl.style.height = scrollHeight + 'px';
textareaEl.style.height = Math.max(80, scrollHeight) + 'px';
textareaEl.style.overflowY = 'hidden';
textareaEl.classList.add('overflow-hidden');
}
}
function isMobile(): boolean {
return typeof window !== 'undefined' && window.innerWidth < 768;
}
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Enter' && !e.shiftKey) {
// On mobile, Enter inserts newline (send via button only)
// On desktop, Enter sends, Shift+Enter inserts newline
if (e.key === 'Enter' && !e.shiftKey && !isMobile()) {
e.preventDefault();
handleSubmit();
}
@@ -161,9 +170,9 @@
<!-- Message body -->
<textarea
bind:this={textareaEl}
placeholder="Write a message..."
class="w-full px-4 py-3 bg-transparent text-sm text-text-primary placeholder-text-secondary resize-none outline-none"
style="min-height: 72px; max-height: 240px;"
placeholder="Write a message... {isMobile() ? '' : '(Shift+Enter for new line)'}"
class="w-full px-4 py-3 bg-transparent text-sm text-text-primary placeholder-text-secondary resize-none outline-none overflow-hidden"
style="min-height: 80px; max-height: 240px;"
bind:value={body}
rows="3"
onkeydown={handleKeydown}
+1 -1
View File
@@ -101,7 +101,7 @@
{:else if $user}
<div class="h-screen flex overflow-hidden bg-bg-primary">
<Sidebar open={sidebarOpen} onclose={() => (sidebarOpen = false)} />
<div class="md:ml-[260px] flex-1 flex flex-col min-w-0">
<div class="md:ml-[260px] flex-1 flex flex-col min-w-0 overflow-x-hidden">
<Header onMenuToggle={() => (sidebarOpen = !sidebarOpen)} />
<main class="flex-1 overflow-y-auto flex flex-col">
{@render children()}
+14 -14
View File
@@ -91,7 +91,7 @@
});
</script>
<div class="p-5 max-w-6xl">
<div class="p-3 md:p-5 max-w-6xl w-full">
<!-- Top bar -->
<div class="flex items-center justify-between mb-5">
<div>
@@ -120,22 +120,22 @@
{/if}
<!-- Summary Cards -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-5">
<div class="card p-4">
<p class="text-xs text-text-secondary mb-1">Total Messages</p>
<p class="text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_messages}</p>
<div class="grid grid-cols-4 gap-2 md:gap-3 mb-5">
<div class="card p-3 md:p-4">
<p class="text-[10px] md:text-xs text-text-secondary mb-1">Messages</p>
<p class="text-lg md:text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_messages}</p>
</div>
<div class="card p-4">
<p class="text-xs text-text-secondary mb-1">Agents</p>
<p class="text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_agents}</p>
<div class="card p-3 md:p-4">
<p class="text-[10px] md:text-xs text-text-secondary mb-1">Agents</p>
<p class="text-lg md:text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_agents}</p>
</div>
<div class="card p-4">
<p class="text-xs text-text-secondary mb-1">Channels</p>
<p class="text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_channels}</p>
<div class="card p-3 md:p-4">
<p class="text-[10px] md:text-xs text-text-secondary mb-1">Channels</p>
<p class="text-lg md:text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : summary.total_channels}</p>
</div>
<div class="card p-4">
<p class="text-xs text-text-secondary mb-1">Conversations</p>
<p class="text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : recentConversations.length}</p>
<div class="card p-3 md:p-4">
<p class="text-[10px] md:text-xs text-text-secondary mb-1">Convos</p>
<p class="text-lg md:text-2xl font-bold text-text-primary font-display">{loadingData ? '-' : recentConversations.length}</p>
</div>
</div>
+1 -1
View File
@@ -108,7 +108,7 @@
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(keyRes.public_key)
applicationServerKey: urlBase64ToUint8Array(keyRes.vapid_public_key)
});
const subJson = sub.toJSON();
await pushApi.subscribe({