• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

medplum / medplum / 30405872363

28 Jul 2026 10:37PM UTC coverage: 91.98% (-0.001%) from 91.981%
30405872363

push

github

web-flow
fix(provider): Enable /new in communications (#10002)

* fix(provider): Enable /new in communications

Signed-off-by: David Yanez <me@davidyanez.com>

* enable location.pathname.endsWith('/new')

Signed-off-by: David Yanez <me@davidyanez.com>

---------

Signed-off-by: David Yanez <me@davidyanez.com>

22656 of 25739 branches covered (88.02%)

Branch coverage included in aggregate %.

4 of 5 new or added lines in 2 files covered. (80.0%)

1 existing line in 1 file now uncovered.

40207 of 42605 relevant lines covered (94.37%)

12283.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.0
/packages/react/src/chat/ThreadInbox/NewTopicDialog.tsx
1
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors
2
// SPDX-License-Identifier: Apache-2.0
3
import { Button, Modal, Stack, Text, TextInput } from '@mantine/core';
4
import { showNotification } from '@mantine/notifications';
5
import { createReference, HTTP_HL7_ORG, normalizeErrorString } from '@medplum/core';
6
import type {
7
  Communication,
8
  Patient,
9
  Practitioner,
10
  Questionnaire,
11
  QuestionnaireResponse,
12
  Reference,
13
} from '@medplum/fhirtypes';
14
import { useMedplum, useMedplumProfile } from '@medplum/react-hooks';
15
import type { JSX } from 'react';
16
import { useMemo, useState } from 'react';
17
import { QuestionnaireForm } from '../../QuestionnaireForm/QuestionnaireForm';
18
import { ResourceInput } from '../../ResourceInput/ResourceInput';
19

20
/**
21
 * Props for the NewTopicDialog component.
22
 * @param subject - The patient to associate with the new thread. When provided and `allowPatientSelection` is false, the patient field is pre-filled and disabled.
23
 * @param opened - Whether the dialog is open.
24
 * @param onClose - Callback fired when the dialog is closed.
25
 * @param onSubmit - Callback fired with the created Communication resource after successful submission.
26
 * @param allowPatientSelection - When true, the patient field is an editable search input. When false (default), the field is pre-filled from `subject` and disabled. Use true for provider-facing contexts, false for patient-facing apps.
27
 */
28
export interface NewTopicDialogProps {
29
  subject: Reference<Patient> | Patient | undefined;
30
  opened: boolean;
31
  onClose: () => void;
32
  onSubmit?: (communication: Communication) => void;
33
  allowPatientSelection?: boolean;
34
}
35

36
export const NewTopicDialog = (props: NewTopicDialogProps): JSX.Element => {
3✔
37
  const { subject, opened, onClose, onSubmit, allowPatientSelection = false } = props;
69✔
38
  const medplum = useMedplum();
69✔
39
  const profile = useMedplumProfile();
69✔
40
  const profileRef = useMemo(() => (profile ? createReference(profile) : undefined), [profile]);
69!
41

42
  const [topic, setTopic] = useState('');
69✔
43
  const [practitioners, setPractitioners] = useState(
69✔
44
    profile?.resourceType === 'Practitioner' ? [createReference(profile) as Reference<Practitioner>] : []
69!
45
  );
46
  const [patient, setPatient] = useState<Reference<Patient> | undefined>(
69✔
47
    subject ? createReference(subject as Patient) : undefined
69!
48
  );
49

50
  // Create initial QuestionnaireResponse with current practitioner as default
51
  const initialResponse: QuestionnaireResponse | undefined = useMemo(() => {
69✔
52
    if (profile?.resourceType === 'Practitioner') {
32!
53
      return {
32✔
54
        resourceType: 'QuestionnaireResponse',
55
        status: 'in-progress',
56
        item: [
57
          {
58
            linkId: 'q1',
59
            answer: [{ valueReference: createReference(profile) }],
60
          },
61
        ],
62
      };
63
    }
64
    return undefined;
×
65
  }, [profile]);
66

67
  const handleSubmit = async (): Promise<void> => {
69✔
68
    if (!patient) {
1!
69
      showNotification({
1✔
70
        title: 'Error',
71
        message: 'Please select a patient',
72
        color: 'red',
73
      });
74
      return;
1✔
75
    }
76

77
    const communication: Communication = {
×
78
      resourceType: 'Communication',
79
      status: 'in-progress',
80
      subject: patient,
81
      sender: profileRef,
82
      recipient: [
83
        patient,
84
        ...practitioners.map((practitioner) => ({
×
85
          reference: practitioner.reference,
86
        })),
87
      ],
88
      topic: {
89
        text: topic,
90
      },
91
    };
92

93
    try {
×
94
      const createdCommunication = await medplum.createResource(communication);
×
95
      // Close before onSubmit so that a URL-driven onClose (e.g. navigating away from
96
      // a /new route) does not override navigation performed by the onSubmit handler.
UNCOV
97
      onClose();
×
NEW
98
      onSubmit?.(createdCommunication);
×
99
    } catch (error) {
100
      showNotification({
×
101
        title: 'Error',
102
        message: normalizeErrorString(error),
103
        color: 'red',
104
      });
105
    }
106
  };
107

108
  return (
69✔
109
    <Modal opened={opened} onClose={onClose} title="New Message" size="md">
110
      <Stack gap="xl">
111
        <Stack gap={0}>
112
          <Text fw={500}>Patient</Text>
113
          {allowPatientSelection && <Text c="dimmed">Select a patient</Text>}
73✔
114

115
          <ResourceInput
116
            resourceType="Patient"
117
            name="patient"
118
            required={true}
119
            defaultValue={patient}
120
            disabled={!allowPatientSelection && !!patient}
134✔
121
            onChange={(value) => {
122
              setPatient(value ? createReference(value) : undefined);
×
123
            }}
124
          />
125
        </Stack>
126

127
        <Stack gap={0}>
128
          <Text fw={500}>Practitioner (optional)</Text>
129
          <Text c="dimmed">Select one or more practitioners</Text>
130

131
          <QuestionnaireForm
132
            questionnaire={questionnaire}
133
            questionnaireResponse={initialResponse}
134
            excludeButtons={true}
135
            onChange={(value: QuestionnaireResponse) => {
136
              const references =
137
                value.item?.[0].answer
13!
138
                  ?.map((item) => item.valueReference)
13✔
139
                  .filter((ref): ref is Reference<Practitioner> => ref !== undefined) ?? [];
13✔
140
              setPractitioners(references);
13✔
141
            }}
142
          />
143
        </Stack>
144

145
        <Stack gap={0}>
146
          <Text fw={500}>Topic (optional)</Text>
147
          <Text c="dimmed">Enter a topic for the message</Text>
148

149
          <TextInput placeholder="Enter your topic" value={topic} onChange={(e) => setTopic(e.target.value)} />
×
150
        </Stack>
151

152
        <Button onClick={handleSubmit}>Next</Button>
153
      </Stack>
154
    </Modal>
155
  );
156
};
157

158
const questionnaire: Questionnaire = {
3✔
159
  resourceType: 'Questionnaire',
160
  status: 'active',
161
  item: [
162
    {
163
      linkId: 'q1',
164
      type: 'reference',
165
      repeats: true,
166
      extension: [
167
        {
168
          url: `${HTTP_HL7_ORG}/fhir/StructureDefinition/questionnaire-referenceResource`,
169
          valueCodeableConcept: {
170
            coding: [
171
              {
172
                system: `${HTTP_HL7_ORG}/fhir/fhir-types`,
173
                display: 'Practitioner',
174
                code: 'Practitioner',
175
              },
176
            ],
177
          },
178
        },
179
      ],
180
    },
181
  ],
182
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc