Skip to content

Commit 36c8e9e

Browse files
docs: Comprehensive README with feature guides + UX enhancements
Documentation: - Complete feature documentation with all 15 data sources - Free vs Paid comparison table - Step-by-step user guides for Builder and Templates - Custom function examples and advanced usage - Keyboard shortcuts documentation UX Enhancements: - Keyboard shortcuts: Ctrl/Cmd+K (search), Ctrl/Cmd+B (builder), Ctrl/Cmd+T (templates) - Escape key closes preview modal - Improved error messages with context and actionable guidance - Detailed status messages for all error codes (404, 429, 500, quota, etc.) Error Messaging Improvements: - ❌ Clear error icons and actionable next steps - ⚠️ Warning indicators for limits and quotas - 🔒 Premium feature lock messages with upgrade links - ⏳ Rate limit guidance with retry suggestions - 🔧 Server error messages with transparency
1 parent a49536d commit 36c8e9e

3 files changed

Lines changed: 259 additions & 11 deletions

File tree

README.md

Lines changed: 186 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,191 @@
1-
# DataSetIQ Excel Add-in (`datasetiq-excel-addin`)
1+
# DataSetIQ Excel Add-in
22

3-
Excel add-in using Office.js with custom functions and a React taskpane.
3+
**Professional Excel add-in for economic and financial data from 15+ global sources.**
44

