diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..3d974746 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,4 @@ +> 0.5% +last 2 versions +not dead +not IE 11 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cb438e7..b237d9bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: matrix: include: - node-version: 24 - docker-image: anzusystems/node:4.0.0-node24-nginx-browsers + docker-image: anzusystems/node:4.1.0-node24-nginx-browsers name: Node ${{ matrix.node-version }} runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index aca64283..64371699 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,10 @@ dist dist-ssr node_modules +# Auto-generated by vite plugins (regenerated by generate:dts in CI) +src/typed-router.d.ts +src/auto-imports.d.ts + # Tests coverage .scannerwork @@ -47,5 +51,9 @@ lerna-debug.log* # Docker docker-compose.override.yml* +# Local tooling artifacts +.common-admin-updated +.playwright-cli/ + # Exclusions !.gitkeep diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000..309347bf --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,17 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 120, + "semi": false, + "singleQuote": true, + "trailingComma": "es5", + "ignorePatterns": [], + "overrides": [ + { + "files": ["**/*.scss"], + "options": { + "trailingComma": "none" + } + } + ], + "exclude": ["dist/", "cypress/", "coverage/"] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..0ce66696 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,101 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "vue", + "typescript" + ], + "categories": { + "correctness": "off" + }, + "env": { + "builtin": true + }, + "ignorePatterns": [ + "**/dist/**", + "**/dist-ssr/**", + "**/coverage/**", + ".stylelintrc.js", + "cypress/**", + "eslint.config.mjs", + "src/typed-router.d.ts", + "src/auto-imports.d.ts" + ], + "rules": { + "vue/no-arrow-functions-in-watch": "error", + "vue/no-deprecated-destroyed-lifecycle": "error", + "vue/no-export-in-script-setup": "error", + "vue/no-lifecycle-after-await": "error", + "vue/prefer-import-from-vue": "error", + "vue/valid-define-emits": "error", + "vue/valid-define-props": "error", + "vue/no-multiple-slot-args": "warn", + "vue/no-required-prop-with-default": "warn", + "vue/require-typed-ref": "error", + "no-array-constructor": "error", + "no-unused-vars": [ + "error", + { + "caughtErrors": "none" + } + ], + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": [ + "../*", + "./*" + ], + "message": "Use absolute imports with @ instead of relative imports" + } + ] + } + ], + "typescript/no-duplicate-enum-values": "error", + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "error", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-unsafe-function-type": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-namespace-keyword": "error", + "typescript/triple-slash-reference": "error" + }, + "overrides": [ + { + "files": [ + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.cts", + "**/*.vue" + ], + "rules": { + "constructor-super": "off", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-unsafe-negation": "off", + "no-var": "error", + "no-with": "off", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error" + } + } + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index f06235c4..00000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 10a50747..00000000 --- a/.prettierrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - printWidth: 120, - semi: false, - singleQuote: true, - bracketSameLine: false, - trailingComma: 'es5', - singleAttributePerLine: true, -} diff --git a/.yarnrc.yml b/.yarnrc.yml index 91b1101f..a1ceb033 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,5 +1,12 @@ +approvedGitRepositories: + - "**" + compressionLevel: mixed enableGlobalCache: false +enableScripts: true + nodeLinker: node-modules + +npmMinimalAgeGate: 0 diff --git a/bin/lint b/bin/lint index 67be7c73..b9ae0fa9 100755 --- a/bin/lint +++ b/bin/lint @@ -2,6 +2,7 @@ # Script used to run the linter check in the application container PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)" +ARGS="$*" cd "${PROJECT_ROOT}" || exit 1 # Initialize default variables # shellcheck disable=SC1091 @@ -13,11 +14,20 @@ docker_env_vars if [ -f /.dockerenv ]; then env-config - echo "[INFO] Running Yarn lint" - yarn lint:tsc - yarn lint:eslint - yarn lint:stylelint + if [[ "$1" == -* ]] || [ $# -eq 0 ]; then + echo "[INFO] Running Yarn lint $@" + yarn run lint "$@" + else + SCRIPT=$1 + shift + # Automatically prefix with 'lint:' if the specific script doesn't exist but the prefixed one does + if ! grep -q "\"$SCRIPT\":" package.json && grep -q "\"lint:$SCRIPT\":" package.json; then + SCRIPT="lint:$SCRIPT" + fi + echo "[INFO] Running Yarn $SCRIPT $@" + yarn run "$SCRIPT" "$@" + fi exit fi -bin/docker-compose exec --user node "${DOCKER_COMPOSE_SERVICE_NAME}" bash -c \'bin/lint\' +bin/docker-compose exec --user node "${DOCKER_COMPOSE_SERVICE_NAME}" bash -c \'bin/lint "${ARGS}"\' diff --git a/cypress/e2e/assetEmpty/audioEmptyAsset.cy.ts b/cypress/e2e/assetEmpty/audioEmptyAsset.cy.ts index 9c563bc3..a31e989b 100644 --- a/cypress/e2e/assetEmpty/audioEmptyAsset.cy.ts +++ b/cypress/e2e/assetEmpty/audioEmptyAsset.cy.ts @@ -18,7 +18,7 @@ describe(`Test audio empty asset, Env: ${CY.cfg}`, cy.getCy('button-confirm').should('be.visible').click() cy.alertMessage(ALERT_CREATE) - cy.urlContains('/asset') + cy.urlContains('/assets') cy.get('.sidebar-info__content > .pa-2 > .v-alert').contains('Neobsahuje žiaden súbor') cy.getCy('button-download').should('be.visible') cy.getCy('button-delete').should('be.visible') @@ -62,7 +62,7 @@ describe(`Test audio empty asset, Env: ${CY.cfg}`, cy.api_getFileID().then((assetID) => { cy.circleLoad() - cy.visit(`/asset/${assetID}`) + cy.visit(`/assets/${assetID}`) cy.circleLoad() cy.get('.dam-image-detail__sidebar [data-cy="custom-field-title"] textarea').eq(0) .invoke('val').then((assetTitle)=>{ diff --git a/cypress/e2e/assetEmpty/documentAssetEmpty.cy.ts b/cypress/e2e/assetEmpty/documentAssetEmpty.cy.ts index e96181a5..8d7b85c8 100644 --- a/cypress/e2e/assetEmpty/documentAssetEmpty.cy.ts +++ b/cypress/e2e/assetEmpty/documentAssetEmpty.cy.ts @@ -18,7 +18,7 @@ describe(`Test document empty asset, Env: ${CY.cfg}`, cy.getCy('button-confirm').should('be.visible').click() cy.alertMessage(ALERT_CREATE) - cy.urlContains('/asset') + cy.urlContains('/assets') cy.get('.sidebar-info__content > .pa-2 > .v-alert').contains('Neobsahuje žiaden súbor') cy.getCy('button-download').should('be.visible') cy.getCy('button-delete').should('be.visible') @@ -43,7 +43,7 @@ describe(`Test document empty asset, Env: ${CY.cfg}`, cy.api_getFileID().then((assetID) => { cy.circleLoad() - cy.visit(`/asset/${assetID}`) + cy.visit(`/assets/${assetID}`) cy.circleLoad() cy.get('.dam-image-detail__sidebar [data-cy="custom-field-title"] textarea').eq(0) .invoke('val').then((assetTitle)=>{ diff --git a/cypress/e2e/assetEmpty/imageAssetEmpty.cy.ts b/cypress/e2e/assetEmpty/imageAssetEmpty.cy.ts index c249293d..b0edddc9 100644 --- a/cypress/e2e/assetEmpty/imageAssetEmpty.cy.ts +++ b/cypress/e2e/assetEmpty/imageAssetEmpty.cy.ts @@ -18,7 +18,7 @@ describe(`Test image empty asset, Env: ${CY.cfg}`, cy.getCy('button-confirm').should('be.visible').click() cy.alertMessage(ALERT_CREATE) - cy.urlContains('/asset') + cy.urlContains('/assets') cy.get('.sidebar-info__content > .pa-2 > .v-alert').contains('Neobsahuje žiaden súbor') cy.get('.sidebar-info .v-btn').contains('Zobraziť viac').click() cy.getCy('button-download').should('be.visible') @@ -53,7 +53,7 @@ describe(`Test image empty asset, Env: ${CY.cfg}`, cy.api_getFileID().then((assetID) => { cy.circleLoad() - cy.visit(`/asset/${assetID}`) + cy.visit(`/assets/${assetID}`) cy.circleLoad() cy.get('.dam-image-detail__sidebar [data-cy="custom-field-title"] textarea[rows]') .invoke('val').then((assetTitle)=>{ diff --git a/cypress/e2e/assetEmpty/videoAssetEmpty.cy.ts b/cypress/e2e/assetEmpty/videoAssetEmpty.cy.ts index 9fdc9359..2ab88065 100644 --- a/cypress/e2e/assetEmpty/videoAssetEmpty.cy.ts +++ b/cypress/e2e/assetEmpty/videoAssetEmpty.cy.ts @@ -18,7 +18,7 @@ describe(`Test video empty asset, Env: ${CY.cfg}`, cy.getCy('button-confirm').should('be.visible').click() cy.alertMessage(ALERT_CREATE) - cy.urlContains('/asset') + cy.urlContains('/assets') cy.get('.sidebar-info__content > .pa-2 > .v-alert').contains('Neobsahuje žiaden súbor') cy.getCy('button-download').should('be.visible') cy.getCy('button-delete').should('be.visible') @@ -59,7 +59,7 @@ describe(`Test video empty asset, Env: ${CY.cfg}`, cy.api_getFileID().then((assetID) => { cy.circleLoad() - cy.visit(`/asset/${assetID}`) + cy.visit(`/assets/${assetID}`) cy.circleLoad() cy.get('.dam-image-detail__sidebar [data-cy="custom-field-title"] textarea').eq(0) .invoke('val').then((assetTitle)=>{ diff --git a/cypress/e2e/assets/assetExifAutofill.cy.ts b/cypress/e2e/assets/assetExifAutofill.cy.ts index 054d0601..67293080 100644 --- a/cypress/e2e/assets/assetExifAutofill.cy.ts +++ b/cypress/e2e/assets/assetExifAutofill.cy.ts @@ -11,7 +11,7 @@ describe(`Test add audio asset to podcast episode function, Env: ${CY.cfg}`, cy.prepareData('image/sampleMeta1.jpg', true, assetIDs) }) it('Check image on Title-Description-Keywords-Artists', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .should('have.value', EXPECTED_TITLE) @@ -35,7 +35,7 @@ describe(`Test add audio asset to podcast episode function, Env: ${CY.cfg}`, cy.prepareData('image/sampleMeta2.jpg', true, assetIDs) }) it('Check image on Subject-ImageDescription-Subjects-Owners', () => { - cy.visit(`/asset/${assetIDs[1]}`) + cy.visit(`/assets/${assetIDs[1]}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .should('have.value', EXPECTED_TITLE) diff --git a/cypress/e2e/assets/audioAsset.cy.ts b/cypress/e2e/assets/audioAsset.cy.ts index 53a1eac1..2807eb2e 100644 --- a/cypress/e2e/assets/audioAsset.cy.ts +++ b/cypress/e2e/assets/audioAsset.cy.ts @@ -9,7 +9,7 @@ describe(`Test asset audio function, Env: ${CY.cfg}`, cy.prepareData('audio/sample.mp3',true, assetIDs) }) it('Create Metadata', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}`) @@ -35,7 +35,7 @@ describe(`Test asset audio function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Edit Metadata', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}-edit`) @@ -45,7 +45,7 @@ describe(`Test asset audio function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Clear Metadata', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }) diff --git a/cypress/e2e/assets/audioAssetToPodcast.cy.ts b/cypress/e2e/assets/audioAssetToPodcast.cy.ts index 72177a72..ff4660d4 100644 --- a/cypress/e2e/assets/audioAssetToPodcast.cy.ts +++ b/cypress/e2e/assets/audioAssetToPodcast.cy.ts @@ -15,7 +15,7 @@ describe(`Test add audio asset to podcast episode function, Env: ${CY.cfg}`, cy.prepareData('audio/sample.mp3', true, assetIDs) }) it('Add audio asset to podcast episode', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}`) @@ -36,7 +36,7 @@ describe(`Test add audio asset to podcast episode function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_CREATE) }) it('Delete podcast', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCy('button-podcast').should('be.visible').click() cy.get('.text-body-2').contains(`${ASSET_TITLE}-edit`).should('exist') diff --git a/cypress/e2e/assets/audioSlots.cy.ts b/cypress/e2e/assets/audioSlots.cy.ts index 6f29c2cb..517ffbdb 100644 --- a/cypress/e2e/assets/audioSlots.cy.ts +++ b/cypress/e2e/assets/audioSlots.cy.ts @@ -11,7 +11,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, cy.prepareData('audio/sample2.mp3', false) }) it('Public-Private', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCy('button-slots').click() cy.get('button.v-btn:contains("Znovu načítať sloty assetu")').should('be.visible').click() @@ -42,14 +42,14 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, cy.get('.sidebar-info').contains('Súbor je neprístupný') }) it('ID', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.getCyVisibleClick('button-slot-actions') cy.getCy('button-slot-copy-id').should('be.visible') }) it('Duplicate slot - free to premium', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.getCyVisibleClick('button-slot-actions') @@ -99,7 +99,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, }) }) it('Remove free file', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.getCy('button-slot-actions').eq(0).click() @@ -122,7 +122,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, }) }) it('Add new free file', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.get('input[type="file"]', { timeout: 10000 }) @@ -143,7 +143,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, }) }) it('Change Slots with each other', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.get('.sidebar-info .mdi-lock-open').eq(1).click() @@ -180,7 +180,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, }) }) it('Twin slot', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.get('.v-btn').contains('Pridať').click() @@ -190,7 +190,7 @@ describe(`Test audio slots function, Env: ${CY.cfg}`, cy.get('.sidebar-info__content .v-chip--link').should('be.visible').click() cy.get('[data-cy="custom-field-title"] textarea[rows]') .should('have.value', twinVideoAssetTitle) - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCyVisibleClick('button-slots') cy.get('.sidebar-info__content .v-chip--link').should('be.visible') diff --git a/cypress/e2e/assets/documentAsset.cy.ts b/cypress/e2e/assets/documentAsset.cy.ts index 06d8ae37..8c8b7839 100644 --- a/cypress/e2e/assets/documentAsset.cy.ts +++ b/cypress/e2e/assets/documentAsset.cy.ts @@ -9,7 +9,7 @@ describe(`Test asset document function, Env: ${CY.cfg}`, cy.prepareData('document/sample.doc', true, assetIDs) }) it('Create Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}`) @@ -27,7 +27,7 @@ describe(`Test asset document function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Edit Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}-edit`) @@ -37,7 +37,7 @@ describe(`Test asset document function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Clear Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }) diff --git a/cypress/e2e/assets/imageAsset.cy.ts b/cypress/e2e/assets/imageAsset.cy.ts index e921350e..fb570425 100644 --- a/cypress/e2e/assets/imageAsset.cy.ts +++ b/cypress/e2e/assets/imageAsset.cy.ts @@ -11,7 +11,7 @@ describe(`Test asset image function, Env: ${CY.cfg}`, cy.prepareData('image/sample.png', true, assetIDs) }) it('Create Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('.v-btn').contains('Zobraziť viac').click() cy.get('[data-cy="custom-field-title"] textarea') @@ -39,7 +39,7 @@ describe(`Test asset image function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Edit Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('.sidebar-info .v-btn').contains('Zobraziť viac').should('be.visible').click() cy.get('[data-cy="custom-field-title"] textarea') @@ -50,7 +50,7 @@ describe(`Test asset image function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Clear Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }) @@ -65,7 +65,7 @@ describe(`Test asset image function, Env: ${CY.cfg}`, cy.getCy('button-slots').should('be.visible') }) it('Test Focus', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCy('button-focus').click() cy.waitSec(1) diff --git a/cypress/e2e/assets/unsplash.cy.ts b/cypress/e2e/assets/unsplash.cy.ts index d5dd88d9..d2a28ae5 100644 --- a/cypress/e2e/assets/unsplash.cy.ts +++ b/cypress/e2e/assets/unsplash.cy.ts @@ -35,7 +35,7 @@ xdescribe(`Test audio slots function, Env: ${CY.cfg}`, it('Visit data', ()=>{ assetIDs.forEach((id)=>{ cy.log(id) - cy.visit(`/asset/${id}`) + cy.visit(`/assets/${id}`) cy.urlContains(id) }) }) diff --git a/cypress/e2e/assets/videoAsset.cy.ts b/cypress/e2e/assets/videoAsset.cy.ts index 0aa0e158..2b7b33d4 100644 --- a/cypress/e2e/assets/videoAsset.cy.ts +++ b/cypress/e2e/assets/videoAsset.cy.ts @@ -9,7 +9,7 @@ describe(`Test asset video function, Env: ${CY.cfg}`, cy.prepareData('video/sample.mp4', true, assetIDs) }) it('Create Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}`) @@ -36,7 +36,7 @@ describe(`Test asset video function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Edit Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}-edit`) @@ -46,7 +46,7 @@ describe(`Test asset video function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_UPDATE) }) it('Clear Metadata', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }) @@ -63,7 +63,7 @@ describe(`Test asset video function, Env: ${CY.cfg}`, cy.getCy('button-image-preview').should('be.visible') }) it('Test Image Preview', ()=> { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCy('button-image-preview').click() cy.waitSec(2) diff --git a/cypress/e2e/assets/videoAssetToVideoShow.cy.ts b/cypress/e2e/assets/videoAssetToVideoShow.cy.ts index a45dde2a..ae064f55 100644 --- a/cypress/e2e/assets/videoAssetToVideoShow.cy.ts +++ b/cypress/e2e/assets/videoAssetToVideoShow.cy.ts @@ -9,7 +9,7 @@ describe(`Test add video asset to video show episode function, Env: ${CY.cfg}`, cy.prepareData('video/sample.mp4', true, assetIDs) }) it('Add video asset to video show episode', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${ASSET_TITLE}`) @@ -27,7 +27,7 @@ describe(`Test add video asset to video show episode function, Env: ${CY.cfg}`, cy.alertMessage(ALERT_CREATE) }) it('Delete video-show episode', ()=>{ - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') cy.getCy('button-video-show').should('be.visible').click() cy.get('.text-body-2').contains(`${VIDEO_SHOW_TITLE}-edit`).should('exist') diff --git a/cypress/e2e/distribution/audioDistribution.cy.ts b/cypress/e2e/distribution/audioDistribution.cy.ts index 14ed3b90..d7380a5c 100644 --- a/cypress/e2e/distribution/audioDistribution.cy.ts +++ b/cypress/e2e/distribution/audioDistribution.cy.ts @@ -23,7 +23,7 @@ describe(`Test distribution Audio function, Env: ${CY.cfg}`, cy.prepareData('audio/sample.mp3', 1, assetIDs) }) it('Distribute audio', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') // Add to podcast @@ -77,7 +77,7 @@ describe(`Test distribution Audio function, Env: ${CY.cfg}`, .should('include.text', 'Distribuovaný') // JW distribution // Logs core-dam check - cy.visit('/log') + cy.visit('/logs') cy.getCy('filter-value').first().click() cy.contains('.v-list-item', 'coreDam').click() cy.getCy('filter-submit').click() diff --git a/cypress/e2e/distribution/videoDistribution.cy.ts b/cypress/e2e/distribution/videoDistribution.cy.ts index 276593e1..be2bd606 100644 --- a/cypress/e2e/distribution/videoDistribution.cy.ts +++ b/cypress/e2e/distribution/videoDistribution.cy.ts @@ -23,7 +23,7 @@ describe(`Test distribution Video function, Env: ${CY.cfg}`, cy.prepareData('video/sample.mp4', 1, assetIDs) }) it('Distribute video', () => { - cy.visit(`/asset/${assetIDs}`) + cy.visit(`/assets/${assetIDs}`) cy.api_waitPageLoad('asset-edit') // Change distribution category @@ -74,7 +74,7 @@ describe(`Test distribution Video function, Env: ${CY.cfg}`, .should('include.text', 'Distribuovaný') // YT distribution // Logs core-dam check - cy.visit('/log') + cy.visit('/logs') cy.getCy('filter-value').first().click() cy.contains('.v-list-item', 'coreDam').click() cy.getCy('filter-submit').click() diff --git a/cypress/e2e/licence/audioLicenceDuplicate.cy.ts b/cypress/e2e/licence/audioLicenceDuplicate.cy.ts index b5a61041..6391879a 100644 --- a/cypress/e2e/licence/audioLicenceDuplicate.cy.ts +++ b/cypress/e2e/licence/audioLicenceDuplicate.cy.ts @@ -15,7 +15,7 @@ describe(`Test asset audio licence duplicate function, Env: ${CY.cfg}`, cy.prepareData('audio/sample.m4a', 0) }) it('CMS-system 1', () => { - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) let idx = 0 @@ -26,7 +26,7 @@ describe(`Test asset audio licence duplicate function, Env: ${CY.cfg}`, }) }) it('CMS-system 2',()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) cy.changeLicence(TESTED_LICENCE_IDS.CMS_SPECTATOR) @@ -41,7 +41,7 @@ describe(`Test asset audio licence duplicate function, Env: ${CY.cfg}`, cy.deleteFile(fileIDs) }) it('Back to main cms licence', ()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_SPECTATOR) cy.changeLicence(TESTED_LICENCE_IDS.CMS_MAIN) }) diff --git a/cypress/e2e/licence/documentLicenceDuplicate.cy.ts b/cypress/e2e/licence/documentLicenceDuplicate.cy.ts index 6177528d..aefca0aa 100644 --- a/cypress/e2e/licence/documentLicenceDuplicate.cy.ts +++ b/cypress/e2e/licence/documentLicenceDuplicate.cy.ts @@ -16,7 +16,7 @@ describe(`Test asset document licence duplicate function, Env: ${CY.cfg}`, cy.prepareData('document/sample.xls', 0) }) it('Cms-system 1', () => { - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) let idx = 0 @@ -27,7 +27,7 @@ describe(`Test asset document licence duplicate function, Env: ${CY.cfg}`, }) }) it('CMS-system 2',()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) cy.changeLicence(TESTED_LICENCE_IDS.CMS_SPECTATOR) @@ -42,7 +42,7 @@ describe(`Test asset document licence duplicate function, Env: ${CY.cfg}`, cy.deleteFile(fileIDs) }) it('Back to main cms licence', ()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_SPECTATOR) cy.changeLicence(TESTED_LICENCE_IDS.CMS_MAIN) }) diff --git a/cypress/e2e/licence/imageLicenceDuplicate.cy.ts b/cypress/e2e/licence/imageLicenceDuplicate.cy.ts index ff574e9e..d4de188c 100644 --- a/cypress/e2e/licence/imageLicenceDuplicate.cy.ts +++ b/cypress/e2e/licence/imageLicenceDuplicate.cy.ts @@ -18,7 +18,7 @@ describe(`Test asset image licence duplicate function, Env: ${CY.cfg}`, cy.prepareData('image/sample.webp', 0) }) it('CMS', () => { - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) let idx = 0 @@ -29,7 +29,7 @@ describe(`Test asset image licence duplicate function, Env: ${CY.cfg}`, }) }) it('Blog-system 1',()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) cy.changeLicence(TESTED_LICENCE_IDS.BLOG1) @@ -41,7 +41,7 @@ describe(`Test asset image licence duplicate function, Env: ${CY.cfg}`, }) }) it('Blog-system 2',()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.BLOG1) cy.changeLicence(TESTED_LICENCE_IDS.BLOG2) @@ -56,7 +56,7 @@ describe(`Test asset image licence duplicate function, Env: ${CY.cfg}`, cy.deleteFile(fileIDs) }) it('Back to main licence', ()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.BLOG2) cy.changeLicence(TESTED_LICENCE_IDS.CMS_MAIN) }) diff --git a/cypress/e2e/licence/videoLicenceDuplicate.cy.ts b/cypress/e2e/licence/videoLicenceDuplicate.cy.ts index 580ead14..6001ebd1 100644 --- a/cypress/e2e/licence/videoLicenceDuplicate.cy.ts +++ b/cypress/e2e/licence/videoLicenceDuplicate.cy.ts @@ -14,7 +14,7 @@ describe(`Test asset video licence duplicate function, Env: ${CY.cfg}`, cy.prepareData('video/sample.mov', 0) }) it('Cms-system 1', () => { - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) let idx = 0 VIDEO_TYPES.forEach((dataFormat)=>{ @@ -24,7 +24,7 @@ describe(`Test asset video licence duplicate function, Env: ${CY.cfg}`, }) }) it('CMS-system 2',()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_MAIN) cy.changeLicence(TESTED_LICENCE_IDS.CMS_SPECTATOR) let idx = 0 @@ -38,7 +38,7 @@ describe(`Test asset video licence duplicate function, Env: ${CY.cfg}`, cy.deleteFile(fileIDs) }) it('Back to main cms licence', ()=>{ - cy.visit('/asset') + cy.visit('/assets') cy.api_waitPageLoad('main', TESTED_LICENCE_IDS.CMS_SPECTATOR) cy.changeLicence(TESTED_LICENCE_IDS.CMS_MAIN) }) diff --git a/cypress/e2e/navigation/headerNavigation.cy.ts b/cypress/e2e/navigation/headerNavigation.cy.ts index 888cab88..0b66129a 100644 --- a/cypress/e2e/navigation/headerNavigation.cy.ts +++ b/cypress/e2e/navigation/headerNavigation.cy.ts @@ -11,11 +11,11 @@ describe(`Test header navigation menu function, Env: ${CY.cfg}`, cy.get('.v-toolbar-title').invoke('text') .should('contain', 'Vytvoriť prázdny asset') cy.getCy('button-close').click() - cy.get('[href="/podcast"]').should('be.visible').click() + cy.get('[href="/podcasts"]').should('be.visible').click() cy.urlContains('podcast') cy.getCy('back-to-assets-settings').should('be.visible').click() cy.get('.v-btn .mdi-view-grid-plus-outline').should('be.visible').click() - cy.get('[href="/video-show"]').click() + cy.get('[href="/video-shows"]').click() cy.urlContains('video-show') cy.getCy('back-to-assets-settings').should('be.visible').click() cy.get('.v-btn .mdi-view-grid-plus-outline').should('be.visible').click() diff --git a/cypress/e2e/settings/assetLicences.cy.ts b/cypress/e2e/settings/assetLicences.cy.ts index 0d383782..a6127fe7 100644 --- a/cypress/e2e/settings/assetLicences.cy.ts +++ b/cypress/e2e/settings/assetLicences.cy.ts @@ -28,12 +28,12 @@ describe( cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/asset-licence') + cy.urlContains('/asset-licences') LICENCE_ID = text }) }) it('Edit asset licence', () => { - cy.visit('asset-licence') + cy.visit('/asset-licences') cy.getCy('filter-integer', 10000).first().type(`${LICENCE_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') diff --git a/cypress/e2e/settings/author.cy.ts b/cypress/e2e/settings/author.cy.ts index 5d06460d..91031131 100644 --- a/cypress/e2e/settings/author.cy.ts +++ b/cypress/e2e/settings/author.cy.ts @@ -29,12 +29,12 @@ describe(`Test authors function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/author') + cy.urlContains('/authors') USER_ID = text }) }) it('Edit author', () => { - cy.visit('/author') + cy.visit('/authors') cy.getCy('filter-string', 20000).first().type(`${USER_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') diff --git a/cypress/e2e/settings/distributionCategory.cy.ts b/cypress/e2e/settings/distributionCategory.cy.ts index 9adcc016..616e29a5 100644 --- a/cypress/e2e/settings/distributionCategory.cy.ts +++ b/cypress/e2e/settings/distributionCategory.cy.ts @@ -8,7 +8,7 @@ describe( if (CY.cfg === 'local'){ //if not - create it('Create "Podcasty" in artemis_podcast_cms', () => { - cy.visit('/distribution-category-select') + cy.visit('/distribution-category-selects') cy.cardLoad() cy.getCy('filter-value').click() cy.get('.v-list-item').contains('Audio').click() @@ -26,7 +26,7 @@ describe( it('Create distribution category', () => { cy.visit('/settings') - cy.visitSubpage('distribution-category-settings', 'distribution-category', 'Kategórie distribúcie') + cy.visitSubpage('distribution-category-settings', 'distribution-categories', 'Kategórie distribúcie') cy.getCyVisibleClick('button-create') cy.getCy('create-panel').should('be.visible') cy.getCy('category-type').should('be.visible').click() @@ -46,11 +46,11 @@ describe( cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/distribution-category') + cy.urlContains('/distribution-categories') }) }) it('Edit distribution category', () => { - cy.visit('distribution-category') + cy.visit('/distribution-categories') cy.getCy('filter-value').click() cy.get('.v-list-item').contains('Audio').click() cy.cardLoad() diff --git a/cypress/e2e/settings/distributionCategorySelect.cy.ts b/cypress/e2e/settings/distributionCategorySelect.cy.ts index f6be10bb..feab1ee3 100644 --- a/cypress/e2e/settings/distributionCategorySelect.cy.ts +++ b/cypress/e2e/settings/distributionCategorySelect.cy.ts @@ -33,7 +33,7 @@ describe( cy.alertMessage(ALERT_UPDATE) }) it('Delete distribution category select', () => { - cy.visit(`/distribution-category-select/${CATEGORY_ID}`) + cy.visit(`/distribution-category-selects/${CATEGORY_ID}`) cy.get('.v-list-item-title').contains(`Name+${RAND_NUM}`) cy.get('.v-list-item-subtitle').contains(RAND_NUM) cy.getCyVisibleClick('button-edit') diff --git a/cypress/e2e/settings/groupPermission.cy.ts b/cypress/e2e/settings/groupPermission.cy.ts index e114ad82..d697ac52 100644 --- a/cypress/e2e/settings/groupPermission.cy.ts +++ b/cypress/e2e/settings/groupPermission.cy.ts @@ -25,12 +25,12 @@ describe( cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(`/${text}`) - cy.urlContains('/permission-group') + cy.urlContains('/permission-groups') GROUP_ID = text }) }) it('Edit permission group', () => { - cy.visit('/permission-group') + cy.visit('/permission-groups') cy.getCy('filter-integer', 10000).type(`${GROUP_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') diff --git a/cypress/e2e/settings/job.cy.ts b/cypress/e2e/settings/job.cy.ts index 70bdd3fa..6c5d4703 100644 --- a/cypress/e2e/settings/job.cy.ts +++ b/cypress/e2e/settings/job.cy.ts @@ -23,17 +23,17 @@ describe(`Test job function, Env: ${CY.cfg}`, const assetID = response.body.data[8].id const podcastID = response.body.data[8].podcasts[0] const assetTitle = response.body.data[8].texts.displayTitle - cy.visit(`/podcast/${podcastID}`) + cy.visit(`/podcasts/${podcastID}`) cy.getCy('podcast-list').click() cy.getCy('podcast-id').contains(podcastID) - cy.visit(`asset/${assetID}`) + cy.visit(`/assets/${assetID}`) cy.getCy('button-podcast').click() cy.getCy('button-delete').click() cy.getCy('button-confirm-delete').click() cy.request('DELETE', `${CY.url.proto}://core-dam.${CY.url.domain}/api/adm/v1/asset/${assetID}`) - cy.visit(`asset/${assetIDs[0]}`) + cy.visit(`/assets/${assetIDs[0]}`) cy.get('[data-cy="custom-field-title"] textarea') .first().clear({ force: true }).type(`${assetTitle}`) cy.get('[id="anzu-asset-detail-sidebar-actions"] [data-cy="button-save"]').click() @@ -66,7 +66,7 @@ describe(`Test job function, Env: ${CY.cfg}`, cy.wrap(premiumText).should('include', 'Hlavný súbor') }) - cy.visit(`${CY.url.proto}://admin-dam.${CY.url.domain}/podcast/${podcastID}`) + cy.visit(`${CY.url.proto}://admin-dam.${CY.url.domain}/podcasts/${podcastID}`) cy.getCy('episode-list').click() cy.getCy('table-detail').eq(0).click() cy.contains('h4', 'Povolené na webe') @@ -76,7 +76,7 @@ describe(`Test job function, Env: ${CY.cfg}`, .parent() .should('contain.text', 'nie') - cy.visit('/job') + cy.visit('/jobs') cy.getCy('button-create').click() cy.getCy('job-select').click() cy.get('.v-list-item').contains('Podcastový synchronizátor').click() @@ -86,7 +86,7 @@ describe(`Test job function, Env: ${CY.cfg}`, cy.get(':nth-child(1) > :nth-child(2) > .v-chip > .v-chip__content') .should('include.text', 'Podcastový synchronizátor') cy.waitForJob() - cy.visit(`asset/${assetIDs[0]}`) + cy.visit(`/assets/${assetIDs[0]}`) cy.getCy('button-distribution').click() cy.get('.sidebar-info__content .text-body-2').contains('Distribuovaný') @@ -104,7 +104,7 @@ describe(`Test job function, Env: ${CY.cfg}`, cy.wrap(premiumTextBlock).should('include', 'sample') }) - cy.visit(`${CY.url.proto}://admin-dam.${CY.url.domain}/podcast/${podcastID}`) + cy.visit(`${CY.url.proto}://admin-dam.${CY.url.domain}/podcasts/${podcastID}`) cy.getCy('episode-list').click() cy.getCy('table-detail').eq(0).click() cy.contains('h4', 'Povolené na webe') diff --git a/cypress/e2e/settings/keyword.cy.ts b/cypress/e2e/settings/keyword.cy.ts index ef670888..9026d278 100644 --- a/cypress/e2e/settings/keyword.cy.ts +++ b/cypress/e2e/settings/keyword.cy.ts @@ -22,12 +22,12 @@ describe(`Test keyword function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/keyword') + cy.urlContains('/keywords') KEYWORD_ID = text }) }) it('Edit keyword', () => { - cy.visit('keyword') + cy.visit('/keywords') cy.getCy('filter-string').first().type(`${KEYWORD_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') diff --git a/cypress/e2e/settings/podcast.cy.ts b/cypress/e2e/settings/podcast.cy.ts index 541b98d6..8d4740cd 100644 --- a/cypress/e2e/settings/podcast.cy.ts +++ b/cypress/e2e/settings/podcast.cy.ts @@ -32,12 +32,12 @@ describe(`Test Podcast function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/podcast') + cy.urlContains('/podcasts') PODCAST_ID = text }) }) it('Edit podcast', () => { - cy.visit('/podcast') + cy.visit('/podcasts') cy.getCy('filter-string').first().type(`${PODCAST_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') @@ -53,7 +53,7 @@ describe(`Test Podcast function, Env: ${CY.cfg}`, cy.getCyVisibleClick('filter-reset') }) it('Create episode', () => { - cy.visit(`/podcast/${PODCAST_ID}`) + cy.visit(`/podcasts/${PODCAST_ID}`) cy.getCy('episode-list').click() cy.getCyVisibleClick('button-create', 10000) cy.getCy('create-panel').should('be.visible') @@ -73,12 +73,12 @@ describe(`Test Podcast function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/podcast') + cy.urlContains('/podcasts') EPISODE_ID = text }) }) it('Edit episode', () => { - cy.visit(`/podcast/${PODCAST_ID}`) + cy.visit(`/podcasts/${PODCAST_ID}`) cy.getCy('episode-list').click() cy.getCy('filter-string', 10000).first().type(`${EPISODE_ID}{ENTER}`) cy.cardLoad() @@ -103,11 +103,11 @@ describe(`Test Podcast function, Env: ${CY.cfg}`, cy.urlNotContains('/edit') }) it('Delete episode', () => { - cy.visit(`/podcast/${PODCAST_ID}/episode/${EPISODE_ID}`) + cy.visit(`/podcasts/${PODCAST_ID}/episodes/${EPISODE_ID}`) cy.cardLoad() cy.getCyVisibleClick('button-delete') cy.getCyVisibleClick('button-confirm-delete') - cy.urlContains('/episode') + cy.urlContains('/episodes') cy.getCy('episode-list').click() cy.getCyVisibleClick('filter-reset') cy.contains(`${EPISODE_ID}`).should('not.exist') diff --git a/cypress/e2e/settings/publicExport.cy.ts b/cypress/e2e/settings/publicExport.cy.ts index f772246f..ed1574f0 100644 --- a/cypress/e2e/settings/publicExport.cy.ts +++ b/cypress/e2e/settings/publicExport.cy.ts @@ -6,7 +6,7 @@ let PUBLIC_EXPORT_ID = '' describe(`Test public export function, Env: ${CY.cfg}`, { tags: ['@publicExport', '@settings'], env: { visitBaseUrl: false } }, () => { it('Create public export', () => { - cy.visit('/public-export') + cy.visit('/public-exports') cy.getCy('button-create').click() cy.getCy('publicExport-slug').type('cms-cypress') cy.getCy('user-asset-licences').type('Sme Family').click() @@ -23,13 +23,13 @@ describe(`Test public export function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/public-export') + cy.urlContains('/public-exports') PUBLIC_EXPORT_ID = text }) }) it('Edit public export', () => { - cy.visit(`/public-export/${PUBLIC_EXPORT_ID}`) + cy.visit(`/public-exports/${PUBLIC_EXPORT_ID}`) cy.cardLoad() cy.get('.v-col').contains('cms-cypress') cy.get('.v-col').contains('Web') @@ -51,7 +51,7 @@ describe(`Test public export function, Env: ${CY.cfg}`, }) it('Delete public export', () => { - cy.visit(`/public-export/${PUBLIC_EXPORT_ID}`) + cy.visit(`/public-exports/${PUBLIC_EXPORT_ID}`) cy.cardLoad() cy.getCy('button-delete').click() cy.getCy('button-confirm-delete').click() diff --git a/cypress/e2e/settings/user.cy.ts b/cypress/e2e/settings/user.cy.ts index d071a641..3c170cf7 100644 --- a/cypress/e2e/settings/user.cy.ts +++ b/cypress/e2e/settings/user.cy.ts @@ -82,7 +82,7 @@ describe(`Test user function, Env: ${CY.cfg}`, cy.urlNotContains('edit') }) it('Reset User', () => { - cy.visit(`/user/${USER_ID}/edit`) + cy.visit(`/users/${USER_ID}/edit`) cy.cardLoad() cy.getCy('user-admin-to-ext-systems').click() EXTERNAL_SYS.forEach((admin) => { diff --git a/cypress/e2e/settings/userPermission.cy.ts b/cypress/e2e/settings/userPermission.cy.ts index 00486155..f390e355 100644 --- a/cypress/e2e/settings/userPermission.cy.ts +++ b/cypress/e2e/settings/userPermission.cy.ts @@ -46,7 +46,7 @@ describe( }) }) it('Edit user', () => { - cy.visit('/anzu-user') + cy.visit('/anzu-users') cy.getCy('filter-integer', 10000).type(`${USER_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') @@ -64,7 +64,7 @@ describe( cy.getCyVisibleClick('filter-reset') }) it('Disable user', () => { - cy.visit('/anzu-user') + cy.visit('/anzu-users') cy.get("[data-cy='filter-string']").first().type(`${USER_EMAIL}{ENTER}`) cy.getCyVisibleClick('table-edit') cy.urlContains('/edit') diff --git a/cypress/e2e/settings/videoShow.cy.ts b/cypress/e2e/settings/videoShow.cy.ts index bf66f0e2..55398919 100644 --- a/cypress/e2e/settings/videoShow.cy.ts +++ b/cypress/e2e/settings/videoShow.cy.ts @@ -25,12 +25,12 @@ describe(`Test video shows function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains('/video-show') + cy.urlContains('/video-shows') VIDEO_SHOW_ID = text }) }) it('Edit video show', ()=>{ - cy.visit('/video-show') + cy.visit('/video-shows') cy.getCy('filter-string').first().type(`${VIDEO_SHOW_ID}{ENTER}`) cy.cardLoad() cy.getCyVisibleClick('table-edit') @@ -44,7 +44,7 @@ describe(`Test video shows function, Env: ${CY.cfg}`, cy.getCyVisibleClick('filter-reset') }) it('Create episode', () =>{ - cy.visit(`/video-show/${VIDEO_SHOW_ID}`) + cy.visit(`/video-shows/${VIDEO_SHOW_ID}`) cy.getCy('episode-list').click() cy.getCy('button-create').click() cy.getCy('episode-title').find('input').eq(0).clear().type(`${EPISODE_TITLE}-edit`) @@ -60,12 +60,12 @@ describe(`Test video shows function, Env: ${CY.cfg}`, cy.urlContains(text) cy.getCyVisibleClick('button-close') cy.urlNotContains(text) - cy.urlContains(`/video-show/${VIDEO_SHOW_ID}`) + cy.urlContains(`/video-shows/${VIDEO_SHOW_ID}`) EPISODE_ID = text }) }) it('Edit episode', ()=>{ - cy.visit(`/video-show/${VIDEO_SHOW_ID}`) + cy.visit(`/video-shows/${VIDEO_SHOW_ID}`) cy.getCy('episode-list').click() cy.getCy('filter-string', 10000).first().type(`${EPISODE_ID}{ENTER}`) cy.cardLoad() @@ -75,9 +75,9 @@ describe(`Test video shows function, Env: ${CY.cfg}`, cy.getCy('button-close').should('be.visible') cy.getCy('button-save').click() cy.alertMessage(ALERT_UPDATE) - cy.urlContains(`/episode/${EPISODE_ID}`) + cy.urlContains(`/episodes/${EPISODE_ID}`) cy.getCyVisibleClick('button-close') - cy.urlNotContains(`/episode/${EPISODE_ID}`) - cy.urlContains(`/video-show/${VIDEO_SHOW_ID}`) + cy.urlNotContains(`/episodes/${EPISODE_ID}`) + cy.urlContains(`/video-shows/${VIDEO_SHOW_ID}`) }) }) diff --git a/cypress/e2e/upload/uploadImages.cy.ts b/cypress/e2e/upload/uploadImages.cy.ts index ca576038..6377b34c 100644 --- a/cypress/e2e/upload/uploadImages.cy.ts +++ b/cypress/e2e/upload/uploadImages.cy.ts @@ -23,7 +23,7 @@ describe(`Test upload of various images, Env: ${CY.cfg}`, { tags: ['@imageUpload cy.uploadFile('image/animation.gif', uploadType, 20000) cy.api_getFileID().then((responseID) => { cy.waitForUpload(ALERT_UPLOAD, 20000) - cy.visit(`/asset/${responseID}`) + cy.visit(`/assets/${responseID}`) cy.api_waitPageLoad('asset-edit') cy.get(':nth-child(9) > .v-col-9').contains('áno') cy.deleteFile([responseID]) diff --git a/doc/changelog/unreleased/85491.md b/doc/changelog/unreleased/85491.md new file mode 100644 index 00000000..93e66c9b --- /dev/null +++ b/doc/changelog/unreleased/85491.md @@ -0,0 +1,60 @@ +planned +=== + +### Added + +- ESLint plugin `eslint-plugin-vuetify` with `flat/recommended-v4` ruleset wired into `eslint.config.mjs` +- Oxlint toolchain: `oxlint`, `eslint-plugin-oxlint`, `oxfmt` (+ `.oxlintrc.json`, `.oxfmtrc.json`) +- `lightningcss` + `browserslist` (+ `.browserslistrc`); vite `css.transformer: 'lightningcss'` with `targets: browserslistToTargets(browserslist('supports css-cascade-layers'))`, `build.cssMinify: 'lightningcss'`, `build.target: 'es2019'` +- `@layer vuetify-core.reset { ... }` block in `src/styles/main.scss` to restore the v3-style universal/form-element reset that v4 dropped +- `package.json` scripts: `lint:oxlint`, `lint:oxlint:fix`, `lint:fix`, `format`, `format:check`; `lint:oxlint` added to `ci`; `lint` dispatches to `lint:fix` on `--fix` +- `bin/lint` smart dispatch (forwards to `yarn lint`, supports `--fix`; `bin/lint ` auto-prefixes to `lint:` when the bare script doesn't exist) +- File-based routing (vue-router 5 native): `vue-router/vite` plugin + `vue-router/auto-routes` in `src/router/index.ts`. New `src/pages/` tree with route groups `(coreDam)` / `(common)`; system pages at root; meta declared per-page via `definePage({ meta })`. Generated `src/typed-router.d.ts` (committed) gives autocomplete on route names. New `scripts/generate-dts.mjs` and `generate:dts` package script (runs first in `ci`) so `typed-router.d.ts` is up-to-date for the TypeScript check +- Breadcrumbs reworked: `` now takes a `:breadcrumbs` prop instead of computing from `route.matched`. Each view composes its chain explicitly via `defineBreadcrumbs(computed(() => [...]))` from `@anzusystems/common-admin`. Mirrors admin-cms. Restores multi-level chains (e.g. podcast episode pages now show `Podcasts → Podcast detail → Episode detail` again, which the flat file-based router would otherwise have collapsed to a single breadcrumb) +- `unplugin-auto-import` (admin-cms parity): auto-imports `vue` / `vue-router` / `pinia` / `useI18n` / `useVuelidate`, plus a curated subset of `@anzusystems/common-admin` value helpers (`isUndefined`, `useAlerts`, `defineBreadcrumbs`, `cloneDeep`, …) and types (`IntegerId`, `DocId`, `DocIdNullable`, `AnzuUserAndTimeTrackingAware`, …). Generates `src/auto-imports.d.ts` (committed). Removed ~6600 lines of import boilerplate across ~530 files +- Manual Rollup chunk splitting in `vite.config.ts`: separate `vue-core` (vue + router + pinia + vue-i18n), `vuetify`, `common-admin`, `tiptap` (incl. prosemirror), `realtime` (socket.io), and `sentry` chunks. Custom `chunkFileNames` derive readable names from `src/` paths instead of `index-.js`. The previously-monolithic 1.6 MB app chunk is gone +- `watch-common-admin` Vite plugin: when `.common-admin-updated` is touched, invalidates all `@anzusystems/common-admin` modules and triggers HMR — needed for local-linked common-admin development +- Shared ESLint plugin from common-admin: `import { recommended as anzuRecommended } from '@anzusystems/common-admin/eslint'` replaces the inline `no-ts-extension` custom rule and adds `anzu/no-fatal-error-axios-check`, `anzu/url-params-match-template`, `anzu/no-deprecated-imports` (the last set to `warn` — 391 hits flag legacy filter/datatable/api helpers; cleanup PR pending) +- Local ESLint rule `anzu-local/valid-route-name`: validates every `{ name: '…' }`, `:route-name`, `routeName:` literal against `src/typed-router.d.ts` so stale route names surface as ESLint errors after route renames +- `browserslist:update` script run before production / development builds (mirrors admin-cms) +- `build:dev` script (`vite build --minify false`) for unminified production-shape builds +- TypeScript project references: `lint:tsc` switched to `NODE_OPTIONS=--max-old-space-size=4096 vue-tsc --build` (uses `tsconfig.json` references + per-project `incremental` build info), `tsconfig.app.json` adds `"types": ["vue-router/auto"]` for typed-router globals. Faster incremental tsc on CI, less prone to OOM on large changes +- ActionbarWrapper moved from `src/components/wrappers/` to `src/layouts/` (admin-cms convention). 51 consumer imports updated + +### Changed + +- **Reorganized project to domain-driven architecture**: `src/domains///{api,components,composables,factory,filter,store,types,valueObject}` grouping per entity. Three top-level systems: `system/` (auth, app shell, system views), `common/` (anzuUser, log, permission, permissionConfig, permissionGroup), `coreDam/` (17 DAM entities + a `shared/` slot for cross-entity DAM code: anzutap, ColorBox, FileUpload, customMetadata, ABtnAdvanced, CachedDamUserChip, FilterClosestColor, distribution helpers, assetFileRoute, upload service). Cross-system generics moved to `src/shared/` (apiClients/, types/, utils/, EnvConfigService.ts, ErrorHandlerApiService.ts, BetaTestFeaturesService.ts, BetaTestFeaturesService.ts, systems.ts, configurationApi.ts). Old top-level folders `src/{components,composables,services,stores,types,model,views,utils}/` deleted entirely. Every file moved via `git mv` so `git log --follow` traces full history through the relocation. ~800 files renamed; ~700 import sites rewritten. The `src/layouts/`, `src/pages/`, `src/plugins/`, `src/styles/`, `src/locales/`, `src/router/` directories stay at root +- `src/domains/system/auth/Auth.ts` renamed to `simpleLogin.ts` to avoid a Linux case-sensitive collision with the existing `auth.ts` composable (TypeScript's `forceConsistentCasingInFileNames` rejects sibling files differing only in case). Affects only the import path; exported symbols (`SimpleLoginForm`) unchanged +- `views/coreDam/externalProviderAsset/` consolidated into `domains/coreDam/externalProvider/components/` (single domain for the entity instead of split asset-of-external-provider folder) +- `eslint.config.mjs`: `anzu/no-deprecated-imports` toggled from `warn` to `off` during the restructure to keep CI signal clean; re-enable in the dedicated cleanup PR +- Migrated 140 Vuetify v4 lint-detected issues automatically: MD2→MD3 typography classes, grid props (`align`/`justify`/`order`/`dense`) → utility classes, deprecated CSS classes and colors +- Manual v4 migrations: removed `dark` from `VAlert`/`VBtn`/`VIcon`; removed `contain` from `VImg` (v4 default is `object-fit: contain`) +- Theme color alpha syntax in anzutap SCSS: `rgb(var(--v-theme-X), 0.Y)` → `rgb(var(--v-theme-X) / Y%)` (broken comma form in v4); same fix for `--v-border-color` / `--v-border-opacity` +- TypeScript: fixed all 41 pre-existing errors. Root cause was unchecked access on optional fields of `DamExtSystemConfig` / `DamExtSystemConfigItem`; fixed with `?.` chaining + `?? false/0/{}` fallbacks. Added missing `locale: null` default to `UserFactory`. **Runtime behavior change**: code that would previously throw on missing config now silently uses the fallback +- Router guards migrated from deprecated `next()` callback to return values (vue-router 5 API): `next({ name: 'X' })` → `return { name: 'X' }`, `next()` → bare `return`. Affects `checkAbility.ts`, `beforeEachRoute.ts`, `composables/system/appInitialize.ts`, and the `vueRouter.beforeEach` registration in `router/index.ts`. Unused `from` parameter dropped from helpers +- `tsconfig.app.json` — dropped redundant `baseUrl: "."` +- `tsconfig.node.json` — removed unused `nightwatch.conf.*` and `playwright.config.*` from `include` +- Downgraded `@vueuse/core` and `@vueuse/integrations` to exact `14.2.1` (14.3.0 ships malformed `/* #__PURE__ */` annotations that Rollup strips with warnings) +- Converted 3 relative imports in `AnzutapToolbar.vue` / `AnzutapToolbarItem.vue` to `@/...` absolute paths (oxlint `no-restricted-imports`) +- Many Vue/SCSS/TS files reformatted via `oxfmt` +- Replaced all `ROUTE.X.Y` references (274 spots) with raw auto-generated route-name literals (`'/(coreDam)/asset/[id]'` etc.) — admin-cms convention. `useRoute()` calls in views with multi-route ambiguity cast `route.params` (`(route.params as { id: string }).id`) or use the typed `useRoute('/route/[id]')` overload +- Upgraded `@tsconfig/node22` → `@tsconfig/node24` (admin-cms parity); `engines` field still permits node 20.19+ +- Pluralized all entity route paths (admin-cms convention): `/asset` → `/assets`, `/user` → `/users`, `/author` → `/authors`, `/keyword` → `/keywords`, `/ext-system` → `/ext-systems`, `/asset-licence{,-group}` → `/asset-licences{,-groups}`, `/distribution-category{,-select}` → `/distribution-categories{,-selects}`, `/author-clean-phrase` → `/author-clean-phrases`, `/public-export` → `/public-exports`, `/podcast{/[id]/episode}` → `/podcasts{/[id]/episodes}`, `/video-show{/[id]/episode}` → `/video-shows{/[id]/episodes}`, `/external-provider` → `/external-providers`, `/job` → `/jobs`, `/anzu-user` → `/anzu-users`, `/log` → `/logs`, `/permission-group` → `/permission-groups`. Page folders renamed via `git mv` (history preserved); all consumer `name:` / `:route-name` literals + `definePage({ path })` overrides + Cypress `cy.visit()` / `cy.urlContains()` updated. **Breaking**: existing bookmarks to old singular URLs return 404 (no redirect layer added) +- `ActionbarWrapper.vue` switched to `RouteParamsGeneric` and string-cast for `route.name` to work with the strict typed-router map +- `checkAbility.ts` redirect target changed from `{ name: 'unauthorized' }` to `'/unauthorized'` (path-based, matches admin-cms) + +### Fixed + +- `distributionCategoryActions.ts` redirected after create to the LIST route with dead `params.id` instead of DETAIL — pre-existing typo uncovered by route-reference audit +- `assetLicenceGroupActions.ts` redirected after update to `/(coreDam)/asset-licence` (wrong domain) instead of `/(coreDam)/asset-licence-group` — pre-existing copy-paste error uncovered by route-reference audit + +### Removed + +- `dark` prop usages (removed in v4) +- `contain` prop usages on `VImg` (v4 default behavior) +- `src/router/routes/` directory (19 manual route files) and `src/router/routes.ts` (`ROUTE` constants) — replaced by file-based `src/pages/` tree +- `AEmptyRouterView` parent wrappers — file folders provide nesting; each leaf page declares its own meta (no inheritance) +- Explicit imports of auto-imported symbols across `src/**` (now provided globally via `unplugin-auto-import`) +- Prettier (`prettier`, `@vue/eslint-config-prettier`, `lint:prettier`, `lint:prettier:fix`) — replaced by `oxfmt` (mirrors admin-cms) +- Inline `tsExtensionPlugin` custom ESLint rule (`no-ts-extension`) — now provided by `anzuRecommended()` from common-admin +- Top-level `src/components/`, `src/composables/`, `src/services/`, `src/stores/`, `src/types/`, `src/model/`, `src/views/`, `src/utils/` folders — every file relocated into `src/domains/` or `src/shared/` diff --git a/doc/changelog/unreleased/template.md b/doc/changelog/unreleased/template.md new file mode 100644 index 00000000..548d3f31 --- /dev/null +++ b/doc/changelog/unreleased/template.md @@ -0,0 +1,8 @@ +planned +=== + +### Added + +### Changed + +### Removed diff --git a/docker/app/local/Dockerfile b/docker/app/local/Dockerfile index b2a44f6c..6b78402f 100644 --- a/docker/app/local/Dockerfile +++ b/docker/app/local/Dockerfile @@ -1,4 +1,4 @@ -FROM anzusystems/node:4.0.0-node24-nginx-browsers +FROM anzusystems/node:4.1.0-node24-nginx-browsers # ### Basic arguments and variables diff --git a/eslint.config.mjs b/eslint.config.mjs index 75fac62d..25f3d1af 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,41 +1,12 @@ import stylistic from '@stylistic/eslint-plugin' -// import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' import pluginVue from 'eslint-plugin-vue' import pluginPinia from 'eslint-plugin-pinia' +import pluginVuetify from 'eslint-plugin-vuetify' +import oxlint from 'eslint-plugin-oxlint' import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' -const tsExtensionPlugin = { - rules: { - 'no-ts-extension': { - meta: { - type: 'problem', - docs: { - description: 'Disallow .ts extension in import statements', - }, - fixable: 'code', - schema: [], - }, - create(context) { - return { - ImportDeclaration(node) { - const source = node.source.value - if (typeof source === 'string' && source.endsWith('.ts')) { - context.report({ - node, - message: 'Do not include .ts extension in import paths', - fix(fixer) { - const sourceText = node.source.raw - const newSource = sourceText.replace(/\.ts(['"])$/, '$1') - return fixer.replaceText(node.source, newSource) - }, - }) - } - }, - } - }, - }, - }, -} +import { recommended as anzuRecommended } from '@anzusystems/common-admin/eslint' +import validRouteName from './eslint/rules/valid-route-name.mjs' export default defineConfigWithVueTs( { @@ -44,11 +15,12 @@ export default defineConfigWithVueTs( }, { name: 'app/files-to-ignore', - ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '.stylelintrc.js', '**/cypress/**'], + ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '.stylelintrc.js', '**/cypress/**', 'src/typed-router.d.ts', 'src/auto-imports.d.ts'], }, pluginVue.configs['flat/essential'], pluginVue.configs['flat/strongly-recommended'], pluginVue.configs['flat/recommended'], + ...pluginVuetify.configs['flat/recommended-v4'], vueTsConfigs.recommended, { plugins: { @@ -64,30 +36,29 @@ export default defineConfigWithVueTs( 'pinia/require-setup-store-properties-export': 'error', }, }, - { - plugins: { - 'ts-extension': tsExtensionPlugin, - }, - rules: { - 'ts-extension/no-ts-extension': 'error', - }, - }, + anzuRecommended({ + // Fully migrated off the deprecated @anzusystems/common-admin barrel onto /labs, + // including the author/keyword cached-tagging components (now on the labs + // AFormRemoteAutocompleteWithCached). Rule fully enforced — no skips. + deprecatedImports: 'error', + }), { plugins: { '@stylistic': stylistic, + 'anzu-local': { + rules: { + 'valid-route-name': validRouteName, + }, + }, }, rules: { + 'anzu-local/valid-route-name': 'error', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-empty-interface': 'off', '@stylistic/semi': ['error', 'never'], '@stylistic/quotes': ['error', 'single', { avoidEscape: true }], - 'vue/multi-word-component-names': [ - 'error', - { - ignores: ['Acl'], - }, - ], + 'vue/multi-word-component-names': 'off', 'vue/valid-v-slot': ['error', { allowModifiers: true }], '@stylistic/object-curly-spacing': ['error', 'always'], '@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxEOF: 1 }], @@ -121,6 +92,6 @@ export default defineConfigWithVueTs( '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-unused-expressions': 'off', }, - } - // skipFormatting, + }, + ...oxlint.buildFromOxlintConfigFile('./.oxlintrc.json') ) diff --git a/eslint/rules/valid-route-name.mjs b/eslint/rules/valid-route-name.mjs new file mode 100644 index 00000000..91c1d596 --- /dev/null +++ b/eslint/rules/valid-route-name.mjs @@ -0,0 +1,111 @@ +import fs from 'node:fs' +import path from 'node:path' + +const TYPED_ROUTER_PATH = path.resolve(process.cwd(), 'src/typed-router.d.ts') + +let cachedRoutes = null +let cachedMtimeMs = 0 + +function loadRoutes() { + let stat + try { + stat = fs.statSync(TYPED_ROUTER_PATH) + } catch { + return cachedRoutes ?? new Set() + } + if (cachedRoutes && stat.mtimeMs === cachedMtimeMs) return cachedRoutes + + const content = fs.readFileSync(TYPED_ROUTER_PATH, 'utf8') + const routes = new Set() + const re = /'([^']+)':\s*RouteRecordInfo/g + let m + while ((m = re.exec(content)) !== null) { + routes.add(m[1]) + } + cachedRoutes = routes + cachedMtimeMs = stat.mtimeMs + return routes +} + +function isRouteKey(key) { + if (!key) return false + const normalized = String(key).replace(/-/g, '').toLowerCase() + return normalized === 'name' || normalized === 'route' || normalized.endsWith('routename') +} + +export default { + meta: { + type: 'problem', + docs: { + description: 'Validate route names against generated src/typed-router.d.ts', + }, + schema: [], + messages: { + unknown: + "Unknown route name '{{name}}'. Not found in src/typed-router.d.ts. " + + 'Run `yarn generate:dts` if routes were just added.', + }, + }, + create(context) { + const routes = loadRoutes() + if (routes.size === 0) return {} + + function checkLiteral(node, value) { + if (typeof value !== 'string') return + if (!value.startsWith('/')) return + if (routes.has(value)) return + context.report({ node, messageId: 'unknown', data: { name: value } }) + } + + function checkProperty(prop) { + if (!prop || prop.type !== 'Property') return + const key = prop.key + const keyName = + key.type === 'Identifier' ? key.name : key.type === 'Literal' ? String(key.value) : null + if (!isRouteKey(keyName)) return + const val = prop.value + if (val && val.type === 'Literal') { + checkLiteral(val, val.value) + } + } + + function checkAttributeLiteral(node) { + const key = node.key + if (!key) return + let name + if (key.type === 'VIdentifier') { + name = key.name + } else if (key.type === 'VDirectiveKey') { + if (key.name?.name !== 'bind') return + const arg = key.argument + if (!arg || arg.type !== 'VIdentifier') return + name = arg.name + } + if (!isRouteKey(name)) return + const val = node.value + if (!val) return + if (val.type === 'VLiteral') { + checkLiteral(val, val.value) + return + } + if (val.type === 'VExpressionContainer' && val.expression?.type === 'Literal') { + checkLiteral(val.expression, val.expression.value) + } + } + + const scriptVisitor = { + Property: checkProperty, + } + + const templateVisitor = { + VAttribute: checkAttributeLiteral, + 'VElement > VStartTag > VAttribute > VExpressionContainer Property': checkProperty, + } + + const parserServices = context.sourceCode?.parserServices ?? context.parserServices + if (parserServices?.defineTemplateBodyVisitor) { + return parserServices.defineTemplateBodyVisitor(templateVisitor, scriptVisitor) + } + return scriptVisitor + }, +} diff --git a/index.html b/index.html index 4f17cf93..d73499af 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,9 @@ +
diff --git a/package.json b/package.json index 987d588f..dc816abf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adam-admin", - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.15.0", "license": "Apache-2.0", "version": "0.0.1", "type": "module", @@ -10,83 +10,95 @@ "scripts": { "dev": "vite", "build": "vite build", - "production": "vite build --mode production", - "development": "vite build --mode development --sourcemap", + "build:dev": "vite build --minify false", + "production": "yarn browserslist:update && vite build --mode production", + "development": "yarn browserslist:update && vite build --mode development --sourcemap", "preview": "vite preview", - "ci": "run-s --print-name lint:tsc lint:eslint lint:stylelint", - "lint": "yarn ci", - "lint:tsc": "vue-tsc --noEmit -p tsconfig.app.json --composite false", + "ci": "run-s --print-name generate:dts lint:tsc lint:oxlint lint:eslint lint:stylelint", + "lint": "[ \"$*\" = \"--fix\" ] && yarn lint:fix || yarn ci", + "lint:fix": "run-s --print-name generate:dts lint:tsc format lint:oxlint:fix lint:eslint:fix lint:stylelint:fix", + "lint:tsc": "NODE_OPTIONS=--max-old-space-size=4096 vue-tsc --build", + "generate:dts": "node scripts/generate-dts.mjs", + "lint:oxlint": "oxlint .", + "lint:oxlint:fix": "oxlint . --fix", "lint:eslint": "eslint", "lint:eslint:fix": "eslint --fix", "lint:stylelint": "stylelint **/*.{scss,vue}", "lint:stylelint:fix": "stylelint **/*.{scss,vue} --fix", - "lint:prettier": "prettier -c \"src/**/*.{ts,vue}\"", - "lint:prettier:fix": "prettier -w \"src/**/*.{ts,vue}\"", + "format": "oxfmt src/", + "format:check": "oxfmt src/ --check", + "browserslist:update": "npx --yes browserslist@latest --update-db", "cy:open": "CYPRESS_CACHE_FOLDER='node_modules/.cache/Cypress' yarn cypress open -C cypress/config/cypress.config.ts" }, "dependencies": { - "@anzusystems/common-admin": "1.47.0-beta.354", - "@floating-ui/dom": "^1.7.5", + "@anzusystems/common-admin": "1.47.0-beta.dev-1782072499", + "@floating-ui/dom": "^1.7.6", "@mdi/font": "7.4.47", - "@sentry/vue": "^10.38.0", - "@tiptap/core": "^3.19.0", - "@tiptap/extension-bold": "^3.19.0", - "@tiptap/extension-italic": "^3.19.0", - "@tiptap/extension-link": "^3.19.0", - "@tiptap/extension-underline": "^3.19.0", - "@tiptap/pm": "^3.19.0", - "@tiptap/starter-kit": "^3.19.0", - "@tiptap/vue-3": "^3.19.0", + "@sentry/vue": "^10.62.0", + "@tiptap/core": "^3.27.1", + "@tiptap/extension-bold": "^3.27.1", + "@tiptap/extension-italic": "^3.27.1", + "@tiptap/extension-link": "^3.27.1", + "@tiptap/extension-underline": "^3.27.1", + "@tiptap/pm": "^3.27.1", + "@tiptap/starter-kit": "^3.27.1", + "@tiptap/vue-3": "^3.27.1", "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", - "@vueuse/core": "14.2.0", - "@vueuse/integrations": "14.2.0", - "axios": "^1.13.5", + "@vueuse/core": "14.2.1", + "@vueuse/integrations": "14.2.1", + "axios": "^1.18.1", "jwt-decode": "^4.0.0", "pinia": "^3.0.4", "rusha": "^0.8.14", "socket.io-client": "^4.8.3", - "sortablejs": "^1.15.6", - "universal-cookie": "^8.0.1", - "uuid": "^13.0.0", - "vue": "3.5.28", - "vue-i18n": "^11.2.8", - "vue-router": "^5.0.2", - "vuetify": "^3.11.8" + "sortablejs": "^1.15.7", + "universal-cookie": "^8.1.2", + "uuid": "^14.0.1", + "vue": "3.5.39", + "vue-i18n": "^11.4.6", + "vue-router": "^5.1.0", + "vuetify": "^4.1.2" }, "devDependencies": { - "@cypress/grep": "^6.0.0", - "@intlify/unplugin-vue-i18n": "^11.0.3", - "@sentry/vite-plugin": "^4.9.0", - "@stylistic/eslint-plugin": "^5.8.0", - "@tsconfig/node22": "^22.0.5", - "@types/node": "^24.10.12", + "@cypress/grep": "^6.0.2", + "@intlify/unplugin-vue-i18n": "^11.2.4", + "@sentry/vite-plugin": "^5.3.0", + "@stylistic/eslint-plugin": "^5.10.0", + "@tsconfig/node24": "^24.0.4", + "@types/node": "^24.13.2", "@types/rusha": "^0.8.3", "@types/sortablejs": "^1.15.9", - "@vitejs/plugin-vue": "^6.0.4", - "@vue/eslint-config-prettier": "^10.2.0", - "@vue/eslint-config-typescript": "^14.6.0", - "@vue/language-server": "3.2.4", - "@vue/tsconfig": "^0.8.1", - "cypress": "^15.10.0", + "@vitejs/plugin-vue": "^6.0.7", + "@vue/eslint-config-typescript": "^14.9.0", + "@vue/language-server": "3.3.6", + "@vue/tsconfig": "^0.9.1", + "browserslist": "^4.28.4", + "cypress": "15.18.0", "cypress-downloadfile": "1.2.4", "cypress-mochawesome-reporter": "^4.0.2", - "eslint": "9.39.2", + "dotenv": "^17.4.2", + "eslint": "10.6.0", + "eslint-plugin-oxlint": "1.72.0", "eslint-plugin-pinia": "^0.4.2", - "eslint-plugin-vue": "^10.7.0", - "npm-run-all2": "^8.0.4", - "postcss": "^8.5.6", + "eslint-plugin-vue": "^10.9.2", + "eslint-plugin-vuetify": "^2.7.2", + "lightningcss": "^1.32.0", + "npm-run-all2": "^9.0.2", + "oxfmt": "^0.57.0", + "oxlint": "1.72.0", + "postcss": "^8.5.16", "postcss-html": "^1.8.1", - "prettier": "^3.8.1", - "sass": "^1.97.3", - "stylelint": "^17.1.1", + "sass": "^1.101.0", + "stylelint": "^17.14.0", "stylelint-config-recommended-vue": "^1.6.1", "stylelint-config-standard-scss": "^17.0.0", "typescript": "5.9.3", - "unplugin": "^3.0.0", - "vite": "^7.3.1", + "unplugin": "^3.3.0", + "unplugin-auto-import": "^21.0.0", + "vite": "^7.3.6", "vite-plugin-vuetify": "^2.1.3", - "vue-eslint-parser": "^10.2.0", - "vue-tsc": "3.2.4" + "vue-eslint-parser": "^10.4.1", + "vue-tsc": "3.3.6" } } diff --git a/scripts/generate-dts.mjs b/scripts/generate-dts.mjs new file mode 100644 index 00000000..485d4910 --- /dev/null +++ b/scripts/generate-dts.mjs @@ -0,0 +1,17 @@ +/** + * Generates the typed-router declaration file + * by briefly running a Vite build (triggers plugin hooks). + * + * Used in CI before type-checking and linting. + */ +import { build } from 'vite' + +await build({ + build: { + write: false, + rollupOptions: { + onLog() {}, + }, + }, + logLevel: 'warn', +}) diff --git a/src/App.vue b/src/App.vue index 5f43ef4e..d4879646 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,7 @@ - - - - diff --git a/src/domains/common/anzuUser/api/anzuUserApi.ts b/src/domains/common/anzuUser/api/anzuUserApi.ts new file mode 100644 index 00000000..babe23e7 --- /dev/null +++ b/src/domains/common/anzuUser/api/anzuUserApi.ts @@ -0,0 +1,51 @@ +import type { AnzuUser } from '@anzusystems/common-admin' +import { useApiFetchByIds, useApiFetchList, useApiRequest } from '@anzusystems/common-admin/labs' +import { damClient } from '@/shared/apiClients/damClient' + +const SYSTEM = 'common' +export const ENTITY = 'anzuUser' + +const END_POINT = '/adm/v1/anzu-user' + +export const useFetchAnzuUserListByIds = () => + useApiFetchByIds({ + client: damClient, + system: SYSTEM, + entity: ENTITY, + urlTemplate: END_POINT, + }) + +export const useFetchAnzuUserList = () => + useApiFetchList({ + client: damClient, + system: SYSTEM, + entity: ENTITY, + urlTemplate: END_POINT, + }) + +export const useFetchAnzuUser = () => + useApiRequest({ + client: damClient, + method: 'GET', + system: SYSTEM, + entity: ENTITY, + urlTemplate: END_POINT + '/:id', + }) + +export const useCreateAnzuUser = () => + useApiRequest({ + client: damClient, + method: 'POST', + system: SYSTEM, + entity: ENTITY, + urlTemplate: END_POINT, + }) + +export const useUpdateAnzuUser = () => + useApiRequest({ + client: damClient, + method: 'PUT', + system: SYSTEM, + entity: ENTITY, + urlTemplate: END_POINT + '/:id', + }) diff --git a/src/views/common/anzuUser/components/AnzuUserCreateButton.vue b/src/domains/common/anzuUser/components/AnzuUserCreateButton.vue similarity index 81% rename from src/views/common/anzuUser/components/AnzuUserCreateButton.vue rename to src/domains/common/anzuUser/components/AnzuUserCreateButton.vue index e9572297..89bc84e8 100644 --- a/src/views/common/anzuUser/components/AnzuUserCreateButton.vue +++ b/src/domains/common/anzuUser/components/AnzuUserCreateButton.vue @@ -1,23 +1,17 @@ + + diff --git a/src/views/common/anzuUser/components/AnzuUserEditForm.vue b/src/domains/common/anzuUser/components/AnzuUserEditForm.vue similarity index 83% rename from src/views/common/anzuUser/components/AnzuUserEditForm.vue rename to src/domains/common/anzuUser/components/AnzuUserEditForm.vue index ec25557d..3f25333e 100644 --- a/src/views/common/anzuUser/components/AnzuUserEditForm.vue +++ b/src/domains/common/anzuUser/components/AnzuUserEditForm.vue @@ -1,23 +1,20 @@ + + diff --git a/src/domains/common/anzuUser/components/AnzuUserFilter.vue b/src/domains/common/anzuUser/components/AnzuUserFilter.vue new file mode 100644 index 00000000..faf1e2c7 --- /dev/null +++ b/src/domains/common/anzuUser/components/AnzuUserFilter.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/domains/common/anzuUser/components/AnzuUserListView.vue b/src/domains/common/anzuUser/components/AnzuUserListView.vue new file mode 100644 index 00000000..0db449bd --- /dev/null +++ b/src/domains/common/anzuUser/components/AnzuUserListView.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/views/common/anzuUser/components/AnzuUserRoleSelect.vue b/src/domains/common/anzuUser/components/AnzuUserRoleSelect.vue similarity index 85% rename from src/views/common/anzuUser/components/AnzuUserRoleSelect.vue rename to src/domains/common/anzuUser/components/AnzuUserRoleSelect.vue index a13f1058..e35e1e19 100644 --- a/src/views/common/anzuUser/components/AnzuUserRoleSelect.vue +++ b/src/domains/common/anzuUser/components/AnzuUserRoleSelect.vue @@ -1,8 +1,6 @@ + + diff --git a/src/domains/common/log/components/LogFilter.vue b/src/domains/common/log/components/LogFilter.vue new file mode 100644 index 00000000..5a4d187b --- /dev/null +++ b/src/domains/common/log/components/LogFilter.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/views/common/log/LogListView.vue b/src/domains/common/log/components/LogListView.vue similarity index 64% rename from src/views/common/log/LogListView.vue rename to src/domains/common/log/components/LogListView.vue index a0aad30c..fccb91bf 100644 --- a/src/views/common/log/LogListView.vue +++ b/src/domains/common/log/components/LogListView.vue @@ -1,14 +1,17 @@