eLabBlog

Entwicklung kundenspezifischer Lösungen mit eLabNext Add-ons

von Tigran Sarkissjan 7 Minuten lesen 02 Apr 2024

AOByte hat vor kurzem eine neue Partnerschaft mit eLabNext, einer All-in-One-Laborverwaltungssoftware, begonnen. eLabNext-Lösungen helfen, die Qualität der Forschung zu verbessern, indem sie Rundum-Tools für jedes Labor bereitstellen. Aufgrund seiner schnellen Expansion hat eLabNext beschlossen, ein SDK zu veröffentlichen, das es Entwicklern ermöglicht, neue Add-ons zu erstellen, die andere Benutzer auf dem eLabNext-Dashboard installieren können. Mit Add-ons können Benutzer Software von Drittanbietern in Dashboards integrieren, z. B. Dropbox, Google Drive, usw. Add-ons ermöglichen es den Benutzern auch, ihrem Dashboard Funktionen hinzuzufügen, ohne darauf warten zu müssen, dass die gewünschte Funktion von eLabNext freigegeben wird.

Unser Unternehmen ist stolz darauf, eLabNext auf seinem Weg zu begleiten. Da wir eine wachsende Nachfrage sehen, haben wir beschlossen, einen Teil unseres Weges bei der Entwicklung benutzerdefinierter Add-ons zu teilen. Dieser Artikel ist ein guter Startpunkt, wenn Sie sich für die Entwicklung von eLabNext-Add-ons interessieren.

Beginn der Add-on-Entwicklung

Um mit der Entwicklung von Add-ons zu beginnen, müssen Sie zunächst in den Einstellungen den Entwicklermodus aktivieren. Navigieren Sie zu Kontoeinstellungen > Entwickler. Der Entwicklermodus wird durch einfaches Umlegen des Schalters aktiviert. Im aktivierten Entwicklermodus versucht das SDK, beim Laden der Seite eine Add-on-JavaScript-Datei von der "Add-on-Skript-URL" zu injizieren. Jedes Mal, wenn Sie das eLabNext-Dashboard aufrufen, wird eine einzelne JavaScript-Datei zur Laufzeit beim Laden der Seite geladen.

Lassen Sie uns nun versuchen, ein einfaches Add-on zu erstellen. Bevor Sie sich in die Programmierung stürzen, finden Sie hier zwei wertvolle Ressourcen: eLabNext SDK-Dokumentation und eLabNext REST API-Dokumentation.

Verwenden Sie die Download-Vorlage auf der Seite mit den Entwicklereinstellungen, um ein leeres Add-on zu erstellen. Dies ist ein funktionierendes Beispiel-Add-on, das über einen HTTP-Server Ihrer Wahl in das SDK eingespeist werden kann. Unser Team verwendet eine NodeJS-basierte http-server für Entwicklungszwecke. Das folgende Add-on erfüllt die einfache Aufgabe, die Aufgabentabelle im Dashboard anzuzeigen. Außerdem ermöglicht es den Benutzern, Aufgaben zu erstellen und zu löschen.

/*
@rootVar: SAMPLE_ADDON
@name: Sample
@description: Sample addon
@author: Stepan Smbatyan
@version: 1.0.0
*/
var SAMPLE_ADDON = {};

((context) => {
  context.init = (config) => {
    $(() => {
      context.SampleAddon = new context.SampleAddon(config);
    });
  };

  context.SampleAddon = new Class({
    Implements: [Options, Events],
    Extends: eLabSDK.Base,
    options: {},
    initialize: function (config) {
      // Store a reference to the function's context
      var self = this;
      // Set the options for the application using the provided configuration
      self.setOptions(config);

      $(document).ready(() => {
        const currentPage = Helper.History.get('pageID');

        const pageID = currentPage || new URLSearchParams(window.location.search).get('pageID');

        renderTaskPage();

        if (pageID === 'tasks') {
          getTasks().then(({ data }) => {
            renderTaskTable(data);

            addDeleteBtnListener();
          });
        }
      });
    },
  });

  // #TODO: remove context.init() when upload as add-on to marketplace
  context.init();
})(SAMPLE_ADDON);

// ======================================= DOM =======================================

/**
 * Renders the task list UI by updating the browser history, creating a button and table,
 * filling the table with task data, and updating the main content section with the table container.
 * @param {Event} e - Optional event object. If provided, prevents the default action.
 */
const renderTaskTable = (data) => {
  const button = createAddTaskButton();
  $('#main-content')
  .html('<section id="tableContainer"></section>')
  .prepend(button.render());

  const table = createTaskTable();
  table.data = data;
  table._renderHTML();
};

/**
 * Creates a custom page for tasks using eLabSDK.
 * This function initializes a new CustomPage object with specified configurations.
 * @returns {CustomPage} A CustomPage object representing the task page.
 */
const renderTaskPage = () => {
  return new eLabSDK.CustomPage({
    rootVar: '.nav-main-level',
    pageID: 'tasks',
    mainMenu: 'Tasks',
    subMenu: 'Task list',
  });
};

/**
 * Creates a button element using the eLabSDK.GUI.Button constructor.
 * The button is configured with a label, CSS class,
 * and an action to show a dialog for updating tasks.
 * @returns {eLabSDK.GUI.Button} - A button element configured to add a new task when clicked.
 */
const createAddTaskButton = () => {
  return new eLabSDK.GUI.Button({
    label: 'Add New Task',
    class: 'addNewTaskBtn',
    action: () => showDialog(DIALOG_CONFIGS.CREATE, createTaskAction),
  });
};

const addDeleteBtnListener = () => {
  $('.deleteBtn').on('click', (e) => {
    const id = e.currentTarget.getAttribute('_dataId');

    showDialog(DIALOG_CONFIGS.DELETE, () => deleteTaskAction(id));
  });
};

/**
 * Creates a table element using the Helper.Table.create method.
 * The table is configured with specified target container, data
 * and columns for displaying task information.
 * @returns {HTMLElement} - A table element configured to display task information.
 */
const createTaskTable = () => {
  return Helper.Table.create({
    target: 'tableContainer',
    caption: null,
    data: {},
    columns: [
      {
        name: 'Full Name',
        key: 'fullName',
        width: '20%',
        cellRender: ({ creator }) => `<b>${creator.fullName}</b>`,
      },
      {
        name: 'Title',
        key: 'title',
        width: '20%',
        cellRender: ({ title }) => `<span>${title || '-'}</span>`,
      },
      {
        name: 'Description',
        key: 'contents',
        width: '45%',
        cellRender: ({ contents }) => `<span>${contents || '-'}</span>`,
      },
      {
        name: 'Created',
        key: 'created',
        width: '10%',
        cellRender: ({ created }) => `<span>${created.split('T')[0]}</span>`,
      },
      {
        name: 'Action',
        key: 'actions',
        width: '5%',
        cellRender: ({ taskID }) => `
          <p class='deleteTranslationIcon deleteBtn' _dataId="${taskID}">
            <i class='fa fa-trash-alt _actionIcon' title='Delete translation'></i>
          </p>
        `,
      },
    ],
  });
};

// ======================================= MODAL =======================================

/**
 * Initiates the deletion of a task identified by its taskId asynchronously.
 * Upon successful deletion, closes any open dialogs, reloads the page to reflect the changes.
 * @param {string} taskId - The ID of the task to be deleted.
 * @returns {Promise<void>} - A Promise that resolves after the task deletion and page reload.
 */
const deleteTaskAction = async (taskId) => {
  await deleteTask(taskId);
  Dialog.closeWait();
  window.location.reload();
};

/**
 * Adding a new task with the provided title and description,
 * closing the dialog window, and reloading the current page.
 * @returns {Promise<void>} A promise that resolves once the actions are updated.
 */
const createTaskAction = async () => {
  const title = $('#title').val();
  const description = $('#description').val();

  await addTask({ title, description });
  Dialog.closeWait();
  window.location.reload();
};

/**
 * Displays a dialog window with specified configuration options and a custom button, 
 * calling the provided callback function when the custom button is clicked.
 *
 * @param {Object} config - The configuration object for the dialog window.
 * @param {string} config.title - The title of the dialog window.
 * @param {number} config.width - The width of the dialog window.
 * @param {string} config.btnOk - The label for the OK button.
 * @param {string} config.btnCancelLabel - The label for the Cancel button.
 * @param {string} config.content - The content to be displayed in the dialog window.
 * @param {string} config.customButtonLabel - The label for the custom button.
 * @param {string} config.customButtonStyle - The style for the custom button.
 * @param {Function} callback - The callback function to be called when the custom button is clicked.
 * @returns {void}
 */
const showDialog = (config, callback) => {
  const {
    title,
    width,
    btnOk,
    btnCancelLabel,
    content,
    customButtonLabel,
    customButtonStyle,
  } = config;

  Dialog.show({
    title,
    width,
    btnOk,
    btnCancelLabel,
    content,
    customButtons: [
      {
        label: customButtonLabel,
        style: customButtonStyle,
        fn: callback,
      },
    ],
  });
};

// ======================================= CONSTANTS =======================================

const DIALOG_CONFIGS = {
  DELETE: {
    title: 'Delete Task',
    width: '550',
    btnOk: false,
    btnCancelLabel: 'Close',
    content: '<p>Are you sure you want to delete this task?</p>',
    customButtonLabel: 'Delete Task',
    customButtonStyle: 'background:#fe810',
  },
  CREATE: {
    title: 'Add New Task',
    width: '550',
    btnOk: false,
    btnCancelLabel: 'Close',
    content: `
      <section>
        <input id="title"  type="text" placeholder="Title" />
        <textarea id="description" placeholder="Description" style="padding-top: 8px;"/>
      </section>
    `,
    customButtonLabel: 'Add Task',
    customButtonStyle: 'background:#fe810',
  },
};


// ======================================= API =======================================

