feat(mail): add email tracking UI to Suite mail app
- Add Tracking folder to sidebar with 📊 icon
- Display tracking stats panel (sent, opened, rate, avg time)
- Show read status indicators (✓✓ green=read, gray=sent)
- Add read count badges for multiple opens
- Implement tracking item details view
- Add CSS for read indicators, stats panel, tooltips
50
PROMPT.md
|
|
@ -5,6 +5,56 @@
|
|||
|
||||
---
|
||||
|
||||
## Official Icons - MANDATORY
|
||||
|
||||
**NEVER generate icons with LLM. ALWAYS use official SVG icons from:**
|
||||
|
||||
```
|
||||
ui/suite/assets/icons/
|
||||
├── gb-logo.svg # Main GB logo
|
||||
├── gb-bot.svg # Bot/assistant
|
||||
├── gb-analytics.svg # Analytics app
|
||||
├── gb-calendar.svg # Calendar app
|
||||
├── gb-chat.svg # Chat app
|
||||
├── gb-compliance.svg # Compliance/security
|
||||
├── gb-designer.svg # Workflow designer
|
||||
├── gb-drive.svg # File storage
|
||||
├── gb-mail.svg # Email
|
||||
├── gb-meet.svg # Video meetings
|
||||
├── gb-paper.svg # Documents
|
||||
├── gb-research.svg # Research/search
|
||||
├── gb-sources.svg # Knowledge sources
|
||||
└── gb-tasks.svg # Task management
|
||||
```
|
||||
|
||||
### Usage in HTML
|
||||
|
||||
```html
|
||||
<!-- Inline SVG (preferred for styling) -->
|
||||
<img src="/assets/icons/gb-chat.svg" alt="Chat" class="icon">
|
||||
|
||||
<!-- With CSS currentColor -->
|
||||
<svg class="icon" style="color: var(--primary);">
|
||||
<use href="/assets/icons/gb-chat.svg#icon"></use>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### Icon Style Guidelines
|
||||
|
||||
- All icons use `stroke="currentColor"` for theming
|
||||
- ViewBox: `0 0 24 24`
|
||||
- Stroke width: `1.5`
|
||||
- Rounded caps and joins
|
||||
- Consistent with GB brand identity
|
||||
|
||||
**DO NOT:**
|
||||
- Generate new icons with AI/LLM
|
||||
- Use emoji or unicode symbols as icons
|
||||
- Use external icon libraries (FontAwesome, etc.)
|
||||
- Create inline SVG content in templates
|
||||
|
||||
---
|
||||
|
||||
## Project Overview
|
||||
|
||||
BotUI is a **dual-mode UI application** built in Rust that runs as either a desktop app (Tauri) or web server (Axum). All business logic is in **botserver** - BotUI is purely presentation + HTTP bridge.
|
||||
|
|
|
|||
12
ui/suite/assets/icons/gb-analytics.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Chart bars - analytics visualization -->
|
||||
<rect x="3" y="14" width="4" height="8" rx="1"/>
|
||||
<rect x="10" y="8" width="4" height="14" rx="1"/>
|
||||
<rect x="17" y="4" width="4" height="18" rx="1"/>
|
||||
<!-- Trend line -->
|
||||
<path d="M3 10 L10 6 L17 2" stroke-dasharray="2 2"/>
|
||||
<!-- Data points -->
|
||||
<circle cx="3" cy="10" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<circle cx="10" cy="6" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<circle cx="17" cy="2" r="1.5" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 678 B |
16
ui/suite/assets/icons/gb-bot.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Bot head - rounded square inspired by GB logo -->
|
||||
<rect x="4" y="4" width="16" height="14" rx="3" ry="3"/>
|
||||
<!-- Eyes - circular like GB branding -->
|
||||
<circle cx="9" cy="10" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<circle cx="15" cy="10" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<!-- Smile -->
|
||||
<path d="M9 14 Q12 16 15 14" fill="none"/>
|
||||
<!-- Antenna -->
|
||||
<line x1="12" y1="4" x2="12" y2="1"/>
|
||||
<circle cx="12" cy="1" r="1" fill="currentColor" stroke="none"/>
|
||||
<!-- Base/body connection -->
|
||||
<path d="M8 18 L8 21 L16 21 L16 18"/>
|
||||
<line x1="10" y1="21" x2="10" y2="23"/>
|
||||
<line x1="14" y1="21" x2="14" y2="23"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 811 B |
16
ui/suite/assets/icons/gb-calendar.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Calendar body - rounded rectangle inspired by GB logo -->
|
||||
<rect x="3" y="4" width="18" height="18" rx="3" ry="3"/>
|
||||
<!-- Calendar header binding rings -->
|
||||
<line x1="8" y1="2" x2="8" y2="6"/>
|
||||
<line x1="16" y1="2" x2="16" y2="6"/>
|
||||
<!-- Header separator -->
|
||||
<line x1="3" y1="10" x2="21" y2="10"/>
|
||||
<!-- Calendar grid dots - representing dates -->
|
||||
<circle cx="8" cy="14" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="12" cy="14" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="16" cy="14" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="8" cy="18" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="12" cy="18" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="16" cy="18" r="1" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 937 B |
27
ui/suite/assets/icons/gb-chart.svg
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired chart icon with characteristic curves -->
|
||||
<defs>
|
||||
<linearGradient id="chartGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#1d4ed8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Base platform - GB style rounded -->
|
||||
<path d="M3 20h18" stroke="url(#chartGradient)" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Bar 1 - shortest -->
|
||||
<rect x="4" y="14" width="3" height="6" rx="1" fill="url(#chartGradient)" opacity="0.6"/>
|
||||
|
||||
<!-- Bar 2 - medium -->
|
||||
<rect x="9" y="10" width="3" height="10" rx="1" fill="url(#chartGradient)" opacity="0.8"/>
|
||||
|
||||
<!-- Bar 3 - tallest with GB curve accent -->
|
||||
<rect x="14" y="5" width="3" height="15" rx="1" fill="url(#chartGradient)"/>
|
||||
|
||||
<!-- Trend line - GB characteristic curve -->
|
||||
<path d="M5.5 13 C8 11, 10 9, 15.5 4" stroke="#10b981" stroke-width="2" stroke-linecap="round" fill="none"/>
|
||||
|
||||
<!-- Accent dot at peak -->
|
||||
<circle cx="15.5" cy="4" r="2" fill="#10b981"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
ui/suite/assets/icons/gb-chat.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Chat bubble - main -->
|
||||
<path d="M21 12c0 4.418-4.029 8-9 8-1.107 0-2.17-.169-3.156-.476L4 21l1.338-3.346C4.493 16.267 4 14.686 4 13c0-4.418 4.029-8 9-8s8 3.582 8 7z"/>
|
||||
<!-- Three dots indicating typing/conversation -->
|
||||
<circle cx="9" cy="12" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" stroke="none"/>
|
||||
<circle cx="15" cy="12" r="1" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 601 B |
21
ui/suite/assets/icons/gb-check.svg
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired checkmark icon -->
|
||||
<defs>
|
||||
<linearGradient id="gbCheckGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10b981"/>
|
||||
<stop offset="100%" style="stop-color:#059669"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Outer circle - GB style -->
|
||||
<circle cx="12" cy="12" r="10" stroke="url(#gbCheckGradient)" stroke-width="1.5" fill="none"/>
|
||||
|
||||
<!-- Inner fill with GB transparency -->
|
||||
<circle cx="12" cy="12" r="8" fill="url(#gbCheckGradient)" opacity="0.15"/>
|
||||
|
||||
<!-- Checkmark path - bold and confident -->
|
||||
<path d="M7 12.5L10.5 16L17 8" stroke="url(#gbCheckGradient)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
|
||||
<!-- Subtle accent dot at tip - GB brand element -->
|
||||
<circle cx="17" cy="8" r="1" fill="#10b981"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 919 B |
8
ui/suite/assets/icons/gb-compliance.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Shield shape - security/compliance -->
|
||||
<path d="M12 2 L20 6 L20 12 C20 16.418 16.418 20 12 22 C7.582 20 4 16.418 4 12 L4 6 Z"/>
|
||||
<!-- Checkmark inside shield -->
|
||||
<polyline points="8 12 11 15 16 9"/>
|
||||
<!-- Document lines - representing compliance docs -->
|
||||
<line x1="9" y1="18" x2="15" y2="18" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 491 B |
29
ui/suite/assets/icons/gb-database.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired database icon with cylindrical storage aesthetic -->
|
||||
<defs>
|
||||
<linearGradient id="dbGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366f1"/>
|
||||
<stop offset="100%" style="stop-color:#4f46e5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Top ellipse - database cap -->
|
||||
<ellipse cx="12" cy="5" rx="8" ry="3" fill="url(#dbGradient)" stroke="currentColor" stroke-width="1"/>
|
||||
|
||||
<!-- Database body sides -->
|
||||
<path d="M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5" stroke="currentColor" stroke-width="1" fill="none"/>
|
||||
|
||||
<!-- Side fill -->
|
||||
<path d="M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5c0 1.66-3.58 3-8 3S4 6.66 4 5z" fill="url(#dbGradient)" opacity="0.85"/>
|
||||
|
||||
<!-- Middle rings - GB horizontal accent lines -->
|
||||
<ellipse cx="12" cy="10" rx="8" ry="2.5" fill="none" stroke="currentColor" stroke-width="0.75" opacity="0.5"/>
|
||||
<ellipse cx="12" cy="15" rx="8" ry="2.5" fill="none" stroke="currentColor" stroke-width="0.75" opacity="0.5"/>
|
||||
|
||||
<!-- Data indicator dots - GB bot eyes reference -->
|
||||
<circle cx="9" cy="12" r="1" fill="#fff" opacity="0.8"/>
|
||||
<circle cx="15" cy="12" r="1" fill="#fff" opacity="0.8"/>
|
||||
|
||||
<!-- Bottom highlight -->
|
||||
<ellipse cx="12" cy="19" rx="6" ry="1.5" fill="#fff" fill-opacity="0.15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
17
ui/suite/assets/icons/gb-designer.svg
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Workflow nodes -->
|
||||
<circle cx="5" cy="5" r="3"/>
|
||||
<circle cx="19" cy="5" r="3"/>
|
||||
<circle cx="5" cy="19" r="3"/>
|
||||
<circle cx="19" cy="19" r="3"/>
|
||||
<!-- Center hub -->
|
||||
<rect x="9" y="9" width="6" height="6" rx="1"/>
|
||||
<!-- Connection lines -->
|
||||
<line x1="8" y1="5" x2="9" y2="10"/>
|
||||
<line x1="16" y1="5" x2="15" y2="10"/>
|
||||
<line x1="8" y1="19" x2="9" y2="14"/>
|
||||
<line x1="16" y1="19" x2="15" y2="14"/>
|
||||
<!-- Flow arrows -->
|
||||
<path d="M12 6 L12 9" stroke-dasharray="1 1"/>
|
||||
<path d="M12 15 L12 18" stroke-dasharray="1 1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
9
ui/suite/assets/icons/gb-drive.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Cloud storage shape -->
|
||||
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/>
|
||||
<!-- Upload arrow -->
|
||||
<polyline points="12 13 12 17"/>
|
||||
<polyline points="10 15 12 13 14 15"/>
|
||||
<!-- Folder tab -->
|
||||
<path d="M5 8h4l2 2h6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
20
ui/suite/assets/icons/gb-folder.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired folder icon -->
|
||||
<defs>
|
||||
<linearGradient id="folderGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#4A90E2"/>
|
||||
<stop offset="100%" style="stop-color:#3672B8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Folder back -->
|
||||
<path d="M2 6C2 4.89543 2.89543 4 4 4H9.17157C9.70201 4 10.2107 4.21071 10.5858 4.58579L12 6H20C21.1046 6 22 6.89543 22 8V18C22 19.1046 21.1046 20 20 20H4C2.89543 20 2 19.1046 2 18V6Z" fill="url(#folderGradient)" opacity="0.85"/>
|
||||
|
||||
<!-- Folder front tab -->
|
||||
<path d="M2 8C2 7.44772 2.44772 7 3 7H21C21.5523 7 22 7.44772 22 8V18C22 19.1046 21.1046 20 20 20H4C2.89543 20 2 19.1046 2 18V8Z" fill="url(#folderGradient)"/>
|
||||
|
||||
<!-- GB bot face on folder - subtle branding -->
|
||||
<circle cx="9" cy="13" r="1" fill="#fff" opacity="0.8"/>
|
||||
<circle cx="15" cy="13" r="1" fill="#fff" opacity="0.8"/>
|
||||
<path d="M10 16 Q12 17.5 14 16" stroke="#fff" stroke-width="1" fill="none" opacity="0.6" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
4
ui/suite/assets/icons/gb-gear.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.4 15C19.2669 15.3016 19.2272 15.6362 19.286 15.9606C19.3448 16.285 19.4995 16.5843 19.73 16.82L19.79 16.88C19.976 17.0657 20.1235 17.2863 20.2241 17.5291C20.3248 17.7719 20.3766 18.0322 20.3766 18.295C20.3766 18.5578 20.3248 18.8181 20.2241 19.0609C20.1235 19.3037 19.976 19.5243 19.79 19.71C19.6043 19.896 19.3837 20.0435 19.1409 20.1441C18.8981 20.2448 18.6378 20.2966 18.375 20.2966C18.1122 20.2966 17.8519 20.2448 17.6091 20.1441C17.3663 20.0435 17.1457 19.896 16.96 19.71L16.9 19.65C16.6643 19.4195 16.365 19.2648 16.0406 19.206C15.7162 19.1472 15.3816 19.1869 15.08 19.32C14.7842 19.4468 14.532 19.6572 14.3543 19.9255C14.1766 20.1938 14.0813 20.5082 14.08 20.83V21C14.08 21.5304 13.8693 22.0391 13.4942 22.4142C13.1191 22.7893 12.6104 23 12.08 23C11.5496 23 11.0409 22.7893 10.6658 22.4142C10.2907 22.0391 10.08 21.5304 10.08 21V20.91C10.0723 20.579 9.96512 20.258 9.77251 19.9887C9.5799 19.7194 9.31074 19.5143 9 19.4C8.69838 19.2669 8.36381 19.2272 8.03941 19.286C7.71502 19.3448 7.41568 19.4995 7.18 19.73L7.12 19.79C6.93425 19.976 6.71368 20.1235 6.47088 20.2241C6.22808 20.3248 5.96783 20.3766 5.705 20.3766C5.44217 20.3766 5.18192 20.3248 4.93912 20.2241C4.69632 20.1235 4.47575 19.976 4.29 19.79C4.10405 19.6043 3.95653 19.3837 3.85588 19.1409C3.75523 18.8981 3.70343 18.6378 3.70343 18.375C3.70343 18.1122 3.75523 17.8519 3.85588 17.6091C3.95653 17.3663 4.10405 17.1457 4.29 16.96L4.35 16.9C4.58054 16.6643 4.73519 16.365 4.794 16.0406C4.85282 15.7162 4.81312 15.3816 4.68 15.08C4.55324 14.7842 4.34276 14.532 4.07447 14.3543C3.80618 14.1766 3.49179 14.0813 3.17 14.08H3C2.46957 14.08 1.96086 13.8693 1.58579 13.4942C1.21071 13.1191 1 12.6104 1 12.08C1 11.5496 1.21071 11.0409 1.58579 10.6658C1.96086 10.2907 2.46957 10.08 3 10.08H3.09C3.42099 10.0723 3.742 9.96512 4.0113 9.77251C4.28059 9.5799 4.48572 9.31074 4.6 9C4.73312 8.69838 4.77282 8.36381 4.714 8.03941C4.65519 7.71502 4.50054 7.41568 4.27 7.18L4.21 7.12C4.02405 6.93425 3.87653 6.71368 3.77588 6.47088C3.67523 6.22808 3.62343 5.96783 3.62343 5.705C3.62343 5.44217 3.67523 5.18192 3.77588 4.93912C3.87653 4.69632 4.02405 4.47575 4.21 4.29C4.39575 4.10405 4.61632 3.95653 4.85912 3.85588C5.10192 3.75523 5.36217 3.70343 5.625 3.70343C5.88783 3.70343 6.14808 3.75523 6.39088 3.85588C6.63368 3.95653 6.85425 4.10405 7.04 4.29L7.1 4.35C7.33568 4.58054 7.63502 4.73519 7.95941 4.794C8.28381 4.85282 8.61838 4.81312 8.92 4.68H9C9.29577 4.55324 9.54802 4.34276 9.72569 4.07447C9.90337 3.80618 9.99872 3.49179 10 3.17V3C10 2.46957 10.2107 1.96086 10.5858 1.58579C10.9609 1.21071 11.4696 1 12 1C12.5304 1 13.0391 1.21071 13.4142 1.58579C13.7893 1.96086 14 2.46957 14 3V3.09C14.0013 3.41179 14.0966 3.72618 14.2743 3.99447C14.452 4.26276 14.7042 4.47324 15 4.6C15.3016 4.73312 15.6362 4.77282 15.9606 4.714C16.285 4.65519 16.5843 4.50054 16.82 4.27L16.88 4.21C17.0657 4.02405 17.2863 3.87653 17.5291 3.77588C17.7719 3.67523 18.0322 3.62343 18.295 3.62343C18.5578 3.62343 18.8181 3.67523 19.0609 3.77588C19.3037 3.87653 19.5243 4.02405 19.71 4.21C19.896 4.39575 20.0435 4.61632 20.1441 4.85912C20.2448 5.10192 20.2966 5.36217 20.2966 5.625C20.2966 5.88783 20.2448 6.14808 20.1441 6.39088C20.0435 6.63368 19.896 6.85425 19.71 7.04L19.65 7.1C19.4195 7.33568 19.2648 7.63502 19.206 7.95941C19.1472 8.28381 19.1869 8.61838 19.32 8.92V9C19.4468 9.29577 19.6572 9.54802 19.9255 9.72569C20.1938 9.90337 20.5082 9.99872 20.83 10H21C21.5304 10 22.0391 10.2107 22.4142 10.5858C22.7893 10.9609 23 11.4696 23 12C23 12.5304 22.7893 13.0391 22.4142 13.4142C22.0391 13.7893 21.5304 14 21 14H20.91C20.5882 14.0013 20.2738 14.0966 20.0055 14.2743C19.7372 14.452 19.5268 14.7042 19.4 15Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
32
ui/suite/assets/icons/gb-globe.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired globe/web icon -->
|
||||
<defs>
|
||||
<linearGradient id="globeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#1d4ed8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Main globe circle -->
|
||||
<circle cx="12" cy="12" r="10" stroke="url(#globeGradient)" stroke-width="1.5" fill="none"/>
|
||||
|
||||
<!-- Horizontal latitude lines - GB curved style -->
|
||||
<ellipse cx="12" cy="12" rx="10" ry="4" stroke="currentColor" stroke-width="1" fill="none" opacity="0.4"/>
|
||||
<ellipse cx="12" cy="12" rx="10" ry="7" stroke="currentColor" stroke-width="1" fill="none" opacity="0.25"/>
|
||||
|
||||
<!-- Vertical meridian line -->
|
||||
<ellipse cx="12" cy="12" rx="4" ry="10" stroke="currentColor" stroke-width="1" fill="none" opacity="0.5"/>
|
||||
|
||||
<!-- Center vertical line -->
|
||||
<line x1="12" y1="2" x2="12" y2="22" stroke="currentColor" stroke-width="1" opacity="0.3"/>
|
||||
|
||||
<!-- Center horizontal line -->
|
||||
<line x1="2" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="1" opacity="0.3"/>
|
||||
|
||||
<!-- GB bot eyes on globe - subtle branding -->
|
||||
<circle cx="9" cy="10" r="1" fill="url(#globeGradient)" opacity="0.7"/>
|
||||
<circle cx="15" cy="10" r="1" fill="url(#globeGradient)" opacity="0.7"/>
|
||||
|
||||
<!-- Subtle highlight arc -->
|
||||
<path d="M6 6 Q8 4 12 4" stroke="white" stroke-width="1.5" fill="none" opacity="0.3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
20
ui/suite/assets/icons/gb-lightbulb.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- General Bots inspired lightbulb icon -->
|
||||
<defs>
|
||||
<linearGradient id="gbLightbulbGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F5A623;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#F8D71C;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- Bulb body with GB circular motif -->
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.87-3.13-7-7-7z" fill="url(#gbLightbulbGradient)"/>
|
||||
<!-- Inner glow representing bot intelligence -->
|
||||
<circle cx="12" cy="9" r="3" fill="#fff" fill-opacity="0.6"/>
|
||||
<!-- Bot eye dot -->
|
||||
<circle cx="12" cy="9" r="1.2" fill="#4A90E2"/>
|
||||
<!-- Base segments -->
|
||||
<rect x="9" y="18" width="6" height="1.5" rx="0.5" fill="#666"/>
|
||||
<rect x="9" y="20" width="6" height="1.5" rx="0.5" fill="#888"/>
|
||||
<!-- Light rays -->
|
||||
<path d="M12 0v1.5M18.5 3.5l-1.06 1.06M21 9h-1.5M18.5 14.5l-1.06-1.06M5.5 3.5l1.06 1.06M3 9h1.5M5.5 14.5l1.06-1.06" stroke="#F5A623" stroke-width="1" stroke-linecap="round" opacity="0.7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
43
ui/suite/assets/icons/gb-lock.svg
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired lock/security icon -->
|
||||
<defs>
|
||||
<linearGradient id="lockGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366f1"/>
|
||||
<stop offset="100%" style="stop-color:#4f46e5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Lock shackle - rounded GB style -->
|
||||
<path d="M7 10V7C7 4.23858 9.23858 2 12 2C14.7614 2 17 4.23858 17 7V10"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
fill="none"/>
|
||||
|
||||
<!-- Lock body - rounded rectangle with GB aesthetic -->
|
||||
<rect x="5" y="10" width="14" height="12" rx="2"
|
||||
fill="url(#lockGradient)"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"/>
|
||||
|
||||
<!-- Keyhole - bot eye inspired circular design -->
|
||||
<circle cx="12" cy="14.5" r="2" fill="#1e1b4b"/>
|
||||
|
||||
<!-- Keyhole slot -->
|
||||
<path d="M12 16V18.5" stroke="#1e1b4b" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Highlight reflection - GB polish -->
|
||||
<path d="M7 12C7 11.4477 7.44772 11 8 11H10C10.5523 11 11 11.4477 11 12"
|
||||
stroke="white"
|
||||
stroke-opacity="0.3"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
fill="none"/>
|
||||
|
||||
<!-- Subtle inner border glow -->
|
||||
<rect x="6" y="11" width="12" height="10" rx="1.5"
|
||||
stroke="white"
|
||||
stroke-opacity="0.1"
|
||||
stroke-width="0.5"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
9
ui/suite/assets/icons/gb-mail.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Envelope body -->
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" ry="2"/>
|
||||
<!-- Envelope flap lines -->
|
||||
<polyline points="2 4 12 13 22 4"/>
|
||||
<!-- Bottom fold accent -->
|
||||
<line x1="2" y1="20" x2="8" y2="14"/>
|
||||
<line x1="22" y1="20" x2="16" y2="14"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 433 B |
11
ui/suite/assets/icons/gb-meet.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Video camera body -->
|
||||
<rect x="2" y="5" width="14" height="14" rx="2" ry="2"/>
|
||||
<!-- Camera lens/record indicator -->
|
||||
<circle cx="9" cy="12" r="3" fill="currentColor" stroke="none" opacity="0.3"/>
|
||||
<circle cx="9" cy="12" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<!-- Viewfinder triangle -->
|
||||
<path d="M16 9.5 L22 6 L22 18 L16 14.5"/>
|
||||
<!-- Signal waves -->
|
||||
<path d="M5 2 Q9 0 13 2" stroke-dasharray="1 1" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 611 B |
16
ui/suite/assets/icons/gb-note.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired document/note icon -->
|
||||
<!-- Page with folded corner -->
|
||||
<path d="M14 2H6C4.9 2 4 2.9 4 4v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6z" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
||||
|
||||
<!-- Folded corner -->
|
||||
<path d="M14 2v6h6" stroke="currentColor" stroke-width="1.5" fill="currentColor" fill-opacity="0.1" stroke-linejoin="round"/>
|
||||
|
||||
<!-- Text lines - GB clean style -->
|
||||
<line x1="8" y1="12" x2="16" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
|
||||
<line x1="8" y1="15" x2="14" y2="15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
||||
<line x1="8" y1="18" x2="12" y2="18" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.3"/>
|
||||
|
||||
<!-- GB accent dot -->
|
||||
<circle cx="17" cy="17" r="1.5" fill="#3b82f6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 942 B |
13
ui/suite/assets/icons/gb-package.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- GB-inspired package icon - hexagonal bot head with package box -->
|
||||
<path d="M12 2L3 7v10l9 5 9-5V7l-9-5z" opacity="0.2" fill="currentColor"/>
|
||||
<path d="M12 2L3 7v10l9 5 9-5V7l-9-5z"/>
|
||||
<path d="M12 22V12"/>
|
||||
<path d="M21 7l-9 5-9-5"/>
|
||||
<!-- Bot eyes -->
|
||||
<circle cx="9" cy="10" r="1" fill="currentColor"/>
|
||||
<circle cx="15" cy="10" r="1" fill="currentColor"/>
|
||||
<!-- Bot antenna -->
|
||||
<path d="M12 2v-1"/>
|
||||
<circle cx="12" cy="0.5" r="0.5" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 645 B |
32
ui/suite/assets/icons/gb-palette.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired palette icon for theming -->
|
||||
<defs>
|
||||
<linearGradient id="paletteGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#8b5cf6"/>
|
||||
<stop offset="100%" style="stop-color:#6366f1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Palette shape - GB rounded organic form -->
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C12.76 22 13.5 21.76 14.1 21.32C14.85 20.78 15.3 19.9 15.3 18.95C15.3 18.44 15.12 17.95 14.82 17.54C14.52 17.12 14.38 16.6 14.55 16.1C14.74 15.53 15.3 15.15 15.9 15.15H18C20.21 15.15 22 13.36 22 11.15C22 6.13 17.52 2 12 2Z"
|
||||
fill="url(#paletteGradient)"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
opacity="0.85"/>
|
||||
|
||||
<!-- Color dots - GB circular branding style -->
|
||||
<!-- Blue dot -->
|
||||
<circle cx="6.5" cy="11" r="2" fill="#3b82f6"/>
|
||||
|
||||
<!-- Green dot -->
|
||||
<circle cx="9" cy="6.5" r="2" fill="#10b981"/>
|
||||
|
||||
<!-- Yellow/Gold dot -->
|
||||
<circle cx="14" cy="6" r="2" fill="#fbbf24"/>
|
||||
|
||||
<!-- Pink/Coral dot -->
|
||||
<circle cx="17" cy="10" r="2" fill="#f472b6"/>
|
||||
|
||||
<!-- Highlight reflection - GB polish -->
|
||||
<path d="M7 4C9 3 11 2.5 13 3" stroke="white" stroke-width="1" stroke-linecap="round" opacity="0.4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
11
ui/suite/assets/icons/gb-paper.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Document body with folded corner -->
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<!-- Folded corner -->
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<!-- Text lines -->
|
||||
<line x1="8" y1="13" x2="16" y2="13"/>
|
||||
<line x1="8" y1="17" x2="14" y2="17"/>
|
||||
<!-- Pen/edit indicator -->
|
||||
<circle cx="8" cy="9" r="1" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 548 B |
10
ui/suite/assets/icons/gb-research.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Magnifying glass -->
|
||||
<circle cx="10" cy="10" r="7"/>
|
||||
<line x1="15" y1="15" x2="21" y2="21"/>
|
||||
<!-- Document/research lines inside -->
|
||||
<line x1="7" y1="8" x2="13" y2="8"/>
|
||||
<line x1="7" y1="11" x2="11" y2="11"/>
|
||||
<!-- Spark/insight indicator -->
|
||||
<path d="M18 4 L19 2 L20 4 L22 5 L20 6 L19 8 L18 6 L16 5 Z" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 527 B |
31
ui/suite/assets/icons/gb-rocket.svg
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired rocket icon - representing launch/deploy -->
|
||||
<defs>
|
||||
<linearGradient id="rocketGradient" x1="0%" y1="100%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="flameGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f97316"/>
|
||||
<stop offset="50%" style="stop-color:#fbbf24"/>
|
||||
<stop offset="100%" style="stop-color:#fcd34d"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Rocket body - GB rounded aesthetic -->
|
||||
<path d="M12 2C12 2 8 6 8 12C8 15 9.5 17 12 19C14.5 17 16 15 16 12C16 6 12 2 12 2Z" fill="url(#rocketGradient)" stroke="currentColor" stroke-width="1"/>
|
||||
|
||||
<!-- Window - circular GB style -->
|
||||
<circle cx="12" cy="9" r="2" fill="#fff" stroke="currentColor" stroke-width="0.75"/>
|
||||
<circle cx="12" cy="9" r="1" fill="#1e3a5f" opacity="0.8"/>
|
||||
|
||||
<!-- Left fin -->
|
||||
<path d="M8 14L5 17L7 18L8 16" fill="url(#rocketGradient)" stroke="currentColor" stroke-width="0.75" stroke-linejoin="round"/>
|
||||
|
||||
<!-- Right fin -->
|
||||
<path d="M16 14L19 17L17 18L16 16" fill="url(#rocketGradient)" stroke="currentColor" stroke-width="0.75" stroke-linejoin="round"/>
|
||||
|
||||
<!-- Flame exhaust -->
|
||||
<path d="M10 19C10 19 11 22 12 23C13 22 14 19 14 19C13.5 20 12.5 21 12 21C11.5 21 10.5 20 10 19Z" fill="url(#flameGradient)"/>
|
||||
<path d="M11 19C11 19 11.5 21 12 21.5C12.5 21 13 19 13 19" fill="#fef08a" opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
18
ui/suite/assets/icons/gb-search.svg
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired search icon with bot-like characteristics -->
|
||||
|
||||
<!-- Main magnifying glass circle - GB rounded aesthetic -->
|
||||
<circle cx="10" cy="10" r="7" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
||||
|
||||
<!-- Inner circle detail - bot eye inspired -->
|
||||
<circle cx="10" cy="10" r="3.5" stroke="currentColor" stroke-width="1" fill="none" opacity="0.4"/>
|
||||
|
||||
<!-- Center highlight - GB bot eye -->
|
||||
<circle cx="10" cy="10" r="1.5" fill="currentColor" opacity="0.6"/>
|
||||
|
||||
<!-- Handle with GB curve -->
|
||||
<path d="M15.5 15.5L21 21" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Subtle reflection arc - GB style -->
|
||||
<path d="M6 7 Q7.5 5.5 9.5 5.5" stroke="currentColor" stroke-width="1" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 878 B |
33
ui/suite/assets/icons/gb-signal.svg
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired signal/broadcast icon -->
|
||||
<defs>
|
||||
<linearGradient id="signalGradient" x1="0%" y1="100%" x2="0%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Outer wave arc -->
|
||||
<path d="M4.5 4.5C1.5 7.5 1.5 12.5 4.5 15.5" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||
<path d="M19.5 4.5C22.5 7.5 22.5 12.5 19.5 15.5" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||
|
||||
<!-- Middle wave arc -->
|
||||
<path d="M7 7C5 9 5 11 7 13" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.6"/>
|
||||
<path d="M17 7C19 9 19 11 17 13" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.6"/>
|
||||
|
||||
<!-- Inner wave arc -->
|
||||
<path d="M9.5 8.5C8.5 9.5 8.5 10.5 9.5 11.5" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.8"/>
|
||||
<path d="M14.5 8.5C15.5 9.5 15.5 10.5 14.5 11.5" stroke="url(#signalGradient)" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.8"/>
|
||||
|
||||
<!-- Center broadcast point - GB bot head style -->
|
||||
<circle cx="12" cy="10" r="2.5" fill="url(#signalGradient)"/>
|
||||
<circle cx="11" cy="9.5" r="0.5" fill="#fff" opacity="0.8"/>
|
||||
<circle cx="13" cy="9.5" r="0.5" fill="#fff" opacity="0.8"/>
|
||||
|
||||
<!-- Antenna tower base -->
|
||||
<path d="M12 12.5V18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M9 18H15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M10 21H14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
||||
<path d="M9 18L10 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
||||
<path d="M15 18L14 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
13
ui/suite/assets/icons/gb-sources.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Book/document stack representing knowledge sources -->
|
||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||||
<!-- Knowledge connection nodes -->
|
||||
<circle cx="10" cy="8" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<circle cx="15" cy="8" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<circle cx="12.5" cy="12" r="1.5" fill="currentColor" stroke="none"/>
|
||||
<!-- Connection lines between nodes -->
|
||||
<line x1="10" y1="8" x2="15" y2="8" opacity="0.5"/>
|
||||
<line x1="10" y1="8" x2="12.5" y2="12" opacity="0.5"/>
|
||||
<line x1="15" y1="8" x2="12.5" y2="12" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 811 B |
16
ui/suite/assets/icons/gb-target.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB Target Icon - Inspired by General Bots aesthetic -->
|
||||
<!-- Outer ring -->
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.3"/>
|
||||
<!-- Middle ring -->
|
||||
<circle cx="12" cy="12" r="6.5" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.6"/>
|
||||
<!-- Inner core - GB style rounded hexagon hint -->
|
||||
<path d="M12 5.5L15.5 8V13L12 16.5L8.5 13V8L12 5.5Z" stroke="currentColor" stroke-width="1.5" fill="currentColor" fill-opacity="0.15" stroke-linejoin="round"/>
|
||||
<!-- Center dot -->
|
||||
<circle cx="12" cy="11" r="1.5" fill="currentColor"/>
|
||||
<!-- Targeting lines -->
|
||||
<line x1="12" y1="2" x2="12" y2="4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<line x1="12" y1="20" x2="12" y2="22" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<line x1="2" y1="12" x2="4" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<line x1="20" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
18
ui/suite/assets/icons/gb-tasks.svg
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Task list container -->
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<!-- Checkbox 1 - checked -->
|
||||
<rect x="6" y="6" width="4" height="4" rx="1"/>
|
||||
<polyline points="7 8 8 9 10 7"/>
|
||||
<!-- Task line 1 -->
|
||||
<line x1="13" y1="8" x2="18" y2="8"/>
|
||||
<!-- Checkbox 2 - checked -->
|
||||
<rect x="6" y="10" width="4" height="4" rx="1"/>
|
||||
<polyline points="7 12 8 13 10 11"/>
|
||||
<!-- Task line 2 -->
|
||||
<line x1="13" y1="12" x2="18" y2="12"/>
|
||||
<!-- Checkbox 3 - unchecked -->
|
||||
<rect x="6" y="14" width="4" height="4" rx="1"/>
|
||||
<!-- Task line 3 -->
|
||||
<line x1="13" y1="16" x2="18" y2="16"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 776 B |
42
ui/suite/assets/icons/gb-tree.svg
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired tree structure icon for file/directory trees -->
|
||||
<defs>
|
||||
<linearGradient id="treeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#1d4ed8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Main vertical trunk -->
|
||||
<path d="M6 3V21" stroke="url(#treeGradient)" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Branch 1 - top level -->
|
||||
<path d="M6 6H10" stroke="url(#treeGradient)" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Node 1 - GB bot-inspired circle -->
|
||||
<circle cx="13" cy="6" r="3" fill="url(#treeGradient)" opacity="0.9"/>
|
||||
<circle cx="12" cy="5.5" r="0.6" fill="#fff"/>
|
||||
<circle cx="14" cy="5.5" r="0.6" fill="#fff"/>
|
||||
|
||||
<!-- Branch 2 - middle level -->
|
||||
<path d="M6 12H10" stroke="url(#treeGradient)" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Node 2 - folder-like rectangle with GB rounding -->
|
||||
<rect x="10" y="10" width="8" height="4" rx="1.5" fill="url(#treeGradient)" opacity="0.7"/>
|
||||
<line x1="12" y1="12" x2="16" y2="12" stroke="#fff" stroke-width="1" stroke-linecap="round" opacity="0.8"/>
|
||||
|
||||
<!-- Branch 3 - bottom level -->
|
||||
<path d="M6 18H10" stroke="url(#treeGradient)" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
<!-- Node 3 - document icon -->
|
||||
<path d="M10 16H16C16.5523 16 17 16.4477 17 17V19C17 19.5523 16.5523 20 16 20H10C10 20 10 16 10 16Z" fill="url(#treeGradient)" opacity="0.5"/>
|
||||
<line x1="12" y1="18" x2="15" y2="18" stroke="#fff" stroke-width="0.75" stroke-linecap="round"/>
|
||||
|
||||
<!-- Sub-branch connector dots - GB style -->
|
||||
<circle cx="6" cy="6" r="1" fill="url(#treeGradient)"/>
|
||||
<circle cx="6" cy="12" r="1" fill="url(#treeGradient)"/>
|
||||
<circle cx="6" cy="18" r="1" fill="url(#treeGradient)"/>
|
||||
|
||||
<!-- Root node at top -->
|
||||
<circle cx="6" cy="3" r="1.5" fill="url(#treeGradient)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
25
ui/suite/assets/icons/gb-warning.svg
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- GB-inspired warning icon - triangular with bot aesthetic -->
|
||||
<defs>
|
||||
<linearGradient id="warningGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#fbbf24"/>
|
||||
<stop offset="100%" style="stop-color:#f59e0b"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Warning triangle with rounded corners - GB style -->
|
||||
<path d="M12 2L2 20C1.7 20.5 2.1 21 2.6 21H21.4C21.9 21 22.3 20.5 22 20L12 2Z"
|
||||
fill="url(#warningGradient)"
|
||||
stroke="#d97706"
|
||||
stroke-width="1"
|
||||
stroke-linejoin="round"/>
|
||||
|
||||
<!-- Bot-style exclamation with rounded elements -->
|
||||
<rect x="11" y="8" width="2" height="6" rx="1" fill="#7c2d12"/>
|
||||
|
||||
<!-- Dot at bottom - circular like GB branding -->
|
||||
<circle cx="12" cy="17" r="1.2" fill="#7c2d12"/>
|
||||
|
||||
<!-- Subtle inner highlight -->
|
||||
<path d="M12 4L4.5 18H19.5L12 4Z" fill="white" fill-opacity="0.15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 992 B |
|
|
@ -184,6 +184,182 @@
|
|||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* Email Read Status Indicator */
|
||||
.mail-item-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.mail-read-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mail-read-indicator.read {
|
||||
background: #e6f4ea;
|
||||
color: #137333;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mail-read-indicator.read {
|
||||
background: #1e3a2f;
|
||||
color: #81c995;
|
||||
}
|
||||
|
||||
.mail-read-indicator.unread {
|
||||
background: #fce8e6;
|
||||
color: #c5221f;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mail-read-indicator.unread {
|
||||
background: #3c2020;
|
||||
color: #f28b82;
|
||||
}
|
||||
|
||||
.mail-read-indicator.pending {
|
||||
background: #fef7e0;
|
||||
color: #b06000;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mail-read-indicator.pending {
|
||||
background: #3c3020;
|
||||
color: #fdd663;
|
||||
}
|
||||
|
||||
.mail-read-indicator .icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Read count badge */
|
||||
.mail-read-count {
|
||||
font-size: 0.625rem;
|
||||
color: #5f6368;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mail-read-count {
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* Sent folder specific styling */
|
||||
.mail-item.sent .mail-item-status {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* Tracking stats panel */
|
||||
.tracking-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .tracking-stats {
|
||||
background: #292a2d;
|
||||
}
|
||||
|
||||
.tracking-stat {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.tracking-stat-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #1a73e8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .tracking-stat-value {
|
||||
color: #8ab4f8;
|
||||
}
|
||||
|
||||
.tracking-stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: #5f6368;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .tracking-stat-label {
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* Read status tooltip */
|
||||
.mail-item[data-read-at]:hover::after {
|
||||
content: attr(data-read-at);
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: #202124;
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mail-item[data-read-at]:hover::after {
|
||||
background: #e8eaed;
|
||||
color: #202124;
|
||||
}
|
||||
|
||||
/* Double check mark icon for read emails */
|
||||
.checkmark-double {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.checkmark-double::before,
|
||||
.checkmark-double::after {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.checkmark-double::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.checkmark-double::after {
|
||||
left: 0.25rem;
|
||||
}
|
||||
|
||||
.checkmark-double.read::before,
|
||||
.checkmark-double.read::after {
|
||||
color: #137333;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .checkmark-double.read::before,
|
||||
[data-theme="dark"] .checkmark-double.read::after {
|
||||
color: #81c995;
|
||||
}
|
||||
|
||||
.checkmark-double.sent::before,
|
||||
.checkmark-double.sent::after {
|
||||
color: #5f6368;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .checkmark-double.sent::before,
|
||||
[data-theme="dark"] .checkmark-double.sent::after {
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* Mail Content View */
|
||||
.mail-content-view {
|
||||
padding: 2rem;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
<div class="mail-layout">
|
||||
<!-- Sidebar -->
|
||||
<div class="panel mail-sidebar">
|
||||
<div style="padding: 1rem; border-bottom: 1px solid #334155;">
|
||||
<div style="padding: 1rem; border-bottom: 1px solid #334155">
|
||||
<button
|
||||
style="width: 100%; padding: 0.75rem; background: #3b82f6; color: white; border: none; border-radius: 0.5rem; cursor: pointer; font-weight: 600;"
|
||||
style="
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
"
|
||||
hx-get="/api/email/compose"
|
||||
hx-target="#mail-content"
|
||||
hx-swap="innerHTML"
|
||||
|
|
@ -13,33 +22,60 @@
|
|||
</div>
|
||||
|
||||
<!-- Folder List -->
|
||||
<div id="mail-folders"
|
||||
<div
|
||||
id="mail-folders"
|
||||
hx-get="/api/email/folders"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML">
|
||||
<div class="nav-item active"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<div
|
||||
class="nav-item active"
|
||||
hx-get="/api/email/list?folder=inbox"
|
||||
hx-target="#mail-list"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<span>📥</span> Inbox
|
||||
<span style="margin-left: auto; font-size: 0.875rem; color: #64748b;">0</span>
|
||||
<span
|
||||
style="
|
||||
margin-left: auto;
|
||||
font-size: 0.875rem;
|
||||
color: #64748b;
|
||||
"
|
||||
>0</span
|
||||
>
|
||||
</div>
|
||||
<div class="nav-item"
|
||||
<div
|
||||
class="nav-item"
|
||||
hx-get="/api/email/list?folder=sent"
|
||||
hx-target="#mail-list"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
onclick="window.currentFolder='sent'"
|
||||
>
|
||||
<span>📤</span> Sent
|
||||
</div>
|
||||
<div class="nav-item"
|
||||
<div
|
||||
class="nav-item"
|
||||
hx-get="/api/email/tracking/list"
|
||||
hx-target="#mail-list"
|
||||
hx-swap="innerHTML"
|
||||
onclick="window.currentFolder='tracking'"
|
||||
>
|
||||
<span>📊</span> Tracking
|
||||
</div>
|
||||
<div
|
||||
class="nav-item"
|
||||
hx-get="/api/email/list?folder=drafts"
|
||||
hx-target="#mail-list"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<span>📝</span> Drafts
|
||||
</div>
|
||||
<div class="nav-item"
|
||||
<div
|
||||
class="nav-item"
|
||||
hx-get="/api/email/list?folder=trash"
|
||||
hx-target="#mail-list"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<span>🗑️</span> Trash
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -47,15 +83,17 @@
|
|||
|
||||
<!-- Mail List -->
|
||||
<div class="panel mail-list">
|
||||
<div style="padding: 1rem; border-bottom: 1px solid #334155;">
|
||||
<div style="padding: 1rem; border-bottom: 1px solid #334155">
|
||||
<h3 id="folder-title">Inbox</h3>
|
||||
</div>
|
||||
<div id="mail-list"
|
||||
<div
|
||||
id="mail-list"
|
||||
hx-get="/api/email/list?folder=inbox"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<!-- Loading state -->
|
||||
<div style="padding: 2rem; text-align: center; color: #64748b;">
|
||||
<div style="padding: 2rem; text-align: center; color: #64748b">
|
||||
Loading emails...
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -64,15 +102,63 @@
|
|||
<!-- Mail Content -->
|
||||
<div class="panel mail-content">
|
||||
<div id="mail-content">
|
||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #64748b;">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 3rem; margin-bottom: 1rem;">📧</div>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #64748b;
|
||||
"
|
||||
>
|
||||
<div style="text-align: center">
|
||||
<div style="font-size: 3rem; margin-bottom: 1rem">📧</div>
|
||||
<h3>Select an email to read</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div</h3>>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tracking Stats Template (loaded when viewing tracking folder) -->
|
||||
<template id="tracking-stats-template">
|
||||
<div class="tracking-stats">
|
||||
<div class="tracking-stat">
|
||||
<div class="tracking-stat-value" id="stat-total-sent">0</div>
|
||||
<div class="tracking-stat-label">Total Sent</div>
|
||||
</div>
|
||||
<div class="tracking-stat">
|
||||
<div class="tracking-stat-value" id="stat-total-read">0</div>
|
||||
<div class="tracking-stat-label">Opened</div>
|
||||
</div>
|
||||
<div class="tracking-stat">
|
||||
<div class="tracking-stat-value" id="stat-read-rate">0%</div>
|
||||
<div class="tracking-stat-label">Open Rate</div>
|
||||
</div>
|
||||
<div class="tracking-stat">
|
||||
<div class="tracking-stat-value" id="stat-avg-time">-</div>
|
||||
<div class="tracking-stat-label">Avg. Time to Open</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Sent Mail Item Template with Read Status -->
|
||||
<template id="sent-mail-item-template">
|
||||
<div class="mail-item sent" data-tracking-id="" data-read-at="">
|
||||
<div class="mail-header">
|
||||
<span class="mail-to"></span>
|
||||
<span class="mail-time"></span>
|
||||
</div>
|
||||
<div class="mail-subject"></div>
|
||||
<div class="mail-item-status">
|
||||
<span class="mail-read-indicator">
|
||||
<span class="checkmark-double"></span>
|
||||
<span class="status-text"></span>
|
||||
</span>
|
||||
<span class="mail-read-count"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.mail-layout {
|
||||
|
|
@ -285,7 +371,9 @@
|
|||
|
||||
/* Loading spinner */
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
|
|
@ -362,52 +450,56 @@
|
|||
|
||||
<script>
|
||||
// Handle folder selection
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.nav-item')) {
|
||||
document.addEventListener("click", function (e) {
|
||||
if (e.target.closest(".nav-item")) {
|
||||
// Update active state
|
||||
document.querySelectorAll('.nav-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
document.querySelectorAll(".nav-item").forEach((item) => {
|
||||
item.classList.remove("active");
|
||||
});
|
||||
e.target.closest('.nav-item').classList.add('active');
|
||||
e.target.closest(".nav-item").classList.add("active");
|
||||
|
||||
// Update folder title
|
||||
const folderName = e.target.closest('.nav-item').textContent.trim().split(' ')[1];
|
||||
const titleEl = document.getElementById('folder-title');
|
||||
const folderName = e.target
|
||||
.closest(".nav-item")
|
||||
.textContent.trim()
|
||||
.split(" ")[1];
|
||||
const titleEl = document.getElementById("folder-title");
|
||||
if (titleEl) {
|
||||
titleEl.textContent = folderName;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle mail selection
|
||||
if (e.target.closest('.mail-item')) {
|
||||
document.querySelectorAll('.mail-item').forEach(item => {
|
||||
item.classList.remove('selected');
|
||||
if (e.target.closest(".mail-item")) {
|
||||
document.querySelectorAll(".mail-item").forEach((item) => {
|
||||
item.classList.remove("selected");
|
||||
});
|
||||
e.target.closest('.mail-item').classList.add('selected');
|
||||
e.target.closest(".mail-item").classList.add("selected");
|
||||
|
||||
// Mark as read
|
||||
e.target.closest('.mail-item').classList.remove('unread');
|
||||
e.target.closest(".mail-item").classList.remove("unread");
|
||||
}
|
||||
});
|
||||
|
||||
// Handle HTMX events for better UX
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
document.body.addEventListener("htmx:beforeRequest", function (evt) {
|
||||
// Add loading state
|
||||
if (evt.detail.target.id === 'mail-list') {
|
||||
evt.detail.target.innerHTML = '<div style="padding: 2rem; text-align: center;"><div class="spinner"></div></div>';
|
||||
if (evt.detail.target.id === "mail-list") {
|
||||
evt.detail.target.innerHTML =
|
||||
'<div style="padding: 2rem; text-align: center;"><div class="spinner"></div></div>';
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
||||
document.body.addEventListener("htmx:afterSwap", function (evt) {
|
||||
// Scroll to top after loading new emails
|
||||
if (evt.detail.target.id === 'mail-list') {
|
||||
if (evt.detail.target.id === "mail-list") {
|
||||
evt.detail.target.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle compose form submission
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
if (evt.detail.elt.matches('.compose-form')) {
|
||||
document.body.addEventListener("htmx:beforeRequest", function (evt) {
|
||||
if (evt.detail.elt.matches(".compose-form")) {
|
||||
// Validate form
|
||||
const form = evt.detail.elt;
|
||||
const to = form.querySelector('[name="to"]').value;
|
||||
|
|
@ -416,24 +508,187 @@ document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
|||
|
||||
if (!to || !subject || !body) {
|
||||
evt.preventDefault();
|
||||
alert('Please fill in all required fields');
|
||||
alert("Please fill in all required fields");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
document.addEventListener('keydown', function(e) {
|
||||
document.addEventListener("keydown", function (e) {
|
||||
// Ctrl/Cmd + N for new email
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "n") {
|
||||
e.preventDefault();
|
||||
document.querySelector('.mail-sidebar button').click();
|
||||
document.querySelector(".mail-sidebar button").click();
|
||||
}
|
||||
|
||||
// Delete key for delete email
|
||||
if (e.key === 'Delete' && document.querySelector('.mail-item.selected')) {
|
||||
const selected = document.querySelector('.mail-item.selected');
|
||||
if (
|
||||
e.key === "Delete" &&
|
||||
document.querySelector(".mail-item.selected")
|
||||
) {
|
||||
const selected = document.querySelector(".mail-item.selected");
|
||||
const deleteBtn = selected.querySelector('[data-action="delete"]');
|
||||
if (deleteBtn) deleteBtn.click();
|
||||
}
|
||||
});
|
||||
|
||||
// Track current folder
|
||||
window.currentFolder = "inbox";
|
||||
|
||||
// Load tracking stats when viewing tracking folder
|
||||
async function loadTrackingStats() {
|
||||
try {
|
||||
const response = await fetch("/api/email/tracking/stats");
|
||||
const data = await response.json();
|
||||
if (data.success && data.data) {
|
||||
document.getElementById("stat-total-sent").textContent =
|
||||
data.data.total_sent;
|
||||
document.getElementById("stat-total-read").textContent =
|
||||
data.data.total_read;
|
||||
document.getElementById("stat-read-rate").textContent =
|
||||
data.data.read_rate.toFixed(1) + "%";
|
||||
if (data.data.avg_time_to_read_hours) {
|
||||
const hours = data.data.avg_time_to_read_hours;
|
||||
if (hours < 1) {
|
||||
document.getElementById("stat-avg-time").textContent =
|
||||
Math.round(hours * 60) + "m";
|
||||
} else if (hours < 24) {
|
||||
document.getElementById("stat-avg-time").textContent =
|
||||
hours.toFixed(1) + "h";
|
||||
} else {
|
||||
document.getElementById("stat-avg-time").textContent =
|
||||
(hours / 24).toFixed(1) + "d";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to load tracking stats:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Render tracking list item
|
||||
function renderTrackingItem(item) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "mail-item sent";
|
||||
div.setAttribute("data-tracking-id", item.tracking_id);
|
||||
if (item.read_at) {
|
||||
div.setAttribute(
|
||||
"data-read-at",
|
||||
"Opened: " + new Date(item.read_at).toLocaleString(),
|
||||
);
|
||||
}
|
||||
|
||||
const statusClass = item.is_read ? "read" : "pending";
|
||||
const statusText = item.is_read ? "Opened" : "Not opened";
|
||||
const checkmarkClass = item.is_read ? "read" : "sent";
|
||||
const readCount = item.read_count > 1 ? `(${item.read_count}x)` : "";
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="mail-header">
|
||||
<span class="mail-to">To: ${item.to_email}</span>
|
||||
<span class="mail-time">${new Date(item.sent_at).toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div class="mail-subject">${item.subject}</div>
|
||||
<div class="mail-item-status">
|
||||
<span class="mail-read-indicator ${statusClass}">
|
||||
<span class="checkmark-double ${checkmarkClass}"></span>
|
||||
<span class="status-text">${statusText}</span>
|
||||
</span>
|
||||
<span class="mail-read-count">${readCount}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
div.addEventListener("click", () => showTrackingDetails(item));
|
||||
return div;
|
||||
}
|
||||
|
||||
// Show tracking details in content panel
|
||||
function showTrackingDetails(item) {
|
||||
const content = document.getElementById("mail-content");
|
||||
const readStatus = item.is_read
|
||||
? `<span class="mail-read-indicator read"><span class="checkmark-double read"></span> Opened</span>`
|
||||
: `<span class="mail-read-indicator pending"><span class="checkmark-double sent"></span> Not opened yet</span>`;
|
||||
|
||||
const readInfo = item.is_read
|
||||
? `
|
||||
<div style="margin-top: 1rem; padding: 1rem; background: #e6f4ea; border-radius: 8px;">
|
||||
<h4 style="margin: 0 0 0.5rem 0; color: #137333;">📬 Email Opened</h4>
|
||||
<p style="margin: 0; color: #137333;">
|
||||
First opened: ${new Date(item.read_at).toLocaleString()}<br>
|
||||
Times opened: ${item.read_count}
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
: `
|
||||
<div style="margin-top: 1rem; padding: 1rem; background: #fef7e0; border-radius: 8px;">
|
||||
<h4 style="margin: 0 0 0.5rem 0; color: #b06000;">⏳ Awaiting Read</h4>
|
||||
<p style="margin: 0; color: #b06000;">
|
||||
This email has not been opened yet.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
content.innerHTML = `
|
||||
<div class="mail-content-view">
|
||||
<div class="mail-header">
|
||||
<h2 class="mail-subject">${item.subject}</h2>
|
||||
<div class="mail-meta">
|
||||
<span>To: <strong>${item.to_email}</strong></span>
|
||||
<span class="mail-date">Sent: ${new Date(item.sent_at).toLocaleString()}</span>
|
||||
</div>
|
||||
<div style="margin-top: 0.5rem;">
|
||||
${readStatus}
|
||||
</div>
|
||||
</div>
|
||||
${readInfo}
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<h4>Tracking ID</h4>
|
||||
<code style="font-size: 0.75rem; color: #5f6368;">${item.tracking_id}</code>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Handle HTMX response for tracking list
|
||||
document.body.addEventListener("htmx:afterSwap", function (evt) {
|
||||
if (
|
||||
evt.detail.target.id === "mail-list" &&
|
||||
window.currentFolder === "tracking"
|
||||
) {
|
||||
loadTrackingStats();
|
||||
|
||||
// Parse and render tracking items
|
||||
try {
|
||||
const response = JSON.parse(evt.detail.xhr.responseText);
|
||||
if (response.success && response.data) {
|
||||
const container = evt.detail.target;
|
||||
container.innerHTML = "";
|
||||
|
||||
// Add stats panel
|
||||
const statsTemplate = document.getElementById(
|
||||
"tracking-stats-template",
|
||||
);
|
||||
if (statsTemplate) {
|
||||
container.appendChild(
|
||||
statsTemplate.content.cloneNode(true),
|
||||
);
|
||||
}
|
||||
|
||||
// Add tracking items
|
||||
response.data.forEach((item) => {
|
||||
container.appendChild(renderTrackingItem(item));
|
||||
});
|
||||
|
||||
if (response.data.length === 0) {
|
||||
container.innerHTML +=
|
||||
'<div class="empty-state"><h3>No tracked emails</h3><p>Sent emails will appear here when tracking is enabled.</p></div>';
|
||||
}
|
||||
|
||||
loadTrackingStats();
|
||||
}
|
||||
} catch (e) {
|
||||
// Not a tracking response, ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||