eLabBlog

Développer des solutions personnalisées avec les modules complémentaires d'eLabNext

Par Tigran Sargsyan 7 minutes de lecture 02 Avr 2024

AOByte a récemment entamé un nouveau partenariat avec eLabNext, un logiciel de gestion de laboratoire tout-en-un. Les solutions eLabNext contribuent à améliorer la qualité de la recherche en fournissant des outils complets pour n'importe quel laboratoire. En raison de son expansion rapide, eLabNext a décidé de lancer un SDK, permettant aux développeurs de créer de nouveaux modules complémentaires que d'autres utilisateurs peuvent installer sur le tableau de bord d'eLabNext. Les add-ons permettent aux utilisateurs d'intégrer des logiciels tiers dans les tableaux de bord, tels que Dropbox, Google Drive, etc. Les add-ons permettent également aux utilisateurs d'ajouter des fonctionnalités à leur tableau de bord sans attendre que la fonctionnalité souhaitée soit publiée par eLabNext.

Notre société est fière d'accompagner eLabNext dans son parcours. Voyant une demande croissante, nous avons décidé de partager une partie de notre expérience dans la création de modules complémentaires personnalisés. Cet article est un bon point de départ si vous êtes intéressé par le développement de modules complémentaires pour eLabNext.

Commencer le développement de modules complémentaires

Pour commencer à développer des modules complémentaires, vous devez d'abord activer le mode développeur dans les paramètres. Accédez à Paramètres du compte > Développeur. Pour activer le mode développeur, il suffit de basculer l'interrupteur. Lorsque le mode développeur est activé, le SDK tente d'injecter un fichier JavaScript de module complémentaire à partir de l'"URL de script de module complémentaire" lors du chargement de la page. Un seul fichier JavaScript sera chargé au chargement de la page à chaque fois que vous naviguerez sur le tableau de bord d'eLabNext.

Essayons maintenant de créer un simple module complémentaire. Avant de nous lancer dans le codage, voici deux ressources précieuses : Documentation eLabNext SDK et Documentation de l'API REST d'eLabNext.

Utilisez le modèle de téléchargement de la page des paramètres du développeur pour créer un module complémentaire vide. Il s'agit d'un exemple de module complémentaire fonctionnel, qui peut être transmis au SDK via un serveur HTTP de votre choix. Notre équipe utilise un serveur de type serveur http à des fins de développement. Le module complémentaire ci-dessous permet d'afficher le tableau des tâches dans le tableau de bord. Il permet également aux utilisateurs de créer et de supprimer des tâches.


/*
@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();
});

L'une des choses essentielles à retenir lors de la création d'un module complémentaire est de donner la priorité à l'utilisation des méthodes du SDK et de l'API par rapport au code personnalisé. Un bon exemple est le rendu des boutons ou les requêtes HTTP. En utilisant les méthodes fournies par le SDK, vous pouvez être sûr, par exemple, que les boutons auront un style correct ou que tous les en-têtes nécessaires seront ajoutés à votre requête HTTP.

Développement de modules complémentaires plus complexes

Il est évident que la plupart des modules complémentaires qui seront créés seront plus complexes que cet exemple. Naturellement, tout en fournissant des fonctionnalités plus complexes, les développeurs aimeraient utiliser la puissance de la décomposition du code en modules, la minimisation du code pour la production, l'écriture de cas de test pour leur code, et l'utilisation de tous les autres avantages du développement web moderne. Tout en travaillant sur les modules complémentaires, nous avons créé un module complémentaire de type "boilerplate", qui permet aux utilisateurs de mettre en place une structure de projet, d'empaqueter, de tester, etc. Le projet est disponible sur GitHub.

N'oubliez pas que le SDK eLabNext prend de l'ampleur et que la documentation doit être complétée. N'hésitez pas à contacter notre équipe si vous vous trouvez dans une situation où vous avez besoin d'aide. Notre équipe continuera à écrire sur le processus de développement des modules complémentaires d'eLabNext. Nous aborderons des sujets tels que les soumissions d'add-ons à eLab Marketplace, les trucs et astuces du développement d'add-ons eLabNext, le développement de fonctionnalités plus complexes, etc.

Recommandé Pour vous

4 min lire 09 Mai 2024
Par Zareh Zurabyan

Numériser votre laboratoire, un échantillon à la fois

De l'évaluation des stocks aux mesures de contrôle de la qualité, apprenez à numériser efficacement votre laboratoire, un échantillon à la fois.

Lire la suite
8 minutes de lecture 03 Mai 2024
Par eLabNext

Comment tirer le meilleur parti de vos procédures de laboratoire

Dans ce blog, nous explorons les éléments essentiels des protocoles de laboratoire et des modes opératoires normalisés. Découvrez comment les solutions de laboratoire numérique peuvent vous aider à rédiger des procédures de laboratoire plus efficaces.

Lire la suite
9 minutes de lecture 02 Mai 2024
Par Zareh Zurabyan

Avantages et inconvénients de l'IA générative dans la biotechnologie

Explorer les avantages et les inconvénients de l'intégration de l'IA générative dans la recherche et le développement biotechnologiques.

Lire la suite

Commencez votre voyage vers un un laboratoire
tout digital aujourd'hui!

Planifiez une démonstration personnelle pour bénéficier des conseils d'un expert et d'une évaluation gratuite du flux de travail du laboratoire.

fr_FRFR