@iankibetsh/vue-streamline 1.3.0 → 1.3.2

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/README.md CHANGED
@@ -1,91 +1,437 @@
1
- # vue-streamline
1
+ # Streamline Vue Plugin
2
2
 
3
- Vue library for streamlining laravel backend services with @iankibet/streamline composer package
3
+ A robust Vue 3 plugin designed for seamless integration with Streamline backend services. It delivers reactive state management, intelligent caching, and dynamic action invocation.
4
4
 
5
5
  ## Installation
6
6
 
7
- ```sh
7
+ ```bash
8
8
  npm install @iankibetsh/vue-streamline
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Setup
12
12
 
13
- In your main.js file, import the library and register it as a plugin:
13
+ ### 1. Register the Plugin
14
14
 
15
- ```js
16
- import {streamline} from '@iankibetsh/vue-streamline'
17
- ```
15
+ ```javascript
16
+ import { createApp } from 'vue';
17
+ import { streamline } from '@iankibetsh/vue-streamline';
18
+ import App from './App.vue';
19
+
20
+ const app = createApp(App);
21
+
22
+ // Define authentication headers
23
+ const streamlineHeaders = {
24
+ Authorization: `Bearer ${localStorage.getItem('access_token')}`
25
+ };
26
+
27
+ // Construct the Streamline endpoint URL
28
+ const streamlineUrl = `${import.meta.env.VITE_APP_API_URL}streamline`;
18
29
 
19
- ```js
20
- Vue.use(streamline, {
21
- streamlineHeaders: {
22
- // Add any headers you want to send with the request
23
- },
24
- streamlineUrl: 'http://localhost:8000/api/streamline' // The url to the streamline route
25
- })
30
+ app.use(streamline, {
31
+ streamlineHeaders,
32
+ streamlineUrl,
33
+ enableCache: true // Optional: enables local storage caching
34
+ });
35
+
36
+ app.mount('#app');
26
37
  ```
27
38
 
39
+ ### 2. Use in Components
40
+
41
+ #### Option A: Full Streamline Integration
28
42
 
29
- ## usage in vue component
43
+ ```vue
44
+ <script setup>
45
+ import { useStreamline } from '@iankibetsh/vue-streamline';
46
+
47
+ const { service, loading, props, getActionUrl } = useStreamline('users', 1);
48
+ </script>
49
+ ```
30
50
 
31
- ```js
32
- import { useStreamline } from '@iankibet/vue-streamline'
51
+ #### Option B: Standalone `getActionUrl`
33
52
 
53
+ If you only need to generate action URLs without the full reactive service:
34
54
 
35
- const {getActionUrl, service:tasksService, props} = useStreamline('tasks')
55
+ ```vue
56
+ <script setup>
57
+ import { getActionUrl } from '@iankibetsh/vue-streamline';
36
58
 
59
+ // Use directly in template or script
60
+ const downloadUrl = getActionUrl('reports:download', reportId, 'pdf');
61
+ </script>
62
+
63
+ <template>
64
+ <a :href="getActionUrl('users:export', userId, 'csv')">Export User</a>
65
+ <h3>Action URL: {{ getActionUrl('users:listUsers', 'admin', 'active') }}</h3>
66
+ </template>
37
67
  ```
38
68
 
39
- ## Calling an action/method in the service
69
+ ## API Reference
70
+
71
+ ### `useStreamline(stream, ...initialArgs)`
72
+
73
+ Primary composable for interfacing with Streamline services.
74
+
75
+ #### Parameters
76
+
77
+ - **`stream`** (`string`): Name of the target stream or service.
78
+ - **`...initialArgs`** (`any`): Optional arguments passed to the stream’s `onMounted` action.
40
79
 
