cabloy 5.1.158 → 5.1.159
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/.cabloy-version +1 -1
- package/.claude/skills/cabloy-contract-loop/SKILL.md +24 -7
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +12 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +28 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +19 -2
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +18 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +14 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +10 -0
- package/.github/workflows/docs-pages.yml +18 -2
- package/CHANGELOG.md +19 -0
- package/package.json +2 -1
- package/repo-docs/.vitepress/config.mjs +34 -4
- package/repo-docs/.vitepress/theme/components/GitHubRepositoriesNav.vue +255 -0
- package/repo-docs/.vitepress/theme/custom.css +21 -0
- package/repo-docs/.vitepress/theme/index.js +7 -1
- package/repo-docs/backend/resource-field-update.md +48 -1
- package/repo-docs/blogs/index.md +2 -0
- package/repo-docs/frontend/form-layout-guide.md +6 -6
- package/repo-docs/frontend/page-guide.md +12 -0
- package/repo-docs/frontend/page-meta-guide.md +27 -0
- package/repo-docs/frontend/ssr-architecture-overview.md +4 -0
- package/repo-docs/frontend/ssr-build-deploy-guide.md +1 -1
- package/repo-docs/frontend/zova-form-source-reading-map.md +2 -2
- package/repo-docs/frontend/zova-form-under-the-hood.md +1 -1
- package/repo-docs/frontend/zova-reactivity-under-the-hood.md +65 -9
- package/repo-docs/fullstack/contract-loop-playbook.md +2 -0
- package/repo-docs/fullstack/ssr-site-and-flavor-setup.md +198 -0
- package/repo-docs/fullstack/vona-zova-integration.md +2 -0
- package/repo-e2e/docs/playwright.config.ts +37 -0
- package/repo-e2e/docs/specs/blogs-index.spec.ts +180 -0
- package/repo-e2e/docs/specs/github-repositories-nav.spec.ts +69 -0
- package/repo-e2e/docs/test-results/.last-run.json +4 -0
- package/repo-e2e/specs/cabloy-basic.spec.ts +136 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +21 -80
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productCreate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productUpdate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectReq.tsx +3 -7
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +8 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +51 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +10 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +99 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +85 -57
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/cli/templates/rest/render.ts +2 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts +13 -3
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +31 -11
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +1 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +68 -8
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +4 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts +0 -6
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/formLayout.test.ts +16 -9
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +4 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +13 -2
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/schema.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +50 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +60 -7
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/table.ts +6 -4
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { expect, test } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
interface IRectangle {
|
|
6
|
+
bottom: number;
|
|
7
|
+
height: number;
|
|
8
|
+
left: number;
|
|
9
|
+
right: number;
|
|
10
|
+
top: number;
|
|
11
|
+
width: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface IBlogsGeometry {
|
|
15
|
+
cards: IRectangle[];
|
|
16
|
+
grid: IRectangle & { display: string };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface IArticleGeometry {
|
|
20
|
+
aside: IRectangle;
|
|
21
|
+
container: IRectangle;
|
|
22
|
+
content: IRectangle;
|
|
23
|
+
contentContainer: IRectangle;
|
|
24
|
+
body: IRectangle;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const blogArticlePaths = [
|
|
28
|
+
'/blogs/ai-react-nextjs-enterprise-architecture-cabloy/',
|
|
29
|
+
'/blogs/cabloy-fullstack-resource-addressing/',
|
|
30
|
+
'/blogs/nextjs-integrated-fullstack-cabloy-contract-loop/',
|
|
31
|
+
'/blogs/vue-object-oriented-zova-beginner-mental-model/',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function collectPageErrors(page: Page) {
|
|
35
|
+
const errors: Error[] = [];
|
|
36
|
+
page.on('pageerror', error => {
|
|
37
|
+
errors.push(error);
|
|
38
|
+
});
|
|
39
|
+
return errors;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function collectConsoleErrors(page: Page) {
|
|
43
|
+
const errors: string[] = [];
|
|
44
|
+
page.on('console', message => {
|
|
45
|
+
const text = message.text();
|
|
46
|
+
if (message.type() === 'error' || /hydration mismatch/i.test(text)) {
|
|
47
|
+
errors.push(text);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return errors;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getDocumentHorizontalOverflow(page: Page) {
|
|
54
|
+
return page.evaluate(() => {
|
|
55
|
+
return (
|
|
56
|
+
Math.max(document.documentElement.scrollWidth, document.body.scrollWidth) - window.innerWidth
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getBlogsGeometry(page: Page) {
|
|
62
|
+
return page.locator('.cabloy-blog-grid').evaluate(grid => {
|
|
63
|
+
const toRectangle = (element: Element) => {
|
|
64
|
+
const { bottom, height, left, right, top, width } = element.getBoundingClientRect();
|
|
65
|
+
return { bottom, height, left, right, top, width };
|
|
66
|
+
};
|
|
67
|
+
const cards = Array.from(grid.querySelectorAll('.cabloy-blog-card'), toRectangle);
|
|
68
|
+
return {
|
|
69
|
+
grid: {
|
|
70
|
+
...toRectangle(grid),
|
|
71
|
+
display: getComputedStyle(grid).display,
|
|
72
|
+
},
|
|
73
|
+
cards,
|
|
74
|
+
};
|
|
75
|
+
}) as Promise<IBlogsGeometry>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getArticleGeometry(page: Page) {
|
|
79
|
+
return page.locator('.VPDoc').evaluate(doc => {
|
|
80
|
+
const getRequiredChild = (parent: Element, selector: string) => {
|
|
81
|
+
const element = parent.querySelector(`:scope > ${selector}`);
|
|
82
|
+
if (!element) throw new Error(`Could not find ${selector}`);
|
|
83
|
+
return element;
|
|
84
|
+
};
|
|
85
|
+
const toRectangle = (element: Element) => {
|
|
86
|
+
const { bottom, height, left, right, top, width } = element.getBoundingClientRect();
|
|
87
|
+
return { bottom, height, left, right, top, width };
|
|
88
|
+
};
|
|
89
|
+
const container = getRequiredChild(doc, '.container');
|
|
90
|
+
const aside = getRequiredChild(container, '.aside');
|
|
91
|
+
const content = getRequiredChild(container, '.content');
|
|
92
|
+
const contentContainer = getRequiredChild(content, '.content-container');
|
|
93
|
+
const body = getRequiredChild(contentContainer, '.main').querySelector('.vp-doc');
|
|
94
|
+
if (!body) throw new Error('Could not find .vp-doc');
|
|
95
|
+
return {
|
|
96
|
+
aside: toRectangle(aside),
|
|
97
|
+
container: toRectangle(container),
|
|
98
|
+
content: toRectangle(content),
|
|
99
|
+
contentContainer: toRectangle(contentContainer),
|
|
100
|
+
body: toRectangle(body),
|
|
101
|
+
};
|
|
102
|
+
}) as Promise<IArticleGeometry>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
test(
|
|
106
|
+
'DOCS-BLOGS-01: Blogs index uses a wide desktop card grid',
|
|
107
|
+
{ tag: '@layout' },
|
|
108
|
+
async ({ page }) => {
|
|
109
|
+
const pageErrors = collectPageErrors(page);
|
|
110
|
+
const consoleErrors = collectConsoleErrors(page);
|
|
111
|
+
|
|
112
|
+
const documentResponse = await page.goto('/blogs/', { waitUntil: 'load' });
|
|
113
|
+
expect(documentResponse?.ok()).toBeTruthy();
|
|
114
|
+
|
|
115
|
+
await expect(page.locator('.Layout.cabloy-blogs-index')).toBeVisible();
|
|
116
|
+
await expect(page.getByRole('heading', { name: 'Blogs', level: 1 })).toBeVisible();
|
|
117
|
+
await expect(page.locator('.VPDoc.has-sidebar')).toHaveCount(0);
|
|
118
|
+
await expect(page.locator('.VPDoc.has-aside')).toHaveCount(0);
|
|
119
|
+
|
|
120
|
+
const cards = page.locator('.cabloy-blog-card');
|
|
121
|
+
await expect(cards).toHaveCount(4);
|
|
122
|
+
for (let index = 0; index < 4; index++) {
|
|
123
|
+
await expect(cards.nth(index)).toBeVisible();
|
|
124
|
+
await expect(cards.nth(index).locator('.cabloy-blog-card__cover img')).toBeVisible();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const geometry = await getBlogsGeometry(page);
|
|
128
|
+
expect(geometry.grid.display).toBe('grid');
|
|
129
|
+
expect(geometry.cards).toHaveLength(4);
|
|
130
|
+
|
|
131
|
+
const [firstCard, ...remainingCards] = geometry.cards;
|
|
132
|
+
expect(firstCard.width).toBeGreaterThanOrEqual(279);
|
|
133
|
+
for (const card of remainingCards) {
|
|
134
|
+
expect(Math.abs(card.top - firstCard.top)).toBeLessThanOrEqual(1);
|
|
135
|
+
expect(Math.abs(card.width - firstCard.width)).toBeLessThanOrEqual(1);
|
|
136
|
+
expect(card.left).toBeGreaterThan(firstCard.left);
|
|
137
|
+
expect(card.left).toBeGreaterThanOrEqual(geometry.grid.left - 1);
|
|
138
|
+
expect(card.right).toBeLessThanOrEqual(geometry.grid.right + 1);
|
|
139
|
+
expect(card.right).toBeLessThanOrEqual(1441);
|
|
140
|
+
}
|
|
141
|
+
expect(firstCard.left).toBeGreaterThanOrEqual(geometry.grid.left - 1);
|
|
142
|
+
expect(firstCard.right).toBeLessThanOrEqual(geometry.grid.right + 1);
|
|
143
|
+
expect(firstCard.right).toBeLessThanOrEqual(1441);
|
|
144
|
+
|
|
145
|
+
await expect.poll(() => getDocumentHorizontalOverflow(page)).toBeLessThanOrEqual(1);
|
|
146
|
+
expect(pageErrors).toEqual([]);
|
|
147
|
+
expect(consoleErrors).toEqual([]);
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
test(
|
|
152
|
+
'DOCS-BLOGS-02: Blog articles use a wide desktop body with an outline aside',
|
|
153
|
+
{ tag: '@layout' },
|
|
154
|
+
async ({ page }) => {
|
|
155
|
+
const pageErrors = collectPageErrors(page);
|
|
156
|
+
const consoleErrors = collectConsoleErrors(page);
|
|
157
|
+
|
|
158
|
+
for (const path of blogArticlePaths) {
|
|
159
|
+
const documentResponse = await page.goto(path, { waitUntil: 'load' });
|
|
160
|
+
expect(documentResponse?.ok()).toBeTruthy();
|
|
161
|
+
|
|
162
|
+
await expect(page.locator('.Layout.cabloy-blogs-article')).toBeVisible();
|
|
163
|
+
await expect(page.locator('.VPDoc.has-sidebar')).toHaveCount(0);
|
|
164
|
+
await expect(page.locator('.VPDoc.has-aside')).toHaveCount(1);
|
|
165
|
+
await expect(page.locator('.aside')).toBeVisible();
|
|
166
|
+
|
|
167
|
+
const geometry = await getArticleGeometry(page);
|
|
168
|
+
expect(geometry.container.width).toBeCloseTo(1216, 0);
|
|
169
|
+
expect(geometry.content.width).toBeCloseTo(960, 0);
|
|
170
|
+
expect(geometry.contentContainer.width).toBeCloseTo(896, 0);
|
|
171
|
+
expect(geometry.body.width).toBeCloseTo(896, 0);
|
|
172
|
+
expect(geometry.aside.left).toBeGreaterThanOrEqual(geometry.content.right - 1);
|
|
173
|
+
expect(geometry.aside.right).toBeLessThanOrEqual(geometry.container.right + 1);
|
|
174
|
+
await expect.poll(() => getDocumentHorizontalOverflow(page)).toBeLessThanOrEqual(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
expect(pageErrors).toEqual([]);
|
|
178
|
+
expect(consoleErrors).toEqual([]);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
const repositories = [
|
|
4
|
+
{
|
|
5
|
+
text: 'github.com/cabloy/cabloy',
|
|
6
|
+
link: 'https://github.com/cabloy/cabloy',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
text: 'github.com/cabloy/cabloy-start',
|
|
10
|
+
link: 'https://github.com/cabloy/cabloy-start',
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
test('DOCS-NAV-01: GitHub icon menu exposes both repository links on desktop', async ({ page }) => {
|
|
15
|
+
const response = await page.goto('/', { waitUntil: 'load' });
|
|
16
|
+
expect(response?.ok()).toBeTruthy();
|
|
17
|
+
|
|
18
|
+
const trigger = page.getByRole('button', { name: 'GitHub repositories' });
|
|
19
|
+
await expect(trigger).toBeVisible();
|
|
20
|
+
await expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
21
|
+
await expect(trigger).not.toHaveText(/GitHub/);
|
|
22
|
+
|
|
23
|
+
await trigger.focus();
|
|
24
|
+
await page.keyboard.press('ArrowDown');
|
|
25
|
+
await expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
26
|
+
|
|
27
|
+
const menu = page.getByRole('menu', { name: 'GitHub repositories' });
|
|
28
|
+
await expect(menu).toBeVisible();
|
|
29
|
+
for (const repository of repositories) {
|
|
30
|
+
const link = menu.getByRole('menuitem', { name: repository.text, exact: true });
|
|
31
|
+
await expect(link).toHaveAttribute('href', repository.link);
|
|
32
|
+
await expect(link).toHaveAttribute('target', '_blank');
|
|
33
|
+
await expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await expect(
|
|
37
|
+
menu.getByRole('menuitem', { name: repositories[0].text, exact: true }),
|
|
38
|
+
).toBeFocused();
|
|
39
|
+
await page.keyboard.press('ArrowDown');
|
|
40
|
+
await expect(
|
|
41
|
+
menu.getByRole('menuitem', { name: repositories[1].text, exact: true }),
|
|
42
|
+
).toBeFocused();
|
|
43
|
+
await page.keyboard.press('Escape');
|
|
44
|
+
await expect(menu).toHaveCount(0);
|
|
45
|
+
await expect(trigger).toBeFocused();
|
|
46
|
+
|
|
47
|
+
await trigger.click();
|
|
48
|
+
await expect(menu).toBeVisible();
|
|
49
|
+
await page.mouse.click(10, 200);
|
|
50
|
+
await expect(menu).toHaveCount(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('DOCS-NAV-02: GitHub repositories remain readable in mobile navigation', async ({ page }) => {
|
|
54
|
+
await page.setViewportSize({ width: 375, height: 812 });
|
|
55
|
+
const response = await page.goto('/', { waitUntil: 'load' });
|
|
56
|
+
expect(response?.ok()).toBeTruthy();
|
|
57
|
+
|
|
58
|
+
await page.locator('.VPNavBarHamburger').click();
|
|
59
|
+
|
|
60
|
+
const screenMenu = page.locator('.VPNavScreen .cabloy-github-repositories--screen');
|
|
61
|
+
await expect(screenMenu).toBeVisible();
|
|
62
|
+
await expect(screenMenu.getByText('GitHub repositories')).toBeVisible();
|
|
63
|
+
for (const repository of repositories) {
|
|
64
|
+
const link = screenMenu.getByRole('link', { name: repository.text, exact: true });
|
|
65
|
+
await expect(link).toHaveAttribute('href', repository.link);
|
|
66
|
+
await expect(link).toHaveAttribute('target', '_blank');
|
|
67
|
+
await expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
@@ -44,12 +44,12 @@ async function loginAsAdmin(page: Page) {
|
|
|
44
44
|
return dashboardResponse;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function waitForStudentSelect(page: Page) {
|
|
47
|
+
function waitForStudentSelect(page: Page, requireSuccess = true) {
|
|
48
48
|
return page.waitForResponse(response => {
|
|
49
49
|
const url = new URL(response.url());
|
|
50
50
|
return (
|
|
51
51
|
response.request().method() === 'GET' &&
|
|
52
|
-
response.ok() &&
|
|
52
|
+
(!requireSuccess || response.ok()) &&
|
|
53
53
|
url.pathname === '/api/training/student' &&
|
|
54
54
|
!response.request().headers()['x-vona-openapi-schema']
|
|
55
55
|
);
|
|
@@ -382,6 +382,140 @@ test(
|
|
|
382
382
|
},
|
|
383
383
|
);
|
|
384
384
|
|
|
385
|
+
test(
|
|
386
|
+
'ATP-BASIC-TABLE-01: Training Student table submits server sorting and pins configured columns',
|
|
387
|
+
{ tag: ['@admin', '@flow'] },
|
|
388
|
+
async ({ page }) => {
|
|
389
|
+
await page.setViewportSize({ width: 1440, height: 900 });
|
|
390
|
+
const pageErrors = collectPageErrors(page);
|
|
391
|
+
await loginAsAdmin(page);
|
|
392
|
+
|
|
393
|
+
const studentName = `Table E2E ${Date.now()}`;
|
|
394
|
+
const accessToken = (await page.context().cookies()).find(
|
|
395
|
+
cookie => cookie.name === 'token',
|
|
396
|
+
)?.value;
|
|
397
|
+
expect(accessToken).toBeTruthy();
|
|
398
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
399
|
+
let studentId: string | number | undefined;
|
|
400
|
+
try {
|
|
401
|
+
const createResponse = await page.request.post('/api/training/student', {
|
|
402
|
+
data: { name: studentName, mobile: '13812345678', level: 1 },
|
|
403
|
+
headers,
|
|
404
|
+
});
|
|
405
|
+
expect(createResponse.ok()).toBeTruthy();
|
|
406
|
+
studentId = (await createResponse.json()).data;
|
|
407
|
+
expect(['string', 'number']).toContain(typeof studentId);
|
|
408
|
+
|
|
409
|
+
const initialSelect = waitForStudentSelect(page);
|
|
410
|
+
await page.getByRole('link', { name: 'Student', exact: true }).click();
|
|
411
|
+
await expect(page).toHaveURL(studentResourceUrl);
|
|
412
|
+
await initialSelect;
|
|
413
|
+
await page.setViewportSize({ width: 800, height: 900 });
|
|
414
|
+
|
|
415
|
+
const nameSort = page.getByRole('button', { name: 'Sort by name', exact: true });
|
|
416
|
+
const nameHeader = nameSort.locator('xpath=ancestor::th');
|
|
417
|
+
const operationsHeader = page.getByRole('columnheader', { name: 'Operations', exact: true });
|
|
418
|
+
await expect(nameSort).toBeVisible();
|
|
419
|
+
await expect(nameHeader).toHaveAttribute('aria-sort', 'none');
|
|
420
|
+
await expect(nameHeader).toHaveCSS('position', 'sticky');
|
|
421
|
+
await expect(nameHeader).toHaveCSS('left', '0px');
|
|
422
|
+
await expect(nameHeader).toHaveCSS('min-width', '240px');
|
|
423
|
+
await expect(nameHeader).toHaveCSS('text-align', 'left');
|
|
424
|
+
await expect(operationsHeader).toHaveCSS('position', 'sticky');
|
|
425
|
+
await expect(operationsHeader).toHaveCSS('right', '0px');
|
|
426
|
+
await expect(operationsHeader).toHaveCSS('min-width', '360px');
|
|
427
|
+
await expect(operationsHeader).toHaveCSS('text-align', 'center');
|
|
428
|
+
|
|
429
|
+
const ascendingResponse = waitForStudentSelect(page, false);
|
|
430
|
+
await nameSort.click();
|
|
431
|
+
await expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
|
|
432
|
+
const ascending = await ascendingResponse;
|
|
433
|
+
expect(ascending.status()).toBe(200);
|
|
434
|
+
const ascendingUrl = new URL(ascending.url());
|
|
435
|
+
expect(JSON.parse(ascendingUrl.searchParams.get('orders')!)).toEqual([['name', 'asc']]);
|
|
436
|
+
|
|
437
|
+
const descendingResponse = waitForStudentSelect(page, false);
|
|
438
|
+
await nameSort.click();
|
|
439
|
+
await expect(nameHeader).toHaveAttribute('aria-sort', 'descending');
|
|
440
|
+
const descending = await descendingResponse;
|
|
441
|
+
expect(descending.status()).toBe(200);
|
|
442
|
+
const descendingUrl = new URL(descending.url());
|
|
443
|
+
expect(JSON.parse(descendingUrl.searchParams.get('orders')!)).toEqual([['name', 'desc']]);
|
|
444
|
+
|
|
445
|
+
const tableWrapper = page
|
|
446
|
+
.locator('div.overflow-x-auto')
|
|
447
|
+
.filter({ has: page.locator('table') })
|
|
448
|
+
.first();
|
|
449
|
+
await expect(tableWrapper).toBeVisible();
|
|
450
|
+
const overflow = await tableWrapper.evaluate(element => {
|
|
451
|
+
return element.scrollWidth > element.clientWidth;
|
|
452
|
+
});
|
|
453
|
+
expect(overflow).toBeTruthy();
|
|
454
|
+
await tableWrapper.evaluate(element => {
|
|
455
|
+
element.scrollLeft = element.scrollWidth;
|
|
456
|
+
});
|
|
457
|
+
await expect(operationsHeader).toHaveCSS('right', '0px');
|
|
458
|
+
|
|
459
|
+
const operationsRow = page
|
|
460
|
+
.locator('tbody tr')
|
|
461
|
+
.filter({
|
|
462
|
+
has: page.getByRole('button', { name: 'Summary', exact: true }),
|
|
463
|
+
})
|
|
464
|
+
.first();
|
|
465
|
+
const operationActions = operationsRow.getByRole('button');
|
|
466
|
+
await expect(operationActions).toHaveCount(4);
|
|
467
|
+
for (const action of await operationActions.all()) {
|
|
468
|
+
await expect(action).toBeVisible();
|
|
469
|
+
}
|
|
470
|
+
const operationsGeometry = await operationActions.evaluateAll(actions => {
|
|
471
|
+
const cell = actions[0]?.closest('td');
|
|
472
|
+
const join = actions[0]?.parentElement;
|
|
473
|
+
const wrapper = cell?.closest('table')?.parentElement;
|
|
474
|
+
if (!cell || !join || !wrapper)
|
|
475
|
+
throw new Error('Could not resolve the Operations action cell');
|
|
476
|
+
const cellRect = cell.getBoundingClientRect();
|
|
477
|
+
const joinRect = join.getBoundingClientRect();
|
|
478
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
479
|
+
const tolerance = 2;
|
|
480
|
+
return {
|
|
481
|
+
fitsCell: join.scrollWidth <= cell.clientWidth,
|
|
482
|
+
actionsFitCell: actions.every(action => {
|
|
483
|
+
const rect = action.getBoundingClientRect();
|
|
484
|
+
return (
|
|
485
|
+
rect.left >= cellRect.left - tolerance && rect.right <= cellRect.right + tolerance
|
|
486
|
+
);
|
|
487
|
+
}),
|
|
488
|
+
joinFitsCell:
|
|
489
|
+
joinRect.left >= cellRect.left - tolerance &&
|
|
490
|
+
joinRect.right <= cellRect.right + tolerance,
|
|
491
|
+
actionsFitWrapper: actions.every(action => {
|
|
492
|
+
const rect = action.getBoundingClientRect();
|
|
493
|
+
return (
|
|
494
|
+
rect.left >= wrapperRect.left - tolerance &&
|
|
495
|
+
rect.right <= wrapperRect.right + tolerance
|
|
496
|
+
);
|
|
497
|
+
}),
|
|
498
|
+
};
|
|
499
|
+
});
|
|
500
|
+
expect(operationsGeometry).toEqual({
|
|
501
|
+
fitsCell: true,
|
|
502
|
+
actionsFitCell: true,
|
|
503
|
+
joinFitsCell: true,
|
|
504
|
+
actionsFitWrapper: true,
|
|
505
|
+
});
|
|
506
|
+
expect(pageErrors).toEqual([]);
|
|
507
|
+
} finally {
|
|
508
|
+
if (studentId !== undefined) {
|
|
509
|
+
const deleteResponse = await page.request.delete(
|
|
510
|
+
`/api/training/student/deleteForce/${studentId}`,
|
|
511
|
+
{ headers },
|
|
512
|
+
);
|
|
513
|
+
expect(deleteResponse.ok()).toBeTruthy();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
);
|
|
518
|
+
|
|
385
519
|
test(
|
|
386
520
|
'ATP-BASIC-FORM-01: Training Student content remains in the Basic Information tab',
|
|
387
521
|
{ tag: ['@admin', '@flow'] },
|
|
@@ -11,7 +11,7 @@ export interface IEntityOptions<%=argv.resourceNameCapitalize%> extends IDecorat
|
|
|
11
11
|
@Entity<IEntityOptions<%=argv.resourceNameCapitalize%>>('<%=argv.moduleResourceName%>', {
|
|
12
12
|
openapi: { title: $locale('<%=argv.resourceNameCapitalize%>') },
|
|
13
13
|
fields: {
|
|
14
|
-
id: $makeMetadata(ZovaRender.order(1, 'core')),
|
|
14
|
+
id: $makeMetadata(ZovaRender.order(1, 'core'), ZovaRender.column({ width: 50, fixed: 'left' })),
|
|
15
15
|
iid: $makeMetadata(ZovaRender.visible(false)),
|
|
16
16
|
deleted: $makeMetadata(ZovaRender.visible(false)),
|
|
17
17
|
createdAt: $makeMetadata(
|
|
@@ -11,7 +11,7 @@ export interface IEntityOptions<%=argv.resourceNameCapitalize%> extends IDecorat
|
|
|
11
11
|
@Entity<IEntityOptions<%=argv.resourceNameCapitalize%>>('<%=argv.moduleResourceName%>', {
|
|
12
12
|
openapi: { title: $locale('<%=argv.resourceNameCapitalize%>') },
|
|
13
13
|
fields: {
|
|
14
|
-
id: $makeMetadata(ZovaRender.order(1, 'core')),
|
|
14
|
+
id: $makeMetadata(ZovaRender.order(1, 'core'), ZovaRender.column({ width: 50, fixed: 'left' })),
|
|
15
15
|
iid: $makeMetadata(ZovaRender.visible(false)),
|
|
16
16
|
deleted: $makeMetadata(ZovaRender.visible(false)),
|
|
17
17
|
createdAt: $makeMetadata(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.92",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
package/vona/pnpm-lock.yaml
CHANGED
|
@@ -548,6 +548,9 @@ importers:
|
|
|
548
548
|
'@tanstack/vue-query':
|
|
549
549
|
specifier: ^5.101.1
|
|
550
550
|
version: 5.101.2(vue@3.5.39(typescript@5.9.3))
|
|
551
|
+
'@tanstack/vue-table':
|
|
552
|
+
specifier: ^8.21.3
|
|
553
|
+
version: 8.21.3(vue@3.5.39(typescript@5.9.3))
|
|
551
554
|
openapi3-ts:
|
|
552
555
|
specifier: ^4.6.0
|
|
553
556
|
version: 4.6.0
|
|
@@ -615,17 +618,17 @@ importers:
|
|
|
615
618
|
specifier: ^5.0.5
|
|
616
619
|
version: 5.0.5
|
|
617
620
|
zova-module-rest-resource:
|
|
618
|
-
specifier: ^5.1.
|
|
621
|
+
specifier: ^5.1.49
|
|
619
622
|
version: 5.1.49
|
|
620
623
|
|
|
621
624
|
.zova-rest/cabloy-basic-web:
|
|
622
625
|
dependencies:
|
|
623
|
-
'@tanstack/query-core':
|
|
624
|
-
specifier: ^5.101.1
|
|
625
|
-
version: 5.101.2
|
|
626
626
|
'@tanstack/vue-query':
|
|
627
627
|
specifier: ^5.101.1
|
|
628
628
|
version: 5.101.2(vue@3.5.39(typescript@5.9.3))
|
|
629
|
+
'@tanstack/vue-table':
|
|
630
|
+
specifier: ^8.21.3
|
|
631
|
+
version: 8.21.3(vue@3.5.39(typescript@5.9.3))
|
|
629
632
|
openapi3-ts:
|
|
630
633
|
specifier: ^4.6.0
|
|
631
634
|
version: 4.6.0
|
|
@@ -639,8 +642,8 @@ importers:
|
|
|
639
642
|
specifier: npm:@cabloy/zod@4.3.8
|
|
640
643
|
version: '@cabloy/zod@4.3.8'
|
|
641
644
|
zova:
|
|
642
|
-
specifier: ^5.1.
|
|
643
|
-
version: 5.1.
|
|
645
|
+
specifier: ^5.1.155
|
|
646
|
+
version: 5.1.155(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
644
647
|
zova-jsx:
|
|
645
648
|
specifier: ^1.1.88
|
|
646
649
|
version: 1.1.88(typescript@5.9.3)
|
|
@@ -657,8 +660,8 @@ importers:
|
|
|
657
660
|
specifier: ^5.1.34
|
|
658
661
|
version: 5.1.34
|
|
659
662
|
zova-module-a-form:
|
|
660
|
-
specifier: ^5.1.
|
|
661
|
-
version: 5.1.
|
|
663
|
+
specifier: ^5.1.50
|
|
664
|
+
version: 5.1.50(vue@3.5.39(typescript@5.9.3))
|
|
662
665
|
zova-module-a-icon:
|
|
663
666
|
specifier: ^5.1.27
|
|
664
667
|
version: 5.1.27
|
|
@@ -666,11 +669,11 @@ importers:
|
|
|
666
669
|
specifier: ^5.1.31
|
|
667
670
|
version: 5.1.31
|
|
668
671
|
zova-module-a-model:
|
|
669
|
-
specifier: ^5.1.
|
|
670
|
-
version: 5.1.
|
|
672
|
+
specifier: ^5.1.36
|
|
673
|
+
version: 5.1.36(vue@3.5.39(typescript@5.9.3))
|
|
671
674
|
zova-module-a-openapi:
|
|
672
|
-
specifier: ^5.1.
|
|
673
|
-
version: 5.1.
|
|
675
|
+
specifier: ^5.1.51
|
|
676
|
+
version: 5.1.51
|
|
674
677
|
zova-module-a-pay:
|
|
675
678
|
specifier: ^5.0.7
|
|
676
679
|
version: 5.0.7
|
|
@@ -693,8 +696,8 @@ importers:
|
|
|
693
696
|
specifier: ^5.0.5
|
|
694
697
|
version: 5.0.5
|
|
695
698
|
zova-module-rest-resource:
|
|
696
|
-
specifier: ^5.1.
|
|
697
|
-
version: 5.1.
|
|
699
|
+
specifier: ^5.1.49
|
|
700
|
+
version: 5.1.49
|
|
698
701
|
|
|
699
702
|
.zova-rest/cabloy-commerce:
|
|
700
703
|
dependencies:
|
|
@@ -1610,7 +1613,7 @@ importers:
|
|
|
1610
1613
|
specifier: ^5.0.3
|
|
1611
1614
|
version: link:modules/a-markdown
|
|
1612
1615
|
vona-module-a-rbac:
|
|
1613
|
-
specifier: ^5.0.
|
|
1616
|
+
specifier: ^5.0.4
|
|
1614
1617
|
version: link:modules/a-rbac
|
|
1615
1618
|
vona-module-a-socket:
|
|
1616
1619
|
specifier: workspace:^
|
|
@@ -8977,9 +8980,6 @@ packages:
|
|
|
8977
8980
|
zova-module-a-fetch@5.1.25:
|
|
8978
8981
|
resolution: {integrity: sha512-kbMB/jIynN0dhivEZLLaY+ic9mU0rZJBeyjXwzAiWuOS330YsxipasxBZhACJD8s29RmLC9EQajI+vVLoBEveQ==}
|
|
8979
8982
|
|
|
8980
|
-
zova-module-a-form@5.1.48:
|
|
8981
|
-
resolution: {integrity: sha512-/YuNFocyP3P6THVrSebi1jRm5+og7YOleBWc7gPzqKUFWTmxN7XBbomRidQWXSRenSd7AWBjNgO3OajhOdswlw==}
|
|
8982
|
-
|
|
8983
8983
|
zova-module-a-form@5.1.49:
|
|
8984
8984
|
resolution: {integrity: sha512-p/faHaDELk2xH4TbuXQ9E+vIUZ4yZYS4URFF0HtrJ+bXDBMCJmjOl3H0encedLh9ZS0dGYpvejHPh3nLvw27JA==}
|
|
8985
8985
|
|
|
@@ -9055,18 +9055,12 @@ packages:
|
|
|
9055
9055
|
zova-module-rest-resource@5.1.49:
|
|
9056
9056
|
resolution: {integrity: sha512-tLf3BLov4GnV1QPivvLro6T2sNdriDZ8vZMsghuUN7dl+PRCG2pI0q9hBCrqe2faRSIcVUXowwnEbEynmaM7Zg==}
|
|
9057
9057
|
|
|
9058
|
-
zova-suite-a-zova@5.1.152:
|
|
9059
|
-
resolution: {integrity: sha512-P/dcMb2fcMpnmDMlrgvpddWUbupnvohD1Jp60VYWoqI0JcWMlYTJvWQWrwszDqiMvMiOTH5mmQBTws0KzbsXpA==}
|
|
9060
|
-
|
|
9061
9058
|
zova-suite-a-zova@5.1.153:
|
|
9062
9059
|
resolution: {integrity: sha512-Q7R6GL5O/Tlqk4cnFYumMR2eeCwAqfnTtFrYQuuQMpz7BmYhAPmMmJQWF2reHlRjG6B4dbOPZ5pxl1iWagY7PA==}
|
|
9063
9060
|
|
|
9064
9061
|
zova-suite-a-zova@5.1.154:
|
|
9065
9062
|
resolution: {integrity: sha512-ii1qFxaBNc4PpNSj+sKgiH0Ryefr4KE0Gb5M8o2Df0hoe8//3uM7QK02GyQDKAK+bu6Q+7mwBZFW8hcD8TrEYw==}
|
|
9066
9063
|
|
|
9067
|
-
zova@5.1.153:
|
|
9068
|
-
resolution: {integrity: sha512-2c+4lhgZgIzM6a066eRU/Ru6CvrSTwRsq1SgsSj+NuLOxUXoj+JPvwUezuRRmBCTukpRMd2nfw8u6vWHzVArTA==}
|
|
9069
|
-
|
|
9070
9064
|
zova@5.1.154:
|
|
9071
9065
|
resolution: {integrity: sha512-QF0f+wwLeuf18hsmZj1/3G/AQ9cNQYMKZiCoBBsTDh7oIhaxsmI5wxtjo59HiOEkk5ZmWPHqwjA4iepTLoKqeg==}
|
|
9072
9066
|
|
|
@@ -15411,14 +15405,6 @@ snapshots:
|
|
|
15411
15405
|
- debug
|
|
15412
15406
|
- supports-color
|
|
15413
15407
|
|
|
15414
|
-
zova-module-a-form@5.1.48(vue@3.5.39(typescript@5.9.3)):
|
|
15415
|
-
dependencies:
|
|
15416
|
-
'@tanstack/vue-form': 1.33.2(vue@3.5.39(typescript@5.9.3))
|
|
15417
|
-
'@tanstack/vue-store': 0.11.0(vue@3.5.39(typescript@5.9.3))
|
|
15418
|
-
transitivePeerDependencies:
|
|
15419
|
-
- '@vue/composition-api'
|
|
15420
|
-
- vue
|
|
15421
|
-
|
|
15422
15408
|
zova-module-a-form@5.1.49(vue@3.5.39(typescript@5.9.3)):
|
|
15423
15409
|
dependencies:
|
|
15424
15410
|
'@tanstack/vue-form': 1.33.2(vue@3.5.39(typescript@5.9.3))
|
|
@@ -15546,40 +15532,6 @@ snapshots:
|
|
|
15546
15532
|
|
|
15547
15533
|
zova-module-rest-resource@5.1.49: {}
|
|
15548
15534
|
|
|
15549
|
-
zova-suite-a-zova@5.1.152(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
15550
|
-
dependencies:
|
|
15551
|
-
zova-module-a-api: 5.1.22
|
|
15552
|
-
zova-module-a-app: 5.1.26
|
|
15553
|
-
zova-module-a-bean: 5.1.33
|
|
15554
|
-
zova-module-a-behavior: 5.1.26
|
|
15555
|
-
zova-module-a-behaviors: 5.1.22
|
|
15556
|
-
zova-module-a-boundary: 5.1.22
|
|
15557
|
-
zova-module-a-command: 5.1.34
|
|
15558
|
-
zova-module-a-fetch: 5.1.25
|
|
15559
|
-
zova-module-a-form: 5.1.49(vue@3.5.39(typescript@5.9.3))
|
|
15560
|
-
zova-module-a-icon: 5.1.27
|
|
15561
|
-
zova-module-a-interceptor: 5.1.31
|
|
15562
|
-
zova-module-a-logger: 5.1.28
|
|
15563
|
-
zova-module-a-meta: 5.1.22
|
|
15564
|
-
zova-module-a-model: 5.1.35(vue@3.5.39(typescript@5.9.3))
|
|
15565
|
-
zova-module-a-openapi: 5.1.50
|
|
15566
|
-
zova-module-a-router: 5.1.34
|
|
15567
|
-
zova-module-a-routerstack: 5.1.27
|
|
15568
|
-
zova-module-a-routertabs: 5.1.34
|
|
15569
|
-
zova-module-a-ssr: 5.1.38
|
|
15570
|
-
zova-module-a-ssrhmr: 5.1.23
|
|
15571
|
-
zova-module-a-ssrserver: 5.1.26
|
|
15572
|
-
zova-module-a-style: 5.1.35
|
|
15573
|
-
zova-module-a-table: 5.1.38(vue@3.5.39(typescript@5.9.3))
|
|
15574
|
-
zova-module-a-zod: 5.1.37
|
|
15575
|
-
zova-module-a-zova: 5.1.103(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
15576
|
-
transitivePeerDependencies:
|
|
15577
|
-
- '@vue/composition-api'
|
|
15578
|
-
- debug
|
|
15579
|
-
- supports-color
|
|
15580
|
-
- typescript
|
|
15581
|
-
- vue
|
|
15582
|
-
|
|
15583
15535
|
zova-suite-a-zova@5.1.153(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
15584
15536
|
dependencies:
|
|
15585
15537
|
zova-module-a-api: 5.1.22
|
|
@@ -15590,13 +15542,13 @@ snapshots:
|
|
|
15590
15542
|
zova-module-a-boundary: 5.1.22
|
|
15591
15543
|
zova-module-a-command: 5.1.34
|
|
15592
15544
|
zova-module-a-fetch: 5.1.25
|
|
15593
|
-
zova-module-a-form: 5.1.
|
|
15545
|
+
zova-module-a-form: 5.1.50(vue@3.5.39(typescript@5.9.3))
|
|
15594
15546
|
zova-module-a-icon: 5.1.27
|
|
15595
15547
|
zova-module-a-interceptor: 5.1.31
|
|
15596
15548
|
zova-module-a-logger: 5.1.28
|
|
15597
15549
|
zova-module-a-meta: 5.1.22
|
|
15598
|
-
zova-module-a-model: 5.1.
|
|
15599
|
-
zova-module-a-openapi: 5.1.
|
|
15550
|
+
zova-module-a-model: 5.1.36(vue@3.5.39(typescript@5.9.3))
|
|
15551
|
+
zova-module-a-openapi: 5.1.51
|
|
15600
15552
|
zova-module-a-router: 5.1.34
|
|
15601
15553
|
zova-module-a-routerstack: 5.1.27
|
|
15602
15554
|
zova-module-a-routertabs: 5.1.34
|
|
@@ -15648,17 +15600,6 @@ snapshots:
|
|
|
15648
15600
|
- typescript
|
|
15649
15601
|
- vue
|
|
15650
15602
|
|
|
15651
|
-
zova@5.1.153(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
15652
|
-
dependencies:
|
|
15653
|
-
zova-core: 5.1.88(patch_hash=144450bd7a567a6067e5857c0cf4c780981b7e90bc48745c09e1696a9b6f1b97)(typescript@5.9.3)
|
|
15654
|
-
zova-suite-a-zova: 5.1.152(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
15655
|
-
transitivePeerDependencies:
|
|
15656
|
-
- '@vue/composition-api'
|
|
15657
|
-
- debug
|
|
15658
|
-
- supports-color
|
|
15659
|
-
- typescript
|
|
15660
|
-
- vue
|
|
15661
|
-
|
|
15662
15603
|
zova@5.1.154(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
15663
15604
|
dependencies:
|
|
15664
15605
|
zova-core: 5.1.88(patch_hash=144450bd7a567a6067e5857c0cf4c780981b7e90bc48745c09e1696a9b6f1b97)(typescript@5.9.3)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
Available: 'Available',
|
|
3
3
|
Category: 'Category',
|
|
4
|
+
CreateProduct: 'Create product',
|
|
4
5
|
Description: 'Description',
|
|
5
6
|
DescriptionHtml: 'Product description (HTML)',
|
|
6
7
|
DescriptionMarkdown: 'Product description (Markdown)',
|
|
@@ -15,6 +16,8 @@ export default {
|
|
|
15
16
|
Unpublished: 'Unpublished',
|
|
16
17
|
Sku: 'SKU',
|
|
17
18
|
SkuAttributeName: 'SKU attribute name',
|
|
19
|
+
UpdateProduct: 'Update product',
|
|
20
|
+
ViewProduct: 'Product details',
|
|
18
21
|
SkuAttributeValue: 'SKU attribute value',
|
|
19
22
|
SkuAttributes: 'SKU attributes',
|
|
20
23
|
SkuCode: 'SKU code',
|