shared-features 0.0.7 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-INTEGRATION-GUIDE.md +315 -0
- package/README.md +207 -2
- package/dist/{admin-notifications-D9n9h-eY.cjs → admin-notifications-D1GgYCJW.cjs} +20 -20
- package/dist/{admin-notifications-D9n9h-eY.cjs.map → admin-notifications-D1GgYCJW.cjs.map} +1 -1
- package/dist/{admin-notifications-p1dy3zIP.js → admin-notifications-NI7I76uY.js} +13 -13
- package/dist/{admin-notifications-p1dy3zIP.js.map → admin-notifications-NI7I76uY.js.map} +1 -1
- package/dist/{broadcasts-3_WfQMNL.cjs → broadcasts-BMoTZIuX.cjs} +10 -10
- package/dist/{broadcasts-3_WfQMNL.cjs.map → broadcasts-BMoTZIuX.cjs.map} +1 -1
- package/dist/{broadcasts-DgZUzqMf.js → broadcasts-DnzZkCoy.js} +12 -12
- package/dist/{broadcasts-DgZUzqMf.js.map → broadcasts-DnzZkCoy.js.map} +1 -1
- package/dist/commonFeatures-Bdt0UZox.js +1255 -0
- package/dist/commonFeatures-Bdt0UZox.js.map +1 -0
- package/dist/commonFeatures-CiqxxOin.cjs +1276 -0
- package/dist/commonFeatures-CiqxxOin.cjs.map +1 -0
- package/dist/commonFeatures-Cr5g1E4M.cjs +200 -0
- package/dist/commonFeatures-Cr5g1E4M.cjs.map +1 -0
- package/dist/commonFeatures-HT-UO7HW.js +201 -0
- package/dist/commonFeatures-HT-UO7HW.js.map +1 -0
- package/dist/components/common/index.d.ts +53 -0
- package/dist/components/common/index.d.ts.map +1 -0
- package/dist/components/index.cjs +31 -23
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +21 -13
- package/dist/config/support.d.ts +10 -0
- package/dist/config/support.d.ts.map +1 -0
- package/dist/firebase/config.d.ts +34 -0
- package/dist/firebase/config.d.ts.map +1 -1
- package/dist/hooks/index.cjs +26 -13
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +24 -11
- package/dist/hooks/useCommonFeatures.d.ts +74 -0
- package/dist/hooks/useCommonFeatures.d.ts.map +1 -0
- package/dist/hooks/useFeatureFlags.d.ts +134 -0
- package/dist/hooks/useFeatureFlags.d.ts.map +1 -0
- package/dist/{AnnouncementModal-Bqy0pn3V.cjs → index-Dt5YjYnK.cjs} +209 -14
- package/dist/index-Dt5YjYnK.cjs.map +1 -0
- package/dist/{AnnouncementModal-sxH4K5gy.js → index-Dv34aG2I.js} +212 -17
- package/dist/index-Dv34aG2I.js.map +1 -0
- package/dist/index.cjs +125 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +173 -108
- package/dist/index.js.map +1 -1
- package/dist/notifications/index.js +14 -14
- package/dist/services/commonFeatures.d.ts +22 -0
- package/dist/services/commonFeatures.d.ts.map +1 -0
- package/dist/services/featureFlags.d.ts +71 -0
- package/dist/services/featureFlags.d.ts.map +1 -0
- package/dist/services/index.cjs +49 -17
- package/dist/services/index.cjs.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +75 -43
- package/dist/services/index.js.map +1 -1
- package/dist/types/commonFeatures.d.ts +194 -0
- package/dist/types/commonFeatures.d.ts.map +1 -0
- package/dist/types/featureFlags.d.ts +203 -0
- package/dist/types/featureFlags.d.ts.map +1 -0
- package/dist/types/index.cjs +15 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +15 -0
- package/dist/types/index.js.map +1 -1
- package/dist/useCommonFeatures-CgyDq6LZ.js +489 -0
- package/dist/useCommonFeatures-CgyDq6LZ.js.map +1 -0
- package/dist/useCommonFeatures-DnDlhmri.cjs +488 -0
- package/dist/useCommonFeatures-DnDlhmri.cjs.map +1 -0
- package/dist/useFeatureFlags-BRJSyH9M.js +368 -0
- package/dist/useFeatureFlags-BRJSyH9M.js.map +1 -0
- package/dist/useFeatureFlags-DXqBJ5Mh.cjs +367 -0
- package/dist/useFeatureFlags-DXqBJ5Mh.cjs.map +1 -0
- package/dist/{useNotificationEvents-D8DVxah1.js → useNotificationEvents-DAmR7FYF.js} +14 -14
- package/dist/{useNotificationEvents-D8DVxah1.js.map → useNotificationEvents-DAmR7FYF.js.map} +1 -1
- package/package.json +18 -7
- package/dist/AnnouncementModal-Bqy0pn3V.cjs.map +0 -1
- package/dist/AnnouncementModal-sxH4K5gy.js.map +0 -1
- package/dist/analytics-40-S_fHC.js +0 -440
- package/dist/analytics-40-S_fHC.js.map +0 -1
- package/dist/analytics-lEzOx2vl.cjs +0 -461
- package/dist/analytics-lEzOx2vl.cjs.map +0 -1
- package/dist/useBroadcasts-DzpCcbC8.js +0 -161
- package/dist/useBroadcasts-DzpCcbC8.js.map +0 -1
- package/dist/useBroadcasts-FP6ZrcY_.cjs +0 -160
- package/dist/useBroadcasts-FP6ZrcY_.cjs.map +0 -1
- package/dist/useCampaigns-BOZ9dDsG.cjs +0 -152
- package/dist/useCampaigns-BOZ9dDsG.cjs.map +0 -1
- package/dist/useCampaigns-D46b9zuf.js +0 -153
- package/dist/useCampaigns-D46b9zuf.js.map +0 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# AI Integration Guide - shared-features
|
|
2
|
+
|
|
3
|
+
Quick reference for AI development agents (Claude Code, Cursor, Copilot, etc.) to integrate shared-features into React + Capacitor projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn add shared-features
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Peer Dependencies
|
|
12
|
+
```bash
|
|
13
|
+
yarn add react react-dom firebase @radix-ui/themes zustand lucide-react
|
|
14
|
+
# Optional for mobile:
|
|
15
|
+
yarn add @capacitor/preferences
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Core Concepts
|
|
19
|
+
|
|
20
|
+
shared-features provides two main systems:
|
|
21
|
+
1. **Advertising Campaigns** - Cross-promotion of Zaions products
|
|
22
|
+
2. **Broadcasts/Notifications** - In-app announcements and alerts
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Add Environment Variables
|
|
27
|
+
|
|
28
|
+
```env
|
|
29
|
+
VITE_SHARED_FEATURES_API_KEY=
|
|
30
|
+
VITE_SHARED_FEATURES_AUTH_DOMAIN=
|
|
31
|
+
VITE_SHARED_FEATURES_PROJECT_ID=
|
|
32
|
+
VITE_SHARED_FEATURES_STORAGE_BUCKET=
|
|
33
|
+
VITE_SHARED_FEATURES_MESSAGING_SENDER_ID=
|
|
34
|
+
VITE_SHARED_FEATURES_APP_ID=
|
|
35
|
+
VITE_SHARED_FEATURES_MEASUREMENT_ID=
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Initialize
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// src/main.tsx
|
|
42
|
+
import { initSharedFeatures } from 'shared-features';
|
|
43
|
+
|
|
44
|
+
if (import.meta.env.VITE_SHARED_FEATURES_API_KEY) {
|
|
45
|
+
initSharedFeatures({
|
|
46
|
+
firebaseConfig: {
|
|
47
|
+
apiKey: import.meta.env.VITE_SHARED_FEATURES_API_KEY,
|
|
48
|
+
authDomain: import.meta.env.VITE_SHARED_FEATURES_AUTH_DOMAIN,
|
|
49
|
+
projectId: import.meta.env.VITE_SHARED_FEATURES_PROJECT_ID,
|
|
50
|
+
storageBucket: import.meta.env.VITE_SHARED_FEATURES_STORAGE_BUCKET,
|
|
51
|
+
messagingSenderId: import.meta.env.VITE_SHARED_FEATURES_MESSAGING_SENDER_ID,
|
|
52
|
+
appId: import.meta.env.VITE_SHARED_FEATURES_APP_ID,
|
|
53
|
+
measurementId: import.meta.env.VITE_SHARED_FEATURES_MEASUREMENT_ID,
|
|
54
|
+
},
|
|
55
|
+
projectId: 'your-project-id',
|
|
56
|
+
projectName: 'Your Project Name',
|
|
57
|
+
platform: 'web', // 'web' | 'android' | 'ios' | 'extension'
|
|
58
|
+
debug: import.meta.env.DEV,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Advertising Components
|
|
64
|
+
|
|
65
|
+
### AdPanel (Recommended)
|
|
66
|
+
Best for sidebars and feature areas.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { AdPanel } from 'shared-features';
|
|
70
|
+
|
|
71
|
+
// Small variant (sidebar)
|
|
72
|
+
<AdPanel variant="small" />
|
|
73
|
+
|
|
74
|
+
// Large variant (feature area)
|
|
75
|
+
<AdPanel variant="large" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### AdSlider
|
|
79
|
+
Auto-rotating carousel of ads.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { AdSlider } from 'shared-features';
|
|
83
|
+
|
|
84
|
+
<AdSlider
|
|
85
|
+
variant="small"
|
|
86
|
+
autoRotate={true}
|
|
87
|
+
interval={5000}
|
|
88
|
+
/>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### AdBanner
|
|
92
|
+
Horizontal banner for headers/footers.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { AdBanner } from 'shared-features';
|
|
96
|
+
|
|
97
|
+
<AdBanner />
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### AdModal (One-time Welcome)
|
|
101
|
+
Shows once per product, great for onboarding.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { useOneTimeAdModal } from 'shared-features';
|
|
105
|
+
|
|
106
|
+
function App() {
|
|
107
|
+
const { AdModalComponent } = useOneTimeAdModal();
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
{AdModalComponent}
|
|
112
|
+
{/* rest of app */}
|
|
113
|
+
</>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### AdUpdateModal (What's New)
|
|
119
|
+
Shows once per version update.
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { useUpdateAdModal } from 'shared-features';
|
|
123
|
+
|
|
124
|
+
function App() {
|
|
125
|
+
const { UpdateModalComponent } = useUpdateAdModal();
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
{UpdateModalComponent}
|
|
130
|
+
{/* rest of app */}
|
|
131
|
+
</>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Broadcasts/Notifications
|
|
137
|
+
|
|
138
|
+
### BroadcastBanner
|
|
139
|
+
Alert banner at top of page.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { BroadcastBanner } from 'shared-features';
|
|
143
|
+
|
|
144
|
+
<BroadcastBanner position="top" />
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### AnnouncementModal
|
|
148
|
+
Modal announcements.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { AnnouncementModal } from 'shared-features';
|
|
152
|
+
|
|
153
|
+
<AnnouncementModal />
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### useBroadcasts Hook
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { useBroadcasts } from 'shared-features';
|
|
160
|
+
|
|
161
|
+
function MyComponent() {
|
|
162
|
+
const { broadcasts, loading, markAsRead, dismissBroadcast } = useBroadcasts();
|
|
163
|
+
|
|
164
|
+
return broadcasts.map(broadcast => (
|
|
165
|
+
<div key={broadcast.id}>
|
|
166
|
+
<h3>{broadcast.title}</h3>
|
|
167
|
+
<p>{broadcast.message}</p>
|
|
168
|
+
<button onClick={() => dismissBroadcast(broadcast.id)}>Dismiss</button>
|
|
169
|
+
</div>
|
|
170
|
+
));
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Hooks Reference
|
|
175
|
+
|
|
176
|
+
| Hook | Description | Returns |
|
|
177
|
+
|------|-------------|---------|
|
|
178
|
+
| `useCampaigns()` | Get active ad campaigns | `{ campaigns, loading, error }` |
|
|
179
|
+
| `useOneTimeAdModal()` | One-time product modal | `{ AdModalComponent }` |
|
|
180
|
+
| `useUpdateAdModal()` | Version update modal | `{ UpdateModalComponent }` |
|
|
181
|
+
| `useBroadcasts()` | Get active broadcasts | `{ broadcasts, loading, markAsRead, dismiss }` |
|
|
182
|
+
|
|
183
|
+
## Services
|
|
184
|
+
|
|
185
|
+
### Analytics Tracking
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { trackImpression, trackClick, trackDismissal } from 'shared-features';
|
|
189
|
+
|
|
190
|
+
// Track ad impression
|
|
191
|
+
await trackImpression(campaignId, productId);
|
|
192
|
+
|
|
193
|
+
// Track ad click
|
|
194
|
+
await trackClick(campaignId, productId);
|
|
195
|
+
|
|
196
|
+
// Track dismissal
|
|
197
|
+
await trackDismissal(campaignId);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Campaigns Service
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { getCampaigns, getCampaignById } from 'shared-features';
|
|
204
|
+
|
|
205
|
+
// Get all active campaigns for current project
|
|
206
|
+
const campaigns = await getCampaigns();
|
|
207
|
+
|
|
208
|
+
// Get specific campaign
|
|
209
|
+
const campaign = await getCampaignById('campaign-id');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Types
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import type {
|
|
216
|
+
SharedFeaturesConfig,
|
|
217
|
+
Campaign,
|
|
218
|
+
Product,
|
|
219
|
+
Broadcast,
|
|
220
|
+
NotificationTemplate,
|
|
221
|
+
AdVariant,
|
|
222
|
+
Platform,
|
|
223
|
+
} from 'shared-features';
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Configuration Interface
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface SharedFeaturesConfig {
|
|
230
|
+
firebaseConfig: {
|
|
231
|
+
apiKey: string;
|
|
232
|
+
authDomain: string;
|
|
233
|
+
projectId: string;
|
|
234
|
+
storageBucket: string;
|
|
235
|
+
messagingSenderId: string;
|
|
236
|
+
appId: string;
|
|
237
|
+
measurementId: string;
|
|
238
|
+
};
|
|
239
|
+
projectId: string; // Your project identifier
|
|
240
|
+
projectName: string; // Display name
|
|
241
|
+
platform: 'web' | 'android' | 'ios' | 'extension';
|
|
242
|
+
debug?: boolean; // Enable debug logging
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Common Patterns
|
|
247
|
+
|
|
248
|
+
### Conditional Rendering (Check Initialization)
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { isInitialized } from 'shared-features';
|
|
252
|
+
|
|
253
|
+
function AdSection() {
|
|
254
|
+
if (!isInitialized()) {
|
|
255
|
+
return null; // Don't render until initialized
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return <AdPanel variant="small" />;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Footer Ad Integration
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
import { AdBanner, isInitialized } from 'shared-features';
|
|
266
|
+
|
|
267
|
+
function Footer() {
|
|
268
|
+
return (
|
|
269
|
+
<footer>
|
|
270
|
+
{isInitialized() && <AdBanner />}
|
|
271
|
+
<p>© 2026 Your Company</p>
|
|
272
|
+
</footer>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Sidebar with Ads
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { AdPanel, isInitialized } from 'shared-features';
|
|
281
|
+
|
|
282
|
+
function Sidebar() {
|
|
283
|
+
return (
|
|
284
|
+
<aside>
|
|
285
|
+
<nav>{/* Navigation */}</nav>
|
|
286
|
+
{isInitialized() && <AdPanel variant="small" />}
|
|
287
|
+
</aside>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Firestore Collections (Reference)
|
|
293
|
+
|
|
294
|
+
| Collection | Purpose |
|
|
295
|
+
|------------|---------|
|
|
296
|
+
| `zaions_campaigns` | Ad campaigns data |
|
|
297
|
+
| `zaions_products` | Products being promoted |
|
|
298
|
+
| `zaions_impressions` | Ad impression analytics |
|
|
299
|
+
| `zaions_broadcasts` | Broadcast messages |
|
|
300
|
+
| `zaions_broadcast_events` | Broadcast analytics |
|
|
301
|
+
| `zaions_notification_templates` | Reusable templates |
|
|
302
|
+
|
|
303
|
+
## Troubleshooting
|
|
304
|
+
|
|
305
|
+
| Issue | Solution |
|
|
306
|
+
|-------|----------|
|
|
307
|
+
| Components not rendering | Check `isInitialized()` returns true |
|
|
308
|
+
| Firebase errors | Verify all 7 config fields are provided |
|
|
309
|
+
| No ads showing | Check projectId matches admin panel config |
|
|
310
|
+
| Styles missing | Ensure `@radix-ui/themes` is imported |
|
|
311
|
+
|
|
312
|
+
## Links
|
|
313
|
+
|
|
314
|
+
- [Full Documentation](./README.md)
|
|
315
|
+
- [Admin Panel](https://aoneahsan.com/admin)
|
package/README.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# shared-features
|
|
2
2
|
|
|
3
|
+
- **[AI Integration Guide](./AI-INTEGRATION-GUIDE.md)** - Quick reference for AI development agents (Claude, Cursor, Copilot)
|
|
4
|
+
|
|
3
5
|
Centralized common features for Zaions projects. Manage ads, notifications, contacts, and more from a single admin panel at [aoneahsan.com](https://aoneahsan.com).
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Three Core Systems
|
|
8
10
|
|
|
9
|
-
This package provides **
|
|
11
|
+
This package provides **three systems** for cross-project feature management:
|
|
10
12
|
|
|
11
13
|
| System | Purpose | Firestore Collections | Admin Location |
|
|
12
14
|
|--------|---------|----------------------|----------------|
|
|
15
|
+
| **Feature Flags** | Version management, feature toggles | `zaions_feature_flags` | `/admin/settings` |
|
|
13
16
|
| **Advertising Campaigns** | Promote Zaions products across apps | `zaions_campaigns`, `zaions_products`, `zaions_impressions` | `/admin/campaigns` |
|
|
14
17
|
| **Broadcasts/Notifications** | In-app notifications, announcements, alerts | `zaions_broadcasts`, `zaions_broadcast_events`, `zaions_notification_templates` | `/admin/notifications` |
|
|
15
18
|
|
|
@@ -96,10 +99,154 @@ if (import.meta.env.VITE_SHARED_FEATURES_API_KEY) {
|
|
|
96
99
|
projectName: 'Your Project Name', // e.g., 'ZTools', '2FA Studio'
|
|
97
100
|
platform: 'web', // or 'android', 'ios', 'extension'
|
|
98
101
|
debug: import.meta.env.DEV, // optional
|
|
102
|
+
// Optional: Lock to specific feature versions
|
|
103
|
+
featureVersions: {
|
|
104
|
+
campaigns: 1,
|
|
105
|
+
broadcasts: 1,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
# FEATURE FLAGS SYSTEM
|
|
114
|
+
|
|
115
|
+
Manage feature availability, versioning, and breaking changes across all consumer projects.
|
|
116
|
+
|
|
117
|
+
## Why Feature Flags?
|
|
118
|
+
|
|
119
|
+
- **Version Control**: Roll out breaking changes gradually
|
|
120
|
+
- **Feature Toggles**: Enable/disable features globally
|
|
121
|
+
- **Deprecation Warnings**: Warn consumers about outdated versions
|
|
122
|
+
- **Maintenance Mode**: Show maintenance messages across all apps
|
|
123
|
+
- **Platform/Project Targeting**: Enable features for specific platforms or projects
|
|
124
|
+
|
|
125
|
+
## Firestore Collection
|
|
126
|
+
|
|
127
|
+
| Collection | Purpose | Access |
|
|
128
|
+
|------------|---------|--------|
|
|
129
|
+
| `zaions_feature_flags` | Global feature configuration (singleton) | Public read, Admin write |
|
|
130
|
+
|
|
131
|
+
## Checking Feature Availability
|
|
132
|
+
|
|
133
|
+
### Check Overall Status
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { useFeatureFlags } from 'shared-features';
|
|
137
|
+
|
|
138
|
+
function App() {
|
|
139
|
+
const { status, loading, isFeatureAvailable, hasDeprecatedFeatures } = useFeatureFlags();
|
|
140
|
+
|
|
141
|
+
if (loading) return <Spinner />;
|
|
142
|
+
|
|
143
|
+
// Handle maintenance mode
|
|
144
|
+
if (status?.maintenanceMode) {
|
|
145
|
+
return <MaintenancePage message={status.maintenanceMessage} />;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Warn about deprecated features
|
|
149
|
+
if (hasDeprecatedFeatures) {
|
|
150
|
+
console.warn('Some features are deprecated:', status?.deprecatedFeatures);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check specific feature
|
|
154
|
+
if (isFeatureAvailable('contactInfo')) {
|
|
155
|
+
return <ContactInfo />;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return <App />;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Check Single Feature
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { useFeature } from 'shared-features';
|
|
166
|
+
|
|
167
|
+
function ContactSection() {
|
|
168
|
+
const { available, loading, deprecated, deprecationWarning } = useFeature('contactInfo');
|
|
169
|
+
|
|
170
|
+
if (loading) return <Spinner />;
|
|
171
|
+
if (!available) return <LegacyContactInfo />;
|
|
172
|
+
|
|
173
|
+
if (deprecated) {
|
|
174
|
+
console.warn(deprecationWarning);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return <NewContactInfo />;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Conditional Rendering with Feature Gates
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { useFeatureGate } from 'shared-features';
|
|
185
|
+
|
|
186
|
+
function MyComponent() {
|
|
187
|
+
const { shouldRender, FallbackOrChildren } = useFeatureGate('socialLinks');
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<FallbackOrChildren fallback={<OldSocialLinks />}>
|
|
191
|
+
<NewSocialLinks />
|
|
192
|
+
</FallbackOrChildren>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Real-time Updates
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { useFeatureFlagsSubscription } from 'shared-features';
|
|
201
|
+
|
|
202
|
+
function App() {
|
|
203
|
+
useFeatureFlagsSubscription((status) => {
|
|
204
|
+
if (status?.maintenanceMode) {
|
|
205
|
+
showMaintenanceBanner(status.maintenanceMessage);
|
|
206
|
+
}
|
|
99
207
|
});
|
|
208
|
+
|
|
209
|
+
return <YourApp />;
|
|
100
210
|
}
|
|
101
211
|
```
|
|
102
212
|
|
|
213
|
+
## Available Features
|
|
214
|
+
|
|
215
|
+
| Feature ID | Description | Status |
|
|
216
|
+
|------------|-------------|--------|
|
|
217
|
+
| `campaigns` | Advertising campaigns | ✅ Enabled |
|
|
218
|
+
| `broadcasts` | Broadcast notifications | ✅ Enabled |
|
|
219
|
+
| `contactInfo` | Contact information | ⏳ Coming Soon |
|
|
220
|
+
| `developerInfo` | Developer information | ⏳ Coming Soon |
|
|
221
|
+
| `socialLinks` | Social media links | ⏳ Coming Soon |
|
|
222
|
+
| `paymentOptions` | Payment methods | ⏳ Coming Soon |
|
|
223
|
+
| `addressInfo` | Address information | ⏳ Coming Soon |
|
|
224
|
+
| `services` | Professional services | ⏳ Coming Soon |
|
|
225
|
+
| `skills` | Skills display | ⏳ Coming Soon |
|
|
226
|
+
| `testimonials` | Client testimonials | ⏳ Coming Soon |
|
|
227
|
+
| `projects` | Portfolio projects | ⏳ Coming Soon |
|
|
228
|
+
|
|
229
|
+
## Feature Versioning
|
|
230
|
+
|
|
231
|
+
When breaking changes are introduced:
|
|
232
|
+
|
|
233
|
+
1. Admin bumps feature version in `zaions_feature_flags`
|
|
234
|
+
2. Consumers using old version get deprecation warnings
|
|
235
|
+
3. Once `minVersion` is bumped, old consumers get `upgradeRequired: true`
|
|
236
|
+
4. Consumers update their code and bump `featureVersions` in config
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Consumer specifies supported versions
|
|
240
|
+
initSharedFeatures({
|
|
241
|
+
// ... other config
|
|
242
|
+
featureVersions: {
|
|
243
|
+
campaigns: 1, // Using v1 API
|
|
244
|
+
broadcasts: 1, // Using v1 API
|
|
245
|
+
contactInfo: 2, // Updated to v2 API
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
103
250
|
---
|
|
104
251
|
|
|
105
252
|
# ADVERTISING CAMPAIGNS SYSTEM
|
|
@@ -343,9 +490,67 @@ interface SharedFeaturesConfig {
|
|
|
343
490
|
projectName: string; // e.g., 'ZTools'
|
|
344
491
|
platform: 'web' | 'android' | 'ios' | 'extension';
|
|
345
492
|
debug?: boolean;
|
|
493
|
+
featureVersions?: ConsumerFeatureVersions; // Optional version preferences
|
|
346
494
|
}
|
|
347
495
|
```
|
|
348
496
|
|
|
497
|
+
## Feature Flag Hooks
|
|
498
|
+
|
|
499
|
+
### `useFeatureFlags(options?)`
|
|
500
|
+
|
|
501
|
+
Check overall feature flags status.
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
interface UseFeatureFlagsOptions {
|
|
505
|
+
autoRefresh?: boolean; // Auto-refresh flags periodically
|
|
506
|
+
refreshInterval?: number; // Refresh interval in ms (default: 5 min)
|
|
507
|
+
autoFetch?: boolean; // Fetch on mount (default: true)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
interface UseFeatureFlagsResult {
|
|
511
|
+
status: SharedFeaturesStatus | null;
|
|
512
|
+
loading: boolean;
|
|
513
|
+
error: string | null;
|
|
514
|
+
refetch: () => Promise<void>;
|
|
515
|
+
isFeatureAvailable: (featureId: FeatureId) => boolean;
|
|
516
|
+
getFeatureAvailability: (featureId: FeatureId) => FeatureAvailability | null;
|
|
517
|
+
hasDeprecatedFeatures: boolean;
|
|
518
|
+
hasUpgradeRequired: boolean;
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### `useFeature(featureId)`
|
|
523
|
+
|
|
524
|
+
Check a single feature's availability.
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
const {
|
|
528
|
+
available, // Feature can be used
|
|
529
|
+
loading, // Check in progress
|
|
530
|
+
enabled, // Feature is enabled (but might need upgrade)
|
|
531
|
+
deprecated, // Using deprecated version
|
|
532
|
+
upgradeRequired, // Must upgrade to use
|
|
533
|
+
deprecationWarning, // Warning message
|
|
534
|
+
unavailableReason, // Why unavailable
|
|
535
|
+
} = useFeature('contactInfo');
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### `useFeatureGate(featureId)`
|
|
539
|
+
|
|
540
|
+
Conditional rendering helper.
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
const { shouldRender, loading, deprecated, FallbackOrChildren } = useFeatureGate('socialLinks');
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### `useFeatureFlagsSubscription(callback)`
|
|
547
|
+
|
|
548
|
+
Real-time feature flag updates.
|
|
549
|
+
|
|
550
|
+
### `useSharedFeaturesOperational()`
|
|
551
|
+
|
|
552
|
+
Quick check if shared-features is operational.
|
|
553
|
+
|
|
349
554
|
## Advertising Hooks
|
|
350
555
|
|
|
351
556
|
### `useCampaigns(options)`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const firestore = require("firebase/firestore");
|
|
3
|
-
const
|
|
3
|
+
const commonFeatures = require("./commonFeatures-CiqxxOin.cjs");
|
|
4
4
|
const COLLECTION_BROADCASTS = "zaions_broadcasts";
|
|
5
5
|
const COLLECTION_TEMPLATES = "zaions_notification_templates";
|
|
6
6
|
const COLLECTION_BROADCAST_EVENTS = "zaions_broadcast_events";
|
|
@@ -51,7 +51,7 @@ function docToTemplate(docId, data) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
async function createBroadcast(input, adminUserId) {
|
|
54
|
-
const db =
|
|
54
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
55
55
|
const broadcastData = {
|
|
56
56
|
...input,
|
|
57
57
|
status: "draft",
|
|
@@ -70,7 +70,7 @@ async function createBroadcast(input, adminUserId) {
|
|
|
70
70
|
return docRef.id;
|
|
71
71
|
}
|
|
72
72
|
async function updateBroadcast(input, adminUserId) {
|
|
73
|
-
const db =
|
|
73
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
74
74
|
const updateData = {
|
|
75
75
|
...input,
|
|
76
76
|
updatedBy: adminUserId,
|
|
@@ -86,11 +86,11 @@ async function updateBroadcast(input, adminUserId) {
|
|
|
86
86
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_BROADCASTS, input.id), updateData);
|
|
87
87
|
}
|
|
88
88
|
async function deleteBroadcast(broadcastId) {
|
|
89
|
-
const db =
|
|
89
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
90
90
|
await firestore.deleteDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId));
|
|
91
91
|
}
|
|
92
92
|
async function getAllBroadcasts() {
|
|
93
|
-
const db =
|
|
93
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
94
94
|
const q = firestore.query(
|
|
95
95
|
firestore.collection(db, COLLECTION_BROADCASTS),
|
|
96
96
|
firestore.orderBy("createdAt", "desc")
|
|
@@ -99,7 +99,7 @@ async function getAllBroadcasts() {
|
|
|
99
99
|
return snapshot.docs.map((d) => docToBroadcast(d.id, d.data()));
|
|
100
100
|
}
|
|
101
101
|
async function getBroadcastsByStatus(status) {
|
|
102
|
-
const db =
|
|
102
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
103
103
|
const q = firestore.query(
|
|
104
104
|
firestore.collection(db, COLLECTION_BROADCASTS),
|
|
105
105
|
firestore.where("status", "==", status),
|
|
@@ -109,13 +109,13 @@ async function getBroadcastsByStatus(status) {
|
|
|
109
109
|
return snapshot.docs.map((d) => docToBroadcast(d.id, d.data()));
|
|
110
110
|
}
|
|
111
111
|
async function getBroadcastById(broadcastId) {
|
|
112
|
-
const db =
|
|
112
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
113
113
|
const docSnap = await firestore.getDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId));
|
|
114
114
|
if (!docSnap.exists()) return null;
|
|
115
115
|
return docToBroadcast(docSnap.id, docSnap.data());
|
|
116
116
|
}
|
|
117
117
|
async function publishBroadcast(broadcastId, adminUserId) {
|
|
118
|
-
const db =
|
|
118
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
119
119
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId), {
|
|
120
120
|
status: "active",
|
|
121
121
|
updatedBy: adminUserId,
|
|
@@ -123,7 +123,7 @@ async function publishBroadcast(broadcastId, adminUserId) {
|
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
async function scheduleBroadcast(broadcastId, scheduledDate, adminUserId) {
|
|
126
|
-
const db =
|
|
126
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
127
127
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId), {
|
|
128
128
|
status: "scheduled",
|
|
129
129
|
startDate: firestore.Timestamp.fromDate(scheduledDate),
|
|
@@ -132,7 +132,7 @@ async function scheduleBroadcast(broadcastId, scheduledDate, adminUserId) {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
async function pauseBroadcast(broadcastId, adminUserId) {
|
|
135
|
-
const db =
|
|
135
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
136
136
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId), {
|
|
137
137
|
status: "draft",
|
|
138
138
|
// Move back to draft
|
|
@@ -141,7 +141,7 @@ async function pauseBroadcast(broadcastId, adminUserId) {
|
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
async function endBroadcast(broadcastId, adminUserId) {
|
|
144
|
-
const db =
|
|
144
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
145
145
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_BROADCASTS, broadcastId), {
|
|
146
146
|
status: "ended",
|
|
147
147
|
endDate: firestore.serverTimestamp(),
|
|
@@ -150,7 +150,7 @@ async function endBroadcast(broadcastId, adminUserId) {
|
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
async function createTemplate(input) {
|
|
153
|
-
const db =
|
|
153
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
154
154
|
const templateData = {
|
|
155
155
|
...input,
|
|
156
156
|
createdAt: firestore.serverTimestamp(),
|
|
@@ -163,18 +163,18 @@ async function createTemplate(input) {
|
|
|
163
163
|
return docRef.id;
|
|
164
164
|
}
|
|
165
165
|
async function updateTemplate(templateId, input) {
|
|
166
|
-
const db =
|
|
166
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
167
167
|
await firestore.updateDoc(firestore.doc(db, COLLECTION_TEMPLATES, templateId), {
|
|
168
168
|
...input,
|
|
169
169
|
updatedAt: firestore.serverTimestamp()
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
172
|
async function deleteTemplate(templateId) {
|
|
173
|
-
const db =
|
|
173
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
174
174
|
await firestore.deleteDoc(firestore.doc(db, COLLECTION_TEMPLATES, templateId));
|
|
175
175
|
}
|
|
176
176
|
async function getAllTemplates() {
|
|
177
|
-
const db =
|
|
177
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
178
178
|
const q = firestore.query(
|
|
179
179
|
firestore.collection(db, COLLECTION_TEMPLATES),
|
|
180
180
|
firestore.orderBy("name", "asc")
|
|
@@ -183,13 +183,13 @@ async function getAllTemplates() {
|
|
|
183
183
|
return snapshot.docs.map((d) => docToTemplate(d.id, d.data()));
|
|
184
184
|
}
|
|
185
185
|
async function getTemplateById(templateId) {
|
|
186
|
-
const db =
|
|
186
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
187
187
|
const docSnap = await firestore.getDoc(firestore.doc(db, COLLECTION_TEMPLATES, templateId));
|
|
188
188
|
if (!docSnap.exists()) return null;
|
|
189
189
|
return docToTemplate(docSnap.id, docSnap.data());
|
|
190
190
|
}
|
|
191
191
|
async function getFirestoreTemplateByEventType(eventType) {
|
|
192
|
-
const db =
|
|
192
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
193
193
|
const q = firestore.query(
|
|
194
194
|
firestore.collection(db, COLLECTION_TEMPLATES),
|
|
195
195
|
firestore.where("eventType", "==", eventType)
|
|
@@ -201,7 +201,7 @@ async function getFirestoreTemplateByEventType(eventType) {
|
|
|
201
201
|
return docToTemplate(docSnap.id, docSnap.data());
|
|
202
202
|
}
|
|
203
203
|
async function getBroadcastAnalytics(broadcastId) {
|
|
204
|
-
const db =
|
|
204
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
205
205
|
const broadcast = await getBroadcastById(broadcastId);
|
|
206
206
|
if (!broadcast) return null;
|
|
207
207
|
const eventsQuery = firestore.query(
|
|
@@ -264,7 +264,7 @@ async function getBroadcastAnalytics(broadcastId) {
|
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
266
|
async function getOverallAnalytics(startDate, endDate) {
|
|
267
|
-
const db =
|
|
267
|
+
const db = commonFeatures.getSharedFeaturesDb();
|
|
268
268
|
const eventsQuery = firestore.query(
|
|
269
269
|
firestore.collection(db, COLLECTION_BROADCAST_EVENTS),
|
|
270
270
|
firestore.where("timestamp", ">=", firestore.Timestamp.fromDate(startDate)),
|
|
@@ -359,4 +359,4 @@ exports.publishBroadcast = publishBroadcast;
|
|
|
359
359
|
exports.scheduleBroadcast = scheduleBroadcast;
|
|
360
360
|
exports.updateBroadcast = updateBroadcast;
|
|
361
361
|
exports.updateTemplate = updateTemplate;
|
|
362
|
-
//# sourceMappingURL=admin-notifications-
|
|
362
|
+
//# sourceMappingURL=admin-notifications-D1GgYCJW.cjs.map
|