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

inventree / InvenTree / 8535197168

03 Apr 2024 07:58AM CUT coverage: 90.342% (-2.2%) from 92.496%
8535197168

Pull #6881

github

web-flow
Merge a1722af93 into 6be2ede5e
Pull Request #6881: [PUI] Add coverage testing

227 of 631 branches covered (35.97%)

Branch coverage included in aggregate %.

0 of 1 new or added line in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

31971 of 35009 relevant lines covered (91.32%)

1.12 hits per line

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

3.95
/src/frontend/src/components/modals/QrCodeModal.tsx
1
import { Trans, t } from '@lingui/macro';
2
import {
3
  Badge,
4
  Button,
5
  Container,
6
  Group,
7
  ScrollArea,
8
  Space,
9
  Stack,
10
  Text
11
} from '@mantine/core';
12
import {
13
  useDocumentVisibility,
14
  useListState,
15
  useLocalStorage
16
} from '@mantine/hooks';
17
import { ContextModalProps } from '@mantine/modals';
18
import { showNotification } from '@mantine/notifications';
19
import { IconX } from '@tabler/icons-react';
20
import { Html5Qrcode } from 'html5-qrcode';
21
import { CameraDevice } from 'html5-qrcode/camera/core';
22
import { Html5QrcodeResult } from 'html5-qrcode/core';
23
import { useEffect, useState } from 'react';
24

25
import { api } from '../../App';
26
import { ApiEndpoints } from '../../enums/ApiEndpoints';
27
import { apiUrl } from '../../states/ApiState';
28

29
export function QrCodeModal({
3✔
30
  context,
31
  id
32
}: ContextModalProps<{ modalBody: string }>) {
×
33
  const [qrCodeScanner, setQrCodeScanner] = useState<Html5Qrcode | null>(null);
×
34
  const [camId, setCamId] = useLocalStorage<CameraDevice | null>({
×
35
    key: 'camId',
36
    defaultValue: null
37
  });
38
  const [ScanningEnabled, setIsScanning] = useState<boolean>(false);
×
39
  const [wasAutoPaused, setWasAutoPaused] = useState<boolean>(false);
×
40
  const documentState = useDocumentVisibility();
×
41

42
  const [values, handlers] = useListState<string>([]);
3✔
43

44
  // Mount QR code once we are loaded
45
  useEffect(() => {
×
46
    setQrCodeScanner(new Html5Qrcode('reader'));
×
47
  }, []);
48

49
  // Stop/star when leaving or reentering page
50
  useEffect(() => {
×
51
    if (ScanningEnabled && documentState === 'hidden') {
×
52
      stopScanning();
×
53
      setWasAutoPaused(true);
×
54
    } else if (wasAutoPaused && documentState === 'visible') {
×
55
      startScanning();
×
56
      setWasAutoPaused(false);
×
57
    }
58
  }, [documentState]);
59

60
  // Scanner functions
61
  function onScanSuccess(
62
    decodedText: string,
63
    decodedResult: Html5QrcodeResult
64
  ) {
65
    qrCodeScanner?.pause();
×
66

67
    handlers.append(decodedText);
×
68
    api
×
69
      .post(apiUrl(ApiEndpoints.barcode), { barcode: decodedText })
70
      .then((response) => {
71
        showNotification({
×
72
          title: response.data?.success || t`Unknown response`,
×
73
          message: JSON.stringify(response.data),
74
          color: response.data?.success ? 'teal' : 'red'
×
75
        });
76
        if (response.data?.url) {
×
77
          window.location.href = response.data.url;
×
78
        }
79
      });
80

81
    qrCodeScanner?.resume();
×
82
  }
83

84
  function onScanFailure(error: string) {
85
    if (
×
86
      error !=
87
      'QR code parse error, error = NotFoundException: No MultiFormat Readers were able to detect the code.'
88
    ) {
89
      console.warn(`Code scan error = ${error}`);
×
90
    }
91
  }
92

93
  function selectCamera() {
94
    Html5Qrcode.getCameras()
×
95
      .then((devices) => {
96
        if (devices?.length) {
×
97
          setCamId(devices[0]);
×
98
        }
99
      })
100
      .catch((err) => {
101
        showNotification({
×
102
          title: t`Error while getting camera`,
103
          message: err,
104
          color: 'red',
105
          icon: <IconX />
106
        });
107
      });
108
  }
109

110
  function startScanning() {
111
    if (camId && qrCodeScanner) {
×
112
      qrCodeScanner
×
113
        .start(
114
          camId.id,
115
          { fps: 10, qrbox: { width: 250, height: 250 } },
116
          (decodedText, decodedResult) => {
117
            onScanSuccess(decodedText, decodedResult);
×
118
          },
119
          (errorMessage) => {
120
            onScanFailure(errorMessage);
×
121
          }
122
        )
123
        .catch((err: string) => {
124
          showNotification({
×
125
            title: t`Error while scanning`,
126
            message: err,
127
            color: 'red',
128
            icon: <IconX />
129
          });
130
        });
131
      setIsScanning(true);
×
132
    }
133
  }
134

135
  function stopScanning() {
136
    if (qrCodeScanner && ScanningEnabled) {
×
137
      qrCodeScanner.stop().catch((err: string) => {
×
138
        showNotification({
×
139
          title: t`Error while stopping`,
140
          message: err,
141
          color: 'red',
142
          icon: <IconX />
143
        });
144
      });
145
      setIsScanning(false);
×
146
    }
147
  }
148

149
  return (
×
150
    <Stack>
151
      <Group>
152
        <Text size="sm">{camId?.label}</Text>
153
        <Space sx={{ flex: 1 }} />
154
        <Badge>{ScanningEnabled ? t`Scanning` : t`Not scanning`}</Badge>
×
155
      </Group>
156
      <Container px={0} id="reader" w={'100%'} mih="300px" />
157
      {!camId ? (
×
158
        <Button onClick={() => selectCamera()}>
×
159
          <Trans>Select Camera</Trans>
160
        </Button>
161
      ) : (
162
        <>
163
          <Group>
164
            <Button
165
              sx={{ flex: 1 }}
166
              onClick={() => startScanning()}
×
167
              disabled={camId != undefined && ScanningEnabled}
×
168
            >
169
              <Trans>Start scanning</Trans>
170
            </Button>
171
            <Button
172
              sx={{ flex: 1 }}
173
              onClick={() => stopScanning()}
×
174
              disabled={!ScanningEnabled}
175
            >
176
              <Trans>Stop scanning</Trans>
177
            </Button>
178
          </Group>
179
          {values.length == 0 ? (
×
180
            <Text color={'grey'}>
181
              <Trans>No scans yet!</Trans>
182
            </Text>
183
          ) : (
184
            <ScrollArea sx={{ height: 200 }} type="auto" offsetScrollbars>
185
              {values.map((value, index) => (
186
                <div key={index}>{value}</div>
×
187
              ))}
188
            </ScrollArea>
189
          )}
190
        </>
191
      )}
192
      <Button
193
        fullWidth
194
        mt="md"
195
        color="red"
196
        onClick={() => {
197
          stopScanning();
×
198
          context.closeModal(id);
×
199
        }}
200
      >
201
        <Trans>Close modal</Trans>
202
      </Button>
203
    </Stack>
204
  );
205
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc