LCOV - code coverage report
Current view: top level - dashql-app/src/session/persistence - session_connection_import_dialog.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 98.6 % 73 72
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import { Button, ButtonVariant } from '../../ui/foundations/button.js';
       4              : import { Overlay, OverlaySize } from '../../ui/foundations/overlay.js';
       5              : import type { ConnectionImportChoice, ConnectionImportPlanItem } from './session_connection_import.js';
       6              : import { connectionParamsLabel } from '../../connections/connection_label.js';
       7              : 
       8              : import * as styles from './session_import_conflict_dialog.module.css';
       9              : import * as ownStyles from './session_connection_import_dialog.module.css';
      10              : 
      11              : interface Props {
      12              :     /// The name of the imported session
      13              :     sessionName: string;
      14              :     /// The connections of the session that may consolidate with existing connections
      15              :     questions: ConnectionImportPlanItem[];
      16              :     /// Is the import running?
      17              :     busy: boolean;
      18              :     /// Import with the chosen connections, keyed by the connection id in the session
      19              :     onConfirm: (choices: Map<string, ConnectionImportChoice>) => void;
      20              :     /// Abort the import
      21              :     onCancel: () => void;
      22              : }
      23              : 
      24              : /// The value of a choice in the form
      25            1 : function choiceValue(choice: ConnectionImportChoice): string {
      26            1 :     return choice.kind === 'link' ? `link:${choice.connectionId}` : 'separate';
      27            1 : }
      28              : 
      29              : /// Ask the user whether imported connections should use existing connections.
      30              : /// Using an existing connection keeps settings in one place, the session then shares the connection
      31              : /// with every other session that uses it.
      32            1 : export function SessionConnectionImportDialog(props: Props): React.ReactElement {
      33            1 :     const titleId = React.useId();
      34            1 :     const descriptionId = React.useId();
      35            1 :     const dialogRef = React.useRef<HTMLElement>(null);
      36            1 :     const [choices, setChoices] = React.useState(() => new Map<string, ConnectionImportChoice>(props.questions.map(item => [
      37            1 :         item.ref.connectionId,
      38            1 :         item.decision.kind === 'ask' ? { kind: 'link', connectionId: item.decision.candidates[0].id } : { kind: 'separate' },
      39            1 :     ])));
      40            1 :     const cancel = React.useCallback(() => {
      41            1 :         if (!props.busy) props.onCancel();
      42            1 :     }, [props.busy, props.onCancel]);
      43              : 
      44            1 :     return (
      45            1 :         <Overlay centered width={OverlaySize.L} onEscape={cancel} onClickOutside={cancel} initialFocusRef={dialogRef}>
      46            1 :             <section
      47            1 :                 ref={dialogRef}
      48            1 :                 className={styles.dialog}
      49            1 :                 role="dialog"
      50            1 :                 aria-modal="true"
      51            1 :                 aria-labelledby={titleId}
      52            1 :                 aria-describedby={descriptionId}
      53            1 :                 aria-busy={props.busy}
      54            1 :                 tabIndex={-1}
      55              :             >
      56            1 :                 <header className={styles.header}>
      57            1 :                     <h2 id={titleId}>Use existing connections?</h2>
      58            1 :                 </header>
      59            1 :                 <div className={styles.body}>
      60            1 :                     <p id={descriptionId} className={styles.description}>
      61            1 :                         The session {props.sessionName} uses connections that look like connections you already have.
      62              :                         Using an existing connection shares its settings with the sessions that already use it.
      63            1 :                     </p>
      64            1 :                     {props.questions.map(item => {
      65            1 :                         if (item.decision.kind !== 'ask') return null;
      66            1 :                         const current = choices.get(item.ref.connectionId);
      67            1 :                         const name = `connection-${item.ref.connectionId}`;
      68            1 :                         const options: Array<{ choice: ConnectionImportChoice; label: string }> = [
      69            1 :                             ...item.decision.candidates.map(candidate => ({
      70            1 :                                 choice: { kind: 'link' as const, connectionId: candidate.id },
      71            1 :                                 label: `Use ${candidate.name ?? connectionParamsLabel(candidate.params)}`,
      72            1 :                             })),
      73            1 :                             { choice: { kind: 'separate' }, label: 'Keep as a new connection' },
      74            1 :                         ];
      75            1 :                         return (
      76            1 :                             <fieldset key={item.ref.connectionId} className={ownStyles.connection} disabled={props.busy}>
      77            1 :                                 <legend className={ownStyles.connection_label}>
      78            1 :                                     {item.ref.name == null
      79            1 :                                         ? connectionParamsLabel(item.ref.params)
      80            0 :                                         : `${item.ref.name} ยท ${connectionParamsLabel(item.ref.params)}`}
      81            1 :                                 </legend>
      82            1 :                                 {options.map(option => (
      83            1 :                                     <label key={choiceValue(option.choice)} className={ownStyles.option}>
      84            1 :                                         <input
      85            1 :                                             type="radio"
      86            1 :                                             name={name}
      87            1 :                                             value={choiceValue(option.choice)}
      88            1 :                                             checked={current != null && choiceValue(current) === choiceValue(option.choice)}
      89            1 :                                             onChange={() => setChoices(prev => new Map(prev).set(item.ref.connectionId, option.choice))}
      90            1 :                                         />
      91            1 :                                         {option.label}
      92            1 :                                     </label>
      93            1 :                                 ))}
      94            1 :                             </fieldset>
      95              :                         );
      96            1 :                     })}
      97            1 :                 </div>
      98            1 :                 <footer className={styles.actions}>
      99            1 :                     <Button disabled={props.busy} onClick={cancel}>Cancel</Button>
     100            1 :                     <Button variant={ButtonVariant.Primary} disabled={props.busy} onClick={() => props.onConfirm(choices)}>
     101              :                         Import
     102            1 :                     </Button>
     103            1 :                 </footer>
     104            1 :             </section>
     105            1 :         </Overlay>
     106              :     );
     107            1 : }
        

Generated by: LCOV version 2.0-1