Skip to content

CELDEV-1338 - Add router-independent admin frontend islands - #561

Open
fpichler wants to merge 2 commits into
devfrom
CELDEV-1338-admin-islands
Open

CELDEV-1338 - Add router-independent admin frontend islands#561
fpichler wants to merge 2 commits into
devfrom
CELDEV-1338-admin-islands

Conversation

@fpichler

@fpichler fpichler commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • make PageAttachments router-independent by passing typed spaceName, docName, and locale context into a shared feature component
  • retain the existing Vue Router SPA through a thin route adapter while reusing the same feature and shared PrimeVue, i18n, VueFinder, logging, confirmation, and component setup
  • add the guarded <cel-page-attachments> island with explicit attribute/property mapping, safe reconnect and attribute-change handling, DOM event forwarding, and a fresh Pinia per mount
  • expose the typed attachment-actions slot with document, path, count, and selected attachments so downstream products can add actions without replacing routes or copying the component
  • replace the legacy inline and editor-tab attachment implementations with the island while preserving server-rendered navigation, established attachment URLs, wrappers, and asynchronous fragment loading
  • publish intentional runtime, PageAttachments, island, and complete stylesheet entry points without unrestricted src/* imports or a Vue Router requirement in reusable package code
  • document the accepted feature/island plus thin-SPA architecture and public compatibility contracts in ADR 0001

Stylesheet contract and containment

  • bundle processed VueFinder CSS together with the scoped Celements/Tailwind styles in @celements/admin-frontend/styles.css, while keeping VueFinder JavaScript external as a peer
  • scope generic vendor, utility, reset, document, transition, .disabled, and .cropper-viewers selectors to the admin surface or known teleport boundaries
  • retain only the documented VueFinder, cropper, OverlayScrollbars, Sonner, and Uppy namespace allowlist globally
  • test the actual package and deployable CSS artifacts for VueFinder coverage, host-page containment, and VueFinder/PrimeVue teleport styling

Forge npm delivery

  • configure the @celements scope for https://forge.celhosting.ch/api/packages/celements/npm/ using FORGE_TOKEN
  • add a separate Jenkinsfile.npm package pipeline; the existing Jenkinsfile continues to publish only the Docker image
  • publish unique x.y.z-snapshot.<BUILD_NUMBER>.<GIT_SHA> versions with the snapshot dist-tag, and require exact admin-frontend-vx.y.z tags for release versions published with latest
  • refuse mutable x.y.z-SNAPSHOT publication and refuse overwriting a package version already present in Forge
  • document pinned consumer installation and supported import examples

CI operations must still configure a Jenkins job whose script path is celements-admin-frontend/Jenkinsfile.npm. No npm package is published automatically until that infrastructure step exists.

Validation

  • npm run build — passed; 3 test files and 16 tests passed
  • npm run lint — passed
  • npm run format — passed
  • npm pack --dry-run --json --cache <writable-temp-cache> — passed; dist/package/styles.css included
  • clean temporary Vite consumer using the packed tarball — passed with the documented peers and public imports, without source aliases or a sibling checkout
  • mvn -f celements-webapp/pom.xml -DskipTests compileBUILD SUCCESS (repository metadata checks emitted local authentication warnings)
  • git diff --check origin/dev...HEAD — passed

Jira: https://synjira.atlassian.net/browse/CELDEV-1338

@fpichler
fpichler requested a review from msladek August 7, 2026 02:17
@fpichler
fpichler marked this pull request as ready for review August 7, 2026 02:21

@msladek msladek Aug 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jenkinsfile.npm and publish-forge-package.sh contain mostly reusable npm-specific publication workflow logic. Please move this into a focused npmPackagePipeline in the server-tools Jenkins shared library, analogous to dockerImagePipeline, and keep this repository’s pipeline declarative:

 @Library('synventis') _

 npmPackagePipeline(appDir: 'celements-admin-frontend')

</div>
</div> ## row
#end ## foreach
#parse('celTemplates/pageAttachmentsIsland.vm')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserve anonymous attachment listing

Replacing the server-rendered template with the Vue island affects normal document views, the attachments page, and the attachment overlay—not only editors. The previous template listed attachments, metadata, history, and download links for all viewers while restricting upload and delete actions to users with edit/admin rights. The island loads this list through PageAttachmentsController, whose prepareRequest() returns 401 when no authenticated user exists before evaluating VIEW rights. Anonymous users therefore lose the attachment listing on public documents despite having VIEW access.

Comment on lines +62 to +63
upload: true,
delete: true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcodes upload and delete to true, while pageAttachmentsIsland.vm passes only the document identity and locale. Because the shared island now replaces attachmentsinline.vm on normal document views and overlays, authenticated users with only VIEW permission receive enabled mutation controls. It regresses the previous UI, which exposed mutations only to authorized users. Pass explicit can-upload and can-delete capabilities through Velocity → custom element → component and use them for the VueFinder feature flags.

Comment on lines +2 to +3
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please align with the legacy-safe Tailwind setup already established for existing Vue islands:

Suggested change
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@import 'tailwindcss/theme.css' layer(theme) prefix(tw);
@import 'tailwindcss/utilities.css' layer(utilities) prefix(tw) important source(none);
@source '..';
@source '../../index.html';

This provides prefixed tw: classes, emits utility declarations with !important, excludes preflight and limits source scanning. Please also remove the ancestor-selector important setting from tailwind.config.js and update all utility references, including @apply and TypeScript class strings, to use the tw: prefix.

#end
<script type="module" src="$pageAttachmentsModule"></script>
<span class="cel_lazyloadJS_exec" style="display:none">import('$pageAttachmentsModule')</span>
<cel-page-attachments

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<cel-page-attachments> remains inline by default. The Vue root’s w-full cannot establish that layout through an inline host.

cel-page-attachments {
  display: block;
}

@msladek msladek assigned fpichler and unassigned msladek Aug 13, 2026

@msladek msladek Aug 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ADR mixes the router-independent feature/island decision with detailed package APIs, CSS selector allowlists and Jenkins/Forge publication policy. That makes the core decision difficult to identify and couples unrelated details that will evolve independently.

Please keep the ADR focused on router-free features, SPA/island adapters, per-mount state isolation, and the light-DOM decision. Move the remaining details to dedicated documentation or omit them when they are already defined by code.

edit: false,
fullscreen: false,
language: false,
history: false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replaced attachmentsinline.vm linked each attachment’s version to the viewattachrev action. The new VueFinder UI sets history: false and exposes no equivalent link. The action itself remains functional, but users can no longer reach attachment revision history from the attachment listing.

"private": false,
"type": "module",
"files": [
"dist"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build produces two different deliverables in the same dist/ tree:

dist/
├─ assets/   # deployable SPA assets for the Caddy container
├─ .vite/    # deployable manifest
├─ package/  # npm library JavaScript and CSS
└─ types/    # npm library declarations

"files": ["dist"] causes npm pack to include all four. Publish only the npm library artifacts:

Suggested change
"dist"
"dist/package",
"dist/types"

const app = createApp(rootComponent, rootProps);
const pinia = createCelementsPinia();
app.config.errorHandler = (err, instance, info) => {
logger.error(err as string, instance, info);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
logger.error(err as string, instance, info);
logger.error(err, instance, info);

avoid as, it is not needed here

}

function optimisticallyDeleteTag(tag: Tag): { backupTags: Tag[]; backupMap: Record<string, Tag[]> } {
function optimisticallyDeleteTag(tag: Tag): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer arrow functions over function declarations, per $synventis-vue-style. This affects several places in this PR/repo, please do a cleanup pass.

Comment on lines +51 to +52
const instanceId = ref(`page_attachments_${crypto.randomUUID()}`);
const finderId = computed(() => instanceId.value);

@msladek msladek Aug 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const instanceId = ref(`page_attachments_${crypto.randomUUID()}`);
const finderId = computed(() => instanceId.value);
const finderId = `page_attachments_${crypto.randomUUID()}`;

no need for reactive state

Comment on lines 51 to 67
@@ -30,19 +58,20 @@
"findandreplacedomtext": "^0.4.6",
"lodash": "^4.17.23",
"luxon": "^3.7.2",
"pinia": "^3.0.4",
"primevue": "^4.5.4",
"tailwind-merge": "^3.5.0",
"tailwindcss-primeui": "^0.6.1",
"vue": "^3.5.30",
"vue-dompurify-html": "^5.3.0",
"vue-i18n": "^11.3.0",
"vue-matomo": "^4.2.0",
"vue-router": "^5.0.4",
"vue3-spinners": "^1.3.3",
"vuefinder": "^4.1.1",
"zod": "^4.3.6"
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@primeuix/themes, findandreplacedomtext, lodash, tailwind-merge, vue-dompurify-html, vue-matomo, vue3-spinners, and zod have no source imports.
tailwindcss-primeui is build-only.
Remove the unused entries and move build-only to devDependencies so consumers do not install them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants