@visns-studio/visns-components 5.15.6 → 5.15.8
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.
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Document,
|
|
4
|
+
Page,
|
|
5
|
+
View,
|
|
6
|
+
Text,
|
|
7
|
+
Image,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
} from '@react-pdf/renderer';
|
|
10
|
+
import { getCameraById } from '../data/verkadaCameras';
|
|
11
|
+
|
|
12
|
+
// Default neutral professional color theme
|
|
13
|
+
const defaultColors = {
|
|
14
|
+
primary: '#2d3748', // Professional dark gray
|
|
15
|
+
secondary: '#4a5568', // Medium gray
|
|
16
|
+
background: '#f7fafc', // Light gray background
|
|
17
|
+
paragraph: '#4a5568', // Medium gray text
|
|
18
|
+
white: '#ffffff',
|
|
19
|
+
border: '#e2e8f0',
|
|
20
|
+
accent: '#3182ce', // Professional blue accent
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Function to get colors - prioritize neutral professional colors over CSS variables
|
|
24
|
+
const getColors = (customColors = {}) => {
|
|
25
|
+
// Use the neutral professional colors as the primary choice
|
|
26
|
+
return {
|
|
27
|
+
...defaultColors,
|
|
28
|
+
...customColors, // Allow custom override if needed
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Function to create styles with dynamic colors
|
|
33
|
+
const createStyles = (colors) => StyleSheet.create({
|
|
34
|
+
// Page styles
|
|
35
|
+
page: {
|
|
36
|
+
fontFamily: 'Helvetica',
|
|
37
|
+
fontSize: 9,
|
|
38
|
+
padding: 15,
|
|
39
|
+
backgroundColor: '#ffffff',
|
|
40
|
+
},
|
|
41
|
+
landscapePage: {
|
|
42
|
+
fontFamily: 'Helvetica',
|
|
43
|
+
fontSize: 10,
|
|
44
|
+
padding: 15,
|
|
45
|
+
backgroundColor: '#ffffff',
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// Compact header styles for site map page
|
|
49
|
+
compactHeader: {
|
|
50
|
+
backgroundColor: colors.primary,
|
|
51
|
+
color: colors.white,
|
|
52
|
+
padding: 8,
|
|
53
|
+
marginBottom: 8,
|
|
54
|
+
textAlign: 'center',
|
|
55
|
+
},
|
|
56
|
+
compactHeaderTitle: {
|
|
57
|
+
fontSize: 16,
|
|
58
|
+
fontWeight: 700,
|
|
59
|
+
margin: 0,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Full header styles for details page
|
|
63
|
+
header: {
|
|
64
|
+
backgroundColor: colors.primary,
|
|
65
|
+
color: colors.white,
|
|
66
|
+
padding: 15,
|
|
67
|
+
marginBottom: 15,
|
|
68
|
+
borderRadius: 0,
|
|
69
|
+
},
|
|
70
|
+
headerTitle: {
|
|
71
|
+
fontSize: 20,
|
|
72
|
+
fontWeight: 700,
|
|
73
|
+
textAlign: 'center',
|
|
74
|
+
marginBottom: 5,
|
|
75
|
+
},
|
|
76
|
+
headerSubtitle: {
|
|
77
|
+
fontSize: 10,
|
|
78
|
+
textAlign: 'center',
|
|
79
|
+
opacity: 0.9,
|
|
80
|
+
marginBottom: 8,
|
|
81
|
+
},
|
|
82
|
+
headerInfo: {
|
|
83
|
+
flexDirection: 'row',
|
|
84
|
+
justifyContent: 'space-between',
|
|
85
|
+
fontSize: 8,
|
|
86
|
+
borderTop: '1px solid rgba(255,255,255,0.2)',
|
|
87
|
+
paddingTop: 8,
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Map page styles - leave room for header text
|
|
91
|
+
mapContainer: {
|
|
92
|
+
flex: 1,
|
|
93
|
+
alignItems: 'center',
|
|
94
|
+
justifyContent: 'center',
|
|
95
|
+
backgroundColor: colors.background,
|
|
96
|
+
borderRadius: 4,
|
|
97
|
+
border: `1px solid ${colors.border}`,
|
|
98
|
+
minHeight: '88%',
|
|
99
|
+
},
|
|
100
|
+
mapImage: {
|
|
101
|
+
maxWidth: '100%',
|
|
102
|
+
maxHeight: '100%',
|
|
103
|
+
objectFit: 'contain',
|
|
104
|
+
},
|
|
105
|
+
mapPlaceholder: {
|
|
106
|
+
padding: 40,
|
|
107
|
+
textAlign: 'center',
|
|
108
|
+
color: '#6b7280',
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// Camera details styles - ultra compact
|
|
112
|
+
detailsHeader: {
|
|
113
|
+
fontSize: 12,
|
|
114
|
+
fontWeight: 700,
|
|
115
|
+
color: colors.primary,
|
|
116
|
+
marginBottom: 8,
|
|
117
|
+
textAlign: 'center',
|
|
118
|
+
},
|
|
119
|
+
cameraCard: {
|
|
120
|
+
marginBottom: 6,
|
|
121
|
+
border: `1px solid ${colors.border}`,
|
|
122
|
+
borderRadius: 3,
|
|
123
|
+
overflow: 'hidden',
|
|
124
|
+
backgroundColor: colors.white,
|
|
125
|
+
},
|
|
126
|
+
cameraHeader: {
|
|
127
|
+
backgroundColor: colors.primary,
|
|
128
|
+
color: colors.white,
|
|
129
|
+
padding: 6,
|
|
130
|
+
flexDirection: 'row',
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
},
|
|
133
|
+
cameraNumber: {
|
|
134
|
+
width: 24,
|
|
135
|
+
height: 24,
|
|
136
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
137
|
+
borderRadius: 12,
|
|
138
|
+
alignItems: 'center',
|
|
139
|
+
justifyContent: 'center',
|
|
140
|
+
marginRight: 8,
|
|
141
|
+
border: '1px solid rgba(255,255,255,0.3)',
|
|
142
|
+
},
|
|
143
|
+
cameraNumberText: {
|
|
144
|
+
fontSize: 11,
|
|
145
|
+
fontWeight: 700,
|
|
146
|
+
color: colors.white,
|
|
147
|
+
},
|
|
148
|
+
cameraTitle: {
|
|
149
|
+
flex: 1,
|
|
150
|
+
},
|
|
151
|
+
cameraTitleText: {
|
|
152
|
+
fontSize: 12,
|
|
153
|
+
fontWeight: 700,
|
|
154
|
+
color: colors.white,
|
|
155
|
+
marginBottom: 1,
|
|
156
|
+
},
|
|
157
|
+
cameraModelText: {
|
|
158
|
+
fontSize: 9,
|
|
159
|
+
color: colors.white,
|
|
160
|
+
opacity: 0.9,
|
|
161
|
+
},
|
|
162
|
+
ptzBadge: {
|
|
163
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
164
|
+
border: '1px solid rgba(255,255,255,0.3)',
|
|
165
|
+
borderRadius: 12,
|
|
166
|
+
paddingHorizontal: 8,
|
|
167
|
+
paddingVertical: 4,
|
|
168
|
+
},
|
|
169
|
+
ptzText: {
|
|
170
|
+
fontSize: 10,
|
|
171
|
+
fontWeight: 600,
|
|
172
|
+
color: colors.white,
|
|
173
|
+
},
|
|
174
|
+
environmentIndicator: {
|
|
175
|
+
width: 12,
|
|
176
|
+
height: 12,
|
|
177
|
+
borderRadius: 6,
|
|
178
|
+
marginLeft: 8,
|
|
179
|
+
border: '2px solid rgba(255,255,255,0.8)',
|
|
180
|
+
},
|
|
181
|
+
environmentIndoorIndicator: {
|
|
182
|
+
backgroundColor: '#22c55e', // Green for indoor
|
|
183
|
+
},
|
|
184
|
+
environmentOutdoorIndicator: {
|
|
185
|
+
backgroundColor: '#dc2626', // Red for outdoor
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// Camera details content - ultra compact
|
|
189
|
+
cameraContent: {
|
|
190
|
+
padding: 4,
|
|
191
|
+
flexDirection: 'row',
|
|
192
|
+
gap: 6,
|
|
193
|
+
},
|
|
194
|
+
cameraImageContainer: {
|
|
195
|
+
width: 60,
|
|
196
|
+
height: 45,
|
|
197
|
+
backgroundColor: colors.background,
|
|
198
|
+
borderRadius: 3,
|
|
199
|
+
border: `1px solid ${colors.border}`,
|
|
200
|
+
alignItems: 'center',
|
|
201
|
+
justifyContent: 'center',
|
|
202
|
+
flexShrink: 0,
|
|
203
|
+
},
|
|
204
|
+
cameraImage: {
|
|
205
|
+
maxWidth: '100%',
|
|
206
|
+
maxHeight: '100%',
|
|
207
|
+
objectFit: 'contain',
|
|
208
|
+
},
|
|
209
|
+
cameraImagePlaceholder: {
|
|
210
|
+
fontSize: 8,
|
|
211
|
+
color: colors.paragraph,
|
|
212
|
+
textAlign: 'center',
|
|
213
|
+
},
|
|
214
|
+
cameraDetailsContainer: {
|
|
215
|
+
flex: 1,
|
|
216
|
+
},
|
|
217
|
+
specsGrid: {
|
|
218
|
+
flexDirection: 'row',
|
|
219
|
+
flexWrap: 'wrap',
|
|
220
|
+
marginBottom: 4,
|
|
221
|
+
gap: 3,
|
|
222
|
+
},
|
|
223
|
+
specItem: {
|
|
224
|
+
backgroundColor: colors.background,
|
|
225
|
+
padding: 3,
|
|
226
|
+
borderRadius: 2,
|
|
227
|
+
borderLeft: `2px solid ${colors.accent}`,
|
|
228
|
+
width: '48%',
|
|
229
|
+
},
|
|
230
|
+
specLabel: {
|
|
231
|
+
fontSize: 6,
|
|
232
|
+
color: colors.paragraph,
|
|
233
|
+
fontWeight: 600,
|
|
234
|
+
textTransform: 'uppercase',
|
|
235
|
+
marginBottom: 1,
|
|
236
|
+
},
|
|
237
|
+
specValue: {
|
|
238
|
+
fontSize: 8,
|
|
239
|
+
color: colors.primary,
|
|
240
|
+
fontWeight: 600,
|
|
241
|
+
},
|
|
242
|
+
installationDetails: {
|
|
243
|
+
backgroundColor: colors.white,
|
|
244
|
+
border: `1px solid ${colors.primary}`,
|
|
245
|
+
borderRadius: 3,
|
|
246
|
+
padding: 4,
|
|
247
|
+
marginBottom: 4,
|
|
248
|
+
},
|
|
249
|
+
installationTitle: {
|
|
250
|
+
fontSize: 8,
|
|
251
|
+
color: colors.paragraph,
|
|
252
|
+
fontWeight: 600,
|
|
253
|
+
textTransform: 'uppercase',
|
|
254
|
+
textAlign: 'center',
|
|
255
|
+
marginBottom: 4,
|
|
256
|
+
},
|
|
257
|
+
coordinatesGrid: {
|
|
258
|
+
flexDirection: 'row',
|
|
259
|
+
gap: 4,
|
|
260
|
+
},
|
|
261
|
+
coordinateItem: {
|
|
262
|
+
backgroundColor: colors.background,
|
|
263
|
+
padding: 3,
|
|
264
|
+
borderRadius: 2,
|
|
265
|
+
border: `1px solid ${colors.secondary}`,
|
|
266
|
+
flex: 1,
|
|
267
|
+
},
|
|
268
|
+
coordinateLabel: {
|
|
269
|
+
fontSize: 7,
|
|
270
|
+
color: colors.paragraph,
|
|
271
|
+
fontWeight: 600,
|
|
272
|
+
textTransform: 'uppercase',
|
|
273
|
+
marginBottom: 1,
|
|
274
|
+
},
|
|
275
|
+
coordinateValue: {
|
|
276
|
+
fontSize: 8,
|
|
277
|
+
color: colors.primary,
|
|
278
|
+
fontWeight: 600,
|
|
279
|
+
fontFamily: 'Helvetica',
|
|
280
|
+
},
|
|
281
|
+
featuresContainer: {
|
|
282
|
+
backgroundColor: colors.background,
|
|
283
|
+
padding: 3,
|
|
284
|
+
borderRadius: 2,
|
|
285
|
+
border: `1px solid ${colors.border}`,
|
|
286
|
+
},
|
|
287
|
+
featuresTitle: {
|
|
288
|
+
fontSize: 7,
|
|
289
|
+
color: colors.paragraph,
|
|
290
|
+
fontWeight: 600,
|
|
291
|
+
textTransform: 'uppercase',
|
|
292
|
+
textAlign: 'center',
|
|
293
|
+
marginBottom: 3,
|
|
294
|
+
},
|
|
295
|
+
featuresGrid: {
|
|
296
|
+
flexDirection: 'row',
|
|
297
|
+
flexWrap: 'wrap',
|
|
298
|
+
gap: 2,
|
|
299
|
+
justifyContent: 'center',
|
|
300
|
+
},
|
|
301
|
+
featureTag: {
|
|
302
|
+
backgroundColor: colors.white,
|
|
303
|
+
color: colors.accent,
|
|
304
|
+
border: `1px solid ${colors.accent}`,
|
|
305
|
+
borderRadius: 8,
|
|
306
|
+
paddingHorizontal: 4,
|
|
307
|
+
paddingVertical: 2,
|
|
308
|
+
fontSize: 7,
|
|
309
|
+
fontWeight: 600,
|
|
310
|
+
},
|
|
311
|
+
noModelWarning: {
|
|
312
|
+
backgroundColor: '#fef2f2',
|
|
313
|
+
border: '1px solid #fca5a5',
|
|
314
|
+
borderRadius: 8,
|
|
315
|
+
padding: 16,
|
|
316
|
+
textAlign: 'center',
|
|
317
|
+
margin: '8px 0',
|
|
318
|
+
},
|
|
319
|
+
warningTitle: {
|
|
320
|
+
fontSize: 12,
|
|
321
|
+
fontWeight: 600,
|
|
322
|
+
color: '#dc2626',
|
|
323
|
+
marginBottom: 4,
|
|
324
|
+
},
|
|
325
|
+
warningText: {
|
|
326
|
+
fontSize: 10,
|
|
327
|
+
color: '#7f1d1d',
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
// Footer styles
|
|
331
|
+
footer: {
|
|
332
|
+
marginTop: 8,
|
|
333
|
+
padding: 6,
|
|
334
|
+
backgroundColor: colors.background,
|
|
335
|
+
borderRadius: 3,
|
|
336
|
+
border: `1px solid ${colors.border}`,
|
|
337
|
+
},
|
|
338
|
+
footerTitle: {
|
|
339
|
+
fontSize: 9,
|
|
340
|
+
fontWeight: 700,
|
|
341
|
+
color: colors.primary,
|
|
342
|
+
textAlign: 'center',
|
|
343
|
+
marginBottom: 4,
|
|
344
|
+
},
|
|
345
|
+
legendContainer: {
|
|
346
|
+
flexDirection: 'row',
|
|
347
|
+
justifyContent: 'center',
|
|
348
|
+
gap: 6,
|
|
349
|
+
marginBottom: 4,
|
|
350
|
+
},
|
|
351
|
+
legendItem: {
|
|
352
|
+
flexDirection: 'row',
|
|
353
|
+
alignItems: 'center',
|
|
354
|
+
gap: 3,
|
|
355
|
+
backgroundColor: colors.white,
|
|
356
|
+
padding: '3px 5px',
|
|
357
|
+
borderRadius: 2,
|
|
358
|
+
border: `1px solid ${colors.border}`,
|
|
359
|
+
},
|
|
360
|
+
legendDot: {
|
|
361
|
+
width: 10,
|
|
362
|
+
height: 10,
|
|
363
|
+
borderRadius: 5,
|
|
364
|
+
border: `1px solid ${colors.white}`,
|
|
365
|
+
},
|
|
366
|
+
legendDotIndoor: {
|
|
367
|
+
backgroundColor: '#22c55e', // Green for indoor
|
|
368
|
+
},
|
|
369
|
+
legendDotOutdoor: {
|
|
370
|
+
backgroundColor: '#dc2626', // Red for outdoor
|
|
371
|
+
},
|
|
372
|
+
legendDotPtz: {
|
|
373
|
+
backgroundColor: '#6b7280',
|
|
374
|
+
border: '2px solid #ef4444',
|
|
375
|
+
},
|
|
376
|
+
legendText: {
|
|
377
|
+
fontSize: 7,
|
|
378
|
+
color: colors.paragraph,
|
|
379
|
+
fontWeight: 600,
|
|
380
|
+
},
|
|
381
|
+
notesContainer: {
|
|
382
|
+
backgroundColor: colors.white,
|
|
383
|
+
padding: 6,
|
|
384
|
+
borderRadius: 3,
|
|
385
|
+
border: `1px solid ${colors.border}`,
|
|
386
|
+
},
|
|
387
|
+
notesTitle: {
|
|
388
|
+
fontSize: 9,
|
|
389
|
+
fontWeight: 700,
|
|
390
|
+
color: colors.primary,
|
|
391
|
+
textAlign: 'center',
|
|
392
|
+
marginBottom: 4,
|
|
393
|
+
},
|
|
394
|
+
noteItem: {
|
|
395
|
+
flexDirection: 'row',
|
|
396
|
+
marginBottom: 3,
|
|
397
|
+
backgroundColor: colors.white,
|
|
398
|
+
padding: 3,
|
|
399
|
+
borderRadius: 2,
|
|
400
|
+
borderLeft: `2px solid ${colors.secondary}`,
|
|
401
|
+
},
|
|
402
|
+
noteText: {
|
|
403
|
+
fontSize: 7,
|
|
404
|
+
color: colors.paragraph,
|
|
405
|
+
lineHeight: 1.3,
|
|
406
|
+
flex: 1,
|
|
407
|
+
},
|
|
408
|
+
disclaimer: {
|
|
409
|
+
textAlign: 'center',
|
|
410
|
+
marginTop: 4,
|
|
411
|
+
paddingTop: 3,
|
|
412
|
+
borderTop: `1px solid ${colors.border}`,
|
|
413
|
+
},
|
|
414
|
+
disclaimerText: {
|
|
415
|
+
fontSize: 6,
|
|
416
|
+
color: colors.paragraph,
|
|
417
|
+
},
|
|
418
|
+
reportId: {
|
|
419
|
+
fontSize: 6,
|
|
420
|
+
color: colors.paragraph,
|
|
421
|
+
marginBottom: 2,
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Camera Card Component
|
|
426
|
+
const CameraCard = ({ camera, number, mode, styles }) => {
|
|
427
|
+
const verkadaCamera = camera.verkadaModel
|
|
428
|
+
? getCameraById(camera.verkadaModel)
|
|
429
|
+
: null;
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<View style={styles.cameraCard}>
|
|
433
|
+
{/* Camera Header */}
|
|
434
|
+
<View style={styles.cameraHeader}>
|
|
435
|
+
<View style={styles.cameraNumber}>
|
|
436
|
+
<Text style={styles.cameraNumberText}>{number}</Text>
|
|
437
|
+
</View>
|
|
438
|
+
<View style={styles.cameraTitle}>
|
|
439
|
+
<Text style={styles.cameraTitleText}>
|
|
440
|
+
Camera {number}
|
|
441
|
+
</Text>
|
|
442
|
+
<Text style={styles.cameraModelText}>
|
|
443
|
+
{verkadaCamera ? verkadaCamera.name : 'Model Not Selected'}
|
|
444
|
+
</Text>
|
|
445
|
+
</View>
|
|
446
|
+
{verkadaCamera?.environment && (
|
|
447
|
+
<View
|
|
448
|
+
style={[
|
|
449
|
+
styles.environmentIndicator,
|
|
450
|
+
verkadaCamera.environment === 'indoor'
|
|
451
|
+
? styles.environmentIndoorIndicator
|
|
452
|
+
: styles.environmentOutdoorIndicator,
|
|
453
|
+
]}
|
|
454
|
+
/>
|
|
455
|
+
)}
|
|
456
|
+
{verkadaCamera?.ptz && (
|
|
457
|
+
<View style={styles.ptzBadge}>
|
|
458
|
+
<Text style={styles.ptzText}>PTZ</Text>
|
|
459
|
+
</View>
|
|
460
|
+
)}
|
|
461
|
+
</View>
|
|
462
|
+
|
|
463
|
+
{/* Camera Content */}
|
|
464
|
+
<View style={styles.cameraContent}>
|
|
465
|
+
{/* Camera Image */}
|
|
466
|
+
<View style={styles.cameraImageContainer}>
|
|
467
|
+
{verkadaCamera?.image ? (
|
|
468
|
+
<Image src={verkadaCamera.image} style={styles.cameraImage} />
|
|
469
|
+
) : (
|
|
470
|
+
<Text style={styles.cameraImagePlaceholder}>
|
|
471
|
+
No Image{'\n'}Available
|
|
472
|
+
</Text>
|
|
473
|
+
)}
|
|
474
|
+
</View>
|
|
475
|
+
|
|
476
|
+
{/* Camera Details */}
|
|
477
|
+
<View style={styles.cameraDetailsContainer}>
|
|
478
|
+
{verkadaCamera ? (
|
|
479
|
+
<View>
|
|
480
|
+
{/* Specifications Grid */}
|
|
481
|
+
<View style={styles.specsGrid}>
|
|
482
|
+
<View style={styles.specItem}>
|
|
483
|
+
<Text style={styles.specLabel}>Resolution</Text>
|
|
484
|
+
<Text style={styles.specValue}>
|
|
485
|
+
{verkadaCamera.resolution}
|
|
486
|
+
</Text>
|
|
487
|
+
</View>
|
|
488
|
+
<View style={styles.specItem}>
|
|
489
|
+
<Text style={styles.specLabel}>Field of View</Text>
|
|
490
|
+
<Text style={styles.specValue}>
|
|
491
|
+
{verkadaCamera.fov}°
|
|
492
|
+
</Text>
|
|
493
|
+
</View>
|
|
494
|
+
<View style={styles.specItem}>
|
|
495
|
+
<Text style={styles.specLabel}>Environment</Text>
|
|
496
|
+
<Text style={styles.specValue}>
|
|
497
|
+
{verkadaCamera.environment}
|
|
498
|
+
</Text>
|
|
499
|
+
</View>
|
|
500
|
+
<View style={styles.specItem}>
|
|
501
|
+
<Text style={styles.specLabel}>IR Range</Text>
|
|
502
|
+
<Text style={styles.specValue}>
|
|
503
|
+
{verkadaCamera.irRange}m
|
|
504
|
+
</Text>
|
|
505
|
+
</View>
|
|
506
|
+
</View>
|
|
507
|
+
|
|
508
|
+
{/* Features */}
|
|
509
|
+
{verkadaCamera.features && verkadaCamera.features.length > 0 && (
|
|
510
|
+
<View style={styles.featuresContainer}>
|
|
511
|
+
<Text style={styles.featuresTitle}>Key Features</Text>
|
|
512
|
+
<View style={styles.featuresGrid}>
|
|
513
|
+
{verkadaCamera.features.slice(0, 6).map((feature, index) => (
|
|
514
|
+
<View key={index} style={styles.featureTag}>
|
|
515
|
+
<Text>{feature}</Text>
|
|
516
|
+
</View>
|
|
517
|
+
))}
|
|
518
|
+
{verkadaCamera.features.length > 6 && (
|
|
519
|
+
<View style={styles.featureTag}>
|
|
520
|
+
<Text>+{verkadaCamera.features.length - 6} more</Text>
|
|
521
|
+
</View>
|
|
522
|
+
)}
|
|
523
|
+
</View>
|
|
524
|
+
</View>
|
|
525
|
+
)}
|
|
526
|
+
</View>
|
|
527
|
+
) : (
|
|
528
|
+
<View style={styles.noModelWarning}>
|
|
529
|
+
<Text style={styles.warningTitle}>⚠ No Camera Model Selected</Text>
|
|
530
|
+
<Text style={styles.warningText}>
|
|
531
|
+
Please configure this camera before installation
|
|
532
|
+
</Text>
|
|
533
|
+
</View>
|
|
534
|
+
)}
|
|
535
|
+
</View>
|
|
536
|
+
</View>
|
|
537
|
+
</View>
|
|
538
|
+
);
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// Main PDF Document Component
|
|
542
|
+
const CameraReportPDF = ({ cameras, mapImageData, mode, customColors }) => {
|
|
543
|
+
const currentDate = new Date();
|
|
544
|
+
const reportId = Date.now().toString(36).toUpperCase();
|
|
545
|
+
const colors = getColors(customColors);
|
|
546
|
+
const styles = createStyles(colors);
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
<Document
|
|
550
|
+
title="Security Camera Installation Report"
|
|
551
|
+
author="VISNS Studio"
|
|
552
|
+
subject="Camera Installation Layout and Specifications"
|
|
553
|
+
creator="VISNS Components"
|
|
554
|
+
>
|
|
555
|
+
{/* Page 1: Site Map */}
|
|
556
|
+
<Page size="A4" orientation="landscape" style={styles.landscapePage}>
|
|
557
|
+
{/* Compact Header */}
|
|
558
|
+
<View style={styles.compactHeader}>
|
|
559
|
+
<Text style={styles.compactHeaderTitle}>
|
|
560
|
+
Site Map Layout
|
|
561
|
+
</Text>
|
|
562
|
+
</View>
|
|
563
|
+
|
|
564
|
+
{/* Map Container - maximized */}
|
|
565
|
+
<View style={styles.mapContainer}>
|
|
566
|
+
{mapImageData ? (
|
|
567
|
+
<Image src={mapImageData} style={styles.mapImage} />
|
|
568
|
+
) : (
|
|
569
|
+
<View style={styles.mapPlaceholder}>
|
|
570
|
+
<Text>Site map will be displayed here</Text>
|
|
571
|
+
<Text>Please upload an image to include the site layout</Text>
|
|
572
|
+
</View>
|
|
573
|
+
)}
|
|
574
|
+
</View>
|
|
575
|
+
</Page>
|
|
576
|
+
|
|
577
|
+
{/* Page 2+: Camera Details */}
|
|
578
|
+
<Page size="A4" style={styles.page}>
|
|
579
|
+
<Text style={styles.detailsHeader}>Camera Specifications & Details</Text>
|
|
580
|
+
|
|
581
|
+
{cameras.map((camera, index) => (
|
|
582
|
+
<CameraCard
|
|
583
|
+
key={camera.id}
|
|
584
|
+
camera={camera}
|
|
585
|
+
number={index + 1}
|
|
586
|
+
mode={mode}
|
|
587
|
+
styles={styles}
|
|
588
|
+
/>
|
|
589
|
+
))}
|
|
590
|
+
|
|
591
|
+
{/* Footer with Legend */}
|
|
592
|
+
<View style={styles.footer}>
|
|
593
|
+
<Text style={styles.footerTitle}>Camera Legend</Text>
|
|
594
|
+
|
|
595
|
+
{/* Legend */}
|
|
596
|
+
<View style={styles.legendContainer}>
|
|
597
|
+
<View style={styles.legendItem}>
|
|
598
|
+
<View style={[styles.legendDot, styles.legendDotIndoor]} />
|
|
599
|
+
<Text style={styles.legendText}>Indoor</Text>
|
|
600
|
+
</View>
|
|
601
|
+
<View style={styles.legendItem}>
|
|
602
|
+
<View style={[styles.legendDot, styles.legendDotOutdoor]} />
|
|
603
|
+
<Text style={styles.legendText}>Outdoor</Text>
|
|
604
|
+
</View>
|
|
605
|
+
{cameras.some(c => getCameraById(c.verkadaModel)?.ptz) && (
|
|
606
|
+
<View style={styles.legendItem}>
|
|
607
|
+
<View style={[styles.legendDot, styles.legendDotPtz]} />
|
|
608
|
+
<Text style={styles.legendText}>PTZ</Text>
|
|
609
|
+
</View>
|
|
610
|
+
)}
|
|
611
|
+
</View>
|
|
612
|
+
|
|
613
|
+
{/* Disclaimer */}
|
|
614
|
+
<View style={styles.disclaimer}>
|
|
615
|
+
<Text style={styles.disclaimerText}>
|
|
616
|
+
Report ID: {reportId} | Generated automatically
|
|
617
|
+
</Text>
|
|
618
|
+
</View>
|
|
619
|
+
</View>
|
|
620
|
+
</Page>
|
|
621
|
+
</Document>
|
|
622
|
+
);
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
export default CameraReportPDF;
|