@eturnity/eturnity_reusable_components 7.48.1-EPDM-10964.0 → 7.48.1-EPDM-12680.12

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,711 @@
1
+ <template>
2
+ <PageContainer>
3
+ <PageTitleContainer>
4
+ <SectionTitleText>{{ $gettext('inverters') }}</SectionTitleText>
5
+ <ButtonIcon
6
+ custom-color="black"
7
+ :icon-name="hasExpandedSection ? 'collapse_all' : 'expand'"
8
+ :text="$gettext(hasExpandedSection ? 'collapse_all' : 'expand_all')"
9
+ @click="toggleAllSections"
10
+ />
11
+ </PageTitleContainer>
12
+ <SectionContainer v-for="(item, index) in dataList" :key="item.inverterId">
13
+ <TopContainer>
14
+ <LeftContainer>
15
+ <TitleContainer>
16
+ <IconWrapper size="36px" @click="toggleSection(item.inverterId)">
17
+ <RCIcon
18
+ color="white"
19
+ :name="isExpanded(item.inverterId) ? 'arrow_up' : 'arrow_down'"
20
+ size="10px"
21
+ />
22
+ </IconWrapper>
23
+ <TextContainer>
24
+ <TitleText :title="item.model"
25
+ >{{
26
+ item.type === 'optimizer' && item.quantity
27
+ ? item.quantity + ' x'
28
+ : ''
29
+ }}
30
+ {{ item.model }}</TitleText
31
+ >
32
+ <TitleSubText
33
+ >{{ item.brandName }} |
34
+ <ContainerValue v-if="item.getkWp() > 1">
35
+ {{
36
+ numberToString({
37
+ value: item.getkWp(),
38
+ numberPrecision: 2,
39
+ })
40
+ }}
41
+ kWp
42
+ </ContainerValue>
43
+ <ContainerValue v-else>
44
+ {{
45
+ numberToString({
46
+ value: 1000 * item.getkWp(),
47
+ numberPrecision: 2,
48
+ minDecimals: 2,
49
+ })
50
+ }}
51
+ Wp
52
+ </ContainerValue></TitleSubText
53
+ >
54
+ </TextContainer>
55
+ </TitleContainer>
56
+ <MarkersContainer>
57
+ <MarkerItem
58
+ v-if="item.mppts.length"
59
+ :background-color="isTargetRatioInRange(item) ? 'green' : 'red'"
60
+ >{{
61
+ numberToString({
62
+ value: 100 * item.getTargetRatio(),
63
+ numberPrecision: 2,
64
+ minDecimals: 0,
65
+ })
66
+ }}%</MarkerItem
67
+ >
68
+ <MarkerItem v-if="item.hasTemplate">
69
+ <span
70
+ :title="
71
+ item.companyProductTemplateName
72
+ ? item.companyProductTemplateName
73
+ : $gettext('no_template_selected')
74
+ "
75
+ >
76
+ {{
77
+ item.companyProductTemplateName
78
+ ? item.companyProductTemplateName
79
+ : $gettext('no_template_selected')
80
+ }}
81
+ </span>
82
+ </MarkerItem>
83
+ <MarkerItem>
84
+ <RCIcon
85
+ color="white"
86
+ :name="getIconName(item.type)"
87
+ size="14px"
88
+ />
89
+ <div>{{ getTypeName(item.type) }}</div>
90
+ </MarkerItem>
91
+ </MarkersContainer>
92
+ <IconsContainer>
93
+ <IconWrapper>
94
+ <RCIcon
95
+ color="red"
96
+ cursor="pointer"
97
+ name="delete"
98
+ size="14px"
99
+ @click="$emit('on-delete', item)"
100
+ />
101
+ </IconWrapper>
102
+ <IconWrapper @click="$emit('on-edit', item)">
103
+ <RCIcon
104
+ color="white"
105
+ cursor="pointer"
106
+ name="edit_button"
107
+ size="14px"
108
+ />
109
+ </IconWrapper>
110
+ <IconWrapper>
111
+ <RCIcon
112
+ color="white"
113
+ cursor="pointer"
114
+ name="document"
115
+ size="14px"
116
+ @click="$emit('on-datasheet', item)"
117
+ />
118
+ </IconWrapper>
119
+ </IconsContainer>
120
+ </LeftContainer>
121
+ <SortingContainer v-if="dataList.length > 1">
122
+ <SortingIconWrapper
123
+ :data-test-id="'move_up_' + index"
124
+ :is-disabled="index === 0"
125
+ @click="index > 0 && handleMoveClick('up', index)"
126
+ >
127
+ <RCIcon
128
+ :color="index === 0 ? 'grey6' : 'grey3'"
129
+ :cursor="index === 0 ? 'not-allowed' : 'pointer'"
130
+ name="move_up"
131
+ size="14px"
132
+ />
133
+ </SortingIconWrapper>
134
+ <SortingIconWrapper
135
+ :data-test-id="'move_down_' + index"
136
+ :is-disabled="index === dataList.length - 1"
137
+ @click="
138
+ index < dataList.length - 1 && handleMoveClick('down', index)
139
+ "
140
+ >
141
+ <RCIcon
142
+ :color="index === dataList.length - 1 ? 'grey6' : 'grey3'"
143
+ :cursor="
144
+ index === dataList.length - 1 ? 'not-allowed' : 'pointer'
145
+ "
146
+ name="move_down"
147
+ size="14px"
148
+ />
149
+ </SortingIconWrapper>
150
+ </SortingContainer>
151
+ </TopContainer>
152
+ <BoxContainer
153
+ v-for="mppt in item.mppts"
154
+ v-show="isExpanded(item.inverterId)"
155
+ :key="mppt.mpptId"
156
+ >
157
+ <BoxTitleWrapper>
158
+ <IconWrapper
159
+ margin-left="4px"
160
+ size="8px"
161
+ @click="toggleMppt(mppt.mpptId)"
162
+ >
163
+ <RCIcon
164
+ color="white"
165
+ cursor="pointer"
166
+ :name="isMpptExpanded(mppt.mpptId) ? 'arrow_up' : 'arrow_down'"
167
+ size="10px"
168
+ />
169
+ </IconWrapper>
170
+ <BoxTitleText>{{ mppt.name }}</BoxTitleText>
171
+ <BoxIconsContainer>
172
+ <BoxIconWrapper>
173
+ <RCIcon
174
+ color="white"
175
+ cursor="pointer"
176
+ name="string_design"
177
+ size="11px"
178
+ />
179
+ <div>
180
+ {{ mppt.strings.length }}/{{ mppt.getNumberOfTerminals() }}
181
+ </div>
182
+ </BoxIconWrapper>
183
+ <BoxIconWrapper>
184
+ <RCIcon
185
+ color="white"
186
+ cursor="pointer"
187
+ name="panels_tool"
188
+ size="11px"
189
+ />
190
+ <div>{{ getNumberOfMpptModules(mppt.strings) }}</div>
191
+ </BoxIconWrapper>
192
+ </BoxIconsContainer>
193
+ </BoxTitleWrapper>
194
+ <div v-show="isMpptExpanded(mppt.mpptId)">
195
+ <StringBox v-for="string in mppt.strings" :key="string.id">
196
+ <StringColorContainer :background-color="string.color" />
197
+ <StringContainer>
198
+ <div>{{ string.name }}</div>
199
+ <StringIconContainer>
200
+ <div>{{ string.modules.length }}</div>
201
+ <RCIcon color="white" name="module" size="14px" />
202
+ </StringIconContainer>
203
+ </StringContainer>
204
+ </StringBox>
205
+ </div>
206
+ </BoxContainer>
207
+ <BoxContainer
208
+ v-if="item.storageSystem && Object.keys(item.storageSystem).length > 0"
209
+ v-show="isExpanded(item.inverterId)"
210
+ :key="item.storageSystem.storage_system_id"
211
+ >
212
+ <BoxTitleWrapper>
213
+ <IconWrapper
214
+ margin-left="4px"
215
+ size="8px"
216
+ @click="toggleMppt(item.storageSystem.storage_system_id)"
217
+ >
218
+ <RCIcon
219
+ color="white"
220
+ cursor="pointer"
221
+ :name="
222
+ isMpptExpanded(item.storageSystem.storage_system_id)
223
+ ? 'arrow_up'
224
+ : 'arrow_down'
225
+ "
226
+ size="10px"
227
+ />
228
+ </IconWrapper>
229
+ <BoxTitleText>{{ $gettext('battery') }}</BoxTitleText>
230
+ </BoxTitleWrapper>
231
+ <div v-show="isMpptExpanded(item.storageSystem.storage_system_id)">
232
+ <BatteryBox>
233
+ <RCIcon color="white" name="battery" size="14px" />
234
+ <BatteryDetailsContainer>
235
+ <BatteryType>{{ item.storageSystem.brand_name }}</BatteryType>
236
+ <BatteryModel>{{ item.storageSystem.model }}</BatteryModel>
237
+ </BatteryDetailsContainer>
238
+ <BatteryValue>
239
+ {{
240
+ numberToString({
241
+ value: item.storageSystem.nominal_capacity_kWh,
242
+ numberPrecision: 2,
243
+ minDecimals: 0,
244
+ })
245
+ }}
246
+ kWh
247
+ </BatteryValue>
248
+ </BatteryBox>
249
+ </div>
250
+ </BoxContainer>
251
+ </SectionContainer>
252
+ <DividerContainer v-if="batteryData.length" />
253
+ <UnassignedContainer v-if="batteryData.length">
254
+ <SectionTitleText>{{ $gettext('battery_information') }}</SectionTitleText>
255
+ <BatteryBox
256
+ v-for="battery in batteryData"
257
+ :key="battery.id"
258
+ :is-unassigned="true"
259
+ >
260
+ <RCIcon color="black" name="battery" size="14px" />
261
+ <BatteryDetailsContainer>
262
+ <UnassignedType>{{ battery.brand_name }}</UnassignedType>
263
+ <UnassignedModel>{{ battery.model }}</UnassignedModel>
264
+ </BatteryDetailsContainer>
265
+ <BatteryValue>
266
+ {{
267
+ numberToString({
268
+ value: battery.nominal_capacity_kWh,
269
+ numberPrecision: 2,
270
+ minDecimals: 0,
271
+ })
272
+ }}
273
+ kWh
274
+ </BatteryValue>
275
+ </BatteryBox>
276
+ </UnassignedContainer>
277
+ </PageContainer>
278
+ </template>
279
+
280
+ <script>
281
+ // import DropdownMenu from '@eturnity/eturnity_reusable_components/src/components/stringDesign/DropdownMenu'
282
+ import styled from 'vue3-styled-components'
283
+ import RCIcon from '../../icon'
284
+ import ButtonIcon from '../../buttons/buttonIcon'
285
+ import { numberToString } from '../../../helpers/numberConverter'
286
+
287
+ const PageContainer = styled.div`
288
+ position: relative;
289
+ `
290
+
291
+ const SectionContainer = styled.div`
292
+ &:not(:last-child) {
293
+ margin-bottom: 8px;
294
+ }
295
+ background-color: ${(props) => props.theme.colors.black};
296
+ padding: 8px;
297
+ border-radius: 4px;
298
+ color: ${(props) => props.theme.colors.white};
299
+ `
300
+
301
+ const TitleContainer = styled.div`
302
+ display: flex;
303
+ align-items: center;
304
+ `
305
+
306
+ const IconsContainer = styled.div`
307
+ display: flex;
308
+ align-items: center;
309
+ margin-left: 36px;
310
+ `
311
+
312
+ const TextContainer = styled.div`
313
+ display: grid;
314
+ `
315
+
316
+ const TitleText = styled.div`
317
+ font-size: 14px;
318
+ white-space: nowrap;
319
+ overflow: hidden;
320
+ text-overflow: ellipsis;
321
+ `
322
+
323
+ const TitleSubText = styled.div`
324
+ font-size: 12px;
325
+ display: flex;
326
+ gap: 4px;
327
+ `
328
+
329
+ const MarkersContainer = styled.div`
330
+ display: flex;
331
+ flex-wrap: wrap;
332
+ gap: 8px;
333
+ margin: 4px 0 2px 36px;
334
+ align-items: end;
335
+ `
336
+
337
+ const MarkerAttrs = { backgroundColor: String }
338
+ const MarkerItem = styled('div', MarkerAttrs)`
339
+ display: flex;
340
+ gap: 5px;
341
+ align-items: center;
342
+ padding: 2px 7px;
343
+ border-radius: 4px;
344
+ background-color: ${(props) =>
345
+ props.backgroundColor
346
+ ? props.theme.colors[props.backgroundColor]
347
+ : props.theme.colors.grey6};
348
+ font-size: 11px;
349
+ color: ${(props) => props.theme.colors.white};
350
+ max-width: 11ch;
351
+ white-space: nowrap;
352
+ overflow: hidden;
353
+ text-overflow: ellipsis;
354
+ & > span {
355
+ overflow: hidden;
356
+ text-overflow: ellipsis;
357
+ }
358
+ `
359
+
360
+ const ContainerValue = styled.div`
361
+ font-size: 12px;
362
+ `
363
+
364
+ const IconAttrs = {
365
+ size: { type: String, default: '36px' },
366
+ marginLeft: String,
367
+ }
368
+ const IconWrapper = styled('div', IconAttrs)`
369
+ display: flex;
370
+ align-items: center;
371
+ justify-content: center;
372
+ border-radius: 4px;
373
+ cursor: pointer;
374
+ width: ${(props) => props.size};
375
+ height: ${(props) => props.size};
376
+ margin-left: ${(props) => props.marginLeft};
377
+ &:hover {
378
+ background: ${(props) =>
379
+ props.marginLeft ? 'transparent' : 'rgba(255, 255, 255, 0.1)'};
380
+ }
381
+
382
+ &:active {
383
+ background: rgba(255, 255, 255, 0.2);
384
+ }
385
+ `
386
+
387
+ const BoxContainer = styled.div`
388
+ border-radius: 4px;
389
+ background: ${(props) => props.theme.colors.grey6};
390
+ padding: 8px;
391
+ margin-top: 8px;
392
+ margin-left: 30px;
393
+ `
394
+
395
+ const BoxTitleWrapper = styled.div`
396
+ display: flex;
397
+ align-items: center;
398
+ gap: 8px;
399
+ `
400
+
401
+ const BoxTitleText = styled.div`
402
+ font-size: 13px;
403
+ `
404
+
405
+ const BoxIconsContainer = styled.div`
406
+ display: flex;
407
+ gap: 8px;
408
+ align-items: center;
409
+ margin-left: auto;
410
+ font-size: 11px;
411
+ `
412
+
413
+ const BoxIconWrapper = styled.div`
414
+ display: flex;
415
+ align-items: center;
416
+ justify-content: center;
417
+ gap: 4px;
418
+ padding: 4px;
419
+ `
420
+
421
+ const StringBox = styled.div`
422
+ display: grid;
423
+ grid-template-columns: auto 1fr;
424
+ margin-top: 8px;
425
+ border-radius: 4px;
426
+ border: 1px solid ${(props) => props.theme.colors.grey3};
427
+ `
428
+
429
+ const StringContainer = styled.div`
430
+ display: flex;
431
+ justify-content: space-between;
432
+ gap: 8px;
433
+ padding: 8px;
434
+ font-size: 11px;
435
+ `
436
+
437
+ const StringColorAttrs = { backgroundColor: String }
438
+ const StringColorContainer = styled('div', StringColorAttrs)`
439
+ background-color: ${(props) => props.backgroundColor};
440
+ width: 22px;
441
+ min-height: 100%;
442
+ border-top-left-radius: 4px;
443
+ border-bottom-left-radius: 4px;
444
+ `
445
+
446
+ const StringIconContainer = styled.div`
447
+ display: flex;
448
+ align-items: center;
449
+ justify-content: center;
450
+ gap: 2px;
451
+ padding: 0 4px;
452
+ `
453
+
454
+ const PageTitleContainer = styled.div`
455
+ position: sticky;
456
+ top: 0;
457
+ background-color: ${(props) => props.theme.colors.black};
458
+ z-index: 99;
459
+ padding: 8px;
460
+ display: flex;
461
+ justify-content: space-between;
462
+ align-items: center;
463
+ padding-bottom: 8px;
464
+ `
465
+
466
+ const SectionTitleText = styled.div`
467
+ font-size: 14px;
468
+ font-weight: 700;
469
+ color: ${(props) => props.theme.colors.white};
470
+ `
471
+
472
+ const BatteryBoxAttrs = { isUnassigned: Boolean }
473
+ const BatteryBox = styled('div', BatteryBoxAttrs)`
474
+ display: flex;
475
+ align-items: center;
476
+ gap: 8px;
477
+ border: 1px solid ${(props) => props.theme.colors.white};
478
+ border-radius: 4px;
479
+ padding: 8px;
480
+ margin-top: 8px;
481
+ background-color: ${(props) =>
482
+ props.isUnassigned ? props.theme.colors.grey2 : ''};
483
+ `
484
+
485
+ const BatteryDetailsContainer = styled.div`
486
+ display: flex;
487
+ flex-direction: column;
488
+ gap: 4px;
489
+ `
490
+
491
+ const BatteryType = styled.div`
492
+ font-size: 11px;
493
+ `
494
+
495
+ const BatteryModel = styled.div`
496
+ font-size: 10px;
497
+ `
498
+
499
+ const BatteryValue = styled.div`
500
+ font-size: 10px;
501
+ align-self: flex-end;
502
+ margin-left: auto;
503
+ `
504
+ const TopContainer = styled.div`
505
+ display: flex;
506
+ justify-content: space-between;
507
+ `
508
+
509
+ const LeftContainer = styled.div``
510
+
511
+ const SortingContainer = styled.div`
512
+ display: flex;
513
+ flex-direction: column;
514
+ align-items: center;
515
+ `
516
+
517
+ const SortingIconWrapperAttrs = { isDisabled: Boolean }
518
+ const SortingIconWrapper = styled('div', SortingIconWrapperAttrs)`
519
+ cursor: ${(props) => (props.isDisabled ? 'not-allowed' : 'pointer')};
520
+ width: 30px;
521
+ height: 30px;
522
+ display: flex;
523
+ align-items: center;
524
+ justify-content: center;
525
+ `
526
+
527
+ const UnassignedContainer = styled.div`
528
+ margin-top: 8px;
529
+ `
530
+
531
+ const DividerContainer = styled.div`
532
+ height: 0.5px;
533
+ width: 100%;
534
+ background-color: ${(props) => props.theme.colors.grey4};
535
+ opacity: 0.6;
536
+ `
537
+
538
+ const UnassignedType = styled.div`
539
+ font-size: 12px;
540
+ line-height: 150%;
541
+ `
542
+
543
+ const UnassignedModel = styled.div`
544
+ font-size: 10px;
545
+ line-height: 150%;
546
+ `
547
+
548
+ export default {
549
+ name: 'DropdownMenu',
550
+ components: {
551
+ SectionContainer,
552
+ TitleContainer,
553
+ IconsContainer,
554
+ TextContainer,
555
+ TitleText,
556
+ TitleSubText,
557
+ RCIcon,
558
+ MarkersContainer,
559
+ MarkerItem,
560
+ ContainerValue,
561
+ IconWrapper,
562
+ BoxContainer,
563
+ BoxTitleWrapper,
564
+ BoxTitleText,
565
+ BoxIconsContainer,
566
+ BoxIconWrapper,
567
+ StringBox,
568
+ StringContainer,
569
+ StringColorContainer,
570
+ StringIconContainer,
571
+ PageTitleContainer,
572
+ SectionTitleText,
573
+ ButtonIcon,
574
+ PageContainer,
575
+ BatteryBox,
576
+ BatteryDetailsContainer,
577
+ BatteryType,
578
+ BatteryModel,
579
+ BatteryValue,
580
+ TopContainer,
581
+ LeftContainer,
582
+ SortingContainer,
583
+ SortingIconWrapper,
584
+ UnassignedContainer,
585
+ DividerContainer,
586
+ UnassignedType,
587
+ UnassignedModel,
588
+ },
589
+ props: {
590
+ dataList: {
591
+ required: true,
592
+ type: Array,
593
+ },
594
+ batteryData: {
595
+ required: true,
596
+ type: Array,
597
+ },
598
+ inverterParameters: {
599
+ required: true,
600
+ type: Object,
601
+ },
602
+ },
603
+ emits: ['on-edit', 'on-move'],
604
+ data() {
605
+ return {
606
+ expandedInverters: [],
607
+ expandedMppts: [],
608
+ numberToString,
609
+ }
610
+ },
611
+ computed: {
612
+ hasExpandedSection() {
613
+ return this.expandedInverters.length > 0
614
+ },
615
+ },
616
+ watch: {
617
+ dataList: {
618
+ handler(newValue) {
619
+ console.log('newValue', newValue)
620
+ },
621
+ deep: true,
622
+ },
623
+ },
624
+ methods: {
625
+ isTargetRatioInRange(inverter) {
626
+ const currentTargetRatio = inverter.getTargetRatio()
627
+ return (
628
+ this.inverterParameters.target_power_ratio - 20 <=
629
+ 100 * currentTargetRatio &&
630
+ this.inverterParameters.target_power_ratio + 20 >=
631
+ 100 * currentTargetRatio
632
+ )
633
+ },
634
+ getNumberOfMpptModules(strings) {
635
+ return strings.reduce((acc, curr) => acc + curr.modules.length, 0)
636
+ },
637
+ isExpanded(id) {
638
+ return this.expandedInverters.includes(id)
639
+ },
640
+ isMpptExpanded(id) {
641
+ return this.expandedMppts.includes(id)
642
+ },
643
+ toggleSection(id) {
644
+ const index = this.expandedInverters.indexOf(id)
645
+ if (index === -1) {
646
+ this.expandedInverters.push(id)
647
+ } else {
648
+ this.expandedInverters.splice(index, 1)
649
+ }
650
+ },
651
+ toggleMppt(id) {
652
+ const index = this.expandedMppts.indexOf(id)
653
+ if (index === -1) {
654
+ this.expandedMppts.push(id)
655
+ } else {
656
+ this.expandedMppts.splice(index, 1)
657
+ }
658
+ },
659
+ toggleAllSections() {
660
+ if (this.hasExpandedSection) {
661
+ this.expandedInverters = []
662
+ this.expandedMppts = []
663
+ } else {
664
+ this.expandedInverters = this.dataList.map((item) => item.inverterId)
665
+ this.expandedMppts = this.dataList.flatMap((item) => [
666
+ ...item.mppts.map((mppt) => mppt.mpptId),
667
+ ...(item.storageSystem
668
+ ? [item.storageSystem.storage_system_id]
669
+ : []),
670
+ ])
671
+ }
672
+ },
673
+ getTypeName(type) {
674
+ const value = type.toLowerCase()
675
+ switch (value) {
676
+ case 'pv':
677
+ return this.$gettext('PV')
678
+ case 'pv_storage':
679
+ return this.$gettext('hybrid')
680
+ case 'storage':
681
+ return this.$gettext('battery')
682
+ case 'optimizer':
683
+ return this.$gettext('inverter_type_optimizer')
684
+ default:
685
+ return this.$gettext('PV')
686
+ }
687
+ },
688
+ getIconName(type) {
689
+ const value = type.toLowerCase()
690
+ switch (value) {
691
+ case 'pv':
692
+ return 'pv'
693
+ case 'pv_storage':
694
+ return 'hybrid'
695
+ case 'storage':
696
+ return 'battery'
697
+ case 'optimizer':
698
+ return 'optimizer'
699
+ default:
700
+ return 'pv'
701
+ }
702
+ },
703
+ handleMoveClick(direction, index) {
704
+ this.$emit('on-move', {
705
+ direction: direction,
706
+ index: index,
707
+ })
708
+ },
709
+ },
710
+ }
711
+ </script>