Change the admin menu
List your app's admin links in CUSTOM_NAV, in src/custom/nav.ts: they appear in the admin side menu just before Profile, translated like the core items. To change, hide, add or move items anywhere in the menu, Arc CMS's own included, use CUSTOM_NAV_CHANGES in src/custom/nav-changes.ts (Change Arc CMS's items).
The menu only shows links. Who may open a page is decided by its route guard alone (Who may open a page). Hiding an item does not keep anyone out of its page, and showing one to a role does not let that role in.
Add an item
-
Add the item to
src/custom/nav.ts:import type { MenuItem } from '../shared/components/side-navbar/side-navbar.component'; export const CUSTOM_NAV: MenuItem[] = [ { label: 'Lessons', labelKey: 'custom.nav.lessons', route: '/admin/lessons', icon: 'fa-solid fa-book-open', allowRoles: ['admin'], }, ]; -
Add the label to
src/custom/i18n/en.json, and the same key tosrc/custom/i18n/hi.json:{ "custom": { "nav": { "lessons": "Lessons" } } } -
Make sure the route exists. An admin page needs an explicit route with the admin shell (Add an admin page).
npm run testfails on an item whose address no route serves, yours or Arc CMS's.
The fields of an item
| Field | For |
|---|---|
id | The item's identity, unique in the menu: what CUSTOM_NAV_CHANGES names. Optional for your own items; give one to an item you want to change for some roles |
label | The English label, shown when there is no labelKey |
labelKey | The translation key shown instead of label |
route | The address the item opens |
externalUrl | An outside address, opened in a new tab, instead of route |
icon | Font Awesome classes, such as fa-solid fa-book-open |
allowRoles | The roles that see the item, such as ['admin'] or ['admin', 'user'] |
subItems | Items shown under this one, which makes it a group that opens and closes |
queryParams | Query parameters added to the item's link |
separator | true for a dividing line instead of a link (give it label: '' and allowRoles) |
feature | A feature id: the item is left out when that feature is off |
An item without allowRoles is never shown, for anyone. Set it on every top-level item. Sub-items are shown to whoever sees their group.
The menu finds Profile and Logout by their id, and clicking the item with the id logout opens the sign-out dialog. Never give your own item a core item's id: the test suite fails on it. A group's open or closed state is kept by its id, or by its label when it has none, so give every group of yours an id or a unique label.
Group items
export const CUSTOM_NAV: MenuItem[] = [
{
label: 'Classroom',
labelKey: 'custom.nav.classroom',
icon: 'fa-solid fa-school',
allowRoles: ['admin'],
subItems: [
{ label: 'Lessons', labelKey: 'custom.nav.lessons', route: '/admin/lessons', icon: 'fa-solid fa-book-open' },
{ label: 'Classes', labelKey: 'custom.nav.classes', route: '/admin/classes', icon: 'fa-solid fa-users' },
],
},
];
A group opens by itself when the current page is one of its sub-items.
Hide an item when a feature is off
Set feature on an item or a sub-item that only makes sense with that feature (Choose your app's features):
{ label: 'Refunds', labelKey: 'custom.nav.refunds', route: '/admin/refunds', icon: 'fa-solid fa-rotate-left',
allowRoles: ['admin'], feature: 'payments' },
A group whose sub-items all go goes with them.
Where the items appear
Your items are placed together, in the order you list them, just before Profile, with a dividing line before Profile. To put one somewhere else, give it an id and move it with CUSTOM_NAV_CHANGES, or add it there with CUSTOM_NAV_CHANGES instead. The menu shows only the items whose allowRoles include the signed-in person's role.
Change Arc CMS's items
Fill in CUSTOM_NAV_CHANGES in src/custom/nav-changes.ts. Its admin list changes the admin menu; its member list changes the member menu the same way (Change the member menu). Each change names an item by its id (The admin menu's ids), never by its label, which is translated:
import type { NavChanges } from '../app/core/nav/nav-changes';
export const CUSTOM_NAV_CHANGES: NavChanges = {
admin: [
// Dashboard opens the app's own page
{ id: 'dashboard', route: '/admin/extension-dashboard' },
// Media Manager is gone from the menu
{ id: 'media', hide: true },
// Users is reworded with the app's own key
{ id: 'users', labelKey: 'custom.nav.people' },
// A new item just after Dashboard, for admins and editors
{ add: { id: 'reports', label: 'Reports', labelKey: 'custom.nav.reports', route: '/admin/reports',
icon: 'fa-solid fa-chart-line', allowRoles: ['admin', 'editor'] }, after: 'dashboard' },
// Settings moves to the top, for admins only
{ id: 'settings', before: 'dashboard', roles: ['admin'] },
],
};
Older copies of Arc CMS had CUSTOM_NAV_CHANGES in src/custom/nav.ts, and that file still ships it, empty. If your app wrote its changes there, they still work: Arc CMS applies the ones in nav.ts first, then the ones in nav-changes.ts. Move them to nav-changes.ts when you next touch the menu, and leave nav.ts's CUSTOM_NAV_CHANGES empty.
A change can:
| To | Write |
|---|---|
| Change an item | { id, ...fields } with any of route, queryParams, externalUrl, icon, label, labelKey and allowRoles |
| Hide an item | { id, hide: true } |
| Add an item | { add: item, before: id }, { add: item, after: id }, or { add: item } for the end of the menu. The item takes the fields above and needs an id of its own |
| Move an item | { id, before: id } or { id, after: id } |
| Do any of these for some roles only | Add roles: ['editor'] to the change (below) |
How the changes are made:
- They are applied in the order you list them, after Arc CMS builds the menu, adds your
CUSTOM_NAVitems and leaves out the items of features that are off. So an item of a feature that is off stays gone, whatever a change says, and an added item with afeaturethat is off is left out too. - A change can name an item at the top level or inside a group (such as
contactsin Audience).beforeandafterput the item in the same list as the item they name, so you can move a link into a group or out of one. A group never goes inside another group. - Setting
routedrops the item'sexternalUrl, and settingexternalUrldrops itsroute: an item opens one or the other. SettinglabelwithoutlabelKeyshows your label as written, untranslated; to reword an item in every admin language, setlabelKeyto a key of yours insrc/custom/i18n/, or reword the core key there (Styles and translations). - A change that names an id the menu does not have is ignored when the app runs (the test suite reports it first, see below). An add whose
beforeorafteritem is not in the menu goes at the end; a move whose item is not there leaves the item where it was. - Profile and Logout cannot be hidden: people must always be able to reach their profile and sign out. A change to their
allowRolescan only add roles, and Logout always opens the sign-out dialog, so itsroute,queryParamsandexternalUrlcannot be changed. You can still reword and move them. - A dividing line is kept before Profile, wherever it is, and lines never lead, trail or come two in a row, so hiding items leaves no gaps.
Change the menu for some roles
A change with roles applies only when the signed-in person's role is one of them. Without roles it applies to every role. This is how one app shows admins, editors and its own roles different menus:
admin: [
// Editors see Media Manager, without Settings
{ id: 'media', allowRoles: ['admin', 'editor'], roles: ['editor'] },
{ id: 'settings', hide: true, roles: ['editor'] },
// Profile and Logout for editors too
{ id: 'profile', allowRoles: ['editor'] },
{ id: 'logout', allowRoles: ['editor'] },
],
The menu shows an item to the roles in its allowRoles; roles on a change decides who the change is for. Each page an editor can now see in the menu still needs a route guard that lets editors in: the menu never gives anyone access.
What the test suite checks
npm run test (in src/app/custom-space.spec.ts) reads your src/custom/nav.ts and src/custom/nav-changes.ts and fails, with the list of valid ids, on:
- a change,
beforeorafterthat names an id the menu does not have (core's, yourCUSTOM_NAVitems', or one an add in the same list brings); - a
routethat is not a path on this site starting with/, or anexternalUrlthat is not anhttps://address; - an add without an
idorlabel, or with an id the menu already has; bothbeforeandafteron one change; an emptyroles; - hiding Profile or Logout, or changing where Logout leads;
- a
labelKeythat no English file has, and an admin label key missing fromsrc/custom/i18n/hi.json.
The admin menu's ids
Arc CMS's items and their ids, in menu order. Each signup form's own group and each content type's link are built from your data and have no id.
| Id | Item | Opens | Sub-items |
|---|---|---|---|
dashboard | Dashboard | /admin/dashboard | |
signup-forms | Signup Forms | /admin/waitlists | |
media | Media Manager | /admin/media | |
content | Content | content-types (Content types), authors (Authors) | |
users | Users | /admin/users | |
audience | Audience | contacts (Contacts), lists (Lists), contact-tags (Tags), contact-fields (Fields), app-users (App users) | |
email | Email + SMS | brand-kit (Brand Kit), composer (Composer), broadcasts (Broadcasts), drip-campaigns (Drip Campaigns), announcements (Announcements), email-logs (Email Logs), sms-logs (SMS Logs) | |
feedback | Feedback | /admin/feedback | |
messages | Messages | /admin/messages | |
products | Products | /admin/products | |
transactions | Transactions | /admin/transactions | |
data | Data | export-data (Export Data), import-data (Import Data), export-files (Export Files), import-files (Import Files) | |
profile | Profile | /admin/profile | |
settings | Settings | /admin/settings | |
about | About | https://arccms.com/about | |
logout | Logout | The sign-out dialog |
The ids are in src/app/core/nav/admin-menu.ts; the changes are made by applyNavChanges() in src/app/core/nav/nav-changes.ts.
Choose an icon
The admin loads Font Awesome Free 6.5.1 (see index.html), so any free icon works. Write the style class and the icon class: fa-solid fa-book-open, fa-regular fa-calendar, fa-brands fa-youtube.
To see which icons and styles exist, search public/assets/icons/fa-index.json: each entry has the icon name (n), its label, its free styles (s) and search terms. npm run icons:index builds that file for the admin's icon picker; it is a core file, so you never need to run it in an app.
A Pro-only icon or style renders as an empty box. Check the styles in the index before you pick one.