Skip to content

Feature Status

Current implementation status of all Mapo v2 features relative to the legacy v1 feature set.

Implemented ✅

Core & Auth

FeatureNotes
useMapoAuth() — login / logout / fetchUserHttpOnly cookie session, no client-stored token
SSR session hydrationServer plugin reads session cookie, hydrates useAuthStore before first render
auth middlewareRedirects to loginUrl if not authenticated; appends ?redirect=
permissions middlewareSupports { model } (derives CRUD actions, populates pagePermissions) and string[] (explicit codenames)
roles middlewareChecks meta.roles against useAuthStore.role
401 auto-logout$mapoFetch interceptor calls auth.reset() and redirects on 401
useCrud<T>() — full CRUD repositorylist, detail, create, update, partialUpdate, delete, updateOrder
useMapoFetch() — auth-aware $fetchReturns the $mapoFetch instance (401/403 interceptors). Auto-imported.
Multipart upload with policyauto | force | disable — transforms payload when files are detected
@mapomodule/utils — base helpersdeepMerge, deepClone, objectDiff, getNestedValue, setNestedValue, formatDate, debounce, normalizeEndpoint, slotNamespace, buildRouteTree, calcMaxMenuNestDepth, isFile, isBlob, isFileOrBlob, findPropPaths, filesInObject, filterObj

State (Pinia)

StoreKey features
useAuthStoreisAuthenticated, username, role, modelPermissions, pagePermissions (populated by permissions middleware when using { model } format)
useSnackStoreshow(message, type, duration), dismiss()
useConfirmStoreask(options): Promise<boolean>
useSidebarStoredrawer, mini — both persisted to cookies, SSR-hydrated
usePermissionsuserCan(model, action) helper

UIKit Shell

Component / FeatureNotes
mapo-default layoutSidebar + Topbar + content + global feedback
MapoSidebarDrawer + mini mode, cookie-persisted state, full slot API
Auto-generated sidebar navBuilt from route.meta.label — no manual config needed
Nested menumeta.parent creates hierarchical groups
Footer itemsmeta.sidebarFooter: true moves item to sidebar footer
MapoTopbarDrawer toggle + left / center / right slots
MapoLoginSplit-panel form, field-level error mapping (backend 400 {username,password}), redirect-aware, slots: brand / panel / footer
MapoSnackBarBridges useSnackStore to Nuxt UI toast notifications
MapoConfirmDialogBridges useConfirmStore to non-dismissible <UModal>
MapoRootComponentsMounts SnackBar + ConfirmDialog globally
MapoThemeToggleDark/light toggle via useColorMode() — drop-in for any slot
MapoLogoutButtonStandalone logout button — props: variant, color, size, iconOnly; slot for custom label
MapoSidebarProfileUser profile row for the sidebar footer — reads from useAuthStore, calls useMapoAuth().logout(); supports mini prop
Theming via CSSCustom CSS file injected after base via uikit.css option
Nuxt UI defaults overrideComponent config via uikit.ui option
Component override systemPlace MapoXxx.vue in app/mapooverride/ to swap any component at build time

Integrations

FeatureNotes
mapo-integrations-camomillaNitro proxy, path rewriting, cookie sync (sessionid__mapo_session), CSRF forwarding
Custom backend integrationPattern documented in Writing your own

List Engine (@mapomodule/uikit)

FeatureNotes
<MapoList> shellComposes <MapoListHead>, <MapoListFilters>, <MapoListActions>, <MapoListTabs>, <MapoListTable>, <MapoListQuickEdit>
Server-side pagination / search / orderingDefault mode: filters, tabs, and pagination all pass as list() params — never baked into the endpoint string
Offline mode (v-model:items)No backend: search / filter / tab / sort / paginate / drag-reorder / delete / quick-edit all run in memory on the parent array
Hybrid mode (client-side prop)Single fetch on mount; filters / sort / paginate run client-side. Mutations (delete, drag, quick-edit) hit BE then refetch
Multi-column sort (offline / hybrid)Sorting columns applied in order, each as a tiebreaker for the previous one (matches TanStack default)
Filters + sorting work simultaneouslyActive filters are preserved when sorting; sorting is preserved when filters change (B1 fix)
endpoint with static query paramsParams in the endpoint URL (e.g. ?ordering=-date) are passed to list() calls; CRUD ops use the clean path (B2 fix)
Selection keyed by lookupPaging or sorting does not move selections
Drag reorder via crud.updateOrderSingle server call — no parallel PATCH storm
Quick-edit modalDriven by the same FieldDescriptor[] used by <MapoDetail> — no bespoke dialog code
Column slots#cell.<key> receives { item, value }, #dtable.toolbar, #dtable.empty, #dtable.loading, #qedit.extra

Detail / Form Engine (@mapomodule/form + @mapomodule/uikit)

FeatureNotes
FieldDescriptor<T> typed discriminated unionKnownFieldType enum + escape hatch for custom string types (CustomDescriptor)
FieldRegistry (mapping / attrs / accessor)Configurable via nuxt.config.mapoForm; global $mapoFormRegistry provided by the module plugin
defineFormField(type, entry, opts) plugin APITyped helper for registering or overriding field components — collision warning unless { override: true }
<MapoForm> / <MapoDetail> auto-inject the global registryThe :registry prop is optional — pass it only to scope a swap
useMapoForm() headless composableDirty tracking (incremental), diff/patch, touched/submitted gate, per-field debounce, i18n (translatable + synci18n + auto initLang), readonly toggle
<MapoForm> / <MapoFormField> / <MapoFormGroup> / <MapoFormTabs>Tab navigation with per-tab error badge; collapsible groups; <MapoUnknownField> fail-soft fallback
Full slot systemfield.<key>, field.<key>.label, .append, .prepend, .hint, .before, .after
MapoFocusPortal + useFocusMode()Focus / expand mode for repeater items, editors, maps
useFormDraft() / useUnsavedChangesGuard()Local-storage draft autosave + beforeunload + onBeforeRouteLeave guard
useProgressiveDisclosure()Show/hide fields based on visible() / show() predicates with smooth transitions
useFormFromSchema(schema, options)JSON Schema → FieldDescriptor[] (Pydantic / DRF), if/then/elsevisible(), exclude, overrides, cycle-safe $ref
Devtools panel/_mapo/devtools/forms inside Nuxt DevTools — registry inventory & live form inspector
<MapoDetail> shellTwo-column layout, useCrud lifecycle, differential PATCH, unsaved-changes guard, 400 → field errors, integrated snack/confirm
<MapoDetailLangSwitch>Language tabs with per-language error badge — syncs the ?lang= query param

Form fields library (@mapomodule/form)

FieldStatusNotes
text / textarea / number / boolean / switch / color / sliderNUI direct mapping via thin wrappers (NuiInput, NuiTextarea, NuiCheckbox, NuiSwitch, NuiSlider)
fileMapoFileField with current-file preview, image thumbnail, and remove button
selectUSelectMenu with value-key / label-key via registry
MapoDateField / MapoTimeField / MapoDateTimeFieldISO ↔ @internationalized/date; datetime accepts tz: 'naive' | 'utc'
MapoFksField (fks / m2m)USelectMenu + debounced remote fetch, removable M2M chips
MapoRepeaterDrag-and-drop (vue-draggable-plus), stable item UIDs, undo stack, templates, contextual mini-card collapse, focus mode
MapoSeoPreview60 / 155 char counters, live SERP preview
MapoWygEditorTiptap v2, custom toolbar, safe Link validator + HTML sanitizer, extensions via attrs.extensions
MapoMapFieldLeaflet SSR-safe via ClientOnly + dynamic import (MapoMapFieldClient)
MapoMediaField / MapoEnhancedMediaField🔲Depends on Phase 6 (Media Manager)

Not yet implemented 🔲

These features existed in Mapo v1 and are planned for v2 but have not been built yet.

Media Manager

File upload, gallery browsing, and media picker dialog.

v1 had: Gallery with pagination and single/multi selection, folder navigation, drag-and-drop uploader, metadata editor with i18n, bulk delete, MediaManagerDialog picker embeddable in form fields.

v2 target: Full rewrite as a Nuxt module (@mapomodule/media). See roadmap Phase 6.

Drag-and-drop reordering of navigation items for end users.

v1 had: MapoMenuManager with tree-drag via vue-draggable, nested items up to 2 levels, live preview of menu structure.

v2 target: Phase 7, using vue-draggable-plus.

i18n

Multi-language content switching and UI translation.

v1 had: @nuxtjs/i18n integration, per-field language switching in Detail, DetailLangSwitch, translation sync helpers, base locale files overridable by the consuming app.

v2 target: @mapomodule/i18n module using @nuxtjs/i18n v9. See roadmap Phase 7.

Standalone UIKit components

ComponentStatusDescription
MapoLogoutButtonReusable logout button wrapping useMapoAuth().logout()
MapoSidebarProfileUser profile row (avatar + username + logout) for the sidebar footer
MapoLangSwitcher 🔲🔲UI language selector (depends on @mapomodule/i18n — Phase 7)
MapoDropArea 🔲🔲Generic drag-and-drop zone — shared by Form fields and Media Manager
MapoPagePreview 🔲🔲Page/template preview panel

useMediaStore (Phase 6)

Pinia store for Media Manager state: gallery items, folders, pagination, selection, edit mode.


Removed from v2 (intentional)

v1 featureReason removed
@mapomodule/routemeta packagedefinePageMeta() in Nuxt 4 natively supports arbitrary meta fields. No parser needed.
@mapomodule/integrations adapter loaderIn Nuxt 4, each integration is a standalone Nuxt module. A central loader is unnecessary abstraction.
$mapo global facadeReplaced by individual composables (useMapoAuth, useCrud, useSnackStore, etc.) — tree-shakable and TypeScript-native.
AxiosReplaced by Nuxt's native $fetch / ofetch.
VuexReplaced by Pinia.
setToken / client-stored JWTSessions are HttpOnly cookies. No token ever reaches client JS.

Released under the MIT License.