/**
 * Retrieves tasks by making a GET request to eLabSDK.
 *
 * @returns {Promise<Array>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.
 */
const getTasks = () => new Promise((resolve, reject) => {
  new eLabSDK.API.Call({
    method: 'GET',
    path: 'tasks',
    onSuccess: (xhr, status, response) => {
      resolve(response);
    },
    onError: (xhr, status, response) => {
      reject(response);
    },
  }).execute();
});

/**
 * Adds a new task with the provided title and description by making a POST request to eLabSDK.
 *
 * @param {Object} task - An object containing the title and description of the task.
 * @param {string} task.title - The title of the task.
 * @param {string} task.description - The description of the task.
 * @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.
 */
const addTask = ({ title, description }) => new Promise((resolve, reject) => {
  const data = {
    assigneeID: 0,
    title,
    contents: description,
  };

  new eLabSDK.API.Call({
    method: 'POST',
    path: 'tasks',
    pathParams: {},
    onSuccess: (xhr, status, response) => {
      resolve(response);
    },
    onError: (xhr, status, response) => {
      reject(response);
    },
  }).execute(data);
});

/**
 * Deletes a task with the specified ID by making a DELETE request to eLabSDK.
 *
 * @param {string} id - The ID of the task to be deleted.
 * @returns {Promise<Object>} A promise that resolves with an array of tasks upon successful retrieval, or rejects with an error response.
 */
const deleteTask = (id) => new Promise((resolve, reject) => {
  new eLabSDK.API.Call({
    method: 'DELETE',
    path: `tasks/${id}`,
    onSuccess: (xhr, status, response) => {
      resolve(response);
    },
    onError: (xhr, status, response) => {
      reject(response);
    },
  }).execute();
});

Eines der wichtigsten Dinge, die bei der Erstellung eines Add-ons zu beachten sind, ist die Bevorzugung von SDK- und API-Methoden gegenüber benutzerdefiniertem Code. Ein gutes Beispiel dafür ist das Rendern von Schaltflächen oder das Ausführen von HTTP-Anfragen. Wenn Sie die vom SDK bereitgestellten Methoden verwenden, können Sie sicher sein, dass z. B. Schaltflächen das richtige Styling haben oder dass alle erforderlichen Header an Ihre HTTP-Anfrage angehängt werden.

Komplexere Add-on-Entwicklung

Natürlich werden die meisten Add-ons, die erstellt werden, komplizierter sein als dieses Beispiel. Natürlich möchten die Entwickler bei der Bereitstellung komplexerer Funktionen die Möglichkeit nutzen, den Code in Module aufzubrechen, den Code für die Produktion zu minimieren, Testfälle für ihren Code zu schreiben und alle anderen Vorteile der modernen Webentwicklung zu nutzen. Während der Arbeit an den Add-ons haben wir ein Boilerplate-Add-on erstellt, mit dem Benutzer eine Projektstruktur, Paketierung, Tests usw. erreichen können. Das Projekt ist zu finden unter GitHub.

Denken Sie daran, dass das eLabNext SDK immer mehr an Fahrt gewinnt und die Dokumentation daher vervollständigt werden muss. Bitte kontaktieren Sie unser Team, wenn Sie sich in einer Situation befinden, in der Sie Hilfe benötigen. Unser Team wird weiterhin über den Entwicklungsprozess von eLabNext-Add-ons schreiben. Wir werden Themen wie das Einreichen von Add-ons auf dem eLab Marketplace, Tipps und Tricks bei der Entwicklung von eLabNext-Add-ons, die Entwicklung komplizierterer Funktionen und so weiter behandeln.

Empfohlen Für Sie

5 Minuten lesen 25. Juli 2024
von eLabNext

Bewältigung von Herausforderungen bei der Auswahl von Laborflächen für Biotech-Start-ups: Einblicke von Savills 

Lernen Sie wertvolle Strategien für die Auswahl des richtigen Laborraums, die Förderung der Zusammenarbeit und die Erzielung von mehr Ergebnissen mit weniger Geld.

Weiterlesen
3 Minuten 11. Juli 2024
von Zareh Zurabyan

Fallstricke von AI in biowissenschaftlichen Labors

Von der Datenqualität bis hin zu ethischen Erwägungen erfahren Sie, wie Sie die Herausforderungen der KI meistern und gleichzeitig die Integration für einen verbesserten Laborbetrieb optimieren können.

Weiterlesen
8 Minuten lesen 09. Juli 2024
von eLabNext

Unser Leitfaden zur Optimierung der Laborbeschaffung

Erfahren Sie, wie Sie die Laborbeschaffung als Teil eines robusten Bestandsverwaltungssystems optimieren können. Erforschen Sie Best Practices und optimieren Sie Ihre Laborabläufe.

Weiterlesen

Starten Sie Ihre Transformation zu einem
All Digital Lab schon heute!

Vereinbaren Sie einen persönlichen Demo-Termin für eine freundliche Beratung und eine kostenlose Bewertung Ihres Labor-Workflows durch unsere Experten.

de_DEDE