from the ini file.
324 |
Are you sure?
325 | `
326 |
327 | deleteConnectionMessage.innerHTML = `${warningMessage}
`;
328 |
329 | const cancelButton = document.createElement("BUTTON");
330 | cancelButton.innerHTML = "Cancel";
331 | cancelButton.addEventListener("click", this.hideDeleteMessageApproval.bind(this))
332 | deleteConnectionMessage.querySelector(".actions").appendChild(cancelButton);
333 |
334 | const deleteButton = document.createElement("BUTTON");
335 | deleteButton.innerHTML = "Delete";
336 | deleteButton.className = "danger";
337 | deleteButton.id = "deleteConnectionButton";
338 | deleteButton.addEventListener("click", this.deleteConnection.bind(this, connection))
339 | deleteConnectionMessage.querySelector(".actions").appendChild(deleteButton);
340 |
341 | // hide controllers
342 | const deleteConnBtn = this.el.querySelector(`#deleteConnBtn_${connection["name"].replace(/ /g, "_")}`);
343 | const actionsContainer =
deleteConnBtn.parentNode;
344 | actionsContainer.style.display = "none"
345 |
346 | // show buttons
347 | const buttonsContainer = actionsContainer.parentNode;
348 | buttonsContainer.prepend(deleteConnectionMessage);
349 | }
350 |
351 | hideDeleteMessageApproval() {
352 | this.el.querySelector("#deleteConnectionMessage")?.remove();
353 | this.el.querySelectorAll(".connection-button-actions")
354 | .forEach(c => (c).style.display = "inline-flex");
355 | }
356 |
357 | /**
358 | * Handle create new connection click
359 | */
360 | handleCreateNewConnectionClick() {
361 | this.el.querySelector("#connectionFormHeader").innerHTML = "Create new connection";
362 |
363 | // hide connectionsContainer
364 | (this.el.querySelector("#connectionsContainer")).style.display = "none";
365 |
366 | // show newConnectionContainer
367 | (this.el.querySelector("#newConnectionContainer")).style.display = "block";
368 |
369 | this.handleCreateNewConnectionChange()
370 | }
371 |
372 | /**
373 | * Handle select new connection
374 | */
375 | handleCreateNewConnectionChange() {
376 | const select = (this.el.querySelector("#selectConnection"));
377 | const key = select.value;
378 |
379 | const connectionTemplate = this.connectionsTemplates[key];
380 |
381 | // capture any user inputs before dropdown changed
382 | const userInputData: { [key: string]: any } = {};
383 |
384 | // get previous selected connection
385 | const previousSelect = sessionStorage.getItem("selectConnection");
386 |
387 | // when the database selection dropdown changes we need to capture any inputs
388 | // entered by the user in the previous form and save them in the session.
389 | // Only the fields which have been changed by the user are saved. This saved
390 | // data can be used to auto-populate the new form.
391 | if (previousSelect) {
392 | const prevConnectionTemplate = this.connectionsTemplates[previousSelect];
393 | const { fields } = prevConnectionTemplate;
394 | fields.forEach((field: { id: string; default?: string }) => {
395 | const id = field.id;
396 | const defaultValue = field.hasOwnProperty("default")
397 | ? field["default"]
398 | : "";
399 | const formField = this.el.querySelector(`#${id}`);
400 | if (formField && formField.value != defaultValue) {
401 | userInputData[id] = formField.value;
402 | }
403 | });
404 | }
405 |
406 | // save the previous form details
407 | sessionStorage.setItem("fieldInputs", JSON.stringify(userInputData));
408 |
409 | // save new DB selection
410 | sessionStorage.setItem("selectConnection", key);
411 |
412 | this.drawConnectionDetailsForm(connectionTemplate);
413 |
414 | }
415 |
416 | /**
417 | * Draws a form to create or edit connections
418 | *
419 | * @param connectionTemplate - new connection template
420 | */
421 | drawConnectionDetailsForm(connectionTemplate: ConnectionTemplate, connectionAlias: string = "") {
422 | const { fields } = connectionTemplate;
423 |
424 | const savedFields = JSON.parse(sessionStorage.getItem("fieldInputs"));
425 |
426 | const connectionFormContainer = this.el.querySelector("#connectionFormContainer");
427 | connectionFormContainer.innerHTML = "";
428 |
429 | const connectionForm = document.createElement("FORM");
430 | connectionForm.id = "connectionForm";
431 | connectionFormContainer.appendChild(connectionForm)
432 |
433 | // add a hidden value to hold the alias, this is used when editing a connection
434 | const hiddenInput = document.createElement("input");
435 | hiddenInput.type = "hidden";
436 | hiddenInput.name = "existingConnectionAlias";
437 | hiddenInput.value = connectionAlias || "";
438 | connectionForm.appendChild(hiddenInput);
439 |
440 | fields.forEach(field => {
441 | // text description
442 | const fieldContainer = document.createElement("DIV");
443 | fieldContainer.className = "field-container";
444 | const label = document.createElement("LABEL");
445 | label.setAttribute("for", field.id);
446 | label.innerHTML = field.label;
447 |
448 | // form value
449 | const input = document.createElement("INPUT");
450 | input.id = field.id;
451 | input.name = field.id;
452 | input.className = "field";
453 |
454 | // check for saved values
455 | const savedInput = savedFields ? savedFields[field.id] || "" : "";
456 |
457 |
458 | // when creating the connection alias field, set the default value
459 | // to "default" if there are no connections, this will ensure that
460 | // the notebook automatically reconnects to the database if the
461 | // kernel is restarted
462 | if (field.id == "connectionName" && this.connections.length === 0) {
463 | if (savedInput) {
464 | input.value = savedInput;
465 | } else {
466 | input.value = "default";
467 | }
468 | }
469 |
470 | // check if any user inputs saved
471 | else if (savedInput) {
472 | input.value = savedInput;
473 | }
474 |
475 | // otherwise, set the default value if there's one
476 | else if (field.default !== undefined) {
477 | input.value = field.default;
478 | }
479 |
480 | input.setAttribute("type", field.type);
481 |
482 | fieldContainer.appendChild(label);
483 | fieldContainer.appendChild(input);
484 |
485 | connectionForm.appendChild(fieldContainer);
486 | })
487 |
488 | const buttonsContainer = document.createElement("DIV");
489 | buttonsContainer.className = "buttons-container";
490 |
491 | // cancel button
492 | const cancelButton = document.createElement("BUTTON");
493 | cancelButton.innerHTML = "Cancel";
494 | cancelButton.className = "secondary";
495 | cancelButton.addEventListener("click", this.drawConnectionsList.bind(this, this.connections))
496 | buttonsContainer.appendChild(cancelButton);
497 |
498 | // submit form button
499 | const submitButton = document.createElement("BUTTON");
500 | submitButton.className = "primary";
501 | buttonsContainer.appendChild(submitButton);
502 |
503 | if (connectionAlias) {
504 | // editing an existing connection
505 | submitButton.innerHTML = "Update";
506 | submitButton.id = "updateConnectionFormButton";
507 | connectionForm.addEventListener("submit", this.handleSubmitNewConnection.bind(this))
508 | } else {
509 | // creating a new connection
510 | submitButton.innerHTML = "Create";
511 | submitButton.id = "createConnectionFormButton";
512 | connectionForm.addEventListener("submit", this.handleSubmitNewConnection.bind(this))
513 | }
514 |
515 | // add buttons to the form
516 | connectionForm.appendChild(buttonsContainer);
517 |
518 |
519 | }
520 |
521 | /**
522 | * Submits new connection form
523 | *
524 | * @param event - Submit event
525 | */
526 | handleSubmitNewConnection(event: SubmitEvent) {
527 | event.preventDefault();
528 | sessionStorage.clear()
529 |
530 | let allFieldsFilled = true;
531 |
532 | // Extract form data
533 | const form = event.target as HTMLFormElement;
534 | const formData = new FormData(form);
535 |
536 |
537 | // Convert form data to a plain object
538 | const formValues: { [key: string]: string } = {};
539 |
540 | for (const [key, value] of formData.entries()) {
541 | const _value = value.toString();
542 |
543 | formValues[key] = _value;
544 |
545 | // Skip validation for existingConnectionAlias field since it's hidden
546 | // and only used when editing a connection
547 | if (key !== "existingConnectionAlias" && _value.length === 0) {
548 | allFieldsFilled = false;
549 | }
550 | }
551 |
552 | const select = this.el.querySelector("#selectConnection");
553 |
554 | const driver = this.connectionsTemplates[select.value].driver;
555 |
556 | formValues["driver"] = driver;
557 |
558 | if (allFieldsFilled) {
559 | this.sendFormData(formValues);
560 | } else {
561 | this.showErrorMessage("Error: Please fill in all fields.")
562 | }
563 | }
564 |
565 |
566 |
567 |
568 | /**
569 | * Sends form data to the backend
570 | *
571 | * @param formData - FormData object
572 | */
573 | sendFormData(formData: { [key: string]: string }) {
574 | const message = {
575 | method: "submit_new_connection",
576 | data: formData
577 | };
578 |
579 |
580 | // NOTE: responses are handled in the `handleMessage` method
581 | this.send(message);
582 | }
583 |
584 | /**
585 | * Handle messages from the backend
586 | *
587 | * @param content - The method to invoke with data
588 | */
589 | handleMessage(content: any) {
590 | const errors = ["connection_error", "connection_name_exists_error"]
591 |
592 | if (errors.includes(content.method)) {
593 | this.showErrorMessage(content.message);
594 | }
595 |
596 | if (content.method === "update_connections") {
597 | this.connections = JSON.parse(content.message);
598 |
599 | this.drawConnectionsList(this.connections);
600 | }
601 |
602 | if (content.method === "connected") {
603 | const connectionName = content.message;
604 | this.activeConnection = connectionName;
605 | this.markConnectedButton(connectionName);
606 | }
607 |
608 |
609 | if (content.method === "check_config_file") {
610 | const isExist = content.message;
611 | const i = this.el.querySelector(".connections-guidelines .no-config-file")
612 | if (isExist) {
613 | i.style.display = "none";
614 |
615 | const iKernelMessage = this.el.querySelector(".connections-guidelines .no-config-file")
616 | iKernelMessage.style.display = (this.connections.length === 0) ? "block" : "none";
617 |
618 | } else {
619 | i.style.display = "block";
620 | }
621 | }
622 | }
623 |
624 | /**
625 | * Marks active connection button
626 | *
627 | * @param connectionName - Active connection name
628 | */
629 | markConnectedButton(connectionName: string) {
630 | this.el.querySelectorAll('.connection-button-actions .connectionStatusButton')
631 | .forEach((button: Element) => {
632 | const buttonEl = (button);
633 | buttonEl.innerHTML = "Connect";
634 | buttonEl.classList.remove("primary");
635 | buttonEl.classList.add("secondary");
636 | });
637 |
638 | const selectedButtonEl = (this.el.querySelector(`#connBtn_${connectionName.replace(/ /g, "_")}`));
639 | selectedButtonEl.innerText = "Connected";
640 | selectedButtonEl.classList.add("primary");
641 | }
642 |
643 | showErrorMessage(error: string) {
644 | const errorEl = this.el.querySelector(".user-error-message");
645 | const errorMessageContainer = errorEl.querySelector("pre");
646 | errorMessageContainer.innerHTML = `${error}`;
647 | errorEl.style.display = "block";
648 | }
649 |
650 | }
651 |
--------------------------------------------------------------------------------