@eturnity/eturnity_reusable_components 7.51.3 → 7.51.4

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,981 @@
1
+ <template>
2
+ <PageContainer>
3
+ <PageTitleContainer data-id="string_design_expand_collapse_section">
4
+ <SectionTitleText>{{ $gettext('inverters') }}</SectionTitleText>
5
+ <ButtonIcon
6
+ custom-color="black"
7
+ :icon-name="hasExpandedSection ? 'collapse_all' : 'expand_all'"
8
+ :text="$gettext(hasExpandedSection ? 'collapse_all' : 'expand_all')"
9
+ @click="toggleAllSections"
10
+ />
11
+ </PageTitleContainer>
12
+ <SectionContainer
13
+ v-for="(item, index) in dataListToDisplay"
14
+ :key="item.inverterId"
15
+ >
16
+ <TopContainer>
17
+ <LeftContainer>
18
+ <TitleContainer>
19
+ <IconWrapper
20
+ v-if="item.type != 'optimizer'"
21
+ size="32px"
22
+ @click="toggleSection(item.inverterId)"
23
+ >
24
+ <RCIcon
25
+ v-if="isItemCollapsible(item)"
26
+ color="white"
27
+ :name="isExpanded(item.inverterId) ? 'arrow_up' : 'arrow_down'"
28
+ size="10px"
29
+ />
30
+ <IconPlaceholder v-else />
31
+ </IconWrapper>
32
+ <TextContainer
33
+ :style="{ marginLeft: item.type == 'optimizer' ? '32px' : '0' }"
34
+ >
35
+ <TitleText :title="item.model">
36
+ {{
37
+ (item.type === 'optimizer' || !hasExpandedSection) &&
38
+ item.quantity
39
+ ? item.quantity + ' x'
40
+ : ''
41
+ }}
42
+ {{ item.brandName }} - {{ item.model }}
43
+ </TitleText>
44
+ <TitleSubText>
45
+ <span>{{ item.brandName }}</span>
46
+ <template
47
+ v-if="itemHasStrings(item) || item.type === 'optimizer'"
48
+ >
49
+ <ContainerValue
50
+ v-if="
51
+ item.getkWp() > 1 &&
52
+ item.type !== 'optimizer' &&
53
+ hasExpandedSection
54
+ "
55
+ >
56
+ |
57
+ {{
58
+ numberToString({
59
+ value: item.getkWp(),
60
+ numberPrecision: 2,
61
+ })
62
+ }}
63
+ kWp
64
+ </ContainerValue>
65
+ <ContainerValue
66
+ v-else-if="item.type !== 'optimizer' && hasExpandedSection"
67
+ >
68
+ |
69
+ {{
70
+ numberToString({
71
+ value: 1000 * item.getkWp(),
72
+ numberPrecision: 2,
73
+ minDecimals: 2,
74
+ })
75
+ }}
76
+ Wp
77
+ </ContainerValue>
78
+ </template>
79
+ </TitleSubText>
80
+ </TextContainer>
81
+ </TitleContainer>
82
+ <MarkersContainer>
83
+ <MarkerItem
84
+ v-if="item.mppts.length && hasExpandedSection"
85
+ :background-color="isTargetRatioInRange(item) ? 'green' : 'red'"
86
+ >{{
87
+ numberToString({
88
+ value: 100 * item.getTargetRatio(),
89
+ numberPrecision: 0,
90
+ minDecimals: 0,
91
+ })
92
+ }}%
93
+ </MarkerItem>
94
+ <MarkerItem v-if="item.hasTemplate && !item.isLoading">
95
+ <span
96
+ :title="
97
+ item.companyProductTemplateName
98
+ ? item.companyProductTemplateName
99
+ : $gettext('no_template_selected')
100
+ "
101
+ >
102
+ {{
103
+ item.companyProductTemplateName
104
+ ? item.companyProductTemplateName
105
+ : $gettext('no_template_selected')
106
+ }}
107
+ </span>
108
+ </MarkerItem>
109
+ <MarkerItem>
110
+ <RCIcon color="white" :name="getIconName(item)" size="14px" />
111
+ <MarkerText :title="getTypeName(item.type)">
112
+ {{ getTypeName(item.type) }}
113
+ </MarkerText>
114
+ </MarkerItem>
115
+ <MarkerItem
116
+ v-if="item.type !== 'optimizer' && item.type !== 'storage'"
117
+ :title="$gettext('AC power is the nominal AC output power.')"
118
+ >
119
+ {{
120
+ numberToString({
121
+ value: item.pacKw,
122
+ numberPrecision: 0,
123
+ minDecimals: 0,
124
+ })
125
+ }}
126
+ {{ $gettext('kWAC') }}
127
+ </MarkerItem>
128
+ <MarkerItem
129
+ v-if="
130
+ (!itemHasStrings(item) || !hasExpandedSection) &&
131
+ item.type !== 'storage'
132
+ "
133
+ :title="$gettext('The DC power is the maximum DC input power.')"
134
+ >
135
+ {{
136
+ numberToString({
137
+ value: item.inputMaxPowerKw,
138
+ numberPrecision: 0,
139
+ minDecimals: 0,
140
+ })
141
+ }}
142
+ {{ $gettext('kWDC') }}
143
+ </MarkerItem>
144
+ <MarkerItem
145
+ v-if="item.type == 'storage'"
146
+ :title="$gettext('charging_ac_power_kva_marker_title')"
147
+ >
148
+ {{
149
+ numberToString({
150
+ value: item.chargingAcPowerKva,
151
+ numberPrecision: 0,
152
+ minDecimals: 0,
153
+ })
154
+ }}
155
+ {{ $gettext('kWAC') }}
156
+ </MarkerItem>
157
+ </MarkersContainer>
158
+ <IconsContainer>
159
+ <IconWrapper
160
+ v-if="nonOptimizerInverterCount > 1 || item.type === 'optimizer'"
161
+ @click="
162
+ !item.isLoading && hasGroupedInverters(item)
163
+ ? $emit('on-delete-grouped', item)
164
+ : $emit('on-delete', item)
165
+ "
166
+ >
167
+ <RCIcon
168
+ :color="item.isLoading ? 'grey' : 'red'"
169
+ :cursor="item.isLoading ? 'not-allowed' : 'pointer'"
170
+ :is-disabled="item.isLoading"
171
+ name="delete"
172
+ size="14px"
173
+ />
174
+ </IconWrapper>
175
+ <IconWrapper @click="!item.isLoading && $emit('on-edit', item)">
176
+ <RCIcon
177
+ :color="item.isLoading ? 'grey' : 'white'"
178
+ :cursor="item.isLoading ? 'not-allowed' : 'pointer'"
179
+ :is-disabled="item.isLoading"
180
+ name="edit_button"
181
+ size="14px"
182
+ />
183
+ </IconWrapper>
184
+ <IconWrapper @click="$emit('on-datasheet', item)">
185
+ <RCIcon
186
+ color="white"
187
+ cursor="pointer"
188
+ name="document"
189
+ size="14px"
190
+ />
191
+ </IconWrapper>
192
+ </IconsContainer>
193
+ </LeftContainer>
194
+ <SortingContainer v-if="dataListToDisplay.length > 1">
195
+ <SortingIconWrapper
196
+ :data-test-id="'move_up_' + index"
197
+ :is-disabled="index === 0"
198
+ @click="index > 0 && handleMoveClick('up', index, item)"
199
+ >
200
+ <RCIcon
201
+ :color="index === 0 ? 'grey6' : 'grey3'"
202
+ :cursor="index === 0 ? 'not-allowed' : 'pointer'"
203
+ name="move_up"
204
+ size="14px"
205
+ />
206
+ </SortingIconWrapper>
207
+ <SortingIconWrapper
208
+ :data-test-id="'move_down_' + index"
209
+ :is-disabled="index === dataListToDisplay.length - 1"
210
+ @click="
211
+ index < dataListToDisplay.length - 1 &&
212
+ handleMoveClick('down', index, item)
213
+ "
214
+ >
215
+ <RCIcon
216
+ :color="
217
+ index === dataListToDisplay.length - 1 ? 'grey6' : 'grey3'
218
+ "
219
+ :cursor="
220
+ index === dataListToDisplay.length - 1
221
+ ? 'not-allowed'
222
+ : 'pointer'
223
+ "
224
+ :is-disabled="index === dataListToDisplay.length - 1"
225
+ name="move_down"
226
+ size="14px"
227
+ />
228
+ </SortingIconWrapper>
229
+ </SortingContainer>
230
+ </TopContainer>
231
+ <BoxContainer
232
+ v-for="mppt in item.mppts"
233
+ v-show="isExpanded(item.inverterId)"
234
+ :key="mppt.mpptId"
235
+ >
236
+ <BoxTitleWrapper>
237
+ <IconWrapper
238
+ v-if="itemHasStrings(item)"
239
+ margin-left="4px"
240
+ size="8px"
241
+ @click="toggleMppt(mppt.mpptId)"
242
+ >
243
+ <RCIcon
244
+ color="white"
245
+ cursor="pointer"
246
+ :name="isMpptExpanded(mppt.mpptId) ? 'arrow_up' : 'arrow_down'"
247
+ size="10px"
248
+ />
249
+ </IconWrapper>
250
+ <BoxTitleText>{{ mppt.name }}</BoxTitleText>
251
+ <BoxIconsContainer>
252
+ <BoxIconWrapper>
253
+ <RCIcon
254
+ color="white"
255
+ cursor="pointer"
256
+ name="string_design"
257
+ size="11px"
258
+ />
259
+ <div>
260
+ {{ mppt.strings.length }}/{{ mppt.getNumberOfTerminals() }}
261
+ </div>
262
+ </BoxIconWrapper>
263
+ <BoxIconWrapper>
264
+ <RCIcon
265
+ color="white"
266
+ cursor="pointer"
267
+ name="panels_tool"
268
+ size="11px"
269
+ />
270
+ <div>{{ getNumberOfMpptModules(mppt.strings) }}</div>
271
+ </BoxIconWrapper>
272
+ </BoxIconsContainer>
273
+ </BoxTitleWrapper>
274
+
275
+ <div v-show="isMpptExpanded(mppt.mpptId) && itemHasStrings(item)">
276
+ <StringBox v-for="string in mppt.strings" :key="string.id">
277
+ <StringColorContainer :background-color="string.color" />
278
+ <StringContainer>
279
+ <div>{{ string.name }}</div>
280
+ <StringIconContainer>
281
+ <div>{{ string.modules.length }}</div>
282
+ <RCIcon color="white" name="module" size="14px" />
283
+ </StringIconContainer>
284
+ </StringContainer>
285
+ </StringBox>
286
+ </div>
287
+ </BoxContainer>
288
+ <template v-if="availableMPPTData(item)">
289
+ <BoxContainer
290
+ v-for="availableItem in availableMPPTData(item)"
291
+ v-show="isExpanded(item.inverterId)"
292
+ :key="availableItem.mpptId"
293
+ >
294
+ <BoxTitleWrapper>
295
+ <IconWrapper
296
+ v-if="itemHasStrings(item)"
297
+ margin-left="4px"
298
+ size="8px"
299
+ @click="toggleMppt(availableItem.mpptId)"
300
+ >
301
+ <RCIcon
302
+ color="white"
303
+ cursor="pointer"
304
+ :name="
305
+ isMpptExpanded(availableItem.mpptId)
306
+ ? 'arrow_up'
307
+ : 'arrow_down'
308
+ "
309
+ size="10px"
310
+ />
311
+ </IconWrapper>
312
+ <BoxTitleText>{{ availableItem.name }}</BoxTitleText>
313
+ <BoxIconsContainer>
314
+ <BoxIconWrapper>
315
+ <RCIcon
316
+ color="white"
317
+ cursor="pointer"
318
+ name="string_design"
319
+ size="11px"
320
+ />
321
+ <div>0/{{ availableItem.numberOfTerminals }}</div>
322
+ </BoxIconWrapper>
323
+ <BoxIconWrapper>
324
+ <RCIcon
325
+ color="white"
326
+ cursor="pointer"
327
+ name="panels_tool"
328
+ size="11px"
329
+ />
330
+ <div>0</div>
331
+ </BoxIconWrapper>
332
+ </BoxIconsContainer>
333
+ </BoxTitleWrapper>
334
+ <EmptyStringBox
335
+ v-show="
336
+ isMpptExpanded(availableItem.mpptId) && itemHasStrings(item)
337
+ "
338
+ />
339
+ </BoxContainer>
340
+ </template>
341
+ <BoxContainer
342
+ v-if="item.storageSystem && Object.keys(item.storageSystem).length > 0"
343
+ v-show="isExpanded(item.inverterId)"
344
+ :key="item.storageSystem.storage_system_id"
345
+ >
346
+ <BoxTitleWrapper>
347
+ <IconWrapper
348
+ margin-left="4px"
349
+ size="8px"
350
+ @click="toggleMppt(item.storageSystem.storage_system_id)"
351
+ >
352
+ <RCIcon
353
+ color="white"
354
+ cursor="pointer"
355
+ :name="
356
+ isMpptExpanded(item.storageSystem.storage_system_id)
357
+ ? 'arrow_up'
358
+ : 'arrow_down'
359
+ "
360
+ size="10px"
361
+ />
362
+ </IconWrapper>
363
+ <BoxTitleText>{{ $gettext('battery') }}</BoxTitleText>
364
+ </BoxTitleWrapper>
365
+ <div v-show="isMpptExpanded(item.storageSystem.storage_system_id)">
366
+ <BatteryBox>
367
+ <RCIcon color="white" name="battery" size="14px" />
368
+ <BatteryDetailsContainer>
369
+ <BatteryType>{{ item.storageSystem.brand_name }}</BatteryType>
370
+ <BatteryModel>{{ item.storageSystem.model }}</BatteryModel>
371
+ </BatteryDetailsContainer>
372
+ <BatteryValue>
373
+ {{
374
+ numberToString({
375
+ value: item.storageSystem.nominal_capacity_kWh,
376
+ numberPrecision: 2,
377
+ minDecimals: 0,
378
+ })
379
+ }}
380
+ kWh
381
+ </BatteryValue>
382
+ </BatteryBox>
383
+ </div>
384
+ </BoxContainer>
385
+ </SectionContainer>
386
+ <DividerContainer v-if="batteryData.length" />
387
+ <UnassignedContainer v-if="batteryData.length">
388
+ <SectionTitleText>{{ $gettext('battery_information') }}</SectionTitleText>
389
+ <BatteryBox
390
+ v-for="battery in batteryData"
391
+ :key="battery.id"
392
+ :is-unassigned="true"
393
+ >
394
+ <RCIcon color="black" name="battery" size="14px" />
395
+ <BatteryDetailsContainer>
396
+ <UnassignedType>{{ battery.brand_name }}</UnassignedType>
397
+ <UnassignedModel>{{ battery.model }}</UnassignedModel>
398
+ </BatteryDetailsContainer>
399
+ <BatteryValue>
400
+ {{
401
+ numberToString({
402
+ value: battery.nominal_capacity_kWh,
403
+ numberPrecision: 2,
404
+ minDecimals: 0,
405
+ })
406
+ }}
407
+ kWh
408
+ </BatteryValue>
409
+ </BatteryBox>
410
+ </UnassignedContainer>
411
+ </PageContainer>
412
+ </template>
413
+
414
+ <script>
415
+ // import DropdownMenu from '@eturnity/eturnity_reusable_components/src/components/stringDesign/DropdownMenu'
416
+ import styled from 'vue3-styled-components'
417
+ import RCIcon from '../../icon'
418
+ import ButtonIcon from '../../buttons/buttonIcon'
419
+ import InfoText from '../../infoText'
420
+ import { numberToString } from '../../../helpers/numberConverter'
421
+
422
+ const PageContainer = styled.div`
423
+ position: relative;
424
+ `
425
+
426
+ const SectionContainer = styled.div`
427
+ &:not(:last-child) {
428
+ margin-bottom: 8px;
429
+ }
430
+ background-color: ${(props) => props.theme.colors.black};
431
+ padding: 8px;
432
+ border-radius: 4px;
433
+ color: ${(props) => props.theme.colors.white};
434
+ `
435
+
436
+ const TitleContainer = styled.div`
437
+ display: flex;
438
+ align-items: center;
439
+ gap: 4px;
440
+ `
441
+
442
+ const IconsContainer = styled.div`
443
+ display: flex;
444
+ align-items: center;
445
+ margin-left: 32px;
446
+ margin-top: 4px;
447
+ `
448
+
449
+ const TextContainer = styled.div`
450
+ display: grid;
451
+ `
452
+
453
+ const TitleText = styled.div`
454
+ font-size: 14px;
455
+ white-space: nowrap;
456
+ overflow: hidden;
457
+ text-overflow: ellipsis;
458
+ `
459
+
460
+ const TitleSubText = styled.div`
461
+ font-size: 12px;
462
+ display: flex;
463
+ gap: 4px;
464
+ `
465
+
466
+ const MarkersContainer = styled.div`
467
+ display: flex;
468
+ flex-wrap: wrap;
469
+ gap: 8px;
470
+ margin: 4px 0 2px 32px;
471
+ align-items: end;
472
+ `
473
+
474
+ const MarkerAttrs = { backgroundColor: String }
475
+ const MarkerItem = styled('div', MarkerAttrs)`
476
+ display: flex;
477
+ gap: 5px;
478
+ align-items: center;
479
+ padding: 2px 7px;
480
+ border-radius: 4px;
481
+ background-color: ${(props) =>
482
+ props.backgroundColor
483
+ ? props.theme.colors[props.backgroundColor]
484
+ : props.theme.colors.grey6};
485
+ font-size: 11px;
486
+ color: ${(props) => props.theme.colors.white};
487
+ & > span {
488
+ overflow: hidden;
489
+ white-space: nowrap;
490
+ text-overflow: ellipsis;
491
+ max-width: 11ch;
492
+ }
493
+ `
494
+
495
+ const MarkerText = styled.div`
496
+ max-width: 11ch;
497
+ white-space: nowrap;
498
+ overflow: hidden;
499
+ text-overflow: ellipsis;
500
+ `
501
+
502
+ const ContainerValue = styled.div`
503
+ font-size: 12px;
504
+ `
505
+
506
+ const IconAttrs = {
507
+ size: { type: String, default: '32px' },
508
+ marginLeft: String,
509
+ }
510
+ const IconWrapper = styled('div', IconAttrs)`
511
+ display: flex;
512
+ align-items: center;
513
+ justify-content: center;
514
+ border-radius: 4px;
515
+ cursor: pointer;
516
+ width: ${(props) => props.size};
517
+ height: ${(props) => props.size};
518
+ margin-left: ${(props) => props.marginLeft};
519
+ &:hover {
520
+ background: ${(props) =>
521
+ props.marginLeft ? 'transparent' : 'rgba(255, 255, 255, 0.1)'};
522
+ }
523
+
524
+ &:active {
525
+ background: rgba(255, 255, 255, 0.2);
526
+ }
527
+ `
528
+
529
+ const BoxContainer = styled.div`
530
+ border-radius: 4px;
531
+ background: ${(props) => props.theme.colors.grey6};
532
+ padding: 8px;
533
+ margin-top: 8px;
534
+ margin-left: 30px;
535
+ `
536
+
537
+ const BoxTitleWrapper = styled.div`
538
+ display: flex;
539
+ align-items: center;
540
+ gap: 8px;
541
+ `
542
+
543
+ const BoxTitleText = styled.div`
544
+ font-size: 13px;
545
+ `
546
+
547
+ const BoxIconsContainer = styled.div`
548
+ display: flex;
549
+ gap: 8px;
550
+ align-items: center;
551
+ margin-left: auto;
552
+ font-size: 11px;
553
+ `
554
+
555
+ const BoxIconWrapper = styled.div`
556
+ display: flex;
557
+ align-items: center;
558
+ justify-content: center;
559
+ gap: 4px;
560
+ padding: 4px;
561
+ `
562
+
563
+ const StringBox = styled.div`
564
+ display: grid;
565
+ grid-template-columns: auto 1fr;
566
+ margin-top: 8px;
567
+ border-radius: 4px;
568
+ border: 1px solid ${(props) => props.theme.colors.grey3};
569
+ `
570
+
571
+ const StringContainer = styled.div`
572
+ display: flex;
573
+ justify-content: space-between;
574
+ gap: 8px;
575
+ padding: 8px;
576
+ font-size: 11px;
577
+ `
578
+
579
+ const StringColorAttrs = { backgroundColor: String }
580
+ const StringColorContainer = styled('div', StringColorAttrs)`
581
+ background-color: ${(props) => props.backgroundColor};
582
+ width: 22px;
583
+ min-height: 100%;
584
+ border-top-left-radius: 2px;
585
+ border-bottom-left-radius: 2px;
586
+ `
587
+
588
+ const StringIconContainer = styled.div`
589
+ display: flex;
590
+ align-items: center;
591
+ justify-content: center;
592
+ gap: 2px;
593
+ padding: 0 4px;
594
+ `
595
+
596
+ const PageTitleContainer = styled.div`
597
+ position: sticky;
598
+ top: 0;
599
+ background-color: ${(props) => props.theme.colors.black};
600
+ z-index: 99;
601
+ padding: 8px;
602
+ display: flex;
603
+ justify-content: space-between;
604
+ align-items: center;
605
+ padding-bottom: 8px;
606
+ `
607
+
608
+ const SectionTitleText = styled.div`
609
+ font-size: 14px;
610
+ font-weight: 700;
611
+ color: ${(props) => props.theme.colors.white};
612
+ `
613
+
614
+ const BatteryBoxAttrs = { isUnassigned: Boolean }
615
+ const BatteryBox = styled('div', BatteryBoxAttrs)`
616
+ display: flex;
617
+ align-items: center;
618
+ gap: 8px;
619
+ border: 1px solid ${(props) => props.theme.colors.white};
620
+ border-radius: 4px;
621
+ padding: 8px;
622
+ margin-top: 8px;
623
+ background-color: ${(props) =>
624
+ props.isUnassigned ? props.theme.colors.grey2 : ''};
625
+ `
626
+
627
+ const BatteryDetailsContainer = styled.div`
628
+ display: flex;
629
+ flex-direction: column;
630
+ gap: 4px;
631
+ `
632
+
633
+ const BatteryType = styled.div`
634
+ font-size: 11px;
635
+ `
636
+
637
+ const BatteryModel = styled.div`
638
+ font-size: 10px;
639
+ `
640
+
641
+ const BatteryValue = styled.div`
642
+ font-size: 10px;
643
+ align-self: flex-end;
644
+ margin-left: auto;
645
+ `
646
+ const TopContainer = styled.div`
647
+ display: flex;
648
+ justify-content: space-between;
649
+ `
650
+
651
+ const LeftContainer = styled.div``
652
+
653
+ const SortingContainer = styled.div`
654
+ display: flex;
655
+ flex-direction: column;
656
+ align-items: center;
657
+ `
658
+
659
+ const SortingIconWrapperAttrs = { isDisabled: Boolean }
660
+ const SortingIconWrapper = styled('div', SortingIconWrapperAttrs)`
661
+ cursor: ${(props) => (props.isDisabled ? 'not-allowed' : 'pointer')};
662
+ width: 30px;
663
+ height: 30px;
664
+ display: flex;
665
+ align-items: center;
666
+ justify-content: center;
667
+
668
+ &:hover {
669
+ background-color: ${(props) =>
670
+ props.isDisabled ? 'transparent' : 'rgba(255, 255, 255, 0.1)'};
671
+ border-radius: 4px;
672
+ }
673
+ `
674
+
675
+ const UnassignedContainer = styled.div`
676
+ margin-top: 8px;
677
+ `
678
+
679
+ const DividerContainer = styled.div`
680
+ height: 0.5px;
681
+ width: 100%;
682
+ background-color: ${(props) => props.theme.colors.grey4};
683
+ opacity: 0.6;
684
+ `
685
+
686
+ const UnassignedType = styled.div`
687
+ font-size: 12px;
688
+ line-height: 150%;
689
+ `
690
+
691
+ const UnassignedModel = styled.div`
692
+ font-size: 10px;
693
+ line-height: 150%;
694
+ `
695
+
696
+ const IconPlaceholder = styled.div`
697
+ width: 32px;
698
+ `
699
+
700
+ const EmptyStringBox = styled.div`
701
+ border: 0.8px dashed ${(props) => props.theme.colors.black};
702
+ border-radius: 4px;
703
+ padding: 8px 8px 8px 0;
704
+ height: 32px;
705
+ width: 233px;
706
+ margin-top: 8px;
707
+ `
708
+
709
+ const InfoWrapper = styled.div`
710
+ display: flex;
711
+ align-items: center;
712
+ justify-content: center;
713
+ width: 32px;
714
+ `
715
+
716
+ export default {
717
+ name: 'DropdownMenu',
718
+ components: {
719
+ SectionContainer,
720
+ TitleContainer,
721
+ IconsContainer,
722
+ TextContainer,
723
+ TitleText,
724
+ TitleSubText,
725
+ RCIcon,
726
+ MarkersContainer,
727
+ MarkerItem,
728
+ ContainerValue,
729
+ IconWrapper,
730
+ BoxContainer,
731
+ BoxTitleWrapper,
732
+ BoxTitleText,
733
+ BoxIconsContainer,
734
+ BoxIconWrapper,
735
+ StringBox,
736
+ StringContainer,
737
+ StringColorContainer,
738
+ StringIconContainer,
739
+ PageTitleContainer,
740
+ SectionTitleText,
741
+ ButtonIcon,
742
+ PageContainer,
743
+ BatteryBox,
744
+ BatteryDetailsContainer,
745
+ BatteryType,
746
+ BatteryModel,
747
+ BatteryValue,
748
+ TopContainer,
749
+ LeftContainer,
750
+ SortingContainer,
751
+ SortingIconWrapper,
752
+ UnassignedContainer,
753
+ DividerContainer,
754
+ UnassignedType,
755
+ UnassignedModel,
756
+ IconPlaceholder,
757
+ EmptyStringBox,
758
+ InfoText,
759
+ InfoWrapper,
760
+ MarkerText,
761
+ },
762
+ props: {
763
+ dataList: {
764
+ required: true,
765
+ type: Array,
766
+ },
767
+ batteryData: {
768
+ required: true,
769
+ type: Array,
770
+ },
771
+ inverterParameters: {
772
+ required: true,
773
+ type: Object,
774
+ },
775
+ isPvAndBatteryActive: {
776
+ required: false,
777
+ type: Boolean,
778
+ },
779
+ },
780
+ emits: ['on-edit', 'on-move', 'on-delete', 'on-delete-grouped'],
781
+ data() {
782
+ return {
783
+ expandedInverters: [],
784
+ expandedMppts: [],
785
+ numberToString,
786
+ }
787
+ },
788
+ computed: {
789
+ dataListToDisplay() {
790
+ let data = this.hasExpandedSection
791
+ ? this.dataList
792
+ : this.compressedDataList
793
+ return data.sort((a, b) => a.lineNr - b.lineNr)
794
+ },
795
+ hasExpandedSection() {
796
+ return this.expandedInverters.length > 0
797
+ },
798
+ hasStringsOrStorage() {
799
+ return this.dataList.some((item) => {
800
+ return (
801
+ item.mppts.some((mppt) => mppt.strings.length > 0) ||
802
+ (item.storageSystem && Object.keys(item.storageSystem).length > 0)
803
+ )
804
+ })
805
+ },
806
+ nonOptimizerInverterCount() {
807
+ return this.dataList.filter((item) => item.type !== 'optimizer').length
808
+ },
809
+ compressedDataList() {
810
+ return this.dataList.reduce((acc, item) => {
811
+ if (item.type == 'optimizer') {
812
+ acc.push(item)
813
+ return acc
814
+ }
815
+ const existingInverter = acc.find((inverter) => {
816
+ return inverter.componentId == item.componentId
817
+ })
818
+ if (!existingInverter) {
819
+ acc.push(item)
820
+ item.quantity = 1
821
+ } else {
822
+ existingInverter.quantity++
823
+ }
824
+ return acc
825
+ }, [])
826
+ },
827
+ },
828
+ created() {
829
+ // Expand all items on creation
830
+ if (this.hasStringsOrStorage) {
831
+ this.expandedInverters = this.dataList.map((item) => item.inverterId)
832
+ this.expandedMppts = this.dataList.flatMap((item) => {
833
+ const availableMppts = this.availableMPPTData(item)
834
+ return [
835
+ ...item.mppts.map((mppt) => mppt.mpptId),
836
+ ...(item.storageSystem
837
+ ? [item.storageSystem.storage_system_id]
838
+ : []),
839
+ ...(availableMppts?.map((mppt) => mppt.mpptId) || []),
840
+ ]
841
+ })
842
+ }
843
+ },
844
+ methods: {
845
+ hasGroupedInverters(item) {
846
+ return item.type != 'optimizer' && item.quantity > 1
847
+ },
848
+ isTargetRatioInRange(inverter) {
849
+ const currentTargetRatio = inverter.getTargetRatio()
850
+ return (
851
+ this.inverterParameters.target_power_ratio - 20 <=
852
+ 100 * currentTargetRatio &&
853
+ this.inverterParameters.target_power_ratio + 20 >=
854
+ 100 * currentTargetRatio
855
+ )
856
+ },
857
+ getNumberOfMpptModules(strings) {
858
+ return strings.reduce((acc, curr) => acc + curr.modules.length, 0)
859
+ },
860
+ availableMPPTData(item) {
861
+ if (item.type === 'optimizer' || item.type === 'storage') {
862
+ return []
863
+ }
864
+ const existingTrackerNumbers = item.mppts.map(
865
+ (mppt) => mppt.trackerNumber
866
+ )
867
+ const filteredAvailableMPPTs = item.availableMPPTs.filter(
868
+ (mppt) => !existingTrackerNumbers.includes(mppt.tracker_number)
869
+ )
870
+
871
+ let mpptData = []
872
+ filteredAvailableMPPTs.forEach((mppt) => {
873
+ mpptData.push({
874
+ mpptId:
875
+ 'available_mppt_' +
876
+ item.companyComponentLibraryId +
877
+ '_' +
878
+ mppt.tracker_number,
879
+ numberOfTerminals: mppt.number_of_terminals,
880
+ name:
881
+ 'MPPT ' +
882
+ (item.mppts.length + filteredAvailableMPPTs.indexOf(mppt) + 1),
883
+ })
884
+ })
885
+ return mpptData
886
+ },
887
+ isExpanded(id) {
888
+ return this.expandedInverters.includes(id)
889
+ },
890
+ isMpptExpanded(id) {
891
+ return this.expandedMppts.includes(id)
892
+ },
893
+ toggleSection(id) {
894
+ const index = this.expandedInverters.indexOf(id)
895
+ if (index === -1) {
896
+ this.expandedInverters.push(id)
897
+ } else {
898
+ this.expandedInverters.splice(index, 1)
899
+ }
900
+ },
901
+ toggleMppt(id) {
902
+ const index = this.expandedMppts.indexOf(id)
903
+ if (index === -1) {
904
+ this.expandedMppts.push(id)
905
+ } else {
906
+ this.expandedMppts.splice(index, 1)
907
+ }
908
+ },
909
+ toggleAllSections() {
910
+ if (this.hasExpandedSection) {
911
+ this.expandedInverters = []
912
+ this.expandedMppts = []
913
+ } else {
914
+ this.expandedInverters = this.dataList.map((item) => item.inverterId)
915
+ this.expandedMppts = this.dataList.flatMap((item) => {
916
+ const hasStrings = !!item.getStrings().length
917
+ return [
918
+ ...(hasStrings ? item.mppts.map((mppt) => mppt.mpptId) : []),
919
+ ...(item.storageSystem
920
+ ? [item.storageSystem.storage_system_id]
921
+ : []),
922
+ ]
923
+ })
924
+ }
925
+ },
926
+ isItemCollapsible(item) {
927
+ return (
928
+ item.mppts.some((mppt) => mppt.strings.length > 0) ||
929
+ (item.storageSystem && Object.keys(item.storageSystem).length > 0) ||
930
+ item.availableMPPTs.length
931
+ )
932
+ },
933
+ itemHasStrings(item) {
934
+ return item.mppts.some((mppt) => mppt.strings.length > 0)
935
+ },
936
+ getTypeName(type) {
937
+ const value = type.toLowerCase()
938
+ switch (value) {
939
+ case 'pv':
940
+ return this.$gettext('PV')
941
+ case 'pv_storage':
942
+ return this.$gettext('hybrid')
943
+ case 'storage':
944
+ return this.$gettext('battery')
945
+ case 'optimizer':
946
+ return this.$gettext('inverter_type_optimizer')
947
+ default:
948
+ return this.$gettext('PV')
949
+ }
950
+ },
951
+ getIconName(item) {
952
+ const value =
953
+ item.type === 'pv_storage'
954
+ ? item.iconName.technology_choice
955
+ : item.type
956
+ switch (value) {
957
+ case 'photovoltaics':
958
+ return 'pv'
959
+ case 'pv':
960
+ return 'pv'
961
+ case 'storage':
962
+ return 'battery'
963
+ case 'battery':
964
+ return 'battery'
965
+ case 'optimizer':
966
+ return 'optimizer'
967
+ default:
968
+ return 'pv'
969
+ }
970
+ },
971
+ handleMoveClick(direction, index, item) {
972
+ this.$emit('on-move', {
973
+ direction: direction,
974
+ index: index,
975
+ item: item,
976
+ isExpanded: this.hasExpandedSection,
977
+ })
978
+ },
979
+ },
980
+ }
981
+ </script>