41
- You can call an action/method in the service by calling the method on the service object
42
- ```js
43
- const res = await tasksService.getTasks()
80
+ #### Returns
44
81
 
45
- // or
82
+ An object with the following reactive properties and utilities:
46
83
 
47
- tasksService.getTasks().then(response => {
48
- console.log(response)
49
- })
84
+ | Property | Type | Description |
85
+ |------------------|-----------------------|-------------|
86
+ | `service` | Reactive Proxy | Proxy for invoking stream actions dynamically. |
87
+ | `loading` | `ref<boolean>` | Indicates ongoing operations. |
88
+ | `props` | Reactive Proxy | Holds properties fetched from the stream. |
89
+ | `getActionUrl` | `Function` | Generates URLs for specific actions. |
90
+ | `confirmAction` | `Function` | Displays confirmation dialogs before actions. |
91
+
92
+ ---
93
+
94
+ ### `getActionUrl(action, ...args)`
95
+
96
+ Standalone function for generating action URLs without reactive features.
97
+
98
+ #### Parameters
99
+
100
+ - **`action`** (`string`): Action name, optionally prefixed with stream name (e.g., `'stream:action'`).
101
+ - **`...args`** (`any`): Arguments to pass as URL parameters.
102
+
103
+ #### Returns
104
+
105
+ - **`string`**: Fully qualified URL for the specified action.
106
+
107
+ #### Usage
108
+
109
+ ```javascript
110
+ import { getActionUrl } from '@iankibetsh/vue-streamline';
111
+
112
+ // Simple action URL
113
+ const url = getActionUrl('download', fileId);
114
+
115
+ // Cross-stream action
116
+ const analyticsUrl = getActionUrl('analytics:track', eventName, userId);
117
+
118
+ // Multiple parameters
119
+ const reportUrl = getActionUrl('reports:generate', reportId, 'pdf', '2024');
50
120
  ```
51
- A swal popup asking the user to confirm the action will be shown before the action is called
52
121
 
53
- ## Pre Confirm Calling a action/method
122
+ ---
123
+
124
+ ## Features
125
+
126
+ ### 1. Dynamic Action Calling
54
127
 
55
- You can pre confirm calling an action/method in the service by calling the method on the service object
56
- ```js
57
- const res = await tasksService.confirm('Are you sure you want to delete this task?').deleteTask(1)
128
+ Invoke backend actions via the `service` proxy:
129
+
130
+ ```javascript
131
+ const { service, loading } = useStreamline('users');
132
+
133
+ // Standard CRUD operations
134
+ await service.fetchAll();
135
+ await service.create({ name: 'John', email: 'john@example.com' });
136
+ await service.update(1, { name: 'Jane' });
137
+ await service.delete(5);
138
+
139
+ // Custom actions with multiple arguments
140
+ await service.customAction(arg1, arg2, arg3);
58
141
  ```
59
142
 
143
+ ### 2. Reactive Properties
60
144
 
61
- ## Getting url to an action/method in the service
145
+ Properties are fetched automatically upon component mount or first access:
62
146
 
