@vc-shell/create-vc-app 2.0.10-pr242.bfb451e → 2.0.10-pr243.88977c0

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.
@@ -1,42 +1,43 @@
1
- import { ref, type Ref } from "vue";
2
- import { useAsync, useLoading } from "@vc-shell/framework";
3
-
4
- export interface <%- ModuleNamePascalCase %>ListQuery {
5
- keyword?: string;
6
- sort?: string;
7
- skip?: number;
8
- take?: number;
9
- }
10
-
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- export default function use<%- ModuleNamePascalCase %>List() {
13
- const data: Ref<Record<string, any>[]> = ref([]);
14
- const totalCount = ref(0);
15
-
16
- const { loading: itemsLoading, action: getItems } = useAsync<<%- ModuleNamePascalCase %>ListQuery>(async (query) => {
17
- // TODO: Replace with real API call
18
- // const result = await apiClient.search({
19
- // keyword: query?.keyword,
20
- // sort: query?.sort,
21
- // skip: query?.skip ?? 0,
22
- // take: query?.take ?? 20,
23
- // });
24
- // data.value = result.results ?? [];
25
- // totalCount.value = result.totalCount ?? 0;
26
- });
27
-
28
- const { loading: deleteLoading, action: removeItems } = useAsync(async (ids?: string[]) => {
29
- // TODO: Replace with real API call
30
- // await Promise.all((ids ?? []).map((id) => apiClient.delete(id)));
31
- });
32
-
33
- const loading = useLoading(itemsLoading, deleteLoading);
34
-
35
- return {
36
- data,
37
- loading,
38
- totalCount,
39
- getItems,
40
- removeItems,
41
- };
42
- }
1
+ import { ref, type Ref } from "vue";
2
+ import { useAsync, useLoading } from "@vc-shell/framework";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ export default function use<%- ModuleNamePascalCase %>List() {
6
+ const data: Ref<Record<string, any>[]> = ref([]);
7
+ const totalCount = ref(0);
8
+ const currentPage = ref(1);
9
+ const searchQuery = ref("");
10
+
11
+ const { loading: itemsLoading, action: fetchItems } = useAsync(async () => {
12
+ // TODO: Replace with real API call
13
+ // const result = await apiClient.search({
14
+ // keyword: searchQuery.value,
15
+ // skip: (currentPage.value - 1) * 20,
16
+ // take: 20,
17
+ // });
18
+ // data.value = result.results ?? [];
19
+ // totalCount.value = result.totalCount ?? 0;
20
+ });
21
+
22
+ const { loading: deleteLoading, action: removeItems } = useAsync(async (ids?: string[]) => {
23
+ // TODO: Replace with real API call
24
+ // await Promise.all((ids ?? []).map((id) => apiClient.delete(id)));
25
+ // await fetchItems();
26
+ });
27
+
28
+ const loading = useLoading(itemsLoading, deleteLoading);
29
+
30
+ async function getItems() {
31
+ await fetchItems();
32
+ }
33
+
34
+ return {
35
+ data,
36
+ loading,
37
+ totalCount,
38
+ currentPage,
39
+ searchQuery,
40
+ getItems,
41
+ removeItems,
42
+ };
43
+ }
@@ -6,28 +6,26 @@
6
6
  width="50%"
7
7
  >
8
8
  <VcDataTable
9
- v-model:search-value="searchValue"
10
- v-model:sort-field="sortField"
11
- v-model:sort-order="sortOrder"
12
9
  :items="data"
13
10
  :total-count="totalCount"
14
- :pagination="{ currentPage, pages }"
15
- :searchable="true"
11
+ :current-page="currentPage"
12
+ :search-value="searchQuery"
16
13
  :state-key="'<%- ModuleNameScreamingSnake %>'"