5-
## Features
6-
- Custom functions: `DSIQ` (spill with headers, newest-first), `DSIQ_LATEST`, `DSIQ_VALUE` (on-or-before), `DSIQ_YOY`, `DSIQ_META`.
7-
- SharedRuntime + CustomFunctionsRuntime 1.3; API key stored in `OfficeRuntime.storage` (10MB cap).
8-
- Deterministic async resolution; single retry with exponential backoff for 429/5xx using `Retry-After` when provided.
9-
- Sidebar React app to connect API key, view entitlements (`/api/public/sheets/me`), search series, and manage disconnect.
10-
- Fallback: if `OfficeRuntime` is unavailable, functions resolve with `Please open DataSetIQ sidebar to connect.` rather than throwing.
11-
- Date normalization to UTC `YYYY-MM-DD`; frequency/start normalization.
5+
## 📊 Features
6+
7+
### Custom Functions
8+
- **`DSIQ(seriesId, [freq], [start])`** – Full time-series array with headers (newest-first)
9+
- **`DSIQ_LATEST(seriesId)`** – Most recent value only
10+
- **`DSIQ_VALUE(seriesId, date)`** – Value on or before specified date
11+
- **`DSIQ_YOY(seriesId)`** – Year-over-year growth rate
12+
- **`DSIQ_META(seriesId, field)`** – Metadata (title, units, frequency, etc.)
13+
14+
### Premium Features 🔓
15+
16+
**Formula Builder Wizard** (🔧 Builder tab)
17+
- Step-by-step formula creation with guided inputs
18+
- Supports all DSIQ function types
19+
- Preview before inserting into cells
20+
21+
**Templates System** (📁 Templates tab)
22+
- Scan current workbook for DSIQ formulas
23+
- Save formula collections as reusable templates
24+
- Load templates into new workbooks instantly
25+
- Manage saved templates (view, load, delete)
26+
27+
**Multi-Series Insert** (Search tab)
28+
- Select multiple series with checkboxes
29+
- Bulk insert with single click
30+
- Saves time on repetitive data pulls
31+
32+
**Enhanced Search** (🔍 Search tab)
33+
- Search across 15 data providers
34+
- Browse by source (FRED, BLS, IMF, OECD, etc.)
35+
- Favorites and Recent tracking
36+
- Live data preview with metadata
37+
- On-demand ingestion for metadata-only datasets
38+
39+
### Data Sources (15 Providers)
40+
- **FRED** – Federal Reserve Economic Data
41+
- **BLS** – Bureau of Labor Statistics
42+
- **BEA** – Bureau of Economic Analysis
43+
- **Census** – US Census Bureau
44+
- **EIA** – Energy Information Administration
45+
- **IMF** – International Monetary Fund
46+
- **OECD** – Organisation for Economic Co-operation
47+
- **World Bank** – Global development data
48+
- **ECB** – European Central Bank
49+
- **Eurostat** – European statistics
50+
- **BOE** – Bank of England
51+
- **ONS** – UK Office for National Statistics
52+
- **StatCan** – Statistics Canada
53+
- **RBA** – Reserve Bank of Australia
54+
- **BOJ** – Bank of Japan
55+
56+
## 🆓 Free vs 💎 Paid Plans
57+
58+
| Feature | Free (No API Key) | Paid (Valid API Key) |
59+
|---------|------------------|----------------------|
60+
| **Custom Functions** | ✅ All functions | ✅ All functions |
61+
| **Observation Limit** | 100 most recent | 1,000 most recent |
62+
| **Search & Insert** | ✅ Basic search | ✅ Enhanced search |
63+
| **Data Preview** | ✅ Available | ✅ Available |
64+
| **Formula Builder** | ❌ Locked | ✅ Unlocked |
65+
| **Templates** | ❌ Locked | ✅ Unlocked |
66+
| **Multi-Insert** | ❌ Not available | ✅ Checkboxes visible |
67+
| **Favorites/Recent** | ✅ Available | ✅ Available |
68+
| **Browse by Source** | ✅ Available | ✅ Available |
69+
70+
**Upgrade Message**: When data is truncated at 100 observations, users see:
71+
> ⚠️ Data limited to 100 most recent observations. Upgrade to a paid plan at datasetiq.com/pricing for up to 1,000 observations per series.
72+
73+
## 🚀 Getting Started
74+
75+
### Installation
76+
1. Download the add-in from Microsoft AppSource or sideload via `manifest.xml`
77+
2. Open Excel → Insert → My Add-ins → DataSetIQ
78+
3. Taskpane opens on the right side
79+
80+
### Connecting Your Account
81+
1. Visit [datasetiq.com/dashboard/api-keys](https://datasetiq.com/dashboard/api-keys)
82+
2. Create an API key (free or paid plan)
83+
3. In Excel taskpane, enter your API key and click "Save"
84+
4. Status shows: ✅ Connected - Premium features unlocked
85+
86+
### Using Custom Functions
87+
88+
**Basic Usage:**
89+
```excel
90+
=DSIQ("FRED-GDP")
91+
// Returns full GDP time-series with Date/Value headers
92+
93+
=DSIQ_LATEST("BLS-CPI")
94+
// Returns latest CPI value
95+
96+
=DSIQ_VALUE("IMF-NGDP", "2023-12-31")
97+
// Returns GDP value on or before Dec 31, 2023
98+
99+
=DSIQ_YOY("FRED-UNRATE")
100+
// Returns year-over-year change in unemployment rate
101+
```
102+
103+
**Advanced Options:**
104+
```excel
105+
=DSIQ("FRED-GDP", "quarterly", "2020-01-01")
106+
// GDP data, quarterly frequency, from 2020 onwards
107+
108+
=DSIQ_META("FRED-GDP", "title")
109+
// Returns metadata field (title, units, frequency, updated, source)
110+
```
111+
112+
## 📖 User Guides
113+
114+
### Formula Builder Wizard
115+
116+
**Step 1: Choose Function**
117+
- Select from DSIQ, DSIQ_LATEST, DSIQ_VALUE, or DSIQ_YOY
118+
- Each function has different parameters
119+
120+
**Step 2: Enter Series ID**
121+
- Enter the series identifier (e.g., "FRED-GDP")
122+
- Use Search tab to find series IDs
123+
124+
**Step 3: Configure Options** (DSIQ and DSIQ_VALUE only)
125+
- **Frequency**: Optional (e.g., "quarterly", "monthly")
126+
- **Start Date**: Optional (e.g., "2020-01-01")
127+
128+
**Step 4: Insert**
129+
- Click "Insert Formula" to place in active cell
130+
- Formula appears in cell and updates automatically
131+
132+
### Templates Guide
133+
134+
**Creating a Template:**
135+
1. Build your workbook with DSIQ formulas
136+
2. Click **🔧 Builder** tab → **Templates** sub-tab
137+
3. Click "🔍 Scan Current Sheet"
138+
4. Review found formulas (count shown)
139+
5. Enter template name (e.g., "Q4 Report")
140+
6. Click "💾 Save Template"
141+
142+
**Loading a Template:**
143+
1. Open new workbook or sheet
144+
2. Navigate to Templates tab
145+
3. Find saved template in list
146+
4. Click "📥 Load" button
147+
5. All formulas inserted at original cell positions
148+
149+
**Managing Templates:**
150+
- **View**: See all saved templates with formula count
151+
- **Load**: Insert template into active sheet
152+
- **Delete**: Remove template (🗑️ button)
153+
154+
### Multi-Series Insert
155+
156+
**Selecting Series:**
157+
1. Search for series in Search tab
158+
2. Check boxes appear next to results (paid users only)
159+
3. Select multiple series by clicking checkboxes
160+
4. Counter updates: "Insert X Series" button appears
161+
162+
**Bulk Insert:**
163+
1. Select active cell where you want first series
164+
2. Click "Insert X Series" button
165+
3. Series inserted vertically (one per row)
166+
4. Uses DSIQ_LATEST function by default
167+
168+
## 🛠 Technical Details
169+
170+
### Architecture
171+
- **SharedRuntime** + **CustomFunctionsRuntime 1.3** for performance
172+
- API key stored in `OfficeRuntime.storage` (10MB capacity, persists across sessions)
173+
- LocalStorage fallback for browser testing
174+
- Deterministic async resolution for reliable cell updates
175+
- Single retry with exponential backoff for 429/5xx errors
176+
- Respects `Retry-After` headers from API
177+
178+
### Error Handling
179+
- **No API Key**: "Please open DataSetIQ sidebar to connect."
180+
- **Invalid Key**: "Invalid API Key. Reconnect at datasetiq.com/dashboard/api-keys"
181+
- **Rate Limited**: "Rate limited. Please retry shortly."
182+
- **Free Limit**: "Free plan limit reached. Upgrade at datasetiq.com/pricing"
183+
- **Server Error**: "Server unavailable. Please retry."
184+
185+
### Date Normalization
186+
- All dates normalized to UTC `YYYY-MM-DD` format
187+
- Handles MM/DD/YYYY, DD/MM/YYYY, ISO 8601, timestamps
188+
- Frequency and start date parameters also normalized
12189

13190
## Scripts
14191
- `npm run start` – Vite dev server for taskpane.

src/shared/api.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,37 @@ export const PREMIUM_FEATURES = {
4040
};
4141

4242
export function getUpgradeMessage(feature: string): string {
43-
return `🔒 ${feature} is a Premium feature. Upgrade at datasetiq.com/pricing to unlock.`;
43+
return `🔒 ${feature} is a Premium feature. Get your API key at datasetiq.com/dashboard/api-keys to unlock all premium features.`;
44+
}
45+
46+
export function getDetailedErrorMessage(code: ErrorCode | undefined, status: number, context?: string): string {
47+
const contextStr = context ? ` [${context}]` : '';
48+
49+
switch (code) {
50+
case 'NO_KEY':
51+
return `❌ API key required${contextStr}. Open the DataSetIQ sidebar and connect your account to access data.`;
52+
case 'INVALID_KEY':
53+
return `❌ Invalid API key${contextStr}. Please verify your key at datasetiq.com/dashboard/api-keys and reconnect.`;
54+
case 'REVOKED_KEY':
55+
return `❌ API key has been revoked${contextStr}. Generate a new key at datasetiq.com/dashboard/api-keys.`;
56+
case 'FREE_LIMIT':
57+
return `⚠️ Free plan data limit reached${contextStr}. Upgrade to a paid plan at datasetiq.com/pricing for extended access.`;
58+
case 'QUOTA_EXCEEDED':
59+
return `⚠️ Daily quota exceeded${contextStr}. Your quota resets at midnight UTC. Upgrade at datasetiq.com/pricing for higher limits.`;
60+
case 'PLAN_REQUIRED':
61+
return `🔒 This feature requires a paid plan${contextStr}. Upgrade at datasetiq.com/pricing to unlock.`;
62+
default:
63+
if (status === 429) {
64+
return `⏳ Rate limit reached${contextStr}. Please wait a moment before retrying.`;
65+
}
66+
if (status >= 500) {
67+
return `🔧 Server temporarily unavailable${contextStr}. Our team has been notified. Please try again in a few minutes.`;
68+
}
69+
if (status === 404) {
70+
return `❓ Series not found${contextStr}. Please verify the series ID is correct.`;
71+
}
72+
return `⚠️ Unable to fetch data${contextStr}. Please try again or contact support if the issue persists.`;
73+
}
4474
}
4575

4676
export interface SearchResult {

src/taskpane/App.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,48 @@ const App: React.FC = () => {
4343

4444
useEffect(() => {
4545
bootstrap();
46-
}, []);
46+
47+
// Keyboard shortcuts
48+
const handleKeyPress = (e: KeyboardEvent) => {
49+
// Ctrl+K or Cmd+K - Focus search
50+
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
51+
e.preventDefault();
52+
setActiveTab('search');
53+
setTimeout(() => {
54+
document.getElementById('search-input')?.focus();
55+
}, 100);
56+
}
57+
58+
// Ctrl+B or Cmd+B - Open Builder
59+
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
60+
e.preventDefault();
61+
if (isPaid) {
62+
setActiveTab('builder');
63+
} else {
64+
setMessage('🔒 Formula Builder is a Premium feature. Upgrade at datasetiq.com/pricing');
65+
}
66+
}
67+
68+
// Ctrl+T or Cmd+T - Open Templates
69+
if ((e.ctrlKey || e.metaKey) && e.key === 't') {
70+
e.preventDefault();
71+
if (isPaid) {
72+
setActiveTab('templates');
73+
} else {
74+
setMessage('🔒 Templates is a Premium feature. Upgrade at datasetiq.com/pricing');
75+
}
76+
}
77+
78+
// Escape - Close preview modal
79+
if (e.key === 'Escape' && previewSeries) {
80+
setPreviewSeries(null);
81+
setPreviewData(null);
82+
}
83+
};
84+
85+
window.addEventListener('keydown', handleKeyPress);
86+
return () => window.removeEventListener('keydown', handleKeyPress);
87+
}, [isPaid, previewSeries]);
4788

4889
async function bootstrap() {
4990
const { key, supported } = await getStoredApiKey();

0 commit comments

Comments
 (0)