@vc-shell/create-vc-app 1.0.203 → 1.0.205

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 (26) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.js +190 -180
  3. package/dist/templates/base/_package.json +5 -5
  4. package/dist/templates/mocks/sample-data/constants.ts +98 -0
  5. package/dist/templates/mocks/sample-data/index.ts +2 -0
  6. package/dist/templates/mocks/sample-data/methods.ts +65 -0
  7. package/dist/templates/sample/classic-module/composables/index.ts +2 -0
  8. package/dist/templates/sample/classic-module/composables/useDetails/index.ts +41 -0
  9. package/dist/templates/sample/classic-module/composables/useList/index.ts +41 -0
  10. package/dist/templates/sample/classic-module/index.ts +8 -0
  11. package/dist/templates/sample/classic-module/locales/en.json +59 -0
  12. package/dist/templates/sample/classic-module/locales/index.ts +2 -0
  13. package/dist/templates/sample/classic-module/pages/details.vue +257 -0
  14. package/dist/templates/sample/classic-module/pages/index.ts +2 -0
  15. package/dist/templates/sample/classic-module/pages/list.vue +267 -0
  16. package/dist/templates/sample/dynamic-module/composables/index.ts +2 -0
  17. package/dist/templates/sample/dynamic-module/composables/useDetails/index.ts +49 -0
  18. package/dist/templates/sample/dynamic-module/composables/useList/index.ts +54 -0
  19. package/dist/templates/sample/dynamic-module/index.ts +8 -0
  20. package/dist/templates/sample/dynamic-module/locales/en.json +69 -0
  21. package/dist/templates/sample/dynamic-module/locales/index.ts +2 -0
  22. package/dist/templates/sample/dynamic-module/pages/details.ts +100 -0
  23. package/dist/templates/sample/dynamic-module/pages/index.ts +4 -0
  24. package/dist/templates/sample/dynamic-module/pages/list.ts +81 -0
  25. package/dist/templates/sample/overrides/main.ts +53 -0
  26. package/package.json +2 -2