63
- You can get the url to an action/method in the service by calling the getActionUrl method on the service object passing the action name and any parameters
64
- ```js
65
- getActionUrl('getTasks', 'active')
66
- ```
147
+ ```javascript
148
+ const { props, loading } = useStreamline('dashboard', userId);
67
149
 
68
- ## Accessing class properties
69
- You can access the class properties defined in the service class using the props object returned by the useStreamline function
70
- ### In your script
71
- Access the props object to get the class properties, use computed to make it reactive
72
- ```js
73
- const title = computed(() => props.title)
150
+ // Properties trigger fetch on access if not yet loaded
151
+ console.log(props.statistics);
152
+ console.log(props.userInfo);
153
+ console.log(props.settings);
74
154
  ```
75
- Then use it in your template
76
- ```html
155
+
156
+ **Auto-fetch triggers:**
157
+ - Component `onMounted` (when `initialArgs` are provided)
158
+ - First property access on the `props` proxy
159
+
160
+ ### 3. Loading States
161
+
162
+ Monitor operation status reactively:
163
+
164
+ ```vue
77
165
  <template>
78
- <div>
79
- <h1>{{title}}</h1>
166
+ <div>
167
+ <div v-if="loading">Loading...</div>
168
+ <div v-else>
169
+ <button @click="service.fetchData()">Fetch Data</button>
80
170
  </div>
171
+ </div>
172
+ </template>
173
+
174
+ <script setup>
175
+ import { useStreamline } from '@iankibetsh/vue-streamline';
176
+
177
+ const { service, loading } = useStreamline('data');
178
+ </script>
179
+ ```
180
+
181
+ ### 4. Local Storage Caching
182
+
183
+ Enable persistent caching across sessions:
184
+
185
+ ```javascript
186
+ app.use(streamline, {
187
+ streamlineUrl: 'https://your-api.com/streamline',
188
+ enableCache: true
189
+ });
190
+ ```
191
+
192
+ **Behavior when enabled:**
193
+ - Data is stored in `localStorage` using a unique key (stream + arguments).
194
+ - Cached data loads instantly on mount.
195
+ - Cache updates after successful fetch operations.
196
+ - Keys are deterministic and scoped per stream and arguments.
197
+
198
+ ### 5. Manual Refresh
199
+
200
+ Force reload of stream properties:
201
+
202
+ ```javascript
203
+ const { service } = useStreamline('products', categoryId);
204
+
205
+ // Refresh properties
206
+ await service.refresh(); // or service.reload()
207
+ ```
208
+
209
+ ### 6. Confirmation Dialogs
210
+
211
+ Prompt users before destructive actions:
212
+
213
+ ```javascript
214
+ const { service } = useStreamline('users');
215
+
216
+ // Default confirmation message
217
+ await service.confirm().delete(userId);
218
+
219
+ // Custom message
220
+ await service.confirm('Are you sure you want to delete this user?').delete(userId);
221
+ ```
222
+
223
+ > Uses `shRepo.runPlainRequest` from the SH Framework for native confirmation dialogs.
224
+
225
+ ### 7. Action URLs
226
+
227
+ Generate fully qualified action URLs:
228
+
229
+ #### Using `getActionUrl` from `useStreamline`
230
+
231
+ ```javascript
232
+ const { getActionUrl } = useStreamline('reports');
233
+
234
+ const downloadUrl = getActionUrl('download', reportId, 'pdf');
235
+ // → https://your-api.com/streamline?action=download&stream=reports&params=reportId,pdf
236
+
237
+ // Cross-stream actions
238
+ const analyticsUrl = getActionUrl('analytics:trackEvent', eventName, eventData);
239
+ ```
240
+
241
+ #### Using Standalone `getActionUrl`
242
+
243
+ For scenarios where you only need URL generation without reactive state management:
244
+
245
+ ```vue
246
+ <script setup>
247
+ import { getActionUrl } from '@iankibetsh/vue-streamline';
248
+
249
+ // Generate URLs directly
250
+ const exportUrl = getActionUrl('users:export', userId, 'csv');
251
+ const reportUrl = getActionUrl('reports:generate', reportId, 'pdf');
252
+ </script>
253
+
254
+ <template>
255
+ <!-- Use in templates -->
256
+ <a :href="getActionUrl('users:export', userId, 'csv')" download>
257
+ Export User
258
+ </a>
259
+
260
+ <!-- Dynamic URLs -->
261
+ <div>
262
+ <h3>API Endpoint: {{ getActionUrl('users:listUsers', 'admin', 'active') }}</h3>
263
+ </div>
81
264
  </template>
82
265
  ```
83
- ### Directly in your template
84
- In your template just access the props object to get the class properties. Prefer using computed to make it reactive
85
- ```html
266
+
267
+ **Benefits of Standalone Import:**
268
+ - Lighter weight when you don't need reactive features
269
+ - Can be used in utility files or non-component contexts
270
+ - Still respects the global `streamlineUrl` configuration
271
+ - Supports all the same features (cross-stream notation, multiple parameters)
272
+
273
+ ### 8. Cross-Stream Actions
274
+
275
+ Execute actions on different streams using colon notation:
276
+
277
+ ```javascript
278
+ const { service } = useStreamline('users');
279
+
280
+ await service['analytics:trackEvent'](eventName, eventData);
281
+ ```
282
+
283
+ ## Advanced Usage
284
+
285
+ ### Immediate Property Access
286
+
287
+ Access properties before loading — fetch triggers automatically:
288
+
289
+ ```javascript
290
+ const { props } = useStreamline('dashboard', userId);
291
+
292
+ watchEffect(() => {
293
+ console.log(props.stats); // Triggers fetch if needed
294
+ });
295
+ ```
296
+
297
+ ### Error Handling
298
+
299
+ All action calls return promises:
300
+
301
+ ```javascript
302
+ const { service } = useStreamline('users');
303
+
304
+ try {
305
+ const result = await service.create(userData);
306
+ console.log('User created:', result);
307
+ } catch (error) {
308
+ console.error('Creation failed:', error);
309
+ }
310
+ ```
311
+
312
+ ### Form Data Integration
313
+
314
+ Pass structured data to actions:
315
+
316
+ ```javascript
317
+ const { service } = useStreamline('posts');
318
+
319
+ const formData = {
320
+ title: 'My Post',
321
+ content: 'Post content',
322
+ tags: ['vue', 'javascript']
323
+ };
324
+
325
+ await service.create(formData);
326
+ ```
327
+
328
+ ## Complete Example
329
+
330
+ ```vue
86
331
  <template>
87
- <div>
88
- <h1>{{props.title}}</h1>
332
+ <div class="user-management">
333
+ <div v-if="loading" class="loading">Loading...</div>
334
+
335
+ <div v-else>
336
+ <h1>Total Users: {{ props.totalUsers }}</h1>
337
+
338
+ <div class="users-list">
339
+ <div v-for="user in props.users" :key="user.id" class="user-card">
340
+ <h3>{{ user.name }}</h3>
341
+ <p>{{ user.email }}</p>
342
+
343
+ <button @click="editUser(user)">Edit</button>
344
+ <button @click="deleteUser(user.id)">Delete</button>
345
+
346
+ <a :href="getActionUrl('exportUser', user.id)" target="_blank" rel="noopener">
347
+ Export Profile
348
+ </a>
349
+ </div>
350
+ </div>
351
+
352
+ <button @click="refreshData">Refresh</button>
353
+ <button @click="addNewUser">Add User</button>
89
354
  </div>
355
+ </div>
90
356
  </template>
357
+
358
+ <script setup>
359
+ import { useStreamline } from '@iankibetsh/vue-streamline';
360
+
361
+ const { service, loading, props, getActionUrl } = useStreamline('users', 'active');
362
+
363
+ const editUser = async (user) => {
364
+ try {
365
+ const result = await service.update(user.id, {
366
+ name: user.name,
367
+ email: user.email
368
+ });
369
+ console.log('User updated:', result);
370
+ } catch (error) {
371
+ console.error('Update failed:', error);
372
+ }
373
+ };
374
+
375
+ const deleteUser = async (userId) => {
376
+ try {
377
+ await service.confirm('Delete this user permanently?').delete(userId);
378
+ await service.refresh();
379
+ } catch (error) {
380
+ console.error('Delete failed:', error);
381
+ }
382
+ };
383
+
384
+ const refreshData = () => service.refresh();
385
+
386
+ const addNewUser = async () => {
387
+ const newUser = { name: 'New User', email: 'newuser@example.com' };
388
+ await service.create(newUser);
389
+ await service.refresh();
390
+ };
391
+ </script>
392
+ ```
393
+
394
+ ## Best Practices
395
+
396
+ 1. **Use descriptive stream names** aligned with backend services.
397
+ 2. **Enable caching** for infrequently updated data.
398
+ 3. **Handle errors gracefully** in component logic.
399
+ 4. **Display loading states** for better user experience.
400
+ 5. **Use confirmation dialogs** for irreversible actions.
401
+ 6. **Leverage `getActionUrl`** for links and downloads.
402
+ 7. **Call `refresh()`** after mutations to synchronize UI.
403
+
404
+ ## `getActionUrl` Function Details
405
+
406
+ ```javascript
407
+ const getActionUrl = (action, ...args) => {
408
+ let targetStream = stream;
409
+ let targetAction = action;
410
+
411
+ if (action.includes(':')) {
412
+ [targetStream, targetAction] = action.split(':');
413
+ }
414
+
415
+ const payload = {
416
+ action: targetAction,
417
+ stream: targetStream,
418
+ params: args
419
+ };
420
+
421
+ return `${streamlineUrl}?${new URLSearchParams(payload).toString()}`;
422
+ };
91
423
  ```
424
+
425
+ **Key Capabilities:**
426
+ - Serializes arguments into query parameters.
427
+ - Supports cross-stream routing via `stream:action`.
428
+ - Produces ready-to-use, fully qualified URLs.
429
+
430
+ ## Dependencies
431
+
432
+ - **Vue 3**: `reactive`, `ref`, `inject`, `onMounted`
433
+ - **@iankibetsh/shframework**: `shApis`, `shRepo`
434
+
435
+ ## License
436
+
437
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iankibetsh/vue-streamline",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Vue library for streamlining laravel backend services with @iankibet/streamline composer package",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/App.vue CHANGED
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
2
  import useStreamline from './composables/useStreamline.js'
3
3
  import { onMounted, ref, toRefs } from 'vue'
4
+ import SimpleGetActionUrl from './SimpleGetActionUrl.vue'
4
5
 
5
6
  const {loading, service:paybillService, getActionUrl,props,confirmAction} = useStreamline('mpesa/paybill',true)
6
7
 
@@ -40,6 +41,7 @@ const refresh = ()=>{
40
41
  <h1 @click="refresh">Refresh</h1>
41
42
  {{ foundPaybill }}
42
43
  </div>
44
+ <simple-get-action-url/>
43
45
  </template>
44
46
 
45
47
  <style scoped>
@@ -0,0 +1,11 @@
1
+ <script setup>
2
+ import getActionUrl from './composables/getActionUrl.js'
3
+ </script>
4
+
5
+ <template>
6
+ <h3>Sample Action Url: {{ getActionUrl('users:listUsers','admin','active') }}</h3>
7
+ </template>
8
+
9
+ <style scoped>
10
+
11
+ </style>
@@ -0,0 +1,7 @@
1
+ import useStreamline from './useStreamline.js'
2
+
3
+
4
+ export default function getActionUrl(actionSlug, ...args) {
5
+ const { getActionUrl } = useStreamline('')
6
+ return getActionUrl(actionSlug, ...args)
7
+ }
package/src/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import useStreamline from './composables/useStreamline.js'
2
+ import getActionUrl from './composables/getActionUrl.js'
2
3
  import streamline from './plugins/streamline.js'
3
4
  export {
4
5
  useStreamline,
6
+ getActionUrl,
5
7
  streamline
6
8
  }
@@ -1,6 +1,6 @@
1
1
  const streamline = {
2
2
  install(app, options) {
3
- app.provide('streamlineUrl', options.streamlineUrl)
3
+ app.provide('streamlineUrl', options.streamlineUrl ?? '/api/streamline')
4
4
  // app.provide('streamlineHeaders', options.streamlineHeaders)
5
5
  app.provide('enableCache', options.enableCache)
6
6
  }