3
\$\begingroup\$

I am developing a microservice that enables business partners to order systems for common customers:

/*
  ordering.js - Terminal ordering JavaScript library.

  (C) 2017 HOMEINFO - Digitale Informationssysteme GmbH

  This library is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library.  If not, see <http://www.gnu.org/licenses/>.

  Maintainer: Richard Neumann <r dot neumann at homeinfo period de>

  Requires:
    * jquery.js
    * sweetalert.js
    * homeinfo.js
    * his.js
*/
'use strict';

var ordering = ordering || {};


/*
    Static globals.
*/
ordering._BASE_URL = 'https://top.secret.com/endpoint';
ordering._ORDERS_ENDPOINT = ordering._BASE_URL + '/order';
ordering._CUSTOMERS_ENDPOINT = ordering._BASE_URL + '/customers';
ordering._CLASSES_ENDPOINT = ordering._BASE_URL + '/classes';
ordering._OSS_ENDPOINT = ordering._BASE_URL + '/oss';


/*
    Runtime globals.
*/
ordering.orders = ordering.orders || [];
ordering.customers = ordering.customers || [];
ordering.classes = ordering.classes || [];
ordering.oss = ordering.oss || [];


/*
    Displays an error using sweetalert.
*/
ordering.error = function (title, text, type) {
    return function () {
        swal({
            title: title,
            text: text,
            type: type || 'error'
        });
    };
};


/*
    Queries orders from the API.
*/
ordering._getOrders = function () {
    return his.auth.get(ordering._ORDERS_ENDPOINT).then(
        function (orders) {
            ordering.orders = orders;
        },
        ordering.error('Fehler.', 'Konnte verfügbare Bestellungen nicht abfragen.')
    );
};


/*
    Queries customers from the API.
*/
ordering._getCustomers = function () {
    return his.auth.get(ordering._CUSTOMERS_ENDPOINT).then(
        function (customers) {
            ordering.customers = customers;
        },
        ordering.error('Fehler.', 'Konnte verfügbare Kunden nicht abfragen.')
    );
};


/*
    Queries classes from the API.
*/
ordering._getClasses = function () {
    return his.auth.get(ordering._CLASSES_ENDPOINT).then(
        function (classes) {
            ordering.classes = classes;
        },
        ordering.error('Fehler.', 'Konnte verfügbare Terminal-Klassen nicht abfragen.')
    );
};


/*
    Queries classes from the API.
*/
ordering._getOSs = function () {
    return his.auth.get(ordering._OSS_ENDPOINT).then(
        function (oss) {
            ordering.oss = oss;
        },
        ordering.error('Fehler.', 'Konnte verfügbare Betriebssysteme nicht abfragen.')
    );
};


/*
    Performs a HIS login.
*/
ordering._login = function () {
    var queryString = new homeinfo.QueryString();
    return his.session.login(queryString.userName, queryString.passwd);
};


/*
    Initializes the page.
*/
ordering.init = function () {
    ordering._login().then(
        ordering._getData().then(
            ordering._initPage
        )
    );
};


/*
    Submit form.
*/
ordering._submitForm = function (event) {
    event.preventDefault();     // Avoid to execute the actual submit of the form.
    his.auth.post(ordering._ORDERS_ENDPOINT, jQuery('#orders').serialize()).then(ordering._resetForm);
};


/*
    Resets the form.
*/
ordering._resetForm = function () {
    document.getElementById('orders').reset();
    ordering.init();
};


/*
    Initialization function.
*/
ordering._getData = function () {
    var getOrders = ordering._getOrders();
    var getCustomers = ordering._getCustomers();
    var getClasses = ordering._getClasses();
    var getOSs = ordering._getOSs();

    return Promise.all([getOrders, getCustomers, getClasses, getOSs]);
};


/*
    Refreshes the orders list.
*/
ordering._renderList = function () {
    var tableContainer = document.getElementById('tableContainer');
    tableContainer.innerHTML = '';
    tableContainer.appendChild(ordering._dom.list());
};


/*
    Renders the orders input.
*/
ordering._renderOrders = function () {
    var formContainer = document.getElementById('formContainer');
    formContainer.innerHTML = '';
    formContainer.appendChild(ordering._dom.orders());
};


/*
    Initializes the page.
*/
ordering._initPage = function () {
    ordering._renderList();
    ordering._renderOrders();
    jQuery('#orders').submit(ordering._submitForm);
};


/*
    Deletes the respective order.
*/
ordering._deleteOrder = function (id) {
    return his.auth.delete(ordering._ORDERS_ENDPOINT + '/' + id).then(ordering._getData).then(ordering._renderList);
};


/*
    Adds an order.
*/
ordering._addOrderForm = function () {
    var orderForms = document.getElementById('orderForms');
    var order = ordering._dom.order(true);
    orderForms.appendChild(order);
};


/*
    Deletes the respective order form.
*/
ordering._deleteOrderForm = function (index) {
    var orderForms = document.getElementById('orderForms');
    var order = document.getElementById('order_' + index);
    orderForms.removeChild(order);
};


/*
    DOM model.
*/
ordering._dom = ordering._dom || {};
ordering._dom.COLUMNS = [
    'Konto', 'Bestelldatum', 'Bearbeitungsstatus', 'Kunde', 'Klasse', 'Betriebssystem', 'Adresse', 'Anmerkung',
    'Stornieren'];


/*
    ID of orders form.
*/
ordering._dom.ORDERS = 0;


/*
    Builds an order set.
*/
ordering._dom.orders = function () {
    var form = document.createElement('form');
    form.setAttribute('id', 'orders');

    var orderForms = document.createElement('div');
    orderForms.setAttribute('id', 'orderForms');
    orderForms.setAttribute('class', 'row row-center');

    var firstOrder = ordering._dom.order();
    orderForms.appendChild(firstOrder);

    form.appendChild(orderForms);
    form.appendChild(ordering._dom.buttonAddOrderForm());
    form.appendChild(document.createTextNode(' '));
    form.appendChild(ordering._dom.submit());
    return form;
};


/*
    Creates a button to add an order form input for a further terminal.
*/
ordering._dom.buttonAddOrderForm = function () {
    var button = document.createElement('button');
    button.setAttribute('class', 'btn btn-success');
    button.setAttribute('onclick', 'ordering._addOrderForm();');
    button.textContent = 'Weiteres Terminal hinzufügen.';
    return button;
};


/*
    Creates a button to delete the respective oder inputs.
*/
ordering._dom.buttonDeleteOrderForm = function (index) {
    var buttonDeleteOrderForm = document.createElement('button');
    buttonDeleteOrderForm.setAttribute('onclick', 'ordering._deleteOrderForm(' + index + ');');
    buttonDeleteOrderForm.setAttribute('class', 'btn btn-danger');
    buttonDeleteOrderForm.textContent = 'X';
    return buttonDeleteOrderForm;
};


/*
    Creates a button to delete the respective order.
*/
ordering._dom.buttonDeleteOrder = function (id) {
    var button = document.createElement('button');
    button.setAttribute('class', 'btn btn-danger');
    button.setAttribute('onclick', 'ordering._deleteOrder(' + id + ');');
    button.textContent = 'X';
    return button;
};


/*
    Creates the title for the terminal order.
*/
ordering._dom.caption = function (index, additional) {
    var caption = document.createElement('legend');

    var textNode = document.createTextNode('Terminal ');
    caption.appendChild(textNode);

    if (additional) {
        caption.appendChild(ordering._dom.buttonDeleteOrderForm(index));
    }

    return caption;
};


/*
    Builds a single order.
*/
ordering._dom.order = function (additional) {
    if (additional == null) {
        additional = false;
    }

    var index = ordering._dom.ORDERS;
    ordering._dom.ORDERS += 1;

    var order = document.createElement('fieldset');
    order.setAttribute('id', 'order_' + index);
    order.setAttribute('class', 'row');

    var caption = ordering._dom.caption(index, additional);
    order.appendChild(caption);

    var columnSelects = document.createElement('div');
    columnSelects.setAttribute('class', 'col-md-6');

    var customerSelect = ordering._dom.customerSelect(index);
    columnSelects.appendChild(customerSelect);

    var classSelect = ordering._dom.classSelect(index);
    columnSelects.appendChild(classSelect);

    var osSelect = ordering._dom.osSelect(index);
    columnSelects.appendChild(osSelect);

    var columnAddress = document.createElement('div');
    columnAddress.setAttribute('class', 'col-md-6');

    var address = ordering._dom.address(index);
    columnAddress.appendChild(address);

    order.append(columnSelects);
    order.append(columnAddress);
    return order;
};


