@opencloning/ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +37 -0
  3. package/src/components/AppAlerts.jsx +19 -0
  4. package/src/components/CloningHistory.jsx +40 -0
  5. package/src/components/DataModelDisplayer.jsx +30 -0
  6. package/src/components/DescriptionEditor.jsx +68 -0
  7. package/src/components/DownloadCloningStrategyDialog.jsx +84 -0
  8. package/src/components/DownloadSequenceFileDialog.jsx +90 -0
  9. package/src/components/DragAndDropCloningHistoryWrapper.jsx +39 -0
  10. package/src/components/DraggableDialogPaper.jsx +16 -0
  11. package/src/components/EditSequenceNameDialog.jsx +90 -0
  12. package/src/components/ExternalServicesStatusCheck.jsx +92 -0
  13. package/src/components/HistoryLoadedDialog.jsx +49 -0
  14. package/src/components/LoadCloningHistoryWrapper.jsx +166 -0
  15. package/src/components/MainSequenceCheckBox.jsx +83 -0
  16. package/src/components/MainSequenceEditor.jsx +165 -0
  17. package/src/components/NetworkNode.jsx +159 -0
  18. package/src/components/NetworkTree.css +127 -0
  19. package/src/components/ObjectTable.jsx +24 -0
  20. package/src/components/OpenCloning.jsx +102 -0
  21. package/src/components/OverhangsDisplay.jsx +25 -0
  22. package/src/components/SequenceEditor.jsx +120 -0
  23. package/src/components/SequenceTab.jsx +14 -0
  24. package/src/components/TemplateSequence.jsx +38 -0
  25. package/src/components/annotation/PlannotateAnnotationReport.jsx +33 -0
  26. package/src/components/annotation/useUpdateAnnotationInMainSequence.js +39 -0
  27. package/src/components/assembler/AssemblePartWidget.jsx +252 -0
  28. package/src/components/assembler/Assembler.jsx +273 -0
  29. package/src/components/assembler/AssemblerPart.jsx +99 -0
  30. package/src/components/assembler/StopIcon.jsx +34 -0
  31. package/src/components/assembler/assembler_data2.json +50 -0
  32. package/src/components/assembler/assembly_component.module.css +81 -0
  33. package/src/components/assembler/moclo.json +110 -0
  34. package/src/components/assembler/sbol_visual_glyphs/LICENSE.html +21 -0
  35. package/src/components/assembler/sbol_visual_glyphs/assembly-scar.svg +63 -0
  36. package/src/components/assembler/sbol_visual_glyphs/cds-stop.svg +85 -0
  37. package/src/components/assembler/sbol_visual_glyphs/cds.svg +60 -0
  38. package/src/components/assembler/sbol_visual_glyphs/chromosomal-locus.svg +78 -0
  39. package/src/components/assembler/sbol_visual_glyphs/engineered-region.svg +56 -0
  40. package/src/components/assembler/sbol_visual_glyphs/five-prime-sticky-restriction-site.svg +56 -0
  41. package/src/components/assembler/sbol_visual_glyphs/origin-of-replication.svg +57 -0
  42. package/src/components/assembler/sbol_visual_glyphs/primer-binding-site.svg +59 -0
  43. package/src/components/assembler/sbol_visual_glyphs/promoter.svg +60 -0
  44. package/src/components/assembler/sbol_visual_glyphs/ribosome-entry-site.svg +56 -0
  45. package/src/components/assembler/sbol_visual_glyphs/specific-recombination-site.svg +59 -0
  46. package/src/components/assembler/sbol_visual_glyphs/terminator.svg +60 -0
  47. package/src/components/assembler/sbol_visual_glyphs/three-prime-sticky-restriction-site.svg +56 -0
  48. package/src/components/assembler/sbol_visual_glyphs.js +36 -0
  49. package/src/components/assembler/useAssembler.js +71 -0
  50. package/src/components/dummy/DummyInterface.js +41 -0
  51. package/src/components/dummy/GetSequenceFileAndDatabaseIdComponent.jsx +59 -0
  52. package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +86 -0
  53. package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +43 -0
  54. package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +43 -0
  55. package/src/components/eLabFTW/ELabFTWFileSelect.jsx +29 -0
  56. package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +107 -0
  57. package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +23 -0
  58. package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +261 -0
  59. package/src/components/eLabFTW/GetPrimerComponent.jsx +55 -0
  60. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +184 -0
  61. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +62 -0
  62. package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +235 -0
  63. package/src/components/eLabFTW/LoadHistoryComponent.jsx +51 -0
  64. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +159 -0
  65. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +54 -0
  66. package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +185 -0
  67. package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +51 -0
  68. package/src/components/eLabFTW/common.js +26 -0
  69. package/src/components/eLabFTW/eLabFTWInterface.js +294 -0
  70. package/src/components/eLabFTW/eLabFTWInterface.test.js +839 -0
  71. package/src/components/eLabFTW/envValues.js +7 -0
  72. package/src/components/eLabFTW/utils.js +39 -0
  73. package/src/components/form/CustomFormHelperText.jsx +10 -0
  74. package/src/components/form/EnzymeMultiSelect.cy.jsx +61 -0
  75. package/src/components/form/EnzymeMultiSelect.jsx +34 -0
  76. package/src/components/form/GetRequestMultiSelect.jsx +107 -0
  77. package/src/components/form/LabelWithTooltip.jsx +16 -0
  78. package/src/components/form/PostRequestSelect.cy.jsx +70 -0
  79. package/src/components/form/PostRequestSelect.jsx +86 -0
  80. package/src/components/form/RequestStatusWrapper.jsx +17 -0
  81. package/src/components/form/RetryAlert.jsx +20 -0
  82. package/src/components/form/ServerErrorMessage.jsx +10 -0
  83. package/src/components/form/SubmitButtonBackendAPI.jsx +15 -0
  84. package/src/components/form/SubmitToDatabaseDialog.jsx +133 -0
  85. package/src/components/form/TextFieldValidate.jsx +67 -0
  86. package/src/components/form/ValidatedTextField.jsx +33 -0
  87. package/src/components/form/intermediates_disclaimer.svg +181 -0
  88. package/src/components/navigation/ButtonWithMenu.jsx +43 -0
  89. package/src/components/navigation/CustomTab.jsx +14 -0
  90. package/src/components/navigation/FeedbackDialog.jsx +34 -0
  91. package/src/components/navigation/GithubCornerRight.jsx +29 -0
  92. package/src/components/navigation/MainAppBar.css +26 -0
  93. package/src/components/navigation/MainAppBar.jsx +205 -0
  94. package/src/components/navigation/SelectExampleDialog.jsx +69 -0
  95. package/src/components/navigation/SelectTemplateDialog.jsx +107 -0
  96. package/src/components/navigation/TabPanel.jsx +28 -0
  97. package/src/components/navigation/VersionDialog.jsx +33 -0
  98. package/src/components/primers/CreatePrimerFromSequenceForm.jsx +42 -0
  99. package/src/components/primers/DownloadPrimersButton.jsx +104 -0
  100. package/src/components/primers/PrimerForm.css +14 -0
  101. package/src/components/primers/PrimerForm.jsx +107 -0
  102. package/src/components/primers/PrimerList.css +46 -0
  103. package/src/components/primers/PrimerList.cy.jsx +95 -0
  104. package/src/components/primers/PrimerList.jsx +126 -0
  105. package/src/components/primers/PrimerTableRow.cy.jsx +57 -0
  106. package/src/components/primers/PrimerTableRow.jsx +84 -0
  107. package/src/components/primers/SelectPrimerForm.jsx +66 -0
  108. package/src/components/primers/import_primers/ImportPrimersButton.jsx +101 -0
  109. package/src/components/primers/import_primers/ImportPrimersTable.jsx +51 -0
  110. package/src/components/primers/import_primers/PrimerDatabaseImportForm.jsx +107 -0
  111. package/src/components/primers/import_primers/styles.css +60 -0
  112. package/src/components/primers/primer_design/SequenceTabComponents/CollapsableLabel.jsx +31 -0
  113. package/src/components/primers/primer_design/SequenceTabComponents/GatewayRoiSelect.jsx +164 -0
  114. package/src/components/primers/primer_design/SequenceTabComponents/OrientationPicker.jsx +37 -0
  115. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignContext.jsx +369 -0
  116. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignEBIC.jsx +24 -0
  117. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignForm.jsx +29 -0
  118. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGatewayBP.jsx +36 -0
  119. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGibsonAssembly.jsx +26 -0
  120. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignHomologousRecombination.jsx +32 -0
  121. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignRestriction.jsx +25 -0
  122. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignSimplePair.jsx +25 -0
  123. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignStepper.jsx +53 -0
  124. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesigner.jsx +80 -0
  125. package/src/components/primers/primer_design/SequenceTabComponents/PrimerResultForm.jsx +49 -0
  126. package/src/components/primers/primer_design/SequenceTabComponents/PrimerSettingsForm.jsx +68 -0
  127. package/src/components/primers/primer_design/SequenceTabComponents/PrimerSpacerForm.jsx +84 -0
  128. package/src/components/primers/primer_design/SequenceTabComponents/RestrictionSpacerForm.jsx +85 -0
  129. package/src/components/primers/primer_design/SequenceTabComponents/StepNavigation.jsx +48 -0
  130. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelEBICSettings.jsx +216 -0
  131. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelResults.jsx +42 -0
  132. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelSelectRoi.jsx +61 -0
  133. package/src/components/primers/primer_design/SequenceTabComponents/TabPannelSettings.jsx +59 -0
  134. package/src/components/primers/primer_design/SequenceTabComponents/primerDesignMinimalValues.json +5 -0
  135. package/src/components/primers/primer_design/SequenceTabComponents/useEBICPrimerDesignSettings.js +31 -0
  136. package/src/components/primers/primer_design/SequenceTabComponents/useEnzymePrimerDesignSettings.js +57 -0
  137. package/src/components/primers/primer_design/SequenceTabComponents/useGatewayPrimerDesignSettings.js +18 -0
  138. package/src/components/primers/primer_design/SequenceTabComponents/usePrimerDesignSettings.js +31 -0
  139. package/src/components/primers/primer_design/SourceComponents/PrimerDesignGatewayBP.jsx +88 -0
  140. package/src/components/primers/primer_design/SourceComponents/PrimerDesignGibsonAssembly.jsx +65 -0
  141. package/src/components/primers/primer_design/SourceComponents/PrimerDesignHomologousRecombination.jsx +84 -0
  142. package/src/components/primers/primer_design/SourceComponents/PrimerDesignSourceForm.jsx +74 -0
  143. package/src/components/primers/primer_design/common/NoAttPSitesError.jsx +31 -0
  144. package/src/components/primers/primer_details/PCRTable.cy.jsx +51 -0
  145. package/src/components/primers/primer_details/PCRTable.jsx +35 -0
  146. package/src/components/primers/primer_details/Primer3Figure.jsx +25 -0
  147. package/src/components/primers/primer_details/PrimerDetailsTds.jsx +39 -0
  148. package/src/components/primers/primer_details/PrimerInfoIcon.cy.jsx +137 -0
  149. package/src/components/primers/primer_details/PrimerInfoIcon.jsx +132 -0
  150. package/src/components/primers/primer_details/TableSection.jsx +17 -0
  151. package/src/components/primers/primer_details/primerDetailsFormatting.js +3 -0
  152. package/src/components/primers/primer_details/useMultiplePrimerDetails.js +29 -0
  153. package/src/components/primers/primer_details/usePCRDetails.js +47 -0
  154. package/src/components/primers/primer_details/usePrimerDetailsEndpoints.js +49 -0
  155. package/src/components/primers/primer_details/useSinglePrimerSequenceDetails.js +25 -0
  156. package/src/components/primers/primersToTabularFile.js +49 -0
  157. package/src/components/primers/primersToTabularFile.test.js +108 -0
  158. package/src/components/settings/SettingsTab.cy.jsx +267 -0
  159. package/src/components/settings/SettingsTab.jsx +170 -0
  160. package/src/components/sources/AssemblyPlanDisplayer.cy.jsx +22 -0
  161. package/src/components/sources/AssemblyPlanDisplayer.jsx +27 -0
  162. package/src/components/sources/CollectionSource.jsx +97 -0
  163. package/src/components/sources/FinishedSource.jsx +397 -0
  164. package/src/components/sources/KnownSourceErrors.jsx +50 -0
  165. package/src/components/sources/MultipleInputsSelector.jsx +63 -0
  166. package/src/components/sources/MultipleOutputsSelector.jsx +63 -0
  167. package/src/components/sources/NewSourceBox.jsx +37 -0
  168. package/src/components/sources/PCRUnitForm.jsx +102 -0
  169. package/src/components/sources/SingleInputSelector.jsx +36 -0
  170. package/src/components/sources/Source.jsx +125 -0
  171. package/src/components/sources/SourceAnnotation.jsx +44 -0
  172. package/src/components/sources/SourceAssembly.jsx +201 -0
  173. package/src/components/sources/SourceBox.css +18 -0
  174. package/src/components/sources/SourceBox.jsx +60 -0
  175. package/src/components/sources/SourceCopySequence.jsx +38 -0
  176. package/src/components/sources/SourceDatabase.jsx +28 -0
  177. package/src/components/sources/SourceFile.jsx +188 -0
  178. package/src/components/sources/SourceGenomeRegion.cy.jsx +131 -0
  179. package/src/components/sources/SourceGenomeRegion.jsx +486 -0
  180. package/src/components/sources/SourceHomologousRecombination.jsx +125 -0
  181. package/src/components/sources/SourceKnownGenomeRegion.jsx +60 -0
  182. package/src/components/sources/SourceManuallyTyped.jsx +116 -0
  183. package/src/components/sources/SourcePCRorHybridization.jsx +165 -0
  184. package/src/components/sources/SourcePolymeraseExtension.jsx +44 -0
  185. package/src/components/sources/SourceRepositoryId.jsx +409 -0
  186. package/src/components/sources/SourceRestriction.jsx +41 -0
  187. package/src/components/sources/SourceReverseComplement.jsx +33 -0
  188. package/src/components/sources/SourceTypeSelector.jsx +94 -0
  189. package/src/components/sources/SubSequenceDisplayer.jsx +70 -0
  190. package/src/components/sources/VerifyDeleteDialog.jsx +23 -0
  191. package/src/components/sources/repositoryMetadata.js +14 -0
  192. package/src/components/verification/LoadFromDatabaseButton.jsx +90 -0
  193. package/src/components/verification/SequencingFileRow.jsx +34 -0
  194. package/src/components/verification/VerificationFileDialog.cy.jsx +176 -0
  195. package/src/components/verification/VerificationFileDialog.jsx +248 -0
  196. package/src/config/defaultMainEditorProps.js +44 -0
  197. package/src/hooks/useAlerts.js +16 -0
  198. package/src/hooks/useBackendAPI.js +51 -0
  199. package/src/hooks/useBackendRoute.js +22 -0
  200. package/src/hooks/useDatabase.js +18 -0
  201. package/src/hooks/useDragAndDropFile.js +31 -0
  202. package/src/hooks/useGatewaySites.js +40 -0
  203. package/src/hooks/useHttpClient.js +12 -0
  204. package/src/hooks/useLoadDatabaseFile.js +108 -0
  205. package/src/hooks/useStoreEditor.js +101 -0
  206. package/src/hooks/useValidateState.js +43 -0
  207. package/vitest.config.js +18 -0
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import { Checkbox, FormControl, FormControlLabel, TextField } from '@mui/material';
3
+ import SubmitButtonBackendAPI from '../form/SubmitButtonBackendAPI';
4
+ import ValidatedTextField from '../form/ValidatedTextField';
5
+ import { stringIsNotDNA } from '@opencloning/store/cloning_utils';
6
+
7
+ function SourceManuallyTyped({ source, requestStatus, sendPostRequest }) {
8
+ const { id: sourceId } = source;
9
+
10
+ const [userInput, setUserInput] = React.useState('');
11
+ const [isCircular, setIsCircular] = React.useState(false);
12
+ const [overhangCrick3prime, setOverhangCrick3prime] = React.useState(0);
13
+ const [overhangWatson3prime, setOverhangWatson3prime] = React.useState(0);
14
+ const [errorStatus, setErrorStatus] = React.useState({ sequence: true });
15
+ const [touched, setTouched] = React.useState(false);
16
+ const [submissionAttempted, setSubmissionAttempted] = React.useState(false);
17
+
18
+ const submissionAllowed = touched && Object.values(errorStatus).every((error) => !error);
19
+ const updateValidationStatus = (fieldName, valid) => {
20
+ setTouched(true);
21
+ // Update the validation status for the given field (taken from the ref.id)
22
+ setErrorStatus((prevErrorStatus) => ({
23
+ ...prevErrorStatus,
24
+ [fieldName]: valid,
25
+ }));
26
+ };
27
+
28
+ const onInputChange = (event) => {
29
+ // Remove all non-letter characters on paste
30
+ if (event.nativeEvent.inputType === 'insertFromPaste') {
31
+ setUserInput(event.target.value.replace(/[^a-zA-Z]/g, ''));
32
+ } else {
33
+ setUserInput(event.target.value);
34
+ }
35
+ };
36
+
37
+ const onSubmit = (event) => {
38
+ event.preventDefault();
39
+ setSubmissionAttempted(true);
40
+ if (submissionAllowed) {
41
+ const requestData = {
42
+ source: { id: sourceId },
43
+ sequence: {
44
+ id: sourceId,
45
+ sequence: userInput,
46
+ circular: isCircular,
47
+ overhang_crick_3prime: overhangCrick3prime,
48
+ overhang_watson_3prime: overhangWatson3prime,
49
+ },
50
+ };
51
+ sendPostRequest({ endpoint: 'manually_typed', requestData, source });
52
+ }
53
+ };
54
+
55
+ const onCircularChange = () => {
56
+ // See constrains of ManuallyTyped in the backend
57
+ const newIsCircular = !isCircular;
58
+ setIsCircular(newIsCircular);
59
+ if (newIsCircular) {
60
+ setOverhangCrick3prime(0);
61
+ setOverhangWatson3prime(0);
62
+ }
63
+ };
64
+
65
+ const sequenceErrorChecker = (s) => (stringIsNotDNA(s) ? { error: true, helperText: 'invalid DNA sequence' } : { error: false, helperText: '' });
66
+
67
+ return (
68
+ <form onSubmit={onSubmit}>
69
+ <ValidatedTextField
70
+ fullWidth
71
+ id="sequence"
72
+ label="Sequence"
73
+ variant="outlined"
74
+ value={userInput}
75
+ onInputChange={onInputChange}
76
+ className="sequence"
77
+ submissionAttempted={submissionAttempted}
78
+ required
79
+ errorChecker={sequenceErrorChecker}
80
+ updateValidationStatus={updateValidationStatus}
81
+ />
82
+ {
83
+ !isCircular && (
84
+ <>
85
+ <FormControl fullWidth>
86
+ <TextField
87
+ label="Overhang crick 3'"
88
+ value={overhangCrick3prime}
89
+ type="number"
90
+ onChange={(event) => setOverhangCrick3prime(event.target.value)}
91
+ />
92
+ </FormControl>
93
+ <FormControl fullWidth>
94
+ <TextField
95
+ label="Overhang watson 3'"
96
+ value={overhangWatson3prime}
97
+ type="number"
98
+ onChange={(event) => setOverhangWatson3prime(event.target.value)}
99
+ />
100
+ </FormControl>
101
+ </>
102
+ )
103
+ }
104
+
105
+ <FormControl fullWidth style={{ textAlign: 'left' }}>
106
+ <FormControlLabel control={<Checkbox value={isCircular} onChange={onCircularChange} />} label="Circular DNA" />
107
+ </FormControl>
108
+ <SubmitButtonBackendAPI
109
+ requestStatus={requestStatus}
110
+ {...(import.meta.env.VITE_UMAMI_WEBSITE_ID && { "data-umami-event": "submit-manually-typed" })}
111
+ >Submit</SubmitButtonBackendAPI>
112
+ </form>
113
+ );
114
+ }
115
+
116
+ export default SourceManuallyTyped;
@@ -0,0 +1,165 @@
1
+ import React from 'react';
2
+ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
3
+ import { Button, Checkbox, FormControl, FormControlLabel, InputAdornment, TextField } from '@mui/material';
4
+
5
+ import { doesSourceHaveOutput, getInputSequencesFromSourceId, getPcrTemplateSequenceId } from '@opencloning/store/cloning_utils';
6
+ import SubmitButtonBackendAPI from '../form/SubmitButtonBackendAPI';
7
+ import PCRUnitForm from './PCRUnitForm';
8
+ import PrimerDesignSourceForm from '../primers/primer_design/SourceComponents/PrimerDesignSourceForm';
9
+ import { cloningActions } from '@opencloning/store/cloning';
10
+ import useStoreEditor from '../../hooks/useStoreEditor';
11
+ import LabelWithTooltip from '../form/LabelWithTooltip';
12
+
13
+ function SourcePCRorHybridization({ source, requestStatus, sendPostRequest }) {
14
+ // Represents a PCR if inputs != [], else is a oligo hybridization
15
+
16
+ const dispatch = useDispatch();
17
+ const { updateStoreEditor } = useStoreEditor();
18
+ const { setCurrentTab, setMainSequenceId } = cloningActions;
19
+ const { id: sourceId } = source;
20
+
21
+ const inputSequences = useSelector((state) => getInputSequencesFromSourceId(state, sourceId), shallowEqual);
22
+ const primers = useSelector((state) => state.cloning.primers);
23
+ const isPcr = inputSequences.length !== 0;
24
+ const hasOutput = useSelector((state) => doesSourceHaveOutput(state.cloning, sourceId));
25
+ const outputIsPrimerDesign = useSelector((state) => isPcr && hasOutput && state.cloning.sequences.find((e) => e.id === source.id).primer_design !== undefined);
26
+
27
+ const [forwardPrimerId, setForwardPrimerId] = React.useState('');
28
+ const [reversePrimerId, setReversePrimerId] = React.useState('');
29
+ const [designingPrimers, setDesigningPrimers] = React.useState(false);
30
+ const [addPrimerFeatures, setAddPrimerFeatures] = React.useState(false);
31
+
32
+ const minimalAnnealingRef = React.useRef(null);
33
+ const allowedMismatchesRef = React.useRef(null);
34
+
35
+ React.useEffect(() => {
36
+ if (isPcr && source.input.length === 3) {
37
+ setForwardPrimerId(source.input[0].sequence);
38
+ setReversePrimerId(source.input[2].sequence);
39
+ } else if (!isPcr && source.input.length === 2) {
40
+ setForwardPrimerId(source.input[0].sequence);
41
+ setReversePrimerId(source.input[1].sequence);
42
+ }
43
+ }, [source.input]);
44
+
45
+ React.useEffect(() => {
46
+ if (source.add_primer_annotations) { setAddPrimerFeatures(source.add_primer_annotations); }
47
+ }, [source.add_primer_annotations]);
48
+
49
+ const onSubmit = (event) => {
50
+ event.preventDefault();
51
+
52
+ const requestData = {
53
+ sequences: inputSequences,
54
+ primers: [forwardPrimerId, reversePrimerId].map((id) => primers.find((p) => p.id === id)),
55
+ source: { id: sourceId, input: inputSequences.map((e) => ({ sequence: e.id })), output_name: source.output_name },
56
+ };
57
+
58
+ if (!isPcr) {
59
+ requestData.source.input = [forwardPrimerId, reversePrimerId].map((id) => ({ sequence: id }));
60
+ const config = { params: { minimal_annealing: minimalAnnealingRef.current.value } };
61
+ sendPostRequest({ endpoint: 'oligonucleotide_hybridization', requestData, config, source });
62
+ } else {
63
+ const config = { params: {
64
+ minimal_annealing: minimalAnnealingRef.current.value,
65
+ allowed_mismatches: allowedMismatchesRef.current.value,
66
+ } };
67
+ requestData.source.add_primer_features = addPrimerFeatures;
68
+ sendPostRequest({ endpoint: 'pcr', requestData, config, source });
69
+ }
70
+ };
71
+
72
+ const onPrimerDesign = () => {
73
+ setDesigningPrimers(true);
74
+ };
75
+
76
+ if (outputIsPrimerDesign && !forwardPrimerId && !reversePrimerId) {
77
+ const goToPrimerDesign = () => {
78
+ dispatch(setCurrentTab(3));
79
+ const templateSequenceId = getPcrTemplateSequenceId(source);
80
+ dispatch(setMainSequenceId(templateSequenceId));
81
+ updateStoreEditor('mainEditor', templateSequenceId);
82
+ // Scroll to the top of the tab panels container
83
+ document.querySelector('.tab-panels-container')?.scrollTo({
84
+ top: 0,
85
+ behavior: 'instant',
86
+ });
87
+ };
88
+ return (
89
+ <Button variant="contained" color="success" sx={{ mt: 1 }} onClick={goToPrimerDesign}>
90
+ <span style={{ fontSize: '1.2em', marginRight: 12 }}>✨</span>
91
+ {' '}
92
+ Design primers
93
+ {' '}
94
+ <span style={{ fontSize: '1.2em', marginLeft: 12 }}>✨</span>
95
+ </Button>
96
+ );
97
+ }
98
+
99
+ if (designingPrimers && !hasOutput) {
100
+ return <PrimerDesignSourceForm source={source} />;
101
+ }
102
+
103
+ return (
104
+ <div className="pcr_or_hybridization">
105
+ <form onSubmit={onSubmit}>
106
+ {isPcr && !hasOutput && !forwardPrimerId && !reversePrimerId && (
107
+ <Button variant="contained" color="success" sx={{ my: 2 }} onClick={onPrimerDesign}>Design primers</Button>
108
+ )}
109
+ <PCRUnitForm
110
+ sourceId={sourceId}
111
+ primers={primers}
112
+ forwardPrimerId={forwardPrimerId}
113
+ reversePrimerId={reversePrimerId}
114
+ onChangeForward={setForwardPrimerId}
115
+ onChangeReverse={setReversePrimerId}
116
+ />
117
+ <FormControl fullWidth>
118
+ <TextField
119
+ label="Minimal annealing length"
120
+ inputRef={minimalAnnealingRef}
121
+ type="number"
122
+ defaultValue={12}
123
+ InputProps={{
124
+ endAdornment: <InputAdornment position="end">bp</InputAdornment>,
125
+ sx: { '& input': { textAlign: 'center' } },
126
+ }}
127
+ />
128
+ </FormControl>
129
+ {isPcr && (
130
+ <>
131
+ <FormControl fullWidth>
132
+ <TextField
133
+ label="Mismatches allowed"
134
+ inputRef={allowedMismatchesRef}
135
+ type="number"
136
+ defaultValue={0}
137
+ InputProps={{
138
+ sx: { '& input': { textAlign: 'center' } },
139
+ }}
140
+ />
141
+ </FormControl>
142
+ <FormControl fullWidth>
143
+ <FormControlLabel
144
+ control={<Checkbox checked={addPrimerFeatures} onChange={() => setAddPrimerFeatures(!addPrimerFeatures)} />}
145
+ label={(
146
+ <LabelWithTooltip label="Add primer features" tooltip="Add features representing the primers to the PCR product" />
147
+ )}
148
+ />
149
+ </FormControl>
150
+ </>
151
+ )}
152
+ {forwardPrimerId && reversePrimerId && (
153
+ <SubmitButtonBackendAPI
154
+ requestStatus={requestStatus}
155
+ {...(import.meta.env.VITE_UMAMI_WEBSITE_ID && { "data-umami-event": !isPcr ? "submit-hybridization" : "submit-pcr" })}
156
+ >
157
+ {!isPcr ? 'Perform hybridization' : 'Perform PCR'}
158
+ </SubmitButtonBackendAPI>
159
+ )}
160
+ </form>
161
+ </div>
162
+ );
163
+ }
164
+
165
+ export default SourcePCRorHybridization;
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { shallowEqual, useSelector } from 'react-redux';
3
+ import { Alert } from '@mui/material';
4
+ import { getInputSequencesFromSourceId } from '@opencloning/store/cloning_utils';
5
+ import SubmitButtonBackendAPI from '../form/SubmitButtonBackendAPI';
6
+
7
+ function SourcePolymeraseExtension({ source, requestStatus, sendPostRequest }) {
8
+ const { id: sourceId } = source;
9
+ const inputSequences = useSelector((state) => getInputSequencesFromSourceId(state, sourceId), shallowEqual);
10
+ const { overhang_crick_3prime, overhang_watson_3prime } = inputSequences[0];
11
+ const invalidInput = (overhang_crick_3prime >= 0) && (overhang_watson_3prime >= 0);
12
+ const onSubmit = (event) => {
13
+ event.preventDefault();
14
+
15
+ const requestData = {
16
+ sequences: inputSequences,
17
+ source: { id: sourceId, input: inputSequences.map((e) => ({ sequence: e.id })) },
18
+ };
19
+ sendPostRequest({ endpoint: 'polymerase_extension', requestData, source });
20
+ };
21
+ // No need for MultipleOutputsSelector, since there is only one output
22
+ return (
23
+ <div className="PolymeraseExtensionSource">
24
+ <form onSubmit={onSubmit}>
25
+ {invalidInput ? (
26
+ <Alert severity="error">
27
+ <strong>Invalid input:</strong>
28
+ {' '}
29
+ no 5&apos; overhangs.
30
+ </Alert>
31
+ ) : (
32
+ <SubmitButtonBackendAPI
33
+ requestStatus={requestStatus}
34
+ {...(import.meta.env.VITE_UMAMI_WEBSITE_ID && { "data-umami-event": "submit-polymerase-extension" })}
35
+ >
36
+ Extend with polymerase
37
+ </SubmitButtonBackendAPI>
38
+ )}
39
+ </form>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ export default SourcePolymeraseExtension;