Skip to content

Commit

Permalink
feat: add support for themes in AdminJS (#6)
Browse files Browse the repository at this point in the history
* chore: draft dark theme

* chore: introduced example app

* chore: draft wide theme

* chore: introduced esm to example app

* chore: introduced esm

* chore: introduced esm

* chore(deps): update deps

* chore(deps): update deps

* chore: add bundled themes to example app

* chore: added root to bundle options

* chore: added cli commands, refactored themes structures

* ci: with node 18

* chore: cleaned up package json

* chore: configured semantic release

* chore: configured cli in scripts

* chore: use babel to build themes

* chore: use babel to build themes

* chore: wip

* chore: removed css from lint

* chore: add theme id to bundler

* chore: improved cli documentations

* chore: fixed types import in generated themes

* chore: cleaned up themes

* chore: bump core and design version

* chore: add CI configuration

* chore: added current theme label in example app

* chore: update README

* chore: added light theme draft

* chore: export light theme

* chore: renamed wide theme export

* chore: bump adminjs packages

* chore: add @adminjs/themes in example app

* fix: fixed bundle themes script

* chore: removed styled-components from babel

* fix: add ignore themes dir when build

* chore: improved top bar nested navigation

* chore: improved top bar nested navigation

* chore: restored no strict config

* style: improved generated files format

* fix(beta): cleaned up dependencies

* fix(beta): fix typescript types building

* fix(beta): fixed dark style for phone input

* chore: update adminjs to version 7

---------

Co-authored-by: Rafal Dziegielewski <[email protected]>
  • Loading branch information
ariansobczak-rst and dziraf authored Apr 18, 2023
1 parent 3d548e9 commit e664aa6
Show file tree
Hide file tree
Showing 88 changed files with 11,003 additions and 7,453 deletions.
18 changes: 18 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"plugins": [],
"presets": [
"@babel/preset-react",
[
"@babel/preset-env",
{
"targets": {
"node": "18"
},
"loose": true,
"modules": false
}
],
"@babel/preset-typescript"
],
"ignore": ["./src/themes/**/*.bundle.js"]
}
23 changes: 23 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": ["plugin:@typescript-eslint/recommended"],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"react/prop-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off"
},
"ignorePatterns": ["node_modules", "dist", "*.css", "example"],
"settings": {
"react": {
"version": "detect"
}
}
}
23 changes: 0 additions & 23 deletions .eslintrc.js

This file was deleted.

33 changes: 5 additions & 28 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

jobs:
test:
name: test
test-and-publish:
name: Test and Publish
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup
uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '18.x'
- uses: actions/cache@v1
id: yarn-cache
with:
Expand All @@ -31,31 +31,8 @@ jobs:
run: yarn lint
- name: Build
run: yarn build
- name: Test
run: yarn test
publish:
name: Publish
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup
uses: actions/setup-node@v2
with:
node-version: '14'
- uses: actions/cache@v1
id: yarn-cache
with:
path: node_modules
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node_modules-
- name: Install
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
- name: Build
run: yarn build
- name: Types
run: yarn types
- name: Release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand Down
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn commitlint --edit $1
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged
4 changes: 4 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint
6 changes: 6 additions & 0 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"**/*.{js,jsx,ts,tsx}": [
"yarn format",
"yarn lint:fix"
]
}
11 changes: 8 additions & 3 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ node_modules
.vscode
test
.eslintrc.js
.eslintrc
.gitignore
.prettierrc.json
.releaserc
commitlint.config.js
commitlint.config.cjs
jest.config.js
tsconfig.build.json
tsconfig.json
src
yarn-error.log
yarn.lock

.husky
example
.babelrc
.lintstagedrc
.prettierrc
.prettierignore
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/theme.bundle.js
File renamed without changes.
7 changes: 2 additions & 5 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"branches": [
"+([0-9])?(.{+([0-9]),x}).x",
"master",
"next",
"next-major",
"main",
{
"name": "beta",
"prerelease": true
Expand All @@ -19,7 +16,7 @@
"semantic-release-slack-bot",
{
"notifyOnSuccess": true,
"notifyOnFail": true
"notifyOnFail": false
}
]
]
Expand Down
69 changes: 63 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,71 @@
# Feature for AdminJS
# Themes for AdminJS

This is a feature template.
Themes let you change the style and default components of your admin panel. There are three elements added to the app by each theme:

## AdminJS
`component overrides` - React components that replace a subset of overridable components

AdminJS is an automatic admin interface which can be plugged into your application. You, as a developer, provide database models (like posts, comments, stores, products or whatever else your application uses), and AdminJS generates UI which allows you (or other trusted users) to manage content.
`theme overrides for @adminjs/design-system` - this is merged into the default config, and then user's branding is applied on top

Check out the example application here: https://adminjs-demo.herokuapp.com/admin/
`custom CSS styles` - a single global CSS file with additional theme styles

Or visit [AdminJS](https://github.com/SoftwareBrothers/adminjs) github page.
## Importing Themes in AdminJS

### Setup

You can add availableThemes configuration option with an array of theme objects (for example imported from @adminjs/themes package) or themes bundled locally with `@adminjs/themes` CLI adminjs-themes bundle. The first theme on the list will be active by default. This can be changed with defaultTheme option - specify the ID of a theme - these are usually the same as the named exports.

```ts
import AdminJS from 'adminjs'
import { light, dark } from '@adminjs/themes'

new AdminJS({
defaultTheme: 'dark', // same as `dark` from id in the theme index file,
availableThemes: [light, dark],
})
```

Bundle and style paths are taken from @adminjs/themes if the theme is officially supported by library or can be imported from a local path like:

```ts
import path from 'path';
import * as url from 'url';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

new AdminJS({
defaultTheme: 'my-custom-theme',
availableThemes: {
id: 'my-custom-theme',
name: 'my custom theme',
overrides: {
colors: {
primary100: '#f00',
},
},
bundlePath: `${path.join(__dirname, `../themes/my-custom-theme`)}/theme.bundle.js`,
stylePath: `${path.join(__dirname, `../themes/my-custom-theme`)}/style.css`,
},
});
```

Full example available in @adminjs/themes example app.

### Per-user themes

Additionally, the theme can be configured per-user by returning theme ID in the theme property of the CurrentAdmin object - this should be implemented via the framework plugin, i.e. authenticate function in @adminjs/express. Since you have full control of that object, returned the theme can be selected based on things like user's role or property in the database.

There's no UI control for changing themes, but you can implement this yourself. The general idea is to keep theme ID in the user resource, and add a button that will update the current user record with the selected theme. Alternatively, you can let the user edit their own record and offer a custom select property. Note that for the currentAdmin object to update the user most likely needs to sign in again.

### CLI

You can create a new theme using `@adminjs/themes` cli. After checkout the repository you can register cli on your machine with `yarn register:cli`. After that, you can use `adminjs-themes` in your command line.

AdminJS Themes CLI provides commands for:

- `adminjs-themes generate <theme name>` - Generating a new theme with the recommended file structure.
- `adminjs-themes bundle` - Bundling all themes component overrides. You can pass theme ID as an argument to bundle ssingle theme.

For more information please use the command `adminjs-themes bundle --help`

## Usage

Expand Down
File renamed without changes.
21 changes: 21 additions & 0 deletions example/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"env": {
"es6": true,
"jest": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"plugins": ["@typescript-eslint", "react", "prettier"],
"settings": {
"react": {
"version": "detect"
}
}
}
17 changes: 17 additions & 0 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: '3.7'

services:
adminjs_themes:
container_name: adminjs-themes
image: postgres:15
environment:
- POSTGRES_DB=adminjs
- POSTGRES_USER=adminjs
- POSTGRES_PASSWORD=adminjs
ports:
- '5432:5432'
volumes:
- adminjs_themes_db:/var/lib/postgresql/data

volumes:
adminjs_themes_db:
7 changes: 7 additions & 0 deletions example/nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verbose": false,
"ignore": ["*.test.*", "*.spec.*"],
"exec": "node --loader ts-node/esm src/server.ts",
"watch": ["src"],
"ext": "ts"
}
45 changes: 45 additions & 0 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "adminjs-themes-example",
"version": "1.0.0",
"type": "module",
"license": "MIT",
"scripts": {
"start": "ts-node-esm src/server",
"dev": "nodemon",
"build": "tsc",
"lint": "eslint src --ext ts",
"docker:up": "docker-compose -f docker-compose.yml up -d"
},
"dependencies": {
"@adminjs/design-system": "^4.0.0-beta-v4.7",
"@adminjs/express": "^6.0.0-beta.3",
"@adminjs/passwords": "^4.0.0-beta.1",
"@adminjs/themes": "^1.0.0-beta.1",
"@adminjs/typeorm": "^5.0.0-beta.1",
"adminjs": "^7.0.0-beta-v7.4",
"argon2": "^0.30.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"connect-pg-simple": "^8.0.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-formidable": "^1.2.0",
"express-session": "^1.17.3",
"nodemon": "^2.0.22",
"pg": "^8.10.0",
"reflect-metadata": "^0.1.13",
"tslib": "^2.5.0",
"typeorm": "^0.3.12"
},
"devDependencies": {
"@types/connect-pg-simple": "^7.0.0",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/express-session": "^1.17.7",
"@types/node": "^18.15.11",
"dotenv-cli": "^7.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.2"
}
}
35 changes: 35 additions & 0 deletions example/src/admin/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import argon from 'argon2';
import PostgresSession from 'connect-pg-simple';
import session from 'express-session';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';

import databaseConfig from '../db/config.js';
import { UserService } from '../modules/user/index.js';

const PostgresStore = PostgresSession(session);

export const sessionStore = new PostgresStore({
conObject: {
connectionString: (databaseConfig as PostgresConnectionOptions).url,
ssl: (databaseConfig as PostgresConnectionOptions).extra?.ssl,
},
tableName: 'sessions',
createTableIfMissing: true,
});

export const authenticate = async (email: string, password: string) => {
const userService = new UserService();
const user = await userService.findUserByEmail(email);

if (user && (await argon.verify(user.password, password))) {
return {
id: user.id,
email: user.email,
role: user.role,
createdAt: user.createdAt,
theme: user.theme,
};
}

return null;
};
Loading

0 comments on commit e664aa6

Please sign in to comment.