/*
    Builds a default option for a dropdown list.
*/
ordering._dom.defaultOption = function (text) {
    var defaultOption = document.createElement('option');

    var attributeSelected = document.createAttribute('selected');
    defaultOption.setAttributeNode(attributeSelected);

    var attributeDisabled = document.createAttribute('disabled');
    defaultOption.setAttributeNode(attributeDisabled);

    defaultOption.textContent = text;
    return defaultOption;
};


/*
    Builds the customer selection dropdown menu.
*/
ordering._dom.customerSelect = function (index) {
    var select = document.createElement('select');
    select.setAttribute('required', true);
    select.setAttribute('name', 'customer_' + index);
    select.setAttribute('class', 'form-control');

    var defaultOption = ordering._dom.defaultOption('Kunde*');
    select.appendChild(defaultOption);

    for (var i = 0; i < ordering.customers.length; i++) {
        var customer = ordering.customers[i];
        var option = document.createElement('option');
        option.setAttribute('value', customer.id);
        option.textContent = customer.company.name;
        select.appendChild(option);
    }

    return select;
};


/*
    Builds the class selection dropdown menu.
*/
ordering._dom.classSelect = function (index) {
    var select = document.createElement('select');
    select.setAttribute('required', true);
    select.setAttribute('name', 'class_' + index);
    select.setAttribute('class', 'form-control');

    var defaultOption = ordering._dom.defaultOption('Klasse*');
    select.appendChild(defaultOption);

    for (var i = 0; i < ordering.classes.length; i++) {
        var class_ = ordering.classes[i];
        var option = document.createElement('option');
        option.setAttribute('value', class_.id);
        option.textContent = class_.full_name;
        select.appendChild(option);
    }

    return select;
};


/*
    Builds the operating system selection dropdown menu.
*/
ordering._dom.osSelect = function (index) {
    var select = document.createElement('select');
    select.setAttribute('required', true);
    select.setAttribute('name', 'os_' + index);
    select.setAttribute('class', 'form-control');

    var defaultOption = ordering._dom.defaultOption('Betriebssystem*');
    select.appendChild(defaultOption);

    for (var i = 0; i < ordering.oss.length; i++) {
        var os = ordering.oss[i];
        var option = document.createElement('option');
        option.setAttribute('value', os.id);
        option.textContent = os.name + ' ' + os.version;
        select.appendChild(option);
    }

    return select;
};


/*
    Builds the address input fields.
*/
ordering._dom.address = function (index) {
    var address = document.createElement('fieldset');
    address.setAttribute('class', 'form-group');

    var street = document.createElement('input');
    street.setAttribute('type', 'text');
    street.setAttribute('required', true);
    street.setAttribute('name', 'street_' + index);
    street.setAttribute('placeholder', 'Straße*');
    address.appendChild(street);

    var houseNumber = document.createElement('input');
    houseNumber.setAttribute('type', 'text');
    houseNumber.setAttribute('required', true);
    houseNumber.setAttribute('name', 'houseNumber_' + index);
    houseNumber.setAttribute('placeholder', 'Hausnummer*');
    address.appendChild(houseNumber);

    var zipCode = document.createElement('input');
    zipCode.setAttribute('type', 'text');
    zipCode.setAttribute('required', true);
    zipCode.setAttribute('name', 'zipCode_' + index);
    zipCode.setAttribute('placeholder', 'PLZ*');
    address.appendChild(zipCode);

    var city = document.createElement('input');
    city.setAttribute('type', 'text');
    city.setAttribute('required', true);
    city.setAttribute('name', 'city_' + index);
    city.setAttribute('placeholder', 'Ort*');
    address.appendChild(city);

    var annotation = document.createElement('input');
    annotation.setAttribute('type', 'text');
    annotation.setAttribute('name', 'annotation_' + index);
    annotation.setAttribute('placeholder', 'Anmerkung');
    address.appendChild(annotation);

    return address;
};


/*
    Builds a submit button.
*/
ordering._dom.submit = function () {
    var submit = document.createElement('input');
    submit.setAttribute('type', 'submit');
    submit.setAttribute('value', 'Kostenpflichtig bestellen.');
    submit.setAttribute('class', 'btn btn-primary');
    return submit;
};