17
- @row-click="onRowClick"
18
- @pagination-click="onPaginationClick"
14
+ @search:change="(val: string) => { searchQuery = val; getItems(); }"
15
+ @item-click="openDetails"
16
+ @pagination-click="(page: number) => { currentPage = page; getItems(); }"
19
17
  >
20
18
  <!-- Add your columns here -->
21
- <VcColumn id="name" :title="$t('<%- ModuleNameScreamingSnake %>.PAGES.LIST.COLUMNS.NAME')" sortable />
22
- <VcColumn id="createdDate" :title="$t('<%- ModuleNameScreamingSnake %>.PAGES.LIST.COLUMNS.CREATED_DATE')" type="datetime" sortable />
19
+ <VcColumn id="name" :header="$t('<%- ModuleNameScreamingSnake %>.PAGES.LIST.COLUMNS.NAME')" sortable />
20
+ <VcColumn id="createdDate" :header="$t('<%- ModuleNameScreamingSnake %>.PAGES.LIST.COLUMNS.CREATED_DATE')" type="datetime" sortable />
23
21
  </VcDataTable>
24
22
  </VcBlade>
25
23
  </template>
26
24
 
27
25
  <script setup lang="ts">
28
- import { useBlade, useDataTableSort, useTableQueryState, useFunctions, type IBladeToolbar } from "@vc-shell/framework";
26
+ import { useBlade, type IBladeToolbar } from "@vc-shell/framework";
29
27
  import { VcBlade, VcDataTable, VcColumn } from "@vc-shell/framework/ui";
30
- import { computed, ref, watch } from "vue";
28
+ import { ref, onMounted } from "vue";
31
29
  import use<%- ModuleNamePascalCase %>List from "../composables/useList";
32
30
  import { useI18n } from "vue-i18n";
33
31
 
@@ -44,57 +42,22 @@ defineBlade({
44
42
 
45
43
  const { t } = useI18n({ useScope: "global" });
46
44
  const { openBlade, exposeToChildren } = useBlade();
47
- const { debounce } = useFunctions();
48
45
 
49
- const PAGE_SIZE = 20;
50
-
51
- const { sortField, sortOrder, sortExpression } = useDataTableSort({
52
- initialField: "createdDate",
53
- initialDirection: "DESC",
54
- });
55
-
56
- const { data, loading, totalCount, getItems } = use<%- ModuleNamePascalCase %>List();
57
-
58
- const searchValue = ref<string>();
59
- const currentPage = ref(1);
60
- const pages = computed(() => Math.ceil(totalCount.value / PAGE_SIZE) || 0);
61
-
62
- // Restore sort/search/page from the URL, then load once below.
63
- const restored = useTableQueryState("<%- ModuleNameScreamingSnake %>").read();
64
- if (restored.sort) {
65
- const [field, direction] = restored.sort.split(":");
66
- sortField.value = field;
67
- sortOrder.value = direction === "DESC" ? -1 : 1;
68
- }
69
- if (restored.search) searchValue.value = restored.search;
70
- if (restored.page) currentPage.value = restored.page;
71
-
72
- function load() {
73
- return getItems({
74
- sort: sortExpression.value,
75
- keyword: searchValue.value || undefined,
76
- skip: (currentPage.value - 1) * PAGE_SIZE,
77
- take: PAGE_SIZE,
78
- });
79
- }
80
-
81
- // One loader for sort/search/page. Debounced so typing doesn't fetch per keystroke.
82
- load();
83
- watch(searchValue, () => (currentPage.value = 1));
84
- watch([sortExpression, searchValue, currentPage], debounce(load, 300));
85
-
86
- const onPaginationClick = (page: number) => {
87
- currentPage.value = page;
88
- };
89
-
90
- const reload = () => load();
46
+ const {
47
+ data,
48
+ loading,
49
+ totalCount,
50
+ currentPage,
51
+ searchQuery,
52
+ getItems,
53
+ } = use<%- ModuleNamePascalCase %>List();
91
54
 
92
55
  const bladeToolbar = ref<IBladeToolbar[]>([
93
56
  {
94
57
  id: "refresh",
95
58
  title: t("<%- ModuleNameScreamingSnake %>.PAGES.LIST.TOOLBAR.REFRESH"),
96
59
  icon: "lucide-refresh-cw",
97
- clickHandler: () => reload(),
60
+ clickHandler: () => getItems(),
98
61
  },
99
62
  {
100
63
  id: "add",
@@ -109,14 +72,15 @@ function openDetails(item?: { id?: string }) {
109
72
  name: "<%- ModuleNamePascalCase %>Details",
110
73
  param: item?.id,
111
74
  async onClose() {
112
- await reload();
75
+ await getItems();
113
76
  },
114
77
  });
115
78
  }
116
79
 
117
- function onRowClick(event: { data: { id?: string } }) {
118
- openDetails(event.data);
119
- }
80
+ onMounted(async () => {
81
+ await getItems();
82
+ })
83
+
120
84
 
121
- exposeToChildren({ reload });
85
+ exposeToChildren({ reload: getItems });
122
86
  </script>
@@ -7,8 +7,6 @@
7
7
  <!-- Blade contents -->
8
8
  <VcDataTable
9
9
  v-model:search-value="searchValue"
10
- v-model:sort-field="sortField"
11
- v-model:sort-order="sortOrder"
12
10
  v-model:active-item-id="selectedItemId"
13
11
  v-model:selection="selectedItems"
14
12
  :loading="loading"
@@ -34,6 +32,7 @@
34
32
  :total-label="$t('SAMPLE_APP.PAGES.LIST.TABLE.TOTALS')"
35
33
  :total-count="totalCount"
36
34
  state-key="SAMPLE_APP"
35
+ @search="onSearchList"
37
36
  @row-click="onItemClick"
38
37
  @pagination-click="onPaginationClick"
39
38
  >
@@ -74,8 +73,8 @@
74
73
  </template>
75
74
 
76
75
  <script lang="ts" setup>
77
- import { computed, ref, watch } from "vue";
78
- import { IBladeToolbar, useBlade, usePopup, useDataTableSort, useTableQueryState, useFunctions } from "@vc-shell/framework";
76
+ import { computed, ref, onMounted, watch } from "vue";
77
+ import { IBladeToolbar, useBlade, usePopup, useTableSort, useFunctions } from "@vc-shell/framework";
79
78
  import type { TableAction } from "@vc-shell/framework";
80
79
  import { VcColumn, VcDataTable, VcBlade } from "@vc-shell/framework/ui";
81
80
  import { useI18n } from "vue-i18n";
@@ -98,35 +97,22 @@ const { param, openBlade, exposeToChildren } = useBlade();
98
97
  const { showConfirmation } = usePopup();
99
98
  const { debounce } = useFunctions();
100
99
 
101
- const PAGE_SIZE = 20;
102
-
103
- const { sortField, sortOrder, sortExpression } = useDataTableSort({
104
- initialField: "createdDate",
100
+ const { sortExpression } = useTableSort({
101
+ initialProperty: "createdDate",
105
102
  initialDirection: "DESC",
106
103
  });
107
104
 
108
- const { getItems, removeItems, data, loading, totalCount, pages } = useList({
105
+ const { getItems, removeItems, data, loading, totalCount, pages, currentPage, searchQuery } = useList({
109
106
  sort: sortExpression.value,
110
- pageSize: PAGE_SIZE,
107
+ pageSize: 20,
111
108
  });
112
109
 
113
- const searchValue = ref<string>();
114
- const currentPage = ref(1);
110
+ const searchValue = ref();
115
111
  const selectedItemId = ref<string>();
116
112
  const selectedItems = ref<MockedItem[]>([]);
117
113
 
118
114
  const selectedIds = computed(() => selectedItems.value.map((item) => item.id).filter(Boolean) as string[]);
119
115
 
120
- // Restore sort/search/page from the URL, then load once below.
121
- const restored = useTableQueryState("SAMPLE_APP").read();
122
- if (restored.sort) {
123
- const [field, direction] = restored.sort.split(":");
124
- sortField.value = field;
125
- sortOrder.value = direction === "DESC" ? -1 : 1;
126
- }
127
- if (restored.search) searchValue.value = restored.search;
128
- if (restored.page) currentPage.value = restored.page;
129
-
130
116
  watch(
131
117
  param,
132
118
  (newVal) => {
@@ -135,29 +121,47 @@ watch(
135
121
  { immediate: true },
136
122
  );
137
123
 
138
- function load() {
139
- return getItems({
124
+ onMounted(async () => {
125
+ await getItems({
126
+ ...searchQuery.value,
140
127
  sort: sortExpression.value,
141
- keyword: searchValue.value || undefined,
142
- skip: (currentPage.value - 1) * PAGE_SIZE,
143
128
  });
144
- }
129
+ });
130
+
131
+ watch(sortExpression, async (value) => {
132
+ await getItems({
133
+ ...searchQuery.value,
134
+ sort: value,
135
+ });
136
+ });
145
137
 
146
- // One loader for sort/search/page. Debounced so typing doesn't fetch per keystroke.
147
- load();
148
- watch(searchValue, () => (currentPage.value = 1));
149
- watch([sortExpression, searchValue, currentPage], debounce(load, 300));
138
+ const onSearchList = debounce(async (keyword: string) => {
139
+ searchValue.value = keyword;
140
+ await getItems({
141
+ ...searchQuery.value,
142
+ keyword,
143
+ });
144
+ }, 1000);
150
145
 
151
- const clearSearch = () => {
146
+ const clearSearch = async () => {
152
147
  searchValue.value = "";
148
+ await getItems({
149
+ ...searchQuery.value,
150
+ keyword: "",
151
+ });
153
152
  };
154
153
 
155
154
  const addItem = () => {
156
- openBlade({ name: "SampleDetails" });
155
+ openBlade({
156
+ name: "SampleDetails",
157
+ });
157
158
  };
158
159
 
159
- const onPaginationClick = (page: number) => {
160
- currentPage.value = page;
160
+ const onPaginationClick = async (page: number) => {
161
+ await getItems({
162
+ ...searchQuery.value,
163
+ skip: (page - 1) * (searchQuery.value.take ?? 20),
164
+ });
161
165
  };
162
166
 
163
167
  const bladeToolbar = ref<IBladeToolbar[]>([
@@ -184,7 +188,11 @@ const title = computed(() => t("SAMPLE_APP.PAGES.LIST.TITLE"));
184
188
 
185
189
  const reload = async () => {
186
190
  selectedItems.value = [];
187
- await load();
191
+ await getItems({
192
+ ...searchQuery.value,
193
+ skip: (currentPage.value - 1) * (searchQuery.value.take ?? 10),
194
+ sort: sortExpression.value,
195
+ });
188
196
  };
189
197
 
190
198
  const onItemClick = (event: { data: MockedItem; index: number; originalEvent: Event }) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vc-shell/create-vc-app",
3
3
  "description": "Application scaffolding",
4
- "version": "2.0.10-pr242.bfb451e",
4
+ "version": "2.0.10-pr243.88977c0",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
7
7
  "files": [
@@ -16,7 +16,7 @@
16
16
  "devDependencies": {
17
17
  "@types/ejs": "^3.1.5",
18
18
  "@types/prompts": "^2.4.4",
19
- "@vc-shell/ts-config": "2.0.10-pr242.bfb451e",
19
+ "@vc-shell/ts-config": "2.0.10-pr243.88977c0",
20
20
  "copyfiles": "^2.4.1",
21
21
  "cross-env": "^7.0.3",
22
22
  "shx": "^0.3.4",