@@ -0,0 +1,267 @@
1
+ <template>
2
+ <VcBlade
3
+ :title="$t('SAMPLE_APP.PAGES.LIST.TITLE')"
4
+ width="50%"
5
+ :expanded="expanded"
6
+ :closable="closable"
7
+ :toolbar-items="bladeToolbar"
8
+ @close="$emit('close:blade')"
9
+ @expand="$emit('expand:blade')"
10
+ @collapse="$emit('collapse:blade')"
11
+ >
12
+ <!-- Blade contents -->
13
+ <VcTable
14
+ :expanded="expanded"
15
+ class="tw-grow tw-basis-0"
16
+ multiselect
17
+ :loading="loading"
18
+ :columns="columns"
19
+ :sort="sort"
20
+ :pages="pages"
21
+ :total-count="totalCount"
22
+ :search-value="searchValue"
23
+ :current-page="currentPage"
24
+ :search-placeholder="$t('SAMPLE_APP.PAGES.LIST.SEARCH.PLACEHOLDER')"
25
+ :total-label="$t('SAMPLE_APP.PAGES.LIST.TABLE.TOTALS')"
26
+ :selected-item-id="selectedItemId"
27
+ state-key="SAMPLE_APP"
28
+ :items="data"
29
+ :item-action-builder="actionBuilder"
30
+ enable-item-actions
31
+ @item-click="onItemClick"
32
+ @header-click="onHeaderClick"
33
+ @selection-changed="onSelectionChanged"
34
+ >
35
+ </VcTable>
36
+ </VcBlade>
37
+ </template>
38
+
39
+ <script lang="ts" setup>
40
+ import { computed, ref, markRaw, onMounted, watch } from "vue";
41
+ import {
42
+ IActionBuilderResult,
43
+ IBladeEvent,
44
+ IBladeToolbar,
45
+ IParentCallArgs,
46
+ ITableColumns,
47
+ useBladeNavigation,
48
+ usePopup,
49
+ } from "@vc-shell/framework";
50
+ import { useI18n } from "vue-i18n";
51
+ import { useList } from "./../composables";
52
+ import Details from "./details.vue";
53
+ import { MockedItem } from "../sample-data";
54
+
55
+ export interface Props {
56
+ expanded?: boolean;
57
+ closable?: boolean;
58
+ param?: string;
59
+ options?: Record<string, unknown>;
60
+ }
61
+
62
+ export interface Emits {
63
+ (event: "parent:call", args: IParentCallArgs): void;
64
+ (event: "collapse:blade"): void;
65
+ (event: "expand:blade"): void;
66
+ (event: "open:blade", blade: IBladeEvent): void;
67
+ (event: "close:blade"): void;
68
+ }
69
+
70
+ defineOptions({
71
+ url: "/sample-list",
72
+ name: "SampleList",
73
+ isWorkspace: true,
74
+ menuItem: {
75
+ title: "SAMPLE_APP.MENU.TITLE",
76
+ icon: "fas fa-file-alt",
77
+ priority: 1,
78
+ },
79
+ });
80
+
81
+ const props = withDefaults(defineProps<Props>(), {
82
+ expanded: true,
83
+ closable: true,
84
+ });
85
+
86
+ defineEmits<Emits>();
87
+
88
+ const { t } = useI18n({ useScope: "global" });
89
+ const { openBlade } = useBladeNavigation();
90
+ const { showConfirmation } = usePopup();
91
+ const { getItems, removeItems, data, loading, totalCount, pages, currentPage } = useList();
92
+
93
+ const sort = ref("createdDate:DESC");
94
+ const searchValue = ref();
95
+ const selectedItemId = ref<string>();
96
+ const selectedIds = ref<string[]>([]);
97
+ const query = ref();
98
+
99
+ watch(
100
+ () => props.param,
101
+ (newVal) => {
102
+ if (newVal) {
103
+ selectedItemId.value = newVal;
104
+
105
+ openBlade({
106
+ blade: markRaw(Details),
107
+ param: newVal,
108
+ onOpen() {
109
+ selectedItemId.value = newVal;
110
+ },
111
+ onClose() {
112
+ selectedItemId.value = undefined;
113
+ },
114
+ });
115
+ }
116
+ },
117
+ { immediate: true },
118
+ );
119
+
120
+ onMounted(async () => {
121
+ await reload();
122
+ });
123
+
124
+ const bladeToolbar = ref<IBladeToolbar[]>([
125
+ {
126
+ id: "refresh",
127
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TOOLBAR.REFRESH")),
128
+ icon: "fas fa-sync-alt",
129
+ async clickHandler() {
130
+ await reload();
131
+ },
132
+ },
133
+ {
134
+ id: "remove",
135
+ icon: "fas fa-trash",
136
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TOOLBAR.REMOVE")),
137
+ async clickHandler() {
138
+ await remove(selectedIds.value);
139
+ },
140
+ disabled: computed(() => selectedIds.value.length === 0),
141
+ },
142
+ ]);
143
+
144
+ const columns = ref<ITableColumns[]>([
145
+ {
146
+ id: "imgSrc",
147
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.IMAGE")),
148
+ type: "image",
149
+ width: "70px",
150
+ },
151
+ {
152
+ id: "name",
153
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.PRODUCT_NAME")),
154
+ alwaysVisible: true,
155
+ },
156
+ {
157
+ id: "description",
158
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.DESCRIPTION")),
159
+ },
160
+ {
161
+ id: "price",
162
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.PRICE")),
163
+ type: "money",
164
+ alwaysVisible: true,
165
+ },
166
+ {
167
+ id: "salePrice",
168
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.SALE_PRICE")),
169
+ type: "money",
170
+ },
171
+ {
172
+ id: "currency.name",
173
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.HEADER.CURRENCY")),
174
+ },
175
+ ]);
176
+
177
+ const title = computed(() => t("SAMPLE_APP.PAGES.LIST.HEADER.TITLE"));
178
+
179
+ const reload = async () => {
180
+ await getItems(query.value);
181
+ };
182
+
183
+ const onItemClick = (item: { id: string }) => {
184
+ openBlade({
185
+ blade: markRaw(Details),
186
+ param: item.id,
187
+ onOpen() {
188
+ selectedItemId.value = item.id;
189
+ },
190
+ onClose() {
191
+ selectedItemId.value = undefined;
192
+ },
193
+ });
194
+ };
195
+
196
+ const onHeaderClick = (item: ITableColumns) => {
197
+ const sortOptions = ["DESC", "ASC", ""];
198
+
199
+ if (item.sortable) {
200
+ if (sort.value.split(":")[0] === item.id) {
201
+ const index = sortOptions.findIndex((x) => {
202
+ const sorting = sort.value.split(":")[1];
203
+ if (sorting) {
204
+ return x === sorting;
205
+ } else {
206
+ return x === "";
207
+ }
208
+ });
209
+
210
+ if (index !== -1) {
211
+ const newSort = sortOptions[(index + 1) % sortOptions.length];
212
+
213
+ if (newSort === "") {
214
+ sort.value = `${item.id}`;
215
+ } else {
216
+ sort.value = `${item.id}:${newSort}`;
217
+ }
218
+ }
219
+ } else {
220
+ sort.value = `${item.id}:${sortOptions[0]}`;
221
+ }
222
+ }
223
+ };
224
+
225
+ const onSelectionChanged = (items: MockedItem[]) => {
226
+ selectedIds.value = items.map((item) => item.id!);
227
+ };
228
+
229
+ const actionBuilder = (): IActionBuilderResult[] => {
230
+ const result: IActionBuilderResult[] = [];
231
+ result.push({
232
+ icon: "fas fa-trash",
233
+ title: computed(() => t("SAMPLE_APP.PAGES.LIST.TABLE.ACTIONS.DELETE")),
234
+ type: "danger",
235
+ position: "left",
236
+ clickHandler(item: MockedItem) {
237
+ if (item.id) {
238
+ if (!selectedIds.value.includes(item.id)) {
239
+ selectedIds.value.push(item.id);
240
+ }
241
+ remove(selectedIds.value);
242
+ selectedIds.value = [];
243
+ }
244
+ },
245
+ });
246
+
247
+ return result;
248
+ };
249
+
250
+ async function remove(ids: string[]) {
251
+ if (
252
+ await showConfirmation(
253
+ t(`SAMPLE_APP.PAGES.ALERTS.DELETE_SELECTED_CONFIRMATION.MESSAGE`, {
254
+ count: selectedIds.value.length,
255
+ }),
256
+ )
257
+ ) {
258
+ await removeItems({ ids });
259
+ await reload();
260
+ }
261
+ }
262
+
263
+ defineExpose({
264
+ title,
265
+ reload,
266
+ });
267
+ </script>
@@ -0,0 +1,2 @@
1
+ export { default as useList } from "./useList";
2
+ export { default as useDetails } from "./useDetails";
@@ -0,0 +1,49 @@
1
+ import { computed, ref, Ref } from "vue";
2
+ import { DetailsBaseBladeScope, DynamicBladeForm, UseDetails, useDetailsFactory } from "@vc-shell/framework";
3
+ import { addNewMockItem, loadMockItem, removeMockItem, updateMockItem } from "../../sample-data/methods";
4
+ import { currencyOptions, MockedItem } from "../../sample-data";
5
+ import { useI18n } from "vue-i18n";
6
+
7
+ export interface DynamicItemScope extends DetailsBaseBladeScope {
8
+ currencyOptions: typeof currencyOptions;
9
+ }
10
+
11
+ export default (args: {
12
+ props: InstanceType<typeof DynamicBladeForm>["$props"];
13
+ emit: InstanceType<typeof DynamicBladeForm>["$emit"];
14
+ mounted: Ref<boolean>;
15
+ }): UseDetails<MockedItem, DynamicItemScope> => {
16
+ const factory = useDetailsFactory<MockedItem>({
17
+ load: loadMockItem,
18
+ saveChanges: (details) => {
19
+ return details.id ? updateMockItem(details) : addNewMockItem(details);
20
+ },
21
+ remove: removeMockItem,
22
+ });
23
+
24
+ const { load, saveChanges, remove, loading, item, validationState } = factory();
25
+
26
+ const scope = ref<DynamicItemScope>({
27
+ currencyOptions,
28
+ });
29
+ const { t } = useI18n({ useScope: "global" });
30
+
31
+ const bladeTitle = computed(() => {
32
+ return args.props.param
33
+ ? item.value?.name
34
+ ? item.value?.name + t("SAMPLE_APP.PAGES.DETAILS.TITLE.DETAILS")
35
+ : t("SAMPLE_APP.PAGES.DETAILS.TITLE.LOADING")
36
+ : "Test App" + t("SAMPLE_APP.PAGES.DETAILS.TITLE.DETAILS");
37
+ });
38
+
39
+ return {
40
+ load,
41
+ saveChanges,
42
+ remove,
43
+ loading,
44
+ item,
45
+ validationState,
46
+ bladeTitle,
47
+ scope: computed(() => scope.value),
48
+ };
49
+ };
@@ -0,0 +1,54 @@
1
+ import { computed, ref, Ref } from "vue";
2
+ import {
3
+ DynamicBladeList,
4
+ ListBaseBladeScope,
5
+ useBladeNavigation,
6
+ useListFactory,
7
+ type TOpenBladeArgs,
8
+ UseList,
9
+ } from "@vc-shell/framework";
10
+ import { loadMockItemsList, removeMockItem } from "../../sample-data/methods";
11
+ import { MockedItem, MockedQuery } from "../../sample-data";
12
+
13
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
14
+ export interface DynamicItemsScope extends ListBaseBladeScope {}
15
+
16
+ export default (args: {
17
+ props: InstanceType<typeof DynamicBladeList>["$props"];
18
+ emit: InstanceType<typeof DynamicBladeList>["$emit"];
19
+ mounted: Ref<boolean>;
20
+ }): UseList<MockedItem[], MockedQuery, DynamicItemsScope> => {
21
+ const factory = useListFactory<MockedItem[], MockedQuery>({
22
+ load: loadMockItemsList,
23
+ remove: async (query, customQuery) => {
24
+ if (customQuery?.ids === null) return;
25
+ for (const id of customQuery.ids) {
26
+ await removeMockItem({ id });
27
+ }
28
+ },
29
+ });
30
+
31
+ const { load, remove, items, pagination, loading, query } = factory();
32
+ const { openBlade, resolveBladeByName } = useBladeNavigation();
33
+
34
+ async function openDetailsBlade(data?: TOpenBladeArgs) {
35
+ await openBlade({
36
+ blade: resolveBladeByName("SampleDetails"),
37
+ ...data,
38
+ });
39
+ }
40
+
41
+ const scope = ref<DynamicItemsScope>({
42
+ openDetailsBlade,
43
+ });
44
+
45
+ return {
46
+ items,
47
+ load,
48
+ remove,
49
+ loading,
50
+ pagination,
51
+ query,
52
+ scope: computed(() => scope.value),
53
+ };
54
+ };
@@ -0,0 +1,8 @@
1
+ import * as schema from "./pages";
2
+ import * as locales from "./locales";
3
+ import * as composables from "./composables";
4
+ import { createDynamicAppModule } from "@vc-shell/framework";
5
+
6
+ export default createDynamicAppModule({ schema, locales, composables });
7
+
8
+ export { schema, composables, locales };
@@ -0,0 +1,69 @@
1
+ {
2
+ "SAMPLE_APP": {
3
+ "MENU": {
4
+ "TITLE": "Sample"
5
+ },
6
+ "PAGES": {
7
+ "LIST": {
8
+ "TOOLBAR": {
9
+ "REFRESH": "Refresh",
10
+ "REMOVE": "Remove"
11
+ },
12
+ "SEARCH": {
13
+ "PLACEHOLDER": "Search keywords"
14
+ },
15
+ "TABLE": {
16
+ "TOTALS": "Count:",
17
+ "HEADER": {
18
+ "IMAGE": "Image",
19
+ "PRODUCT_NAME": "Product name",
20
+ "DESCRIPTION": "Description",
21
+ "PRICE": "Price",
22
+ "SALE_PRICE": "Sale price",
23
+ "CURRENCY": "Currency"
24
+ },
25
+ "EMPTY": {
26
+ "TITLE": "There are no content yet",
27
+ "ACTION": "Add content"
28
+ },
29
+ "NOT_FOUND": {
30
+ "TITLE": "No content found.",
31
+ "ACTION": "Reset search"
32
+ },
33
+ "ACTIONS": {
34
+ "DELETE": "Delete"
35
+ }
36
+ },
37
+ "EMPTY": {
38
+ "NO_ITEMS": "There are no items yet"
39
+ }
40
+ },
41
+ "DETAILS": {
42
+ "TITLE": {
43
+ "DETAILS": " details",
44
+ "LOADING": "Loading..."
45
+ },
46
+ "TOOLBAR": {
47
+ "SAVE": "Save",
48
+ "DELETE": "Delete"
49
+ },
50
+ "FIELDS": {
51
+ "NAME": "Name",
52
+ "CONTENT": "Content",
53
+ "GUID": "GUID",
54
+ "DESCRIPTION": "Description",
55
+ "PRICE": "Price",
56
+ "SALE_PRICE": "Sale price"
57
+ }
58
+ },
59
+ "ALERTS": {
60
+ "CLOSE_CONFIRMATION": "You have unsaved changes. Close anyway?",
61
+ "DELETE": "Are you sure you want to delete this item?",
62
+ "DELETE_SELECTED_CONFIRMATION": {
63
+ "MESSAGE": "Are you sure you want to delete {count} selected items?",
64
+ "ALL": "all {totalCount}"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,2 @@
1
+ import * as en from "./en.json";
2
+ export { en };
@@ -0,0 +1,100 @@
1
+ import { DynamicDetailsSchema } from "@vc-shell/framework";
2
+
3
+ export const details: DynamicDetailsSchema = {
4
+ settings: {
5
+ url: "/sample-details",
6
+ id: "SampleDetails",
7
+ localizationPrefix: "SAMPLE_APP",
8
+ composable: "useDetails",
9
+ component: "DynamicBladeForm",
10
+ toolbar: [
11
+ {
12
+ id: "save",
13
+ icon: "fas fa-save",
14
+ title: "SAMPLE_APP.PAGES.DETAILS.TOOLBAR.SAVE",
15
+ method: "saveChanges",
16
+ },
17
+ {
18
+ id: "delete",
19
+ icon: "fas fa-trash",
20
+ title: "SAMPLE_APP.PAGES.DETAILS.TOOLBAR.DELETE",
21
+ method: "remove",
22
+ },
23
+ ],
24
+ },
25
+ content: [
26
+ {
27
+ id: "dynamicItemForm",
28
+ component: "vc-form",
29
+ children: [
30
+ {
31
+ id: "name",
32
+ component: "vc-input",
33
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.NAME",
34
+ rules: { required: true },
35
+ property: "name",
36
+ },
37
+ {
38
+ id: "contentCard",
39
+ component: "vc-card",
40
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.CONTENT",
41
+ fields: [
42
+ {
43
+ id: "guid",
44
+ component: "vc-input",
45
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.GUID",
46
+ property: "guid",
47
+ rules: {
48
+ required: true,
49
+ regex: /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/,
50
+ },
51
+ },
52
+ {
53
+ id: "description",
54
+ component: "vc-textarea",
55
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.DESCRIPTION",
56
+ property: "description",
57
+ rules: { required: true },
58
+ },
59
+ ],
60
+ },
61
+ {
62
+ id: "prices",
63
+ component: "vc-card",
64
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.PRICE",
65
+ fields: [
66
+ {
67
+ id: "price-fieldset",
68
+ component: "vc-fieldset",
69
+ columns: 2,
70
+ fields: [
71
+ {
72
+ id: "price",
73
+ component: "vc-input-currency",
74
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.PRICE",
75
+ property: "price",
76
+ rules: { required: true },
77
+ options: "currencyOptions",
78
+ optionProperty: "currency.name",
79
+ optionValue: "value",
80
+ optionLabel: "label",
81
+ },
82
+ {
83
+ id: "salePrice",
84
+ component: "vc-input-currency",
85
+ label: "SAMPLE_APP.PAGES.DETAILS.FIELDS.SALE_PRICE",
86
+ property: "salePrice",
87
+ rules: { required: true },
88
+ options: "currencyOptions",
89
+ optionProperty: "currency.name",
90
+ optionValue: "value",
91
+ optionLabel: "label",
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ ],
100
+ };
@@ -0,0 +1,4 @@
1
+ import { grid as testAppList } from "./list";
2
+ import { details as testAppDetails } from "./details";
3
+
4
+ export { testAppList, testAppDetails };
@@ -0,0 +1,81 @@
1
+ import { DynamicGridSchema } from "@vc-shell/framework";
2
+
3
+ export const grid: DynamicGridSchema = {
4
+ settings: {
5
+ url: "/sample-list",
6
+ id: "SampleList",
7
+ titleTemplate: "Sample list",
8
+ localizationPrefix: "SAMPLE_APP",
9
+ isWorkspace: true,
10
+ composable: "useList",
11
+ component: "DynamicBladeList",
12
+ toolbar: [
13
+ {
14
+ id: "refresh",
15
+ icon: "fas fa-sync-alt",
16
+ title: "SAMPLE_APP.PAGES.LIST.TOOLBAR.REFRESH",
17
+ method: "refresh",
18
+ },
19
+ {
20
+ id: "remove",
21
+ icon: "fas fa-trash",
22
+ title: "SAMPLE_APP.PAGES.LIST.TOOLBAR.REMOVE",
23
+ method: "removeItems",
24
+ },
25
+ ],
26
+ menuItem: {
27
+ title: "SAMPLE_APP.MENU.TITLE",
28
+ icon: "fas fa-file-alt",
29
+ priority: 1,
30
+ },
31
+ },
32
+ content: [
33
+ {
34
+ id: "itemsGrid",
35
+ component: "vc-table",
36
+ multiselect: true,
37
+ actions: [
38
+ {
39
+ id: "delete",
40
+ icon: "fas fa-trash",
41
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.ACTIONS.DELETE",
42
+ method: "removeItems",
43
+ position: "left",
44
+ type: "danger",
45
+ },
46
+ ],
47
+ columns: [
48
+ {
49
+ id: "imgSrc",
50
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.IMAGE",
51
+ type: "image",
52
+ width: "70px",
53
+ },
54
+ {
55
+ id: "name",
56
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.PRODUCT_NAME",
57
+ alwaysVisible: true,
58
+ },
59
+ {
60
+ id: "description",
61
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.DESCRIPTION",
62
+ },
63
+ {
64
+ id: "price",
65
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.PRICE",
66
+ type: "money",
67
+ alwaysVisible: true,
68
+ },
69
+ {
70
+ id: "salePrice",
71
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.SALE_PRICE",
72
+ type: "money",
73
+ },
74
+ {
75
+ id: "currency.name",
76
+ title: "SAMPLE_APP.PAGES.LIST.TABLE.HEADER.CURRENCY",
77
+ },
78
+ ],
79
+ },
80
+ ],
81
+ };
@@ -0,0 +1,53 @@
1
+ import VirtoShellFramework, { notification, useUser, useLanguages } from "@vc-shell/framework";
2
+ import { createApp } from "vue";
3
+ import { router } from "./router";
4
+ import * as locales from "./locales";
5
+ import { RouterView } from "vue-router";
6
+ import {{ModuleNamePascalCase}} from "./modules/{{ModuleName}}";
7
+ import SampleModule from "./modules/sample";
8
+
9
+ // Load required CSS
10
+ import "./styles/index.scss";
11
+ import "@fortawesome/fontawesome-free/css/all.min.css";
12
+ import "roboto-fontface/css/roboto/roboto-fontface.css";
13
+ import "@vc-shell/framework/dist/index.css";
14
+
15
+ async function startApp() {
16
+ const { loadUser } = useUser();
17
+ await loadUser();
18
+
19
+ const { currentLocale, setLocale } = useLanguages();
20
+
21
+ const app = createApp(RouterView)
22
+ .use(VirtoShellFramework, {
23
+ router,
24
+ platformUrl: import.meta.env.APP_PLATFORM_URL,
25
+ i18n: {
26
+ locale: import.meta.env.APP_I18N_LOCALE,
27
+ fallbackLocale: import.meta.env.APP_I18N_FALLBACK_LOCALE,
28
+ },
29
+ })
30
+ // {{ModuleNamePascalCase}} module initialization
31
+ .use({{ModuleNamePascalCase}}, { router })
32
+ // Sample module initialization
33
+ .use(SampleModule, { router })
34
+ .use(router);
35
+
36
+ Object.entries(locales).forEach(([key, message]) => {
37
+ app.config.globalProperties.$mergeLocaleMessage(key, message);
38
+ });
39
+
40
+ setLocale(currentLocale.value);
41
+
42
+ app.config.errorHandler = (err) => {
43
+ notification.error((err as Error).toString(), {
44
+ timeout: 5000,
45
+ });
46
+ };
47
+
48
+ await router.isReady();
49
+
50
+ app.mount("#app");
51
+ }
52
+
53
+ startApp();