Fixed notification composition

This commit is contained in:
2026-10-22 23:36:03 +02:00
parent 46c6ddcc80
commit 5050c901bc
9 changed files with 2949 additions and 358 deletions
@@ -17,7 +17,6 @@ import {
RotateCcw,
X,
Smartphone,
Filter,
} from 'lucide-react';
import { useInAppNotifications, DraftMessage } from '../hooks/useInAppNotifications';
import { ComposeMessageModal } from '../components/notifications/ComposeMessageModal';
@@ -28,7 +27,6 @@ import { useAuth } from '../auth/AuthProvider';
import { stripMarkdown } from '../lib/markdownUtils';
type MailboxFolder = 'inbox' | 'starred' | 'sent' | 'drafts' | 'trash';
type PlatformFilter = 'all' | 'inapp' | 'email';
export const InboxPage: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams();
@@ -60,8 +58,6 @@ export const InboxPage: React.FC = () => {
// Active folder
const [activeFolder, setActiveFolder] = useState<MailboxFolder>('inbox');
// Platform filter
const [platformFilter, setPlatformFilter] = useState<PlatformFilter>('all');
// Search query
const [searchTerm, setSearchTerm] = useState('');
// Filter for unread only in Inbox
@@ -82,11 +78,11 @@ export const InboxPage: React.FC = () => {
const [inlineReplyText, setInlineReplyText] = useState('');
const [inlineReplySuccess, setInlineReplySuccess] = useState(false);
// Active folder message items
// Active folder message items (always In-App notifications)
const folderMessages = useMemo(() => {
switch (activeFolder) {
case 'inbox':
return allMailboxNotifications;
return inboxNotifications;
case 'starred':
return starredNotifications;
case 'sent':
@@ -96,20 +92,13 @@ export const InboxPage: React.FC = () => {
case 'drafts':
return [];
default:
return allMailboxNotifications;
return inboxNotifications;
}
}, [activeFolder, allMailboxNotifications, starredNotifications, sentNotifications, trashedNotifications]);
}, [activeFolder, inboxNotifications, starredNotifications, sentNotifications, trashedNotifications]);
// Filtered by search, platform, and unread toggle
// Filtered by search and unread toggle
const filteredMessages = useMemo(() => {
return folderMessages.filter(item => {
// Platform filter
if (platformFilter === 'inapp' && Number(item.platform) !== NotificationPlatform.InApp) {
return false;
}
if (platformFilter === 'email' && Number(item.platform) !== NotificationPlatform.Email) {
return false;
}
// Unread only filter
if (activeFolder === 'inbox' && showUnreadOnly) {
if (Number(item.status) === NotificationStatus.Read) return false;
@@ -122,7 +111,7 @@ export const InboxPage: React.FC = () => {
const dest = (item.destination || '').toLowerCase();
return subject.includes(term) || message.includes(term) || dest.includes(term);
});
}, [folderMessages, platformFilter, activeFolder, showUnreadOnly, searchTerm]);
}, [folderMessages, activeFolder, showUnreadOnly, searchTerm]);
// Filtered drafts for Drafts folder
const filteredDrafts = useMemo(() => {
@@ -266,11 +255,11 @@ export const InboxPage: React.FC = () => {
<div className="flex items-center gap-2">
<h1 className="text-sm font-bold text-white tracking-tight">Mailbox & Communications</h1>
<span className="text-[10px] font-semibold text-blue-300 bg-blue-500/10 border border-blue-500/30 px-2 py-0.5 rounded-full flex items-center gap-1">
Markdown & Branded HTML Engine
In-App Messaging
</span>
</div>
<p className="text-[11px] text-slate-400">
In-App markdown messaging and executive branded email notifications
In-App notifications and internal platform communications
</p>
</div>
</div>
@@ -406,10 +395,14 @@ export const InboxPage: React.FC = () => {
<FileText className="w-4 h-4 text-purple-400" />
<span>Drafts</span>
</div>
{drafts.length > 0 && (
{drafts.length > 0 ? (
<span className="text-[10px] font-bold text-purple-300 bg-purple-500/20 border border-purple-500/30 px-2 py-0.5 rounded-full">
{drafts.length}
</span>
) : (
<span className="text-[11px] text-slate-400 font-mono">
0
</span>
)}
</button>
@@ -436,57 +429,6 @@ export const InboxPage: React.FC = () => {
</button>
</nav>
{/* Platform Filter Buttons */}
<div className="pt-3 border-t border-slate-800/80 space-y-2">
<span className="text-[10px] font-semibold uppercase tracking-wider text-slate-400 px-2 block">
Channel Filter
</span>
<div className="flex flex-col gap-1">
<button
type="button"
onClick={() => setPlatformFilter('all')}
className={`w-full flex items-center justify-between px-2.5 py-1.5 rounded-lg text-xs font-medium transition cursor-pointer ${
platformFilter === 'all'
? 'bg-slate-800 text-white font-semibold'
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-900'
}`}
>
<div className="flex items-center gap-2">
<Filter className="w-3.5 h-3.5 text-blue-400" />
<span>All Channels</span>
</div>
</button>
<button
type="button"
onClick={() => setPlatformFilter('inapp')}
className={`w-full flex items-center justify-between px-2.5 py-1.5 rounded-lg text-xs font-medium transition cursor-pointer ${
platformFilter === 'inapp'
? 'bg-blue-600/25 text-blue-200 font-semibold border border-blue-500/30'
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-900'
}`}
>
<div className="flex items-center gap-2">
<Smartphone className="w-3.5 h-3.5 text-blue-400" />
<span>In-App Markdown</span>
</div>
</button>
<button
type="button"
onClick={() => setPlatformFilter('email')}
className={`w-full flex items-center justify-between px-2.5 py-1.5 rounded-lg text-xs font-medium transition cursor-pointer ${
platformFilter === 'email'
? 'bg-emerald-600/25 text-emerald-200 font-semibold border border-emerald-500/30'
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-900'
}`}
>
<div className="flex items-center gap-2">
<Mail className="w-3.5 h-3.5 text-emerald-400" />
<span>Email HTML</span>
</div>
</button>
</div>
</div>
{/* Quick Filter: Unread Toggle */}
{activeFolder === 'inbox' && (
<div className="pt-2 border-t border-slate-800/80">
@@ -507,10 +449,10 @@ export const InboxPage: React.FC = () => {
<div className="p-3 rounded-2xl bg-slate-900/40 border border-slate-800/60 space-y-1 text-[11px] text-slate-400">
<div className="flex items-center gap-1.5 text-slate-300 font-semibold">
<span className="w-2 h-2 rounded-full bg-emerald-400" />
<span>Format Standards</span>
<span>In-App Notifications</span>
</div>
<p className="text-[10px] text-slate-400 leading-snug">
InApp messages are stored as Markdown. Email notifications are transformed to branded HTML.
Messages are delivered directly to your In-App Mailbox and Notification Bell.
</p>
</div>
</aside>
@@ -571,7 +513,7 @@ export const InboxPage: React.FC = () => {
>
<div className="flex items-center justify-between gap-1">
<span className="text-[10px] font-bold text-purple-300 bg-purple-500/15 border border-purple-500/30 px-1.5 py-0.2 rounded">
Draft (Markdown)
Draft
</span>
<span className="text-[10px] text-slate-400 font-mono">
{formatDate(d.updatedAt)}
@@ -620,7 +562,6 @@ export const InboxPage: React.FC = () => {
const isSelected = selectedMessage?.id === item.id;
const isUnread = Number(item.status) !== NotificationStatus.Read;
const isStarred = starredIds.includes(String(item.id));
const isEmail = Number(item.platform) === NotificationPlatform.Email;
return (
<div
@@ -649,15 +590,9 @@ export const InboxPage: React.FC = () => {
</div>
<div className="flex items-center gap-1.5 shrink-0">
{isEmail ? (
<span className="text-[9px] font-bold text-emerald-300 bg-emerald-500/15 border border-emerald-500/30 px-1.5 py-0.2 rounded font-mono">
Email
</span>
) : (
<span className="text-[9px] font-bold text-blue-300 bg-blue-500/15 border border-blue-500/30 px-1.5 py-0.2 rounded font-mono">
InApp
</span>
)}
<span className="text-[9px] font-bold text-blue-300 bg-blue-500/15 border border-blue-500/30 px-1.5 py-0.2 rounded font-mono">
In-App
</span>
<button
type="button"
@@ -707,7 +642,7 @@ export const InboxPage: React.FC = () => {
<FileText className="w-12 h-12 text-slate-600 mb-3" />
<h3 className="text-sm font-bold text-white">Drafts Folder</h3>
<p className="text-xs text-slate-400 max-w-md mt-1 mb-4">
Click any draft on the left pane to resume composing in Markdown and dispatch your notification.
Click any draft on the left pane to resume composing and dispatch your notification.
</p>
<button
type="button"
@@ -725,7 +660,7 @@ export const InboxPage: React.FC = () => {
</div>
<h3 className="text-sm font-bold text-white">No Message Selected</h3>
<p className="text-xs text-slate-400 max-w-sm mt-1 mb-4">
Select an in-app conversation or email notice from the list to view its rendered markdown and attachments.
Select an in-app conversation or email notice from the list to view its contents and attachments.
</p>
<button
type="button"
@@ -815,17 +750,10 @@ export const InboxPage: React.FC = () => {
{selectedMessage.subject || 'In-App Notification'}
</h2>
<div className="flex flex-wrap items-center gap-2 mt-2">
{Number(selectedMessage.platform) === NotificationPlatform.Email ? (
<span className="text-[10px] font-semibold text-emerald-300 bg-emerald-500/15 border border-emerald-500/30 px-2 py-0.5 rounded-md flex items-center gap-1">
<Mail className="w-3 h-3 text-emerald-400" />
Channel: Email (Branded HTML)
</span>
) : (
<span className="text-[10px] font-semibold text-blue-300 bg-blue-500/15 border border-blue-500/30 px-2 py-0.5 rounded-md flex items-center gap-1">
<Smartphone className="w-3 h-3 text-blue-400" />
Channel: InApp (Markdown)
</span>
)}
<span className="text-[10px] font-semibold text-blue-300 bg-blue-500/15 border border-blue-500/30 px-2 py-0.5 rounded-md flex items-center gap-1">
<Smartphone className="w-3 h-3 text-blue-400" />
Channel: In-App
</span>
<span className="text-[10px] font-mono text-slate-400 bg-slate-900 px-2 py-0.5 rounded-md border border-slate-800">
ID: #{selectedMessage.id}
@@ -852,7 +780,7 @@ export const InboxPage: React.FC = () => {
: selectedMessage.destination || 'PFM Automated Dispatcher'}
</p>
<span className="text-[10px] text-slate-400 font-mono">
({Number(selectedMessage.platform) === NotificationPlatform.Email ? 'SMTP Protocol' : 'InApp Protocol'})
(In-App Notification)
</span>
</div>
<p className="text-[11px] text-slate-400 mt-0.5">
@@ -884,10 +812,10 @@ export const InboxPage: React.FC = () => {
<div className="flex items-center justify-between">
<h4 className="text-xs font-bold text-white flex items-center gap-1.5">
<Reply className="w-3.5 h-3.5 text-blue-400" />
<span>Quick Markdown Reply</span>
<span>Quick Reply</span>
</h4>
<span className="text-[10px] text-slate-400">
Supports Markdown formatting (e.g. **bold**, - lists, `code`)
Send a quick response below or open the rich text composer
</span>
</div>
@@ -902,8 +830,8 @@ export const InboxPage: React.FC = () => {
rows={4}
value={inlineReplyText}
onChange={e => setInlineReplyText(e.target.value)}
placeholder="Type your markdown reply... (e.g. **Approved** milestone deliverable proof)"
className="w-full p-3.5 rounded-2xl bg-slate-900/90 border border-slate-700/80 hover:border-slate-600 focus:border-blue-500 text-slate-100 placeholder-slate-400 text-xs focus:outline-none transition shadow-inner resize-y font-mono leading-relaxed"
placeholder="Type your reply..."
className="w-full p-3.5 rounded-2xl bg-slate-900/90 border border-slate-700/80 hover:border-slate-600 focus:border-blue-500 text-slate-100 placeholder-slate-400 text-xs focus:outline-none transition shadow-inner resize-y leading-relaxed"
/>
<div className="flex items-center justify-between">