npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

crud-ui

v0.3.0

Published

A "CMS-light" express.js router that serves UI for a single CRUD resource.

Downloads

8

Readme

CRUD UI

A "CMS-light" express.js router that serves UI for a single CRUD resource.

npm install --save crud-ui

Simple example

const http = require('http');
const express = require('express');

const crudUI = require('crud-ui');

const app = express();

app.use(
  '/',
  crudUI({
    name: 'user',
    recordId: 'id',
    fields: [
      {
        name: 'name',
        label: 'Name',
      },
      {
        name: 'phone',
        label: 'Phone number',
      },
    ],
    actions: {
      getList: () => {
        return [
          { name: 'Mark', phone: '+123 456 789' },
          { name: 'Milo', phone: '' },
          { name: 'Sandra', phone: '+1 33 55 66' },
        ];
      },
    },
  })
);

http.createServer(app).listen(3000);

Minimal example

More details

The UI is rendered server-side using a vanilla bootstrap layout (feel free to slap your own theme on top of it). Validation is performed by validate.js, but you can plug in your own library. Every part of the UI in general is overridable through options.

By default, each crudUI router serves a single REST-like resource - a table view, with links to each item's detail page, edit page and delete popup (there are other view modes too, eg. the single record mode). You can use multiple crudUI handlers to work with multiple resources. We provide an option of adding a top level menu, so you can navigate between them (and/or other parts of your app).

As the name says, this library is for the UI layer only. Since you are providing functions which resolve various operations, you can plug whatever database or other backend you want. We also don't do authentication. It is recommended to plug in your own auth middleware before the crudUI handler is reached.

To summarize:

This library is for you if:
  • You already have a node.js app and just want to plug in a little configuration screen so that sales people can tweak a few options or something.

  • You don't want CMS to dictate your backend. You are using a 3rd party store, already have a database or just want to structure your backend and API how you like it.

  • You have straightforward UI requirements - basic server-side rendered CRUD with flat objects.

  • You have relatively little data to manage (we don't support pagination yet).

  • You like self-contained low-impact libraries. We have very few direct dependencies, on top of 2 required peer dependencies (express.js and body-parser).

This library is NOT for you if:
  • You want a fully managed CMS solution with minimal coding.

  • You want to serve an interface towards customers. Our UI is functional, but simple and generic looking. While you can overwrite every view with your own code, if you start doing that all the time, maybe it's time to put up a real UI :-)

  • You have a complicated object model, requiring advanced entity relations or crazy tree views

  • You want to make a blog. We don't have a WYSIWYG editor yet.

Advanced example

const http = require('http');
const express = require('express');

const port = process.env.PORT || 3000;

const { crudUI, CUIField, FIELD_TYPES, CUI_MODES } = require('crud-ui');

const app = express();

const data = [
  { id: 1, name: 'Axe' },
  { id: 2, name: 'Barry', description: 'This\nIs\nBarry!' },
  { id: 3, name: 'Cindy', gender: 'female' },
];

app.use(
  '/admin/users',
  crudUI({
    name: 'user',
    mode: CUI_MODES.simple_list,
    recordId: 'id',
    navigation: {
      brand: {
        title: 'Tester',
        url: '/admin/users',
      },
      left: [
        {
          title: 'Users',
          url: '/admin/users',
        },
        {
          title: 'Projects',
          url: '/admin/projects',
        },
      ],
      right: [
        {
          title: 'User',
          items: [
            {
              title: 'Home',
              url: '/admin/users',
            },
            {
              title: '---',
            },
            {
              render: (/** CUIContext */ ctx) => {
                return `<button class="dropdown-item" onclick="alert('logout')">Log out</button>`;
              },
            },
          ],
        },
      ],
    },
    fields: [
      new CUIField({
        type: FIELD_TYPES.string,
        name: 'id',
        label: 'ID',
        noEdit: true,
      }),
      new CUIField({
        type: FIELD_TYPES.string,
        name: 'name',
        label: 'Name',
        helpText: "Person's full name and surname",
        validate: {
          presence: {
            allowEmpty: false,
          },
        },
        validateEdit: {
          length: { minimum: 20 },
        },
      }),
      new CUIField({
        type: FIELD_TYPES.text,
        name: 'description',
        label: 'Description',
        validateCreate: (ctx, val) => {
          if (val.indexOf('cheese') < 0) {
            return 'must contain word cheese';
          }
        },
      }),
      new CUIField({
        type: FIELD_TYPES.select,
        name: 'gender',
        label: 'Gender',
        values: ['male', 'female', 'other'],
        nullOption: true
      }),
    ],
    actions: {
      getList: ctx => {
        return data;
      },
      getSingle: (ctx, id) => {
        return data.find(item => String(item.id) === String(id));
      },
      create: (ctx, payload) => {
        const id = data.reduce((max, item) => Math.max(item.id, max), 0) + 1;
        const item = { ...payload, id };
        data.push(item);
        return item;
      },
      update: (ctx, id, payload) => {
        const existing = data.find(item => String(item.id) === String(id));
        if (!existing) {
          throw new Error(`Not found: ${id}`);
        }
        Object.assign(existing, payload);
        return existing;
      },
      delete: (ctx, id) => {
        const index = data.findIndex(item => String(item.id) === String(id));
        if (index < 0) {
          throw new Error(`Not found: ${id}`);
        }
        const item = data.splice(index, 1)[0];
        return item;
      },
    },
  })
);

app.get('/', (req, res) => {
  return res.set('content-type', 'text/html').send(`
  <body>
    <h1>This is just a tester</h1>
    <h4>
      <a href="/admin/users">Go to admin</a>
    </h4>
  </body>
  `);
});

const server = http.createServer(app);
server.listen(port, () => {
  console.log(`Listening on http://localhost:${port}`);
});

Advanced example 1

Advanced example 2

Advanced example 3

For an even bigger example, look into the kitchen_sink tester app.

Development

Follow the development and TODO-s here: https://trello.com/b/3vSgeUxa/crudui

Release log can be found here.

NOTE: I use this project in production for one micro CMS, and it's working fine. Still, given its lack of unit tests and relative immaturity, use at your own risk.

License

MIT