/*
    Builds the list of existing orders.
*/
ordering._dom.list = function () {
    var table = document.createElement('table');
    table.setAttribute('id', 'list');
    table.setAttribute('class', 'table  table-hover');

    var thead = document.createElement('thead');
    var tr = document.createElement('tr');
    var th;

    for (var i = 0; i < ordering._dom.COLUMNS.length; i++) {
        th = document.createElement('th');
        th.setAttribute('scope', 'col');
        th.textContent = ordering._dom.COLUMNS[i];
        tr.appendChild(th);
    }

    thead.appendChild(tr);

    var tbody = document.createElement('tbody');
    var empty = true;

    for (var attr in ordering.orders) {
        if (ordering.orders.hasOwnProperty(attr)) {
            empty = false;
            tbody.appendChild(ordering._dom.listItem(ordering.orders[attr]));
        }
    }

    if (empty) {
        var h2 = document.createElement('h2');
        h2.setAttribute('align', 'center');
        h2.textContent = 'Keine Bestellungen vorhanden.';
        return h2;
    }

    table.appendChild(thead);
    table.appendChild(tbody);
    return table;
};


/*
    Builds the list items for each existing order.
*/
ordering._dom.listItem = function (order) {
    var row = document.createElement('tr');

    var columnAccount = document.createElement('td');
    columnAccount.textContent = order.account.name;
    row.appendChild(columnAccount);

    var columnIssued = document.createElement('td');
    columnIssued.textContent = order.issued;
    row.appendChild(columnIssued);

    var columnAccepted = document.createElement('td');
    var notProcessedYet = 'Noch nicht bearbeitet.';

    switch (order.accepted) {
    case null:
        columnAccepted.textContent = notProcessedYet;
        break;
    case undefined:
        columnAccepted.textContent = notProcessedYet;
        break;
    case true:
        columnAccepted.textContent = 'Angelegt.';
        break;
    case false:
        columnAccepted.textContent = 'Auftrag abgewiesen.';
        break;
    }

    row.appendChild(columnAccepted);

    var columnCustomer = document.createElement('td');
    columnCustomer.textContent = order.customer.company.name + ' (' + order.customer.id + ')';
    row.appendChild(columnCustomer);

    var columnClass = document.createElement('td');
    columnClass.textContent = order.class_.name;
    row.appendChild(columnClass);

    var columnOs = document.createElement('td');
    columnOs.textContent = order.os.name + ' ' + order.os.version;
    row.appendChild(columnOs);

    var columnAddress = document.createElement('td');
    var address = order.address;
    columnAddress.textContent = address.street + ' ' + address.house_number + ', ' + address.zip_code + ' ' + address.city;
    row.appendChild(columnAddress);

    var columnAnnotation = document.createElement('td');
    columnAnnotation.textContent = order.annotation || '-';
    row.appendChild(columnAnnotation);

    var columnDelete = document.createElement('td');
    columnDelete.appendChild(ordering._dom.buttonDeleteOrder(order.id));
    row.appendChild(columnDelete);

    return row;
};


jQuery(document).ready(ordering.init);

The corresponsing HTML document is:

<!DOCTYPE HTML>
<head>
    <meta charset="utf-8"/>
    <title>Terminal Bestellung</title>
    <link rel="stylesheet" href="https://libraries.homeinfo.de/bootstrap/latest/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
    <link rel="stylesheet" href="https://libraries.homeinfo.de/sweetalert/dist/sweetalert.css">
    <script src="https://libraries.homeinfo.de/jquery/jquery-latest.min.js"></script>
    <script src="https://libraries.homeinfo.de/bootstrap/latest/js/bootstrap.min.js"></script>
    <script src="https://libraries.homeinfo.de/sweetalert/dist/sweetalert.min.js"></script>
    <script src="https://javascript.homeinfo.de/homeinfo.min.js"></script>
    <script src="https://javascript.homeinfo.de/his/his.js"></script>
    <script src="https://javascript.homeinfo.de/his/session.js"></script>
    <script src="ordering.js"></script>
</head>
<body>
  <h1 align="center">Ihre Bestellungen bei HOMEINFO</h1>
  <br>
  <div class="container">
    <div id="tableContainer" class="row"></div>
    <br>
    <div id="formContainer" class="row"></div>
  </div>
</body>

I'd like to have general feedback especially on the JavaScript code.

\$\endgroup\$
1
\$\begingroup\$

If you ask for specific questions, we can provide better feedback.

A few thoughts:

  • if you're including jQuery, go ahead and use it for DOM generation, as it will be much more concise and readable, instead of all the bulky createElement, setAttribute code. And the event handlers can be done through jQuery a little nicer.
  • if you're allowed, you have good names for the functions in the comments, and I'd use them. For example, listItem would be more clearly buildListItem; ordering._dom.list would probably be better buildExistingOrdersTable (it's not a list!). I would find buildSubmitButton clearer than ordering._dom.submit.
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.