Fixed notification composition
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user