feat(spa): port community sub-screens + drop PrimeVue dependency (Wave 7+8)
Wave 7: rewrite all community/* views (404, Detail, Discover, Offline, Profile, Upload) to use src/manager/components/ui/* primitives + the local useToast composable instead of PrimeVue Button/InputText/Select/Checkbox/ Textarea/useToast. Wave 8: rename toast.add() → toast.push() at remaining list-screen callsites (Combines, Constraints, Derivations, FixedValues, Pipelines, Wildcards) and migrate the imports to ../composables/useToast. Drop the PrimeVue plugin install from src/manager/main.ts, delete styles/preset.ts and the now-empty styles/theme.css (legacy compat aliases inlined into tokens.css), simplify tailwind.css (no more layer ordering), and remove primevue, @primevue/themes, @primevue/forms from package.json. Strip PrimeVue/ToastService/ConfirmationService plugin entries from all __tests__ mounts. Result: zero `from "primevue"` / `from "@primevue"` imports in src/. 338 vitest tests pass, 289 pytest tests pass, vue-tsc clean, extension bundle within budget.
This commit is contained in:
+1
-4
@@ -26,15 +26,12 @@
|
||||
"vue": "^3.5.12",
|
||||
"vue-router": "^4.4.5",
|
||||
"pinia": "^2.2.6",
|
||||
"primevue": "^4.2.5",
|
||||
"primeicons": "^7.0.0",
|
||||
"@primevue/themes": "^4.2.5"
|
||||
"primeicons": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.14",
|
||||
"postcss": "^8.4.47",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"@primevue/forms": "^4.2.5",
|
||||
"@comfyorg/comfyui-frontend-types": "^1.42.15",
|
||||
"@commitlint/cli": "^19.5.0",
|
||||
"@commitlint/config-conventional": "^19.5.0",
|
||||
|
||||
Generated
-104
@@ -8,18 +8,12 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@primevue/themes':
|
||||
specifier: ^4.2.5
|
||||
version: 4.5.4
|
||||
pinia:
|
||||
specifier: ^2.2.6
|
||||
version: 2.3.1(typescript@5.9.3)(vue@3.5.33(typescript@5.9.3))
|
||||
primeicons:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
primevue:
|
||||
specifier: ^4.2.5
|
||||
version: 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
vue:
|
||||
specifier: ^3.5.12
|
||||
version: 3.5.33(typescript@5.9.3)
|
||||
@@ -36,9 +30,6 @@ importers:
|
||||
'@commitlint/config-conventional':
|
||||
specifier: ^19.5.0
|
||||
version: 19.8.1
|
||||
'@primevue/forms':
|
||||
specifier: ^4.2.5
|
||||
version: 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
'@semantic-release/changelog':
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3(semantic-release@25.0.3(typescript@5.9.3))
|
||||
@@ -597,43 +588,6 @@ packages:
|
||||
resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@primeuix/forms@0.1.0':
|
||||
resolution: {integrity: sha512-LctcQidb+B5PuvAFWH24YH/SIzmHlOabLHpaTeGY/k51iBv1WyCp+5w9JMYuMB/BplSvV0ZGySxQVkN5Azr/aQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primeuix/styled@0.7.4':
|
||||
resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primeuix/styles@2.0.3':
|
||||
resolution: {integrity: sha512-2ykAB6BaHzR/6TwF8ShpJTsZrid6cVIEBVlookSdvOdmlWuevGu5vWOScgIwqWwlZcvkFYAGR/SUV3OHCTBMdw==}
|
||||
|
||||
'@primeuix/themes@2.0.3':
|
||||
resolution: {integrity: sha512-3fS1883mtCWhgUgNf/feiaaDSOND4EBIOu9tZnzJlJ8QtYyL6eFLcA6V3ymCWqLVXQ1+lTVEZv1gl47FIdXReg==}
|
||||
|
||||
'@primeuix/utils@0.6.4':
|
||||
resolution: {integrity: sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/core@4.5.5':
|
||||
resolution: {integrity: sha512-JpkXhq1ddc70JdsC3CC4dM+UbeeWuCW/8DpS9dNBfrOk824TLSlRlMEGFyVKqRMn5WPQvYLiy3xXfLQeNdSqhQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@primevue/forms@4.5.5':
|
||||
resolution: {integrity: sha512-LUeIt6oItwCZyBreQ7ycErE8aEjPmBWOTq1VPLhyaATcnzMBOZRIiwclaYk8s/tf5VYMqN9N4B80XnXoO7OdvQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/icons@4.5.5':
|
||||
resolution: {integrity: sha512-eteOhTdAOXEYE9qW1AOrBBgDxQ2szHJxSkEK1XVdV2TKxGM5FQf03Ovms0VDyZTc16XBIgvwYjXJQS0BPbhPaA==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/themes@4.5.4':
|
||||
resolution: {integrity: sha512-rUFZxMHLanTZdvZq4zgZPk+KRBZ3s7fE3bBK32OrZBkHQhEJmkJ7Ftd4w4QFlXyz1B7c+k5invZiOOCjwHXg9Q==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
deprecated: 'Deprecated. This package is no longer maintained. Please migrate to @primeuix/themes: https://www.npmjs.com/package/@primeuix/themes'
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.60.2':
|
||||
resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==}
|
||||
cpu: [arm]
|
||||
@@ -2525,10 +2479,6 @@ packages:
|
||||
primeicons@7.0.0:
|
||||
resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==}
|
||||
|
||||
primevue@4.5.5:
|
||||
resolution: {integrity: sha512-Kv5REIewCdP806QaoU+4nBXfmpzOGFKkZ9qH4KsL6MjiAQVc4PUzypt8erl4r3Vzh3nr3aWZIxkxYRRsLGiX2A==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
@@ -3644,50 +3594,6 @@ snapshots:
|
||||
'@pnpm/network.ca-file': 1.0.2
|
||||
config-chain: 1.1.13
|
||||
|
||||
'@primeuix/forms@0.1.0':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.6.4
|
||||
|
||||
'@primeuix/styled@0.7.4':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.6.4
|
||||
|
||||
'@primeuix/styles@2.0.3':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.7.4
|
||||
|
||||
'@primeuix/themes@2.0.3':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.7.4
|
||||
|
||||
'@primeuix/utils@0.6.4': {}
|
||||
|
||||
'@primevue/core@4.5.5(vue@3.5.33(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.7.4
|
||||
'@primeuix/utils': 0.6.4
|
||||
vue: 3.5.33(typescript@5.9.3)
|
||||
|
||||
'@primevue/forms@4.5.5(vue@3.5.33(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@primeuix/forms': 0.1.0
|
||||
'@primeuix/utils': 0.6.4
|
||||
'@primevue/core': 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@primevue/icons@4.5.5(vue@3.5.33(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.6.4
|
||||
'@primevue/core': 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@primevue/themes@4.5.4':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.7.4
|
||||
'@primeuix/themes': 2.0.3
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.60.2':
|
||||
optional: true
|
||||
|
||||
@@ -5534,16 +5440,6 @@ snapshots:
|
||||
|
||||
primeicons@7.0.0: {}
|
||||
|
||||
primevue@4.5.5(vue@3.5.33(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.7.4
|
||||
'@primeuix/styles': 2.0.3
|
||||
'@primeuix/utils': 0.6.4
|
||||
'@primevue/core': 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
'@primevue/icons': 4.5.5(vue@3.5.33(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
proto-list@1.2.4: {}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
categories: {
|
||||
@@ -39,7 +36,7 @@ describe("Categories.vue", () => {
|
||||
it("renders the page header and 'New category' card", async () => {
|
||||
apiCat.list.mockResolvedValue({ items: [] });
|
||||
const wrap = mount(Categories, {
|
||||
global: { plugins: [PrimeVue, ToastService] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Categories");
|
||||
@@ -56,7 +53,7 @@ describe("Categories.vue", () => {
|
||||
],
|
||||
});
|
||||
const wrap = mount(Categories, {
|
||||
global: { plugins: [PrimeVue, ToastService] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.find('[data-test="cat-row-cat_a"]').exists()).toBe(true);
|
||||
@@ -80,7 +77,7 @@ describe("Categories.vue", () => {
|
||||
total: 3,
|
||||
});
|
||||
const wrap = mount(Categories, {
|
||||
global: { plugins: [PrimeVue, ToastService] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.get('[data-test="cat-count-cat_a"]').text()).toBe("2");
|
||||
@@ -92,7 +89,7 @@ describe("Categories.vue", () => {
|
||||
id: "cat_new", name: "Mood", color: "#fbbf24", icon: null, sort_order: 0,
|
||||
});
|
||||
const wrap = mount(Categories, {
|
||||
global: { plugins: [PrimeVue, ToastService] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
await flushPromises();
|
||||
await wrap.get('[data-test="new-cat-name"]').setValue("Mood");
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ColorPicker from "../components/ColorPicker.vue";
|
||||
|
||||
function makeWrapper(modelValue = "#7c3aed") {
|
||||
return mount(ColorPicker, {
|
||||
props: { modelValue },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,7 +55,7 @@ describe("ColorPicker.vue", () => {
|
||||
it("uses provided ariaLabel on the swatch button", () => {
|
||||
const wrap = mount(ColorPicker, {
|
||||
props: { modelValue: "#abcdef", ariaLabel: "Pick brand color" },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
expect(wrap.get('[data-test="color-swatch"]').attributes("aria-label"))
|
||||
.toBe("Pick brand color");
|
||||
@@ -65,7 +64,7 @@ describe("ColorPicker.vue", () => {
|
||||
it("supports custom presets prop", async () => {
|
||||
const wrap = mount(ColorPicker, {
|
||||
props: { modelValue: "#000000", presets: ["#111111", "#222222"] },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
await openPopover(wrap);
|
||||
expect(wrap.findAll('[data-test="color-preset"]').length).toBe(2);
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -47,7 +44,7 @@ function makeRouter() {
|
||||
describe("CombineEditor.vue", () => {
|
||||
it("renders 'New combine' heading when no id", async () => {
|
||||
const wrap = mount(CombineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New combine");
|
||||
@@ -66,7 +63,7 @@ describe("CombineEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(CombineEditor, {
|
||||
props: { id: "cb_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit combine");
|
||||
@@ -77,7 +74,7 @@ describe("CombineEditor.vue", () => {
|
||||
|
||||
it("save without name does not call api", async () => {
|
||||
const wrap = mount(CombineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const saveBtn = wrap.find('[data-test="save-btn"]');
|
||||
@@ -94,7 +91,7 @@ describe("CombineEditor.vue", () => {
|
||||
version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(CombineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -116,7 +113,7 @@ describe("CombineEditor.vue", () => {
|
||||
|
||||
it("detected inputs panel shows the $vars from the template", async () => {
|
||||
const wrap = mount(CombineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const ta = wrap.find('[data-test="cb-template"] textarea');
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Combines, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../community/mockApi", async () => {
|
||||
const actual = await vi.importActual<typeof import("../community/mockApi")>("../community/mockApi");
|
||||
return {
|
||||
@@ -79,7 +76,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(CommunityDiscover, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../community/mockApi", async () => {
|
||||
const actual = await vi.importActual<typeof import("../community/mockApi")>("../community/mockApi");
|
||||
return {
|
||||
@@ -44,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(CommunityUpload, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -72,7 +69,7 @@ function makeRouter() {
|
||||
describe("ConstraintEditor.vue", () => {
|
||||
it("renders 'New constraint' heading when no id", async () => {
|
||||
const wrap = mount(ConstraintEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New constraint");
|
||||
@@ -99,7 +96,7 @@ describe("ConstraintEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(ConstraintEditor, {
|
||||
props: { id: "cn_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit constraint");
|
||||
@@ -128,7 +125,7 @@ describe("ConstraintEditor.vue", () => {
|
||||
version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(ConstraintEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -157,7 +154,7 @@ describe("ConstraintEditor.vue", () => {
|
||||
|
||||
it("save without source/target shows warn and does not call api", async () => {
|
||||
const wrap = mount(ConstraintEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ConstraintMatrix from "../components/ConstraintMatrix.vue";
|
||||
import type { ConstraintMatrix as Matrix } from "../api/types";
|
||||
|
||||
@@ -15,7 +14,7 @@ function lastEmitted(
|
||||
function mountGrid(modelValue: Matrix = {}, rows = ["red", "blue"], cols = ["warm", "cool"]) {
|
||||
return mount(ConstraintMatrix, {
|
||||
props: { rows, cols, modelValue },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Constraints, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -59,7 +55,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Dashboard, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +98,7 @@ describe("Dashboard.vue", () => {
|
||||
const router = makeRouter();
|
||||
const push = vi.spyOn(router, "push");
|
||||
const wrap = mount(Dashboard, {
|
||||
global: { plugins: [router, PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [router] },
|
||||
});
|
||||
await flushPromises();
|
||||
const stats = wrap.findAll(".dashboard__stat");
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -47,7 +44,7 @@ function makeRouter() {
|
||||
describe("DerivationEditor.vue", () => {
|
||||
it("renders 'New derivation' heading when no id", async () => {
|
||||
const wrap = mount(DerivationEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New derivation");
|
||||
@@ -56,7 +53,7 @@ describe("DerivationEditor.vue", () => {
|
||||
|
||||
it("starts with no rules and shows the empty-state card", async () => {
|
||||
const wrap = mount(DerivationEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.find('[data-test="rules-empty"]').exists()).toBe(true);
|
||||
@@ -66,7 +63,7 @@ describe("DerivationEditor.vue", () => {
|
||||
|
||||
it("Add rule increments the rules length", async () => {
|
||||
const wrap = mount(DerivationEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const vm = wrap.vm as unknown as { rules: DerivationRule[] };
|
||||
@@ -85,7 +82,7 @@ describe("DerivationEditor.vue", () => {
|
||||
payload: { rules: [] }, version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(DerivationEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -133,7 +130,7 @@ describe("DerivationEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(DerivationEditor, {
|
||||
props: { id: "dv_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit derivation");
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
|
||||
import DerivationRuleCard from "../components/DerivationRuleCard.vue";
|
||||
import type { DerivationRule } from "../api/types";
|
||||
|
||||
@@ -21,7 +19,7 @@ function makeRule(overrides: Partial<DerivationRule> = {}): DerivationRule {
|
||||
function mountCard(rule: DerivationRule, index = 0) {
|
||||
return mount(DerivationRuleCard, {
|
||||
props: { modelValue: rule, index },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Derivations, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -46,7 +43,7 @@ function makeRouter() {
|
||||
describe("FixedEditor.vue", () => {
|
||||
it("renders 'New fixed values' heading when no id", async () => {
|
||||
const wrap = mount(FixedEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New fixed values");
|
||||
@@ -61,7 +58,7 @@ describe("FixedEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(FixedEditor, {
|
||||
props: { id: "fv_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit fixed values");
|
||||
@@ -72,7 +69,7 @@ describe("FixedEditor.vue", () => {
|
||||
|
||||
it("save without name shows warn toast and does not call api", async () => {
|
||||
const wrap = mount(FixedEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const saveBtn = wrap.find('[data-test="save-btn"]');
|
||||
@@ -83,7 +80,7 @@ describe("FixedEditor.vue", () => {
|
||||
|
||||
it("flags invalid identifier rows with inline error", async () => {
|
||||
const wrap = mount(FixedEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -105,7 +102,7 @@ describe("FixedEditor.vue", () => {
|
||||
version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(FixedEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(FixedValues, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import HistoryPanel from "../components/HistoryPanel.vue";
|
||||
import type { ModuleHistoryEntry } from "../api/types";
|
||||
|
||||
@@ -20,7 +19,7 @@ describe("HistoryPanel.vue", () => {
|
||||
it("does not render when closed", () => {
|
||||
const wrap = mount(HistoryPanel, {
|
||||
props: { open: false, entries: [] },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
attachTo: document.body,
|
||||
});
|
||||
// Teleport puts the contents on body when open=true; closed = nothing.
|
||||
@@ -31,7 +30,7 @@ describe("HistoryPanel.vue", () => {
|
||||
it("renders empty state when no entries", async () => {
|
||||
const wrap = mount(HistoryPanel, {
|
||||
props: { open: true, entries: [] },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
attachTo: document.body,
|
||||
});
|
||||
const empty = document.body.querySelector('[data-test="history-empty"]');
|
||||
@@ -47,7 +46,7 @@ describe("HistoryPanel.vue", () => {
|
||||
];
|
||||
const wrap = mount(HistoryPanel, {
|
||||
props: { open: true, entries },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
attachTo: document.body,
|
||||
});
|
||||
const list = document.body.querySelector('[data-test="history-list"]');
|
||||
@@ -72,7 +71,7 @@ describe("HistoryPanel.vue", () => {
|
||||
it("emits update:open=false when close button clicked", async () => {
|
||||
const wrap = mount(HistoryPanel, {
|
||||
props: { open: true, entries: [makeEntry()] },
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
attachTo: document.body,
|
||||
});
|
||||
const closeBtn = document.body.querySelector(
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -60,7 +57,7 @@ afterEach(() => vi.clearAllMocks());
|
||||
|
||||
function mountView() {
|
||||
return mount(ImportExport, {
|
||||
global: { plugins: [PrimeVue, ToastService] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -46,7 +43,7 @@ function makeRouter() {
|
||||
describe("PipelineEditor.vue", () => {
|
||||
it("renders 'New pipeline' heading when no id", async () => {
|
||||
const wrap = mount(PipelineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New pipeline");
|
||||
@@ -61,7 +58,7 @@ describe("PipelineEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(PipelineEditor, {
|
||||
props: { id: "pl_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit pipeline");
|
||||
@@ -70,7 +67,7 @@ describe("PipelineEditor.vue", () => {
|
||||
|
||||
it("save without name shows warn toast and skips API", async () => {
|
||||
const wrap = mount(PipelineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const saveBtn = wrap.find('[data-test="save-btn"]');
|
||||
@@ -86,7 +83,7 @@ describe("PipelineEditor.vue", () => {
|
||||
payload: { steps: [] }, version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(PipelineEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import PipelineSteps from "../components/PipelineSteps.vue";
|
||||
import type { ModuleRow, ModuleType, PipelineStep } from "../api/types";
|
||||
|
||||
@@ -42,7 +41,7 @@ function mountSteps(steps: PipelineStep[], modules: ModuleRow[]) {
|
||||
modulesById,
|
||||
modulesByKind: buildKindGroups(modules),
|
||||
},
|
||||
global: { plugins: [PrimeVue] },
|
||||
global: { plugins: [] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Pipelines, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import type { ModuleRow } from "../api/types";
|
||||
|
||||
vi.mock("../api/client", () => {
|
||||
@@ -105,7 +103,7 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
function mountRunner() {
|
||||
return mount(TestRunner, { global: { plugins: [PrimeVue, ToastService] } });
|
||||
return mount(TestRunner, { global: { plugins: [] } });
|
||||
}
|
||||
|
||||
describe("TestRunner.vue", () => {
|
||||
|
||||
@@ -2,9 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -50,7 +47,7 @@ function findByText(wrap: ReturnType<typeof mount>, text: string) {
|
||||
describe("WildcardEditor.vue", () => {
|
||||
it("renders 'New wildcard' heading when no id", async () => {
|
||||
const wrap = mount(WildcardEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("New wildcard");
|
||||
@@ -64,7 +61,7 @@ describe("WildcardEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(WildcardEditor, {
|
||||
props: { id: "wc_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(wrap.text()).toContain("Edit wildcard");
|
||||
@@ -78,7 +75,7 @@ describe("WildcardEditor.vue", () => {
|
||||
payload: { options: [] }, version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(WildcardEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -94,7 +91,7 @@ describe("WildcardEditor.vue", () => {
|
||||
|
||||
it("save without name shows warn toast and does not call api", async () => {
|
||||
const wrap = mount(WildcardEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const saveBtn = wrap.find('[data-test="save-btn"]');
|
||||
@@ -105,7 +102,7 @@ describe("WildcardEditor.vue", () => {
|
||||
|
||||
it("auto-derives var_binding from name while untouched", async () => {
|
||||
const wrap = mount(WildcardEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -124,7 +121,7 @@ describe("WildcardEditor.vue", () => {
|
||||
version: 1, created_at: "", updated_at: "",
|
||||
});
|
||||
const wrap = mount(WildcardEditor, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
@@ -146,7 +143,7 @@ describe("WildcardEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(WildcardEditor, {
|
||||
props: { id: "wc_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const rti = wrap.findComponent({ name: "RichTextInput" });
|
||||
@@ -163,7 +160,7 @@ describe("WildcardEditor.vue", () => {
|
||||
});
|
||||
const wrap = mount(WildcardEditor, {
|
||||
props: { id: "wc_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const varInput = wrap.find('[data-test="identity-var-binding"]')
|
||||
@@ -195,7 +192,7 @@ describe("WildcardEditor.vue", () => {
|
||||
|
||||
const wrap = mount(WildcardEditor, {
|
||||
props: { id: "wc_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
@@ -219,7 +216,7 @@ describe("WildcardEditor.vue", () => {
|
||||
}));
|
||||
const wrap = mount(WildcardEditor, {
|
||||
props: { id: "wc_a" },
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
await flushPromises();
|
||||
const nameInput = wrap.find('[data-test="identity-name"]');
|
||||
|
||||
@@ -2,10 +2,6 @@ import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createMemoryHistory, createRouter } from "vue-router";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
api: {
|
||||
modules: {
|
||||
@@ -45,7 +41,7 @@ function makeRouter() {
|
||||
|
||||
function mountView() {
|
||||
return mount(Wildcards, {
|
||||
global: { plugins: [makeRouter(), PrimeVue, ToastService, ConfirmationService] },
|
||||
global: { plugins: [makeRouter()] },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-16
@@ -1,11 +1,7 @@
|
||||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import PrimeVue from "primevue/config";
|
||||
import { WildcardPreset } from "./styles/preset";
|
||||
import ToastService from "primevue/toastservice";
|
||||
import ConfirmationService from "primevue/confirmationservice";
|
||||
import "primeicons/primeicons.css";
|
||||
import "./styles/theme.css";
|
||||
import "./styles/tokens.css";
|
||||
import "./styles/community.css";
|
||||
import "./styles/tailwind.css";
|
||||
import App from "./App.vue";
|
||||
@@ -20,15 +16,4 @@ app.use(router);
|
||||
// Apply persisted theme before mount so the first paint is correct.
|
||||
// `useUiStore` reads localStorage and listens to OS preference changes.
|
||||
useUiStore(pinia).initializeTheme();
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: WildcardPreset,
|
||||
options: {
|
||||
darkModeSelector: ".wp-dark",
|
||||
cssLayer: { name: "primevue", order: "tailwind-base, primevue, tailwind-utilities" },
|
||||
},
|
||||
},
|
||||
});
|
||||
app.use(ToastService);
|
||||
app.use(ConfirmationService);
|
||||
app.mount("#app");
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { definePreset } from "@primevue/themes";
|
||||
import Aura from "@primevue/themes/aura";
|
||||
|
||||
/** Aura derivative driven by the SPA `--wp-*` token vocabulary so PrimeVue
|
||||
* components (Button, DataTable, Tag, Toast) render with project colors
|
||||
* out of the box.
|
||||
*
|
||||
* The `primary` palette mirrors the prototype's violet accent scale (see
|
||||
* src/manager/styles/tokens.css). 950 is extrapolated as accent-900 mixed
|
||||
* ~30% with #000 (no 950 stop is defined in the prototype).
|
||||
*
|
||||
* Surface colors point at `--wp-bg*` so flipping the theme tokens at runtime
|
||||
* also flips the PrimeVue surface scope.
|
||||
*/
|
||||
export const WildcardPreset = definePreset(Aura, {
|
||||
semantic: {
|
||||
primary: {
|
||||
50: "#f5f3ff",
|
||||
100: "#ede9fe",
|
||||
200: "#ddd6fe",
|
||||
300: "#c4b5fd",
|
||||
400: "#a78bfa",
|
||||
500: "#8b5cf6",
|
||||
600: "#7c3aed",
|
||||
700: "#6d28d9",
|
||||
800: "#5b21b6",
|
||||
900: "#4c1d95",
|
||||
950: "#3b1480",
|
||||
},
|
||||
colorScheme: {
|
||||
dark: {
|
||||
surface: {
|
||||
0: "var(--wp-text)",
|
||||
50: "var(--wp-bg-4)",
|
||||
100: "var(--wp-bg-4)",
|
||||
200: "var(--wp-text-dim)",
|
||||
300: "var(--wp-text-muted)",
|
||||
400: "var(--wp-text-muted)",
|
||||
500: "var(--wp-text-muted)",
|
||||
600: "var(--wp-border-strong)",
|
||||
700: "var(--wp-bg-4)",
|
||||
800: "var(--wp-bg-3)",
|
||||
900: "var(--wp-bg-2)",
|
||||
950: "var(--wp-bg-1)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,52 +1,3 @@
|
||||
/* Declare layer order so PrimeVue's `primevue` layer sits between Tailwind's
|
||||
* base reset and Tailwind's utilities. Matches the cssLayer config in main.ts.
|
||||
* Without layers, PrimeVue + Tailwind utility specificity collide.
|
||||
*/
|
||||
@layer tailwind-base, primevue, tailwind-utilities;
|
||||
|
||||
@layer tailwind-base {
|
||||
@tailwind base;
|
||||
}
|
||||
|
||||
@layer tailwind-utilities {
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
}
|
||||
|
||||
/* Compact button + form-control density for the SPA. PrimeVue Aura defaults
|
||||
* are sized for large touch targets; this app is keyboard-first so we use
|
||||
* a tighter rhythm (matches the old project). */
|
||||
.p-button {
|
||||
padding: 0.4rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.p-button-sm,
|
||||
.p-button[data-pc-section="root"][size="small"] {
|
||||
padding: 0.3rem 0.6rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.p-button-icon-only {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
.p-button-icon-only.p-button-sm {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
/* Tighter inputs, dropdowns, selects to match button density. */
|
||||
.p-inputtext,
|
||||
.p-select,
|
||||
.p-inputnumber-input {
|
||||
padding: 0.4rem 0.6rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.p-textarea {
|
||||
padding: 0.5rem 0.6rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
/* DataTable rows */
|
||||
.p-datatable-tbody > tr > td,
|
||||
.p-datatable-thead > tr > th {
|
||||
padding: 0.55rem 0.75rem;
|
||||
}
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/* Centralized stylesheet entry for the SPA Manager.
|
||||
*
|
||||
* Re-exports the design-token vocabulary extracted from the React/HTML
|
||||
* prototype at docs/design-handoff/wildcardpipeline/project/styles.css,
|
||||
* plus a small compatibility layer that maps the legacy `--wp-*` token
|
||||
* names used by existing .vue components onto the new vocabulary.
|
||||
*
|
||||
* Imported by src/manager/main.ts before tailwind.css so utilities can
|
||||
* reference any --wp-* token.
|
||||
*/
|
||||
@import "./tokens.css";
|
||||
|
||||
/* PrimeVue 4 uses `darkModeSelector: ".wp-dark"` (see src/manager/main.ts).
|
||||
* Mark the dark scope explicitly. The class is applied eagerly on <html>
|
||||
* for the v1 dark-only experience. */
|
||||
html.wp-dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* ---------- Legacy compatibility aliases ----------
|
||||
* Existing .vue components reference an older flat set of token names
|
||||
* (--wp-bg2, --wp-text2, --wp-accent, etc.). Map them to the new tokens
|
||||
* so the screen-by-screen port can roll out without a flag-day rewrite. */
|
||||
:root {
|
||||
/* Surfaces */
|
||||
--wp-bg2: var(--wp-bg-2);
|
||||
--wp-bg3: var(--wp-bg-3);
|
||||
--wp-bg4: var(--wp-bg-4);
|
||||
|
||||
/* Text tiers */
|
||||
--wp-text2: var(--wp-text-muted);
|
||||
--wp-text3: var(--wp-text-dim);
|
||||
--wp-text4: var(--wp-text-dim);
|
||||
|
||||
/* Borders */
|
||||
--wp-border2: var(--wp-border-strong);
|
||||
|
||||
/* Accent — old flat aliases */
|
||||
--wp-accent: var(--wp-accent-500);
|
||||
--wp-accent2: var(--wp-accent-400);
|
||||
--wp-accent-glow: rgba(139, 92, 246, 0.15);
|
||||
|
||||
/* Module-kind aliases (legacy single-color tokens) */
|
||||
--wp-violet: var(--wp-kind-wildcard);
|
||||
--wp-violet-bg: var(--wp-kind-wildcard-bg);
|
||||
--wp-teal: var(--wp-kind-fixed);
|
||||
--wp-teal-bg: var(--wp-kind-fixed-bg);
|
||||
--wp-green: var(--wp-kind-combine);
|
||||
--wp-green-bg: var(--wp-kind-combine-bg);
|
||||
--wp-amber: var(--wp-kind-derivation);
|
||||
--wp-amber-bg: var(--wp-kind-derivation-bg);
|
||||
--wp-rose: var(--wp-kind-constraint);
|
||||
--wp-rose-bg: var(--wp-kind-constraint-bg);
|
||||
--wp-red: var(--wp-danger);
|
||||
--wp-red-bg: rgba(239, 68, 68, 0.12);
|
||||
|
||||
/* Type alias */
|
||||
--wp-font-sans: var(--wp-font);
|
||||
}
|
||||
@@ -2221,3 +2221,52 @@ input, textarea, select { font: inherit; }
|
||||
:root {
|
||||
--wp-brand-gradient: linear-gradient(135deg, var(--wp-brand-1), var(--wp-brand-2), var(--wp-brand-3));
|
||||
}
|
||||
|
||||
/* ---------- Color-scheme hint ----------
|
||||
* The app applies `wp-dark` / `wp-theme-light` classes on <html> from
|
||||
* stores/uiStore.ts. Mark the dark scope explicitly so form controls (date
|
||||
* pickers, scrollbars) respect the theme. */
|
||||
html.wp-dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* ---------- Legacy compatibility aliases ----------
|
||||
* Existing .vue components reference an older flat set of token names
|
||||
* (--wp-bg2, --wp-text2, --wp-accent, etc.). Map them to the new tokens
|
||||
* so the screen-by-screen port can roll out without a flag-day rewrite. */
|
||||
:root {
|
||||
/* Surfaces */
|
||||
--wp-bg2: var(--wp-bg-2);
|
||||
--wp-bg3: var(--wp-bg-3);
|
||||
--wp-bg4: var(--wp-bg-4);
|
||||
|
||||
/* Text tiers */
|
||||
--wp-text2: var(--wp-text-muted);
|
||||
--wp-text3: var(--wp-text-dim);
|
||||
--wp-text4: var(--wp-text-dim);
|
||||
|
||||
/* Borders */
|
||||
--wp-border2: var(--wp-border-strong);
|
||||
|
||||
/* Accent — old flat aliases */
|
||||
--wp-accent: var(--wp-accent-500);
|
||||
--wp-accent2: var(--wp-accent-400);
|
||||
--wp-accent-glow: rgba(139, 92, 246, 0.15);
|
||||
|
||||
/* Module-kind aliases (legacy single-color tokens) */
|
||||
--wp-violet: var(--wp-kind-wildcard);
|
||||
--wp-violet-bg: var(--wp-kind-wildcard-bg);
|
||||
--wp-teal: var(--wp-kind-fixed);
|
||||
--wp-teal-bg: var(--wp-kind-fixed-bg);
|
||||
--wp-green: var(--wp-kind-combine);
|
||||
--wp-green-bg: var(--wp-kind-combine-bg);
|
||||
--wp-amber: var(--wp-kind-derivation);
|
||||
--wp-amber-bg: var(--wp-kind-derivation-bg);
|
||||
--wp-rose: var(--wp-kind-constraint);
|
||||
--wp-rose-bg: var(--wp-kind-constraint-bg);
|
||||
--wp-red: var(--wp-danger);
|
||||
--wp-red-bg: rgba(239, 68, 68, 0.12);
|
||||
|
||||
/* Type alias */
|
||||
--wp-font-sans: var(--wp-font);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
import Checkbox from "../components/ui/Checkbox.vue";
|
||||
@@ -42,7 +42,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,32 +53,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
import Checkbox from "../components/ui/Checkbox.vue";
|
||||
@@ -56,7 +56,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,32 +67,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
import Checkbox from "../components/ui/Checkbox.vue";
|
||||
@@ -49,7 +49,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,32 +60,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
import Checkbox from "../components/ui/Checkbox.vue";
|
||||
@@ -42,7 +42,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,32 +53,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
import Checkbox from "../components/ui/Checkbox.vue";
|
||||
@@ -54,7 +54,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,32 +65,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from "../composables/useToast";
|
||||
import ModuleListView from "../components/ModuleListView.vue";
|
||||
import RichTextPreview from "../components/RichTextPreview.vue";
|
||||
import Button from "../components/ui/Button.vue";
|
||||
@@ -91,7 +91,7 @@ async function fetch() {
|
||||
try {
|
||||
await store.fetchAll();
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Load failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,32 +102,32 @@ function edit(row: ModuleRow) {
|
||||
async function copyId(id: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
toast.add({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
toast.push({ severity: "info", summary: "ID copied", detail: id, life: 1500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
toast.push({ severity: "error", summary: "Copy failed", life: 2000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function dup(row: ModuleRow) {
|
||||
try {
|
||||
await store.duplicate(row.id);
|
||||
toast.add({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Duplicated", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Duplicate failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function fav(row: ModuleRow) {
|
||||
try { await store.toggleFavorite(row.id); }
|
||||
catch (e) { toast.add({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
catch (e) { toast.push({ severity: "error", summary: "Favorite failed", detail: String(e), life: 4000 }); }
|
||||
}
|
||||
|
||||
async function del(row: ModuleRow) {
|
||||
try {
|
||||
await store.remove(row.id);
|
||||
toast.add({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
toast.push({ severity: "success", summary: "Deleted", detail: row.name, life: 2000 });
|
||||
} catch (e) {
|
||||
toast.add({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
toast.push({ severity: "error", summary: "Delete failed", detail: String(e), life: 4000 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Community → 404 state.
|
||||
*/
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -18,7 +18,7 @@ function back() {
|
||||
<div class="big">404</div>
|
||||
<h3>Module not found</h3>
|
||||
<p>It might have been unpublished, renamed, or moved.</p>
|
||||
<Button label="Back to Discover" icon="pi pi-arrow-left" class="mt-3" @click="back" />
|
||||
<Button icon="arrow-left" class="mt-3" @click="back">Back to Discover</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
import { useToast } from "../../composables/useToast";
|
||||
import { useCommunityStore } from "../../stores/communityStore";
|
||||
import { engineSatisfies } from "../../community/mockApi";
|
||||
import { renderMarkdown } from "../../community/markdown";
|
||||
@@ -68,9 +68,9 @@ async function onInstall() {
|
||||
installing.value = true;
|
||||
try {
|
||||
await store.install(atom.value.id);
|
||||
toast.add({ severity: "success", summary: "Installed", detail: atom.value.name, life: 2500 });
|
||||
toast.push({ severity: "success", summary: "Installed", detail: atom.value.name, life: 2500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Install failed", detail: "Registry unreachable.", life: 3000 });
|
||||
toast.push({ severity: "error", summary: "Install failed", detail: "Registry unreachable.", life: 3000 });
|
||||
} finally {
|
||||
installing.value = false;
|
||||
}
|
||||
@@ -80,16 +80,16 @@ async function onUninstall() {
|
||||
if (!atom.value) return;
|
||||
try {
|
||||
await store.uninstall(atom.value.id);
|
||||
toast.add({ severity: "info", summary: "Removed", detail: atom.value.name, life: 2500 });
|
||||
toast.push({ severity: "info", summary: "Removed", detail: atom.value.name, life: 2500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Could not remove", life: 3000 });
|
||||
toast.push({ severity: "error", summary: "Could not remove", life: 3000 });
|
||||
}
|
||||
}
|
||||
|
||||
async function onStar() {
|
||||
if (!atom.value) return;
|
||||
if (!store.currentUser) {
|
||||
toast.add({ severity: "info", summary: "Sign in to star modules.", life: 2500 });
|
||||
toast.push({ severity: "info", summary: "Sign in to star modules.", life: 2500 });
|
||||
return;
|
||||
}
|
||||
await store.toggleStar(atom.value.id);
|
||||
@@ -137,13 +137,11 @@ onMounted(() => load());
|
||||
<template>
|
||||
<div class="wp-comm-page">
|
||||
<Button
|
||||
label="Back to Community"
|
||||
icon="pi pi-arrow-left"
|
||||
severity="secondary"
|
||||
text
|
||||
variant="ghost"
|
||||
icon="arrow-left"
|
||||
class="self-start"
|
||||
@click="onBack"
|
||||
/>
|
||||
>Back to Community</Button>
|
||||
|
||||
<div v-if="loading" class="wp-comm-empty">Loading...</div>
|
||||
|
||||
@@ -180,29 +178,26 @@ onMounted(() => load());
|
||||
<div class="wp-comm-detail-hero__actions">
|
||||
<Button
|
||||
v-if="installed"
|
||||
label="Installed — Remove"
|
||||
icon="pi pi-check"
|
||||
severity="secondary"
|
||||
variant="secondary"
|
||||
icon="check"
|
||||
@click="onUninstall"
|
||||
/>
|
||||
>Installed — Remove</Button>
|
||||
<Button
|
||||
v-else
|
||||
:label="atom.type === 'bundle' ? 'Install pack' : 'Install module'"
|
||||
icon="pi pi-download"
|
||||
variant="primary"
|
||||
icon="download"
|
||||
:disabled="!compatible || installing"
|
||||
@click="onInstall"
|
||||
/>
|
||||
>{{ atom.type === 'bundle' ? 'Install pack' : 'Install module' }}</Button>
|
||||
<Button
|
||||
:label="starred ? 'Starred' : 'Star'"
|
||||
:icon="starred ? 'pi pi-star-fill' : 'pi pi-star'"
|
||||
severity="secondary"
|
||||
variant="secondary"
|
||||
:icon="starred ? 'star-fill' : 'star'"
|
||||
@click="onStar"
|
||||
/>
|
||||
>{{ starred ? 'Starred' : 'Star' }}</Button>
|
||||
<Button
|
||||
icon="pi pi-flag"
|
||||
variant="ghost"
|
||||
icon="flag"
|
||||
aria-label="Report module"
|
||||
severity="secondary"
|
||||
text
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
*/
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import InputText from "primevue/inputtext";
|
||||
import Select from "primevue/select";
|
||||
import Checkbox from "primevue/checkbox";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
import Input from "../../components/ui/Input.vue";
|
||||
import Select from "../../components/ui/Select.vue";
|
||||
import Checkbox from "../../components/ui/Checkbox.vue";
|
||||
import { useCommunityStore } from "../../stores/communityStore";
|
||||
import CommunityCard from "../../community/CommunityCard.vue";
|
||||
import { KIND_ICON, KIND_LABEL, fmtNumber } from "../../community/format";
|
||||
@@ -102,17 +102,15 @@ onMounted(async () => {
|
||||
</div>
|
||||
<div class="wp-comm-page__actions">
|
||||
<Button
|
||||
label="My profile"
|
||||
icon="pi pi-user"
|
||||
severity="secondary"
|
||||
outlined
|
||||
variant="outline"
|
||||
icon="user"
|
||||
@click="router.push('/community/profile')"
|
||||
/>
|
||||
>My profile</Button>
|
||||
<Button
|
||||
label="Publish"
|
||||
icon="pi pi-upload"
|
||||
variant="primary"
|
||||
icon="upload"
|
||||
@click="router.push('/community/upload')"
|
||||
/>
|
||||
>Publish</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -152,41 +150,38 @@ onMounted(async () => {
|
||||
|
||||
<!-- Filter bar -->
|
||||
<section class="wp-comm-filters">
|
||||
<span class="wp-comm-filters__search p-input-icon-left">
|
||||
<i class="pi pi-search" />
|
||||
<InputText
|
||||
<div class="wp-comm-filters__search">
|
||||
<Input
|
||||
v-model="q"
|
||||
icon="search"
|
||||
placeholder="Search modules, tags, authors..."
|
||||
class="w-full"
|
||||
aria-label="Search community"
|
||||
/>
|
||||
</span>
|
||||
<Select
|
||||
v-model="kind"
|
||||
:options="KIND_OPTIONS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
placeholder="Kind"
|
||||
class="w-40"
|
||||
aria-label="Filter by kind"
|
||||
/>
|
||||
<Select
|
||||
v-model="sort"
|
||||
:options="SORT_OPTIONS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
placeholder="Sort"
|
||||
class="w-44"
|
||||
aria-label="Sort modules"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-40">
|
||||
<Select
|
||||
v-model="kind"
|
||||
:options="KIND_OPTIONS"
|
||||
placeholder="Kind"
|
||||
aria-label="Filter by kind"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-44">
|
||||
<Select
|
||||
v-model="sort"
|
||||
:options="SORT_OPTIONS"
|
||||
placeholder="Sort"
|
||||
aria-label="Sort modules"
|
||||
/>
|
||||
</div>
|
||||
<label class="wp-comm-filters__toggle">
|
||||
<Checkbox v-model="verifiedOnly" :binary="true" /> Verified only
|
||||
<Checkbox v-model="verifiedOnly" /> Verified only
|
||||
</label>
|
||||
<label class="wp-comm-filters__toggle">
|
||||
<Checkbox v-model="compatibleOnly" :binary="true" /> Compatible (engine {{ store.engineVersion }})
|
||||
<Checkbox v-model="compatibleOnly" /> Compatible (engine {{ store.engineVersion }})
|
||||
</label>
|
||||
<label class="wp-comm-filters__toggle wp-comm-filters__toggle--nsfw">
|
||||
<Checkbox v-model="includeNsfw" :binary="true" /> Show 18+
|
||||
<Checkbox v-model="includeNsfw" /> Show 18+
|
||||
</label>
|
||||
</section>
|
||||
|
||||
@@ -223,12 +218,10 @@ onMounted(async () => {
|
||||
<p>Try clearing filters or broaden your search.</p>
|
||||
<Button
|
||||
class="mt-3"
|
||||
label="Clear filters"
|
||||
icon="pi pi-filter-slash"
|
||||
severity="secondary"
|
||||
outlined
|
||||
variant="outline"
|
||||
icon="filter-slash"
|
||||
@click="clearFilters"
|
||||
/>
|
||||
>Clear filters</Button>
|
||||
</div>
|
||||
<div v-else class="wp-comm-grid">
|
||||
<CommunityCard v-for="atom in store.feed" :key="atom.id" :atom="atom" />
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
import { useCommunityStore } from "../../stores/communityStore";
|
||||
import { isForceOffline, setForceOffline } from "../../community/mockApi";
|
||||
|
||||
@@ -46,23 +46,19 @@ function forceOffline() {
|
||||
Community registry is offline. We'll keep retrying.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<Button label="Try again" icon="pi pi-refresh" @click="retry" />
|
||||
<Button variant="primary" icon="refresh" @click="retry">Try again</Button>
|
||||
<Button
|
||||
v-if="forced"
|
||||
label="Force online (demo)"
|
||||
icon="pi pi-check"
|
||||
severity="secondary"
|
||||
outlined
|
||||
variant="outline"
|
||||
icon="check"
|
||||
@click="forceOnline"
|
||||
/>
|
||||
>Force online (demo)</Button>
|
||||
<Button
|
||||
v-else
|
||||
label="Force offline (demo)"
|
||||
icon="pi pi-times-circle"
|
||||
severity="secondary"
|
||||
outlined
|
||||
variant="outline"
|
||||
icon="times-circle"
|
||||
@click="forceOffline"
|
||||
/>
|
||||
>Force offline (demo)</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
import { useCommunityStore } from "../../stores/communityStore";
|
||||
import CommunityCard from "../../community/CommunityCard.vue";
|
||||
import { findInCatalog } from "../../community/mockApi";
|
||||
@@ -54,13 +54,11 @@ onMounted(() => {
|
||||
<template>
|
||||
<div class="wp-comm-page">
|
||||
<Button
|
||||
label="Back to Community"
|
||||
icon="pi pi-arrow-left"
|
||||
severity="secondary"
|
||||
text
|
||||
variant="ghost"
|
||||
icon="arrow-left"
|
||||
class="self-start"
|
||||
@click="back"
|
||||
/>
|
||||
>Back to Community</Button>
|
||||
|
||||
<div v-if="!store.currentUser" class="wp-comm-empty">
|
||||
<i class="pi pi-user wp-comm-empty__icon" aria-hidden="true" />
|
||||
@@ -86,8 +84,8 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<Button label="Publish" icon="pi pi-upload" @click="router.push('/community/upload')" />
|
||||
<Button label="Sign out" icon="pi pi-sign-out" severity="secondary" outlined @click="store.signOut()" />
|
||||
<Button variant="primary" icon="upload" @click="router.push('/community/upload')">Publish</Button>
|
||||
<Button variant="outline" icon="sign-out" @click="store.signOut()">Sign out</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -108,7 +106,7 @@ onMounted(() => {
|
||||
<div v-if="!uploadAtoms.length" class="wp-comm-empty">
|
||||
<i class="pi pi-upload wp-comm-empty__icon" aria-hidden="true" />
|
||||
<h3>You haven't published anything yet.</h3>
|
||||
<Button class="mt-3" label="Publish a module" icon="pi pi-upload" @click="router.push('/community/upload')" />
|
||||
<Button class="mt-3" variant="primary" icon="upload" @click="router.push('/community/upload')">Publish a module</Button>
|
||||
</div>
|
||||
<div v-else class="wp-comm-grid">
|
||||
<CommunityCard v-for="atom in uploadAtoms" :key="atom.id" :atom="atom" />
|
||||
@@ -158,11 +156,9 @@ onMounted(() => {
|
||||
<td style="color: var(--wp-text-muted);">{{ relativeTime(entry.at) }}</td>
|
||||
<td style="text-align: right;">
|
||||
<Button
|
||||
label="Open"
|
||||
severity="secondary"
|
||||
text
|
||||
variant="ghost"
|
||||
@click="router.push({ name: 'community-detail', params: { id: entry.id } })"
|
||||
/>
|
||||
>Open</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
*/
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Button from "primevue/button";
|
||||
import InputText from "primevue/inputtext";
|
||||
import Textarea from "primevue/textarea";
|
||||
import Select from "primevue/select";
|
||||
import Checkbox from "primevue/checkbox";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import Button from "../../components/ui/Button.vue";
|
||||
import Input from "../../components/ui/Input.vue";
|
||||
import Textarea from "../../components/ui/Textarea.vue";
|
||||
import Select from "../../components/ui/Select.vue";
|
||||
import Checkbox from "../../components/ui/Checkbox.vue";
|
||||
import { useToast } from "../../composables/useToast";
|
||||
import { useCommunityStore } from "../../stores/communityStore";
|
||||
import { KIND_LABEL } from "../../community/format";
|
||||
import type { CommunityUploadPayload } from "../../community/types";
|
||||
@@ -72,7 +72,7 @@ const detailsValid = computed(
|
||||
async function submit() {
|
||||
if (!detailsValid.value) return;
|
||||
if (!store.currentUser) {
|
||||
toast.add({ severity: "warn", summary: "Sign in to publish.", life: 2500 });
|
||||
toast.push({ severity: "warn", summary: "Sign in to publish.", life: 2500 });
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
@@ -80,9 +80,9 @@ async function submit() {
|
||||
const result = await store.upload(form);
|
||||
newId.value = result.id;
|
||||
step.value = 4;
|
||||
toast.add({ severity: "success", summary: "Published", detail: result.name, life: 2500 });
|
||||
toast.push({ severity: "success", summary: "Published", detail: result.name, life: 2500 });
|
||||
} catch {
|
||||
toast.add({ severity: "error", summary: "Publish failed", detail: "Registry unreachable.", life: 3000 });
|
||||
toast.push({ severity: "error", summary: "Publish failed", detail: "Registry unreachable.", life: 3000 });
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
@@ -108,13 +108,11 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<template>
|
||||
<div class="wp-comm-page">
|
||||
<Button
|
||||
label="Back"
|
||||
icon="pi pi-arrow-left"
|
||||
severity="secondary"
|
||||
text
|
||||
variant="ghost"
|
||||
icon="arrow-left"
|
||||
class="self-start"
|
||||
@click="back"
|
||||
/>
|
||||
>Back</Button>
|
||||
|
||||
<header class="wp-comm-page__header">
|
||||
<div>
|
||||
@@ -177,18 +175,17 @@ function onTagKey(event: KeyboardEvent) {
|
||||
|
||||
<div v-if="form.atom === 'single'" class="form-row">
|
||||
<label class="form-label">Module kind</label>
|
||||
<Select
|
||||
v-model="form.type"
|
||||
:options="KIND_OPTIONS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
class="select-narrow"
|
||||
aria-label="Module kind"
|
||||
/>
|
||||
<div class="select-narrow">
|
||||
<Select
|
||||
v-model="form.type"
|
||||
:options="KIND_OPTIONS"
|
||||
aria-label="Module kind"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<Button label="Next" icon="pi pi-arrow-right" icon-pos="right" @click="step = 2" />
|
||||
<Button variant="primary" icon-right="arrow-right" @click="step = 2">Next</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -196,11 +193,11 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<div v-if="step === 2" class="wp-card-body">
|
||||
<div class="form-row">
|
||||
<label class="form-label">Name *</label>
|
||||
<InputText v-model="form.name" placeholder="Hair Color Pro" aria-label="Module name" />
|
||||
<Input v-model="form.name" placeholder="Hair Color Pro" aria-label="Module name" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="form-label">Tagline *</label>
|
||||
<InputText v-model="form.tagline" placeholder="One sentence about what this does." aria-label="Tagline" />
|
||||
<Input v-model="form.tagline" placeholder="One sentence about what this does." aria-label="Tagline" />
|
||||
</div>
|
||||
<div class="form-grid">
|
||||
<div>
|
||||
@@ -208,8 +205,6 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<Select
|
||||
v-model="form.category"
|
||||
:options="CATEGORY_OPTIONS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
aria-label="Category"
|
||||
/>
|
||||
</div>
|
||||
@@ -218,30 +213,28 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<Select
|
||||
v-model="form.license"
|
||||
:options="LICENSE_OPTIONS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
aria-label="License"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Min engine</label>
|
||||
<InputText v-model="form.engine_min_version" aria-label="Minimum engine version" />
|
||||
<Input v-model="form.engine_min_version" aria-label="Minimum engine version" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Version</label>
|
||||
<InputText v-model="form.version" placeholder="1.0.0" aria-label="Version" />
|
||||
<Input v-model="form.version" placeholder="1.0.0" aria-label="Version" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="form-label">Tags *</label>
|
||||
<div class="tag-row">
|
||||
<InputText
|
||||
<Input
|
||||
v-model="tagInput"
|
||||
placeholder="portrait, fashion, vibrant..."
|
||||
aria-label="Tag input"
|
||||
@keydown="onTagKey"
|
||||
/>
|
||||
<Button label="Add" severity="secondary" outlined @click="addTag" />
|
||||
<Button variant="outline" @click="addTag">Add</Button>
|
||||
</div>
|
||||
<div v-if="form.tags.length" class="tag-list">
|
||||
<span v-for="t in form.tags" :key="t" class="wp-comm-card__tag">
|
||||
@@ -259,7 +252,7 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<label class="form-label">README (Markdown)</label>
|
||||
<Textarea
|
||||
v-model="form.readme"
|
||||
rows="8"
|
||||
:rows="8"
|
||||
class="mono"
|
||||
placeholder="# What's inside Describe the module..."
|
||||
aria-label="README"
|
||||
@@ -267,18 +260,17 @@ function onTagKey(event: KeyboardEvent) {
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="wp-comm-filters__toggle">
|
||||
<Checkbox v-model="form.nsfw" :binary="true" /> Mark as 18+ (NSFW)
|
||||
<Checkbox v-model="form.nsfw" /> Mark as 18+ (NSFW)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-actions form-actions--split">
|
||||
<Button label="Back" icon="pi pi-arrow-left" severity="secondary" @click="step = 1" />
|
||||
<Button variant="secondary" icon="arrow-left" @click="step = 1">Back</Button>
|
||||
<Button
|
||||
label="Review"
|
||||
icon="pi pi-arrow-right"
|
||||
icon-pos="right"
|
||||
variant="primary"
|
||||
icon-right="arrow-right"
|
||||
:disabled="!detailsValid"
|
||||
@click="step = 3"
|
||||
/>
|
||||
>Review</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -300,13 +292,13 @@ function onTagKey(event: KeyboardEvent) {
|
||||
By publishing, you agree to the registry's content guidelines. You can edit metadata or unpublish at any time from your profile.
|
||||
</p>
|
||||
<div class="form-actions form-actions--split">
|
||||
<Button label="Back" icon="pi pi-arrow-left" severity="secondary" @click="step = 2" />
|
||||
<Button variant="secondary" icon="arrow-left" @click="step = 2">Back</Button>
|
||||
<Button
|
||||
label="Publish"
|
||||
icon="pi pi-upload"
|
||||
variant="primary"
|
||||
icon="upload"
|
||||
:loading="submitting"
|
||||
@click="submit"
|
||||
/>
|
||||
>Publish</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -316,8 +308,8 @@ function onTagKey(event: KeyboardEvent) {
|
||||
<h3>Published!</h3>
|
||||
<p>Your module is live with id <code>{{ newId }}</code>.</p>
|
||||
<div class="form-actions" style="justify-content: center;">
|
||||
<Button label="View on Community" icon="pi pi-arrow-right" @click="viewModule" />
|
||||
<Button label="Back to Discover" severity="secondary" outlined @click="back" />
|
||||
<Button variant="primary" icon-right="arrow-right" @click="viewModule">View on Community</Button>
|
||||
<Button variant="outline" @click="back">Back to Discover</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -384,5 +376,5 @@ function onTagKey(event: KeyboardEvent) {
|
||||
.review-grid > div strong { color: var(--wp-text); font-size: 13px; }
|
||||
.review-grid__full { grid-column: 1 / -1; }
|
||||
.select-narrow { max-width: 240px; }
|
||||
.mono :deep(textarea) { font-family: var(--wp-font-mono); font-size: 12px; }
|
||||
.mono :deep(.wp-textarea) { font-family: var(--wp-font-mono); font-size: 12px; }
|
||||
</style>
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/wp/images/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/wp/images/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/wp/images/site.webmanifest" />
|
||||
<script type="module" crossorigin src="/wp/assets/manager-Da1TVId8.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/wp/assets/manager-Bh6ktjk4.css">
|
||||
<script type="module" crossorigin src="/wp/assets/manager-D-gQtvLK.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/wp/assets/manager-DyPxejbp.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Reference in New Issue
Block a user