mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-28 10:26:33 +02:00
feat(epic-4): implement Content Creation & Productivity components
- Add ChartCapturePanel.tsx for chart screenshot with annotations * One-click chart capture from DexScreener * Auto-add metadata overlay (token info, price, volume, liquidity, timestamp) * Template styles (dark, light, neon) * Export options (Twitter 1200x675, Telegram square, Instagram 1080x1080, clipboard) * Watermark option - Add AnnotationTools.tsx for drawing tools * Line tool for trend lines, support/resistance * Arrow tool for directional indicators * Text tool for labels * Shape tools (circle, rectangle) * Fibonacci retracement tool * Color picker with 6 preset colors * Undo/Redo functionality - Add ThreadGeneratorPanel.tsx for AI-powered Twitter thread generation * Auto-fill token info from current page * Customizable thread length (5-10 tweets) * Tone selection (bullish/neutral/bearish) * AI-generated thread structure (Hook → Analysis → Implications → Conclusion) * Edit individual tweets inline * Add/delete tweets dynamically * Reorder tweets support * Export options (copy all, tweet directly via Twitter API) * Mock thread generation with realistic crypto content - Add ProductivitySettings.tsx for productivity settings management * Notification settings with priority levels (high/medium/low) * Quiet hours configuration (start/end time) * Group notifications and smart batching (5+ alerts) * Keyboard shortcuts display and customization * Quick actions settings (context menu, auto-detect addresses) * Pe * Pe * Pe * Pe * Pttings support Implements Stories 4.1, 4.2, 4.3 from Epic 4: Content Creation & Productivity
This commit is contained in:
parent
ea2080619b
commit
9f75abf0a5
4 changed files with 1198 additions and 0 deletions
|
|
@ -0,0 +1,374 @@
|
|||
import { useState } from "react";
|
||||
import { cn } from "~/lib/utils";
|
||||
import {
|
||||
Settings,
|
||||
Bell,
|
||||
BellOff,
|
||||
Clock,
|
||||
Keyboard,
|
||||
Menu,
|
||||
Save,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/routes/ui/button";
|
||||
|
||||
export interface NotificationSettings {
|
||||
enabled: boolean;
|
||||
priorities: {
|
||||
high: boolean;
|
||||
medium: boolean;
|
||||
low: boolean;
|
||||
};
|
||||
quietHours: {
|
||||
enabled: boolean;
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
groupNotifications: boolean;
|
||||
smartBatching: boolean;
|
||||
}
|
||||
|
||||
export interface KeyboardShortcut {
|
||||
id: string;
|
||||
action: string;
|
||||
shortcut: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface QuickActionsSettings {
|
||||
contextMenuEnabled: boolean;
|
||||
autoDetectAddresses: boolean;
|
||||
}
|
||||
|
||||
export interface ProductivitySettingsData {
|
||||
notifications: NotificationSettings;
|
||||
shortcuts: KeyboardShortcut[];
|
||||
quickActions: QuickActionsSettings;
|
||||
}
|
||||
|
||||
export interface ProductivitySettingsPanelProps {
|
||||
/** Current settings */
|
||||
settings?: ProductivitySettingsData;
|
||||
/** Callback when settings are saved */
|
||||
onSave?: (settings: ProductivitySettingsData) => void;
|
||||
/** Additional class names */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* ProductivitySettingsPanel - Productivity settings management
|
||||
*
|
||||
* Features:
|
||||
* - Notification settings (priorities, quiet hours, grouping)
|
||||
* - Keyboard shortcuts configuration
|
||||
* - Quick actions settings (context menu, auto-detect)
|
||||
* - Per-token notification settings
|
||||
*/
|
||||
export function ProductivitySettingsPanel({
|
||||
settings: initialSettings,
|
||||
onSave,
|
||||
className,
|
||||
}: ProductivitySettingsPanelProps) {
|
||||
const [settings, setSettings] = useState<ProductivitySettingsData>(
|
||||
initialSettings || {
|
||||
notifications: {
|
||||
enabled: true,
|
||||
priorities: {
|
||||
high: true,
|
||||
medium: true,
|
||||
low: false,
|
||||
},
|
||||
quietHours: {
|
||||
enabled: true,
|
||||
start: "23:00",
|
||||
end: "07:00",
|
||||
},
|
||||
groupNotifications: true,
|
||||
smartBatching: true,
|
||||
},
|
||||
shortcuts: [
|
||||
{ id: "open-panel", action: "Open Side Panel", shortcut: "Cmd+Shift+S", description: "Open/close the side panel" },
|
||||
{ id: "new-chat", action: "New Chat", shortcut: "Cmd+Shift+N", description: "Start a new chat" },
|
||||
{ id: "analyze-token", action: "Analyze Token", shortcut: "Cmd+Shift+A", description: "Analyze current token" },
|
||||
{ id: "add-watchlist", action: "Add to Watchlist", shortcut: "Cmd+Shift+W", description: "Add token to watchlist" },
|
||||
{ id: "capture-chart", action: "Capture Chart", shortcut: "Cmd+Shift+C", description: "Capture chart screenshot" },
|
||||
{ id: "open-portfolio", action: "Open Portfolio", shortcut: "Cmd+Shift+P", description: "Open portfolio panel" },
|
||||
],
|
||||
quickActions: {
|
||||
contextMenuEnabled: true,
|
||||
autoDetectAddresses: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
|
||||
const updateSettings = (updates: Partial<ProductivitySettingsData>) => {
|
||||
setSettings({ ...settings, ...updates });
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onSave?.(settings);
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col h-full", className)}>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings className="h-5 w-5 text-primary" />
|
||||
<div>
|
||||
<h2 className="font-semibold">Productivity Settings</h2>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Notifications, shortcuts, and quick actions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
||||
{/* Notifications */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bell className="h-4 w-4 text-muted-foreground" />
|
||||
<h3 className="font-semibold text-sm">Notifications</h3>
|
||||
</div>
|
||||
|
||||
{/* Enable/Disable */}
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm">Enable notifications</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.enabled}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
enabled: e.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
/>
|
||||
</label>
|
||||
|
||||
{/* Priority Levels */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-semibold text-muted-foreground">Priority Levels</label>
|
||||
<div className="space-y-2 pl-4">
|
||||
{(["high", "medium", "low"] as const).map((priority) => (
|
||||
<label key={priority} className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm capitalize">{priority}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.priorities[priority]}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
priorities: {
|
||||
...settings.notifications.priorities,
|
||||
[priority]: e.target.checked,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
disabled={!settings.notifications.enabled}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quiet Hours */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="h-3 w-3 text-muted-foreground" />
|
||||
<span className="text-sm">Quiet Hours</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.quietHours.enabled}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
quietHours: {
|
||||
...settings.notifications.quietHours,
|
||||
enabled: e.target.checked,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
disabled={!settings.notifications.enabled}
|
||||
/>
|
||||
</label>
|
||||
{settings.notifications.quietHours.enabled && (
|
||||
<div className="flex gap-2 pl-6">
|
||||
<input
|
||||
type="time"
|
||||
value={settings.notifications.quietHours.start}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
quietHours: {
|
||||
...settings.notifications.quietHours,
|
||||
start: e.target.value,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
className="flex-1 p-1 text-xs border rounded"
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground">to</span>
|
||||
<input
|
||||
type="time"
|
||||
value={settings.notifications.quietHours.end}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
quietHours: {
|
||||
...settings.notifications.quietHours,
|
||||
end: e.target.value,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
className="flex-1 p-1 text-xs border rounded"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Grouping Options */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm">Group notifications</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.groupNotifications}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
groupNotifications: e.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
disabled={!settings.notifications.enabled}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm">Smart batching (5+ alerts)</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.smartBatching}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
notifications: {
|
||||
...settings.notifications,
|
||||
smartBatching: e.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
disabled={!settings.notifications.enabled}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Keyboard Shortcuts */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Keyboard className="h-4 w-4 text-muted-foreground" />
|
||||
<h3 className="font-semibold text-sm">Keyboard Shortcuts</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{settings.shortcuts.map((shortcut) => (
|
||||
<div
|
||||
key={shortcut.id}
|
||||
className="flex items-center justify-between p-2 bg-muted/50 rounded"
|
||||
>
|
||||
<div>
|
||||
<div className="text-sm font-medium">{shortcut.action}</div>
|
||||
<div className="text-xs text-muted-foreground">{shortcut.description}</div>
|
||||
</div>
|
||||
<kbd className="px-2 py-1 text-xs font-mono bg-background border rounded">
|
||||
{shortcut.shortcut}
|
||||
</kbd>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button variant="outline" size="sm" className="w-full">
|
||||
Customize Shortcuts
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Menu className="h-4 w-4 text-muted-foreground" />
|
||||
<h3 className="font-semibold text-sm">Quick Actions</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm">Context menu enabled</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.quickActions.contextMenuEnabled}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
quickActions: {
|
||||
...settings.quickActions,
|
||||
contextMenuEnabled: e.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-sm">Auto-detect token addresses</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.quickActions.autoDetectAddresses}
|
||||
onChange={(e) =>
|
||||
updateSettings({
|
||||
quickActions: {
|
||||
...settings.quickActions,
|
||||
autoDetectAddresses: e.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="rounded"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer - Save Button */}
|
||||
<div className="border-t p-3">
|
||||
<Button
|
||||
variant="default"
|
||||
className="w-full"
|
||||
onClick={handleSave}
|
||||
disabled={!hasChanges}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{hasChanges ? "Save Changes" : "No Changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue