@vc-shell/vc-app-skill 2.0.10-pr247.f47cc74 → 2.0.11

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.
Files changed (21) hide show
  1. package/package.json +1 -1
  2. package/runtime/VERSION +1 -1
  3. package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
  4. package/runtime/knowledge/docs/core/blade-navigation/table-query-state/useTableQueryState.docs.md +95 -0
  5. package/runtime/knowledge/docs/core/composables/useAppInsights/useAppInsights.docs.md +1 -1
  6. package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +34 -9
  7. package/runtime/knowledge/docs/core/composables/usePlatformLocaleSync/usePlatformLocaleSync.docs.md +34 -0
  8. package/runtime/knowledge/docs/core/composables/useResponsive/useResponsive.docs.md +1 -1
  9. package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +1 -1
  10. package/runtime/knowledge/docs/core/composables/useUser/useUser.docs.md +1 -1
  11. package/runtime/knowledge/docs/core/notifications/composables/useBladeNotifications.docs.md +184 -0
  12. package/runtime/knowledge/docs/core/notifications/composables/useBroadcastFilter.docs.md +117 -0
  13. package/runtime/knowledge/docs/core/notifications/composables/useNotificationContext.docs.md +150 -0
  14. package/runtime/knowledge/docs/core/notifications/composables/useNotificationStore.docs.md +113 -0
  15. package/runtime/knowledge/docs/core/notifications/notifications.docs.md +1 -1
  16. package/runtime/knowledge/docs/modules/assets/assets-details.docs.md +123 -0
  17. package/runtime/knowledge/docs/modules/assets-manager/assets-manager.docs.md +1 -1
  18. package/runtime/knowledge/docs/shell/dashboard/draggable-dashboard/dashboard-widget-skeleton.docs.md +33 -0
  19. package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +67 -10
  20. package/runtime/knowledge/docs/ui/composables/useDataTablePagination.docs.md +44 -23
  21. package/runtime/knowledge/docs/ui/composables/useDataTableSort.docs.md +16 -8
@@ -29,7 +29,6 @@ import { useDataTablePagination } from "@vc-shell/framework";
29
29
  const pagination = useDataTablePagination({
30
30
  pageSize: 20,
31
31
  totalCount: computed(() => searchResult.value?.totalCount ?? 0),
32
- onPageChange: ({ skip }) => loadItems({ ...searchQuery.value, skip }),
33
32
  });
34
33
  ```
35
34
 
@@ -37,6 +36,8 @@ const pagination = useDataTablePagination({
37
36
  <VcDataTable :items="items" :total-count="pagination.totalCount" :pagination="pagination" @pagination-click="pagination.goToPage" />
38
37
  ```
39
38
 
39
+ > **Note:** Let `@pagination-click` only update the page (`goToPage`) and load from one watcher that reads `pagination.skip` together with sort and search — see the recipe below. `onPageChange` (load on each page change) still works for simple tables, but do not combine it with that watcher or the page loads twice.
40
+
40
41
  ## API
41
42
 
42
43
  ### Parameters (Options)
@@ -49,15 +50,16 @@ const pagination = useDataTablePagination({
49
50
 
50
51
  ### Returns (`reactive()` object)
51
52
 
52
- | Property | Type | Description |
53
- | ------------- | ------------------------ | --------------------------------------------- |
54
- | `currentPage` | `number` | Current 1-based page number (writable) |
55
- | `pages` | `number` (readonly) | Total number of pages |
56
- | `skip` | `number` (readonly) | Current skip offset for API calls |
57
- | `pageSize` | `number` (readonly) | Resolved page size |
58
- | `totalCount` | `number` (readonly) | Resolved total item count |
59
- | `goToPage` | `(page: number) => void` | Navigate to page; fires `onPageChange` |
60
- | `reset` | `() => void` | Reset to page 1; does NOT fire `onPageChange` |
53
+ | Property | Type | Description |
54
+ | ------------- | ------------------------ | ----------------------------------------------------------------------- |
55
+ | `currentPage` | `number` | Current 1-based page number (writable) |
56
+ | `pages` | `number` (readonly) | Total number of pages |
57
+ | `skip` | `number` (readonly) | Current skip offset for API calls |
58
+ | `pageSize` | `number` (readonly) | Resolved page size |
59
+ | `totalCount` | `number` (readonly) | Resolved total item count |
60
+ | `goToPage` | `(page: number) => void` | Navigate to page; fires `onPageChange` |
61
+ | `setPage` | `(page: number) => void` | Seed the page WITHOUT firing `onPageChange` — use for pull-restore seed |
62
+ | `reset` | `() => void` | Reset to page 1; does NOT fire `onPageChange` |
61
63
 
62
64
  All properties are auto-unwrapped by `reactive()` — no `.value` access needed in script or template.
63
65
 
@@ -65,30 +67,43 @@ All properties are auto-unwrapped by `reactive()` — no `.value` access needed
65
67
 
66
68
  ```vue
67
69
  <script setup lang="ts">
68
- import { ref, computed } from "vue";
69
- import { useDataTablePagination, useDataTableSort } from "@vc-shell/framework";
70
+ import { ref, computed, watch } from "vue";
71
+ import { useDataTablePagination, useDataTableSort, useTableQueryState } from "@vc-shell/framework";
70
72
 
71
73
  // Data layer
72
74
  const searchResult = ref<{ results: Item[]; totalCount: number }>();
73
- const searchQuery = ref({ take: 20, skip: 0 });
74
75
 
75
- async function loadItems(query?: Partial<typeof searchQuery.value>) {
76
- searchQuery.value = { ...searchQuery.value, ...query };
77
- searchResult.value = await api.search(searchQuery.value);
76
+ async function loadItems(query: { sort?: string; keyword?: string; skip?: number }) {
77
+ searchResult.value = await api.search({ take: 20, ...query });
78
78
  }
79
79
 
80
- // Pagination state
80
+ // State only — no self-loading callback.
81
81
  const pagination = useDataTablePagination({
82
82
  pageSize: 20,
83
83
  totalCount: computed(() => searchResult.value?.totalCount ?? 0),
84
- onPageChange: ({ skip }) => loadItems({ skip }),
85
84
  });
86
-
87
- // Sort state
88
85
  const { sortField, sortOrder, sortExpression } = useDataTableSort({
89
86
  initialField: "createdDate",
90
87
  initialDirection: "DESC",
91
88
  });
89
+ const searchValue = ref<string>();
90
+
91
+ // Restore from the URL, then load once. setPage seeds the page without loading.
92
+ const restored = useTableQueryState("product-list").read();
93
+ if (restored.sort) {
94
+ const [field, dir] = restored.sort.split(":");
95
+ sortField.value = field;
96
+ sortOrder.value = dir === "DESC" ? -1 : 1;
97
+ }
98
+ if (restored.search) searchValue.value = restored.search;
99
+ if (restored.page) pagination.setPage(restored.page);
100
+
101
+ // One loader reading sort + search + page.
102
+ watch(
103
+ () => ({ sort: sortExpression.value, keyword: searchValue.value || undefined, skip: pagination.skip }),
104
+ (query) => loadItems(query),
105
+ { immediate: true },
106
+ );
92
107
  </script>
93
108
 
94
109
  <template>
@@ -97,6 +112,12 @@ const { sortField, sortOrder, sortExpression } = useDataTableSort({
97
112
  :total-count="pagination.totalCount"
98
113
  :pagination="pagination"
99
114
  @pagination-click="pagination.goToPage"
115
+ @search="
116
+ (kw) => {
117
+ searchValue = kw;
118
+ pagination.setPage(1);
119
+ }
120
+ "
100
121
  v-model:sort-field="sortField"
101
122
  v-model:sort-order="sortOrder"
102
123
  >
@@ -130,7 +151,6 @@ export function useOffers() {
130
151
  const pagination = useDataTablePagination({
131
152
  pageSize: 20,
132
153
  totalCount: computed(() => searchResult.value?.totalCount ?? 0),
133
- onPageChange: ({ skip }) => loadOffers({ ...searchQuery.value, skip }),
134
154
  });
135
155
 
136
156
  return {
@@ -141,7 +161,7 @@ export function useOffers() {
141
161
  }
142
162
  ```
143
163
 
144
- Blade then simply binds:
164
+ The blade owns the single loader watch (reading `pagination.skip` with sort/search) and binds:
145
165
 
146
166
  ```vue
147
167
  <VcDataTable :total-count="pagination.totalCount" :pagination="pagination" @pagination-click="pagination.goToPage" />
@@ -150,7 +170,8 @@ Blade then simply binds:
150
170
  ## Details
151
171
 
152
172
  - **`reactive()` return**: The composable wraps its return in `reactive()`, so all `Ref`/`ComputedRef` properties are auto-unwrapped. Access with `pagination.pages` (not `pagination.pages.value`). This allows the object to be passed directly as `:pagination` prop to VcDataTable without intermediate conversion.
153
- - **Event callback, not load**: `onPageChange` is an event notification (like VueUse's `useOffsetPagination`). The composable does not know about data fetching it just notifies.
173
+ - **Event callback, not load**: `onPageChange` is an event notification (like VueUse's `useOffsetPagination`). The composable does not fetch data. With a single loader watcher you usually omit it and read `pagination.skip` from the watcher instead.
174
+ - **`setPage` vs `goToPage`**: `goToPage(n)` updates the page and fires `onPageChange`; `setPage(n)` updates it without the callback. Use `setPage` to seed the page from a URL restore so the seed itself does not load.
154
175
  - **No auto-clamp**: When `totalCount` decreases (e.g. after deletion), `currentPage` is NOT automatically clamped. Call `reset()` or `goToPage(1)` explicitly after mutations.
155
176
  - **reset() is silent**: `reset()` sets `currentPage = 1` but does NOT fire `onPageChange`. This prevents accidental double-fetches when the consumer resets pagination during a reload.
156
177
  - **Pure without callback**: Omit `onPageChange` and the composable works as pure state — useful for unit tests or when the consumer prefers to watch properties reactively.
@@ -84,14 +84,16 @@ const { sortField, sortOrder, sortExpression, resetSort } = useDataTableSort({
84
84
 
85
85
  const items = ref([]);
86
86
  const loading = ref(false);
87
+ const currentPage = ref(1);
88
+ const PAGE_SIZE = 20;
87
89
 
88
- async function loadItems() {
90
+ async function loadItems(query: { sort?: string; skip?: number }) {
89
91
  loading.value = true;
90
92
  try {
91
93
  const response = await api.searchProducts({
92
- sort: sortExpression.value, // e.g. "createdDate:DESC" or undefined
93
- skip: 0,
94
- take: 20,
94
+ sort: query.sort, // e.g. "createdDate:DESC" or undefined
95
+ skip: query.skip ?? 0,
96
+ take: PAGE_SIZE,
95
97
  });
96
98
  items.value = response.results;
97
99
  } finally {
@@ -99,8 +101,13 @@ async function loadItems() {
99
101
  }
100
102
  }
101
103
 
102
- // Reload whenever sort changes; { immediate: true } loads data on mount
103
- watch(sortExpression, () => loadItems(), { immediate: true });
104
+ // Load from one watcher that reads sort together with search and page, so changing
105
+ // several at once is a single request. { immediate: true } also does the first load.
106
+ watch(
107
+ () => ({ sort: sortExpression.value, skip: (currentPage.value - 1) * PAGE_SIZE }),
108
+ (query) => loadItems(query),
109
+ { immediate: true },
110
+ );
104
111
  </script>
105
112
 
106
113
  <template>
@@ -134,12 +141,13 @@ watch(sortExpression, () => loadItems(), { immediate: true });
134
141
  - **Sort expression format**: `sortExpression` returns `"field:DIRECTION"` when both `sortField` and a non-zero `sortOrder` are set, otherwise `undefined`. This format is directly compatible with VirtoCommerce Platform search endpoints.
135
142
  - **Reset behavior**: `resetSort()` restores `sortField` and `sortOrder` to the values passed at construction time. If no initial options were provided, both are cleared.
136
143
  - **Numeric order convention**: `VcDataTable` uses PrimeVue's numeric sort order convention (`1`/`-1`/`0`). This composable encapsulates the mapping so call sites never need to handle the numeric values directly.
137
- - **Stateless regarding data**: The composable only manages the sort field and direction. Combine it with your own data-fetching logic using `watch(sortExpression, ...)`.
144
+ - **Stateless regarding data**: The composable only manages the sort field and direction. Read `sortExpression` from the same loader watcher that reads search and page, instead of giving sort its own `watch(sortExpression, load)`. See VcDataTable → URL query persistence.
138
145
 
139
146
  ## Tips
140
147
 
141
148
  - Always mark columns as `sortable` in your `<VcColumn>` definitions for the sort indicator icon to appear.
142
- - Use `{ immediate: true }` on the `watch(sortExpression, ...)` call to trigger the initial data load with the correct sort on mount.
149
+ - Use `{ immediate: true }` on the combined loader watcher to trigger the initial (or URL-restored) data load with the correct sort on mount.
150
+ - On reload, seed `sortField`/`sortOrder` from `useTableQueryState(stateKey).read().sort` (`"field:DIR"`) before the loader watcher, so restore costs a single request.
143
151
  - Call `resetSort()` alongside any "clear all filters" action to keep sort state consistent with the rest of the filter UI.
144
152
 
145
153
  ## Related