873 lines
22 KiB
Markdown
Executable File
873 lines
22 KiB
Markdown
Executable File
# Dewemoji User Flow & UX Brief
|
||
|
||
**Version:** 1.0
|
||
**Date:** February 8, 2026
|
||
**Purpose:** Define seamless user experience across visitor → logged → paid states
|
||
**Related Doc:** dewemoji-direction-2026.md
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
Dewemoji implements a **hybrid quick-add flow** where Personal users can add keywords instantly from emoji detail pages (80% of use cases) while maintaining full CRUD power in the dashboard (20% management). This creates a frictionless progression: discovery → personalization → habit, without disrupting the free user experience.
|
||
|
||
**Core UX Principle:** *"Add keywords where you discover emojis, manage them where you organize."*
|
||
|
||
---
|
||
|
||
## 1. User State Definitions
|
||
|
||
### 1.1 Visitor (Non-Logged)
|
||
**Who:** Anyone landing on dewemoji.com or using Chrome extension (no account)
|
||
**Intent:** Quick emoji search and usage
|
||
**Experience:** Zero friction, no login walls
|
||
|
||
### 1.2 Free User (Logged, No Subscription)
|
||
**Who:** Registered account, free tier
|
||
**Intent:** Exploring Personal features, considering upgrade
|
||
**Experience:** Full free features + gentle upgrade hints
|
||
|
||
### 1.3 Personal User (Logged, Paid)
|
||
**Who:** Active Personal subscription ($4.99/mo, $49/yr, or $99 lifetime)
|
||
**Intent:** Building and syncing personal keyword library
|
||
**Experience:** Full personalization power across all touchpoints
|
||
|
||
---
|
||
|
||
## 2. Primary User Flows
|
||
|
||
### 2.1 Discovery Flow (All Users)
|
||
|
||
```
|
||
User lands on dewemoji.com
|
||
↓
|
||
Search bar: "bekicot"
|
||
↓
|
||
Results page: 🐌 snail (public keywords: "snail, escargot")
|
||
↓
|
||
Click emoji → Detail page
|
||
↓
|
||
[State-dependent experience - see Section 3]
|
||
```
|
||
|
||
**Key Insight:** All users start here. Free discovery hooks them; Personal lets them own it.
|
||
|
||
---
|
||
|
||
### 2.2 Personalization Flow (Personal Users)
|
||
|
||
#### Primary: Quick Add from Detail Page (80% Usage)
|
||
|
||
```
|
||
Personal user searches "snail"
|
||
↓
|
||
Detail page shows:
|
||
┌─────────────────────────────────────┐
|
||
│ 🐌 Snail │
|
||
│ │
|
||
│ Public Keywords: snail, escargot │
|
||
│ │
|
||
│ Your Keywords: (none yet) │
|
||
│ [+ Add Your Keyword] │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
User clicks [+ Add Your Keyword]
|
||
↓
|
||
Inline modal appears:
|
||
┌─────────────────────────────────────┐
|
||
│ What do you call this emoji? │
|
||
│ ┌─────────────────────────────────┐│
|
||
│ │ bekicot, siput ││
|
||
│ └─────────────────────────────────┘│
|
||
│ Language: [Indonesian ▾] │
|
||
│ [Cancel] [Save Keyword] │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
Saves instantly → Toast: "Added 'bekicot' to your library ✓"
|
||
↓
|
||
Detail page now shows:
|
||
┌─────────────────────────────────────┐
|
||
│ 🐌 Snail │
|
||
│ │
|
||
│ Public Keywords: snail, escargot │
|
||
│ │
|
||
│ Your Keywords: │
|
||
│ • bekicot (ID) [edit] [×] │
|
||
│ • siput (ID) [edit] [×] │
|
||
│ [+ Add Another] │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
Next search: "bekicot" → 🐌 appears in results!
|
||
```
|
||
|
||
**Why This Flow:**
|
||
- **Contextual:** User is *already looking at* the emoji they want to name
|
||
- **Fast:** 2 clicks (add button → save), ~5 seconds total
|
||
- **Immediate gratification:** See change instantly in searches
|
||
- **Low friction:** No navigation away from discovery flow
|
||
|
||
---
|
||
|
||
#### Secondary: Dashboard Management (20% Usage)
|
||
|
||
**When Used:**
|
||
- Bulk editing/organizing keywords
|
||
- Reviewing all keywords at once
|
||
- Exporting/importing keyword libraries
|
||
- Fixing typos across multiple entries
|
||
|
||
```
|
||
User navigates to Dashboard → My Keywords tab
|
||
↓
|
||
Table view:
|
||
┌───────────────────────────────────────────────────────────┐
|
||
│ Emoji | Your Keywords | Lang | Actions │
|
||
├───────────────────────────────────────────────────────────┤
|
||
│ 🐌 | bekicot, siput | ID | [Edit] [Delete] │
|
||
│ 😊 | senyum, happy | ID | [Edit] [Delete] │
|
||
│ 🔥 | api, fire, lit | ID | [Edit] [Delete] │
|
||
└───────────────────────────────────────────────────────────┘
|
||
[+ Add Keyword] [Import JSON] [Export JSON] [Search/Filter]
|
||
↓
|
||
User clicks [+ Add Keyword]
|
||
↓
|
||
Modal: Search emoji picker → Select 🎉 → Add keywords
|
||
↓
|
||
Saves to table → Auto-sync to extension
|
||
```
|
||
|
||
**Why Dashboard Exists:**
|
||
- Power users want bulk operations
|
||
- Easy to review/audit entire library
|
||
- Import/export for backup
|
||
- Edit mistakes without finding emoji again
|
||
|
||
---
|
||
|
||
### 2.3 Upgrade Flow (Free → Personal)
|
||
|
||
#### Trigger Points
|
||
|
||
**1. On Detail Page (Soft Prompt)**
|
||
```
|
||
Free user on emoji detail page sees:
|
||
┌─────────────────────────────────────┐
|
||
│ 🐌 Snail │
|
||
│ │
|
||
│ Public Keywords: snail, escargot │
|
||
│ │
|
||
│ 💎 Want to add your own keywords? │
|
||
│ like 'bekicot' or 'siput'? │
|
||
│ [Upgrade to Personal →] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
**2. On Search Miss (Strong Prompt)**
|
||
```
|
||
User searches "bekicot" → No results
|
||
↓
|
||
Results page shows:
|
||
┌─────────────────────────────────────┐
|
||
│ No results for "bekicot" │
|
||
│ │
|
||
│ 💡 With Personal, you can create │
|
||
│ "bekicot → 🐌" and sync it │
|
||
│ everywhere. │
|
||
│ [Try Personal Free for 7 Days] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
**3. From Extension (Contextual)**
|
||
```
|
||
Extension user searches "bekicot" → Not found
|
||
↓
|
||
Extension shows inline banner:
|
||
"Add 'bekicot' to your library with Personal"
|
||
[Upgrade] button → Opens dewemoji.com/upgrade
|
||
```
|
||
|
||
**4. From Dashboard (Clear Path)**
|
||
```
|
||
Free user clicks "My Keywords" tab
|
||
↓
|
||
Shows empty state:
|
||
┌─────────────────────────────────────┐
|
||
│ 📚 Your Personal Keyword Library │
|
||
│ │
|
||
│ You have 0 keywords. │
|
||
│ │
|
||
│ Upgrade to Personal to: │
|
||
│ ✓ Add unlimited keywords │
|
||
│ ✓ Search in your language │
|
||
│ ✓ Sync across all devices │
|
||
│ │
|
||
│ [Start 7-Day Free Trial] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 3. State-Dependent UI Elements
|
||
|
||
### 3.1 Emoji Detail Page
|
||
|
||
#### Visitor (Non-Logged)
|
||
```html
|
||
<!-- Public info only -->
|
||
<div class="emoji-detail">
|
||
<h1>🐌 Snail</h1>
|
||
<section class="public-keywords">
|
||
<h3>Keywords</h3>
|
||
<ul>
|
||
<li>snail</li>
|
||
<li>escargot</li>
|
||
<li>slow</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<div class="cta-banner">
|
||
<p>💡 Want to search in your language?</p>
|
||
<button>Sign Up Free</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### Free User (Logged)
|
||
```html
|
||
<!-- Public info + muted personal section -->
|
||
<div class="emoji-detail">
|
||
<h1>🐌 Snail</h1>
|
||
|
||
<section class="public-keywords">
|
||
<h3>Keywords</h3>
|
||
<ul><li>snail</li><li>escargot</li></ul>
|
||
</section>
|
||
|
||
<section class="user-keywords muted">
|
||
<h3>Your Keywords</h3>
|
||
<div class="upgrade-overlay">
|
||
<p>💎 Add personal keywords like 'bekicot'</p>
|
||
<button class="upgrade-btn">Upgrade to Personal</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
```
|
||
|
||
#### Personal User (Logged + Paid)
|
||
```html
|
||
<!-- Full personalization active -->
|
||
<div class="emoji-detail">
|
||
<h1>🐌 Snail</h1>
|
||
|
||
<section class="public-keywords">
|
||
<h3>Public Keywords</h3>
|
||
<ul><li>snail</li><li>escargot</li><li>slow</li></ul>
|
||
</section>
|
||
|
||
<section class="user-keywords active">
|
||
<h3>Your Keywords</h3>
|
||
<ul class="keyword-list">
|
||
<li>
|
||
<span class="keyword">bekicot</span>
|
||
<span class="lang-tag">ID</span>
|
||
<button class="edit-btn">edit</button>
|
||
<button class="delete-btn">×</button>
|
||
</li>
|
||
<li>
|
||
<span class="keyword">siput</span>
|
||
<span class="lang-tag">ID</span>
|
||
<button class="edit-btn">edit</button>
|
||
<button class="delete-btn">×</button>
|
||
</li>
|
||
</ul>
|
||
<button class="add-keyword-btn">+ Add Keyword</button>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- Add Keyword Modal (hidden until clicked) -->
|
||
<dialog id="add-keyword-modal">
|
||
<h3>What do you call this emoji?</h3>
|
||
<input type="text" placeholder="Enter keywords (comma-separated)" />
|
||
<select name="language">
|
||
<option value="en">English</option>
|
||
<option value="id">Indonesian</option>
|
||
<option value="ko">Korean</option>
|
||
<option value="ja">Japanese</option>
|
||
</select>
|
||
<div class="modal-actions">
|
||
<button class="cancel">Cancel</button>
|
||
<button class="save primary">Save Keyword</button>
|
||
</div>
|
||
</dialog>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 Search Results Page
|
||
|
||
#### All Users (Public Results)
|
||
```html
|
||
<div class="search-results">
|
||
<div class="emoji-card">
|
||
<span class="emoji">🐌</span>
|
||
<span class="name">Snail</span>
|
||
<span class="matched-keyword">snail</span>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
#### Personal User (Blended Results)
|
||
```html
|
||
<div class="search-results">
|
||
<!-- Private keyword match (appears first) -->
|
||
<div class="emoji-card user-match">
|
||
<span class="emoji">🐌</span>
|
||
<span class="name">Snail</span>
|
||
<span class="matched-keyword">
|
||
bekicot
|
||
<span class="badge personal">Your keyword</span>
|
||
</span>
|
||
<button class="quick-edit">Edit</button>
|
||
</div>
|
||
|
||
<!-- Public keyword matches (below) -->
|
||
<div class="emoji-card">
|
||
<span class="emoji">🐌</span>
|
||
<span class="name">Snail</span>
|
||
<span class="matched-keyword">snail</span>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
### 3.3 Dashboard (Personal Users Only)
|
||
|
||
```html
|
||
<div class="dashboard">
|
||
<nav class="tabs">
|
||
<button class="active">My Keywords</button>
|
||
<button>API Keys</button>
|
||
<button>Billing</button>
|
||
</nav>
|
||
|
||
<!-- My Keywords Tab -->
|
||
<div class="tab-content" id="keywords">
|
||
<div class="toolbar">
|
||
<button class="primary">+ Add Keyword</button>
|
||
<button>Import JSON</button>
|
||
<button>Export JSON</button>
|
||
<input type="search" placeholder="Search your keywords..." />
|
||
</div>
|
||
|
||
<table class="keyword-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Emoji</th>
|
||
<th>Your Keywords</th>
|
||
<th>Language</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>🐌</td>
|
||
<td>bekicot, siput</td>
|
||
<td><span class="lang-tag">ID</span></td>
|
||
<td>
|
||
<button class="edit">Edit</button>
|
||
<button class="delete">Delete</button>
|
||
</td>
|
||
</tr>
|
||
<!-- More rows... -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
## 4. API Endpoints for UX Flows
|
||
|
||
### 4.1 Detail Page Quick Add
|
||
|
||
**Fetch user's keywords for specific emoji:**
|
||
```
|
||
GET /v1/emoji/:slug?include_user_keywords=true
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Response:
|
||
{
|
||
"emoji": {...},
|
||
"public_keywords": ["snail", "escargot"],
|
||
"user_keywords": [
|
||
{"id": "uk_1", "keyword": "bekicot", "lang": "id"},
|
||
{"id": "uk_2", "keyword": "siput", "lang": "id"}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Add keyword from detail page:**
|
||
```
|
||
POST /v1/keywords/quick
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Body:
|
||
{
|
||
"emoji_slug": "snail",
|
||
"keywords": ["bekicot", "siput"],
|
||
"lang": "id"
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"success": true,
|
||
"added": ["bekicot", "siput"],
|
||
"emoji_slug": "snail"
|
||
}
|
||
```
|
||
|
||
**Edit single keyword:**
|
||
```
|
||
PATCH /v1/keywords/:id
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Body:
|
||
{
|
||
"keyword": "bekicot kecil"
|
||
}
|
||
```
|
||
|
||
**Delete keyword:**
|
||
```
|
||
DELETE /v1/keywords/:id
|
||
Authorization: Bearer dew_abc123...
|
||
```
|
||
|
||
---
|
||
|
||
### 4.2 Dashboard Bulk Operations
|
||
|
||
**List all user keywords:**
|
||
```
|
||
GET /v1/keywords?page=1&limit=50
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Response:
|
||
{
|
||
"keywords": [
|
||
{
|
||
"id": "uk_1",
|
||
"emoji_slug": "snail",
|
||
"emoji": "🐌",
|
||
"keyword": "bekicot",
|
||
"lang": "id",
|
||
"created_at": "2026-02-08T10:30:00Z"
|
||
}
|
||
// ... more
|
||
],
|
||
"total": 47,
|
||
"page": 1
|
||
}
|
||
```
|
||
|
||
**Bulk import:**
|
||
```
|
||
POST /v1/keywords/import
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Body:
|
||
{
|
||
"keywords": [
|
||
{"emoji_slug": "snail", "keywords": ["bekicot"], "lang": "id"},
|
||
{"emoji_slug": "fire", "keywords": ["api"], "lang": "id"}
|
||
]
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"imported": 2,
|
||
"skipped": 0,
|
||
"errors": []
|
||
}
|
||
```
|
||
|
||
**Export:**
|
||
```
|
||
GET /v1/keywords/export?format=json
|
||
Authorization: Bearer dew_abc123...
|
||
|
||
Response: (Download JSON file)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Frontend Implementation Specs
|
||
|
||
### 5.1 Emoji Detail Page Changes
|
||
|
||
**File:** `/pages/emoji/[slug].js` (or equivalent)
|
||
|
||
**Required Elements:**
|
||
|
||
1. **User keywords section** (Personal only)
|
||
- Fetch on page load: `GET /v1/emoji/:slug?include_user_keywords=true`
|
||
- Show list of user's keywords with edit/delete buttons
|
||
- Show "Add Keyword" button
|
||
|
||
2. **Add keyword modal**
|
||
- Input: comma-separated keywords
|
||
- Dropdown: language selector (EN, ID, KO, JA, etc.)
|
||
- Save button → POST `/v1/keywords/quick`
|
||
- Success → update UI without page reload
|
||
|
||
3. **Inline edit**
|
||
- Click "edit" → input becomes editable
|
||
- Save → PATCH `/v1/keywords/:id`
|
||
- Cancel → revert
|
||
|
||
4. **Inline delete**
|
||
- Click "×" → confirm modal
|
||
- Yes → DELETE `/v1/keywords/:id` → remove from UI
|
||
|
||
**State Management:**
|
||
```javascript
|
||
const [userKeywords, setUserKeywords] = useState([]);
|
||
const [isPersonal, setIsPersonal] = useState(false);
|
||
|
||
// On mount
|
||
useEffect(() => {
|
||
if (user?.tier === 'personal') {
|
||
fetchUserKeywords(emojiSlug);
|
||
}
|
||
}, [emojiSlug, user]);
|
||
|
||
// Add keyword
|
||
const handleAddKeyword = async (keywords, lang) => {
|
||
const result = await api.post('/keywords/quick', {
|
||
emoji_slug: emojiSlug,
|
||
keywords: keywords.split(',').map(k => k.trim()),
|
||
lang
|
||
});
|
||
|
||
if (result.success) {
|
||
fetchUserKeywords(emojiSlug); // Refresh list
|
||
toast.success('Keywords added!');
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 5.2 Dashboard Implementation
|
||
|
||
**File:** `/pages/dashboard.js`
|
||
|
||
**Tabs:**
|
||
1. My Keywords (default)
|
||
2. API Keys
|
||
3. Billing
|
||
|
||
**My Keywords Tab Components:**
|
||
|
||
```javascript
|
||
// Main table component
|
||
<KeywordTable
|
||
keywords={keywords}
|
||
onEdit={handleEdit}
|
||
onDelete={handleDelete}
|
||
onSearch={handleSearch}
|
||
/>
|
||
|
||
// Toolbar actions
|
||
<Toolbar>
|
||
<AddKeywordButton onClick={openModal} />
|
||
<ImportButton />
|
||
<ExportButton />
|
||
<SearchInput onChange={handleSearch} />
|
||
</Toolbar>
|
||
|
||
// Add keyword modal (with emoji picker)
|
||
<AddKeywordModal
|
||
onSave={handleAddKeyword}
|
||
onCancel={closeModal}
|
||
/>
|
||
```
|
||
|
||
**Features:**
|
||
- Pagination (50 per page)
|
||
- Search/filter by keyword or emoji
|
||
- Bulk select + delete
|
||
- Sort by: date added, emoji name, language
|
||
|
||
---
|
||
|
||
### 5.3 Upgrade Flow Pages
|
||
|
||
**File:** `/pages/upgrade.js` or `/pages/pricing.js`
|
||
|
||
**Components:**
|
||
|
||
1. **Feature comparison table**
|
||
```html
|
||
<table class="pricing-comparison">
|
||
<tr>
|
||
<th>Feature</th>
|
||
<th>Free</th>
|
||
<th>Personal</th>
|
||
</tr>
|
||
<tr>
|
||
<td>Public emoji search</td>
|
||
<td>✅ Unlimited</td>
|
||
<td>✅ Unlimited</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Private keywords</td>
|
||
<td>❌</td>
|
||
<td>✅ Unlimited</td>
|
||
</tr>
|
||
<!-- ... -->
|
||
</table>
|
||
```
|
||
|
||
2. **Pricing cards**
|
||
```html
|
||
<div class="pricing-cards">
|
||
<div class="card">
|
||
<h3>Monthly</h3>
|
||
<p class="price">$4.99/mo</p>
|
||
<button>Subscribe</button>
|
||
</div>
|
||
<div class="card recommended">
|
||
<span class="badge">Best Value</span>
|
||
<h3>Annual</h3>
|
||
<p class="price">$49/year</p>
|
||
<p class="savings">Save 17%</p>
|
||
<button>Subscribe</button>
|
||
</div>
|
||
<div class="card">
|
||
<h3>Lifetime</h3>
|
||
<p class="price">$99 once</p>
|
||
<button>Buy Now</button>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
3. **Stripe checkout integration**
|
||
```javascript
|
||
const handleCheckout = async (plan) => {
|
||
const session = await api.post('/stripe/checkout', {
|
||
plan, // 'monthly' | 'annual' | 'lifetime'
|
||
success_url: `${baseUrl}/dashboard?upgraded=true`,
|
||
cancel_url: `${baseUrl}/upgrade`
|
||
});
|
||
|
||
window.location.href = session.url;
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 6. UX Copy & Messaging
|
||
|
||
### 6.1 Upgrade Prompts
|
||
|
||
**Soft (Detail Page):**
|
||
> 💡 Want to search in your language? Add personal keywords like 'bekicot' for 🐌
|
||
> [Upgrade to Personal →]
|
||
|
||
**Strong (Search Miss):**
|
||
> No results for "bekicot"
|
||
>
|
||
> 💎 **Create your own keywords with Personal**
|
||
> Add "bekicot → 🐌" and sync it across all devices
|
||
> [Try Free for 7 Days]
|
||
|
||
**Empty State (Dashboard):**
|
||
> 📚 **Your Personal Keyword Library**
|
||
>
|
||
> You have 0 keywords.
|
||
>
|
||
> Upgrade to Personal to:
|
||
> ✓ Add unlimited keywords
|
||
> ✓ Search in your language
|
||
> ✓ Sync across all devices
|
||
>
|
||
> [Start 7-Day Free Trial]
|
||
|
||
---
|
||
|
||
### 6.2 Success Messages
|
||
|
||
**After adding keyword:**
|
||
> ✓ Added "bekicot" to your library
|
||
|
||
**After editing:**
|
||
> ✓ Keyword updated
|
||
|
||
**After deleting:**
|
||
> ✓ Keyword removed
|
||
|
||
**After upgrade:**
|
||
> 🎉 Welcome to Personal! Start adding your keywords.
|
||
|
||
---
|
||
|
||
### 6.3 Error Messages
|
||
|
||
**Duplicate keyword:**
|
||
> ⚠️ You already have "bekicot" for this emoji
|
||
|
||
**Network error:**
|
||
> ❌ Couldn't save. Check your connection and try again.
|
||
|
||
**Unauthorized:**
|
||
> 🔒 Please log in to add personal keywords
|
||
|
||
---
|
||
|
||
## 7. Mobile Considerations
|
||
|
||
### 7.1 Detail Page on Mobile
|
||
|
||
- Add keyword button: **sticky bottom bar** (always visible)
|
||
- Modal: **full-screen** on mobile (easier typing)
|
||
- Keyword list: **swipeable cards** (swipe left to delete)
|
||
|
||
### 7.2 Dashboard on Mobile
|
||
|
||
- Tabs: **horizontal scroll** or bottom nav
|
||
- Table: **card view** (stack columns vertically)
|
||
- Actions: **swipe gestures** (edit/delete)
|
||
|
||
---
|
||
|
||
## 8. Performance & Optimization
|
||
|
||
### 8.1 Caching Strategy
|
||
|
||
**Client-side:**
|
||
- Cache user keywords in localStorage/sessionStorage
|
||
- Only refetch when:
|
||
- User adds/edits/deletes
|
||
- Page reload after 5 minutes
|
||
|
||
**API-side:**
|
||
- ETag support (already implemented)
|
||
- Cache user keywords per user_id (Redis)
|
||
- Invalidate on mutation
|
||
|
||
### 8.2 Loading States
|
||
|
||
**Detail page:**
|
||
```
|
||
Loading user keywords...
|
||
└─ Skeleton: [▯▯▯▯] [▯▯▯] [▯▯▯▯▯]
|
||
```
|
||
|
||
**Dashboard:**
|
||
```
|
||
Loading your library...
|
||
└─ Table skeleton (5 rows)
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Analytics & Tracking
|
||
|
||
### Key Events to Track
|
||
|
||
**Discovery:**
|
||
- `emoji_viewed` (slug, user_tier)
|
||
- `public_search` (query, has_results)
|
||
- `search_miss` (query) → upgrade opportunity
|
||
|
||
**Personalization:**
|
||
- `keyword_added` (method: 'quick_add' | 'dashboard')
|
||
- `keyword_edited`
|
||
- `keyword_deleted`
|
||
- `keywords_exported`
|
||
|
||
**Conversion:**
|
||
- `upgrade_prompt_shown` (location: 'detail' | 'search' | 'dashboard')
|
||
- `upgrade_clicked` (location)
|
||
- `trial_started`
|
||
- `subscription_created` (plan: 'monthly' | 'annual' | 'lifetime')
|
||
|
||
**Usage:**
|
||
- `private_keyword_searched` (query, has_results)
|
||
- `extension_synced` (keyword_count)
|
||
|
||
---
|
||
|
||
## 10. Testing Checklist
|
||
|
||
### 10.1 User State Tests
|
||
|
||
- [ ] Visitor can search and view emoji details (no login)
|
||
- [ ] Visitor sees upgrade CTA (non-intrusive)
|
||
- [ ] Free user sees muted keyword section
|
||
- [ ] Free user can't add keywords without upgrade
|
||
- [ ] Personal user sees active keyword section
|
||
- [ ] Personal user can add keywords from detail page
|
||
- [ ] Personal user can edit/delete keywords inline
|
||
|
||
### 10.2 Flow Tests
|
||
|
||
- [ ] Quick add: add keyword → appears in list immediately
|
||
- [ ] Quick add: new keyword searchable instantly
|
||
- [ ] Dashboard: bulk add works correctly
|
||
- [ ] Dashboard: export downloads valid JSON
|
||
- [ ] Dashboard: import restores keywords
|
||
- [ ] Search: private keywords appear first in results
|
||
- [ ] Search: private keywords have "Your keyword" badge
|
||
|
||
### 10.3 Edge Cases
|
||
|
||
- [ ] Add duplicate keyword → shows error
|
||
- [ ] Delete last keyword for emoji → removes emoji from search
|
||
- [ ] Offline: queue mutations, sync when online
|
||
- [ ] Rate limit: show friendly error
|
||
- [ ] API error: show retry option
|
||
|
||
---
|
||
|
||
## 11. Implementation Priority
|
||
|
||
### Phase 1: Detail Page Quick Add (Week 1)
|
||
1. Add user keywords section to detail page
|
||
2. Build add keyword modal
|
||
3. Implement POST /keywords/quick endpoint
|
||
4. Add inline edit/delete
|
||
|
||
### Phase 2: Search Blending (Week 1)
|
||
1. Update search to blend private + public
|
||
2. Add "Your keyword" badge to results
|
||
3. Show private matches first
|
||
|
||
### Phase 3: Dashboard (Week 2)
|
||
1. Build My Keywords tab with table
|
||
2. Add bulk actions (import/export)
|
||
3. Implement search/filter
|
||
|
||
### Phase 4: Upgrade Flow (Week 2)
|
||
1. Build upgrade prompts (detail, search miss, dashboard)
|
||
2. Create pricing page
|
||
3. Integrate Stripe checkout
|
||
|
||
---
|
||
|
||
## 12. Open Questions & Future Enhancements
|
||
|
||
1. **Keyword suggestions:** Should we suggest keywords based on emoji name/category?
|
||
2. **Keyboard shortcuts:** Quick add keyword with hotkey (Ctrl+K)?
|
||
3. **Recently added:** Show "Recently added keywords" widget on dashboard?
|
||
4. **Keyword collections:** Group keywords by category/theme?
|
||
5. **Sharing:** Allow users to share their keyword libraries with friends?
|
||
|
||
---
|
||
|
||
**End of UX Brief**
|
||
|
||
This document defines the seamless user experience for Dewemoji's core value proposition: instant, contextual personalization. Implementation should prioritize the detail page quick-add flow (80% of usage) while building dashboard for power users (20%). |