Update pricing UX, billing flows, and API rules
This commit is contained in:
873
dewemoji-ux-flow-brief.md
Executable file
873
dewemoji-ux-flow-brief.md
Executable file
@@ -0,0 +1,873 @@
|
||||
# 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%).
|
||||
Reference in New Issue
Block a user