@@ -32,7 +32,8 @@ import {
3232} from 'features/reports/report-vessel-group/vessel-group-report.slice'
3333import { selectSearchQuery } from 'features/search/search.config.selectors'
3434import { resetSidebarScroll } from 'features/sidebar/sidebar.utils'
35- import { selectUserData } from 'features/user/selectors/user.selectors'
35+ import { selectIsGFWUser , selectUserData } from 'features/user/selectors/user.selectors'
36+ import { DEFAULT_VESSEL_IDENTITY_ID } from 'features/vessel/vessel.config'
3637import {
3738 selectHasVesselGroupSearchVessels ,
3839 selectHasVesselGroupVesselsOverflow ,
@@ -87,6 +88,7 @@ import {
8788 selectVesselGroupModalOpen ,
8889 selectVesselGroupModalSearchIdField ,
8990 selectVesselGroupModalSources ,
91+ selectVesselGroupModalUnmatchedIDs ,
9092 selectVesselGroupModalVessels ,
9193 selectVesselGroupSearchStatus ,
9294 setVesselGroupModalName ,
@@ -101,10 +103,12 @@ function VesselGroupModal(): React.ReactElement<any> {
101103 const dispatch = useAppDispatch ( )
102104 const [ buttonLoading , setButtonLoading ] = useState < VesselGroupConfirmationMode | '' > ( '' )
103105 const userData = useSelector ( selectUserData )
106+ const isGFWUser = useSelector ( selectIsGFWUser )
104107 const isModalOpen = useSelector ( selectVesselGroupModalOpen )
105108 const confirmationMode = useSelector ( selectVesselGroupConfirmationMode )
106109 const searchIdField = useSelector ( selectVesselGroupModalSearchIdField )
107110 const csvData = useSelector ( selectVesselGroupModalCsvData )
111+ const unmatchedIDs = useSelector ( selectVesselGroupModalUnmatchedIDs )
108112 const selectedCsvColumns = useSelector ( selectVesselGroupModalCsvColumns )
109113 const editingVesselGroupId = useSelector ( selectVesselGroupEditId )
110114 const vesselGroupModalSearchIds = useSelector ( selectVesselGroupsModalSearchIds )
@@ -156,21 +160,24 @@ function VesselGroupModal(): React.ReactElement<any> {
156160 if ( editingVesselGroup ?. name ) {
157161 dispatch ( setVesselGroupModalName ( editingVesselGroup ?. name ) )
158162 }
159- } , [ editingVesselGroup ?. name ] )
163+ } , [ dispatch , editingVesselGroup ?. name ] )
164+
165+ const vesselGroupModalSources = useSelector ( selectVesselGroupModalSources )
160166
161167 const sourceOptions = useMemo (
162168 ( ) =>
163- vesselDatasets . map ( ( d ) => ( {
164- id : d . id ,
165- label : getDatasetLabel ( d ) ,
166- } ) ) ,
167- [ vesselDatasets ]
169+ isGFWUser
170+ ? vesselDatasets . map ( ( d ) => ( {
171+ id : d . id ,
172+ label : getDatasetLabel ( d ) ,
173+ } ) )
174+ : [ ] ,
175+ [ vesselDatasets , isGFWUser ]
168176 )
169- const vesselGroupModalSources = useSelector ( selectVesselGroupModalSources )
170177
171178 const sourcesSelected = useMemo (
172- ( ) => sourceOptions . filter ( ( s ) => vesselGroupModalSources ?. includes ( s . id ) ) ,
173- [ sourceOptions , vesselGroupModalSources ]
179+ ( ) => ( isGFWUser ? sourceOptions . filter ( ( s ) => vesselGroupModalSources ?. includes ( s . id ) ) : [ ] ) ,
180+ [ isGFWUser , sourceOptions , vesselGroupModalSources ]
174181 )
175182
176183 const setGroupName = useCallback (
@@ -213,9 +220,23 @@ function VesselGroupModal(): React.ReactElement<any> {
213220 csvData ?: VesselGroupCsvData [ ]
214221 csvColumns ?: string [ ]
215222 } ) => {
216- const datasets = sourcesSelected . length
217- ? sourcesSelected . map ( ( { id } ) => id )
218- : sourceOptions . map ( ( { id } ) => id )
223+ const datasets = isGFWUser
224+ ? sourcesSelected . length
225+ ? sourcesSelected . map ( ( { id } ) => id )
226+ : sourceOptions . map ( ( { id } ) => id )
227+ : [ DEFAULT_VESSEL_IDENTITY_ID ]
228+
229+ trackEvent ( {
230+ category : TrackCategory . VesselGroups ,
231+ action : `match vessels from ${ ids ? 'IDs' : csvData && 'CSV' } to create a vessel group` ,
232+ label : getEventLabel ( [
233+ transmissionDateFrom && `active after: ${ transmissionDateFrom } ` ,
234+ transmissionDateTo && `active before: ${ transmissionDateTo } ` ,
235+ datasets && `datasets: ${ datasets . join ( ', ' ) } ` ,
236+ searchIdField && `id field: ${ searchIdField } ` ,
237+ ] ) ,
238+ } )
239+
219240 searchVesselGroupsVesselsRef . current = dispatch (
220241 searchVesselGroupsVesselsThunk ( {
221242 ids,
@@ -239,7 +260,16 @@ function VesselGroupModal(): React.ReactElement<any> {
239260 setError ( ( action . payload as any ) ?. message || '' )
240261 }
241262 } ,
242- [ dispatch , sourcesSelected , sourceOptions , t , transmissionDateFrom , transmissionDateTo ]
263+ [
264+ isGFWUser ,
265+ sourcesSelected ,
266+ sourceOptions ,
267+ transmissionDateFrom ,
268+ transmissionDateTo ,
269+ searchIdField ,
270+ dispatch ,
271+ t ,
272+ ]
243273 )
244274
245275 useEffect ( ( ) => {
@@ -303,11 +333,11 @@ function VesselGroupModal(): React.ReactElement<any> {
303333 dispatchSearchVesselsGroupsThunk ( { csvData, csvColumns : selectedCsvColumns } )
304334 }
305335 } , [
306- dispatchSearchVesselsGroupsThunk ,
307336 vesselGroupModalSearchIds ,
308337 searchIdField ,
309338 csvData ,
310339 selectedCsvColumns ,
340+ dispatchSearchVesselsGroupsThunk ,
311341 ] )
312342
313343 const onCreateGroupClick = useCallback (
@@ -396,8 +426,6 @@ function VesselGroupModal(): React.ReactElement<any> {
396426 dispatch ( resetVesselGroupReportData ( ) )
397427 dispatch ( fetchVesselGroupReportThunk ( { vesselGroupId : editingVesselGroupId } ) )
398428 }
399- close ( )
400- setButtonLoading ( '' )
401429 trackEvent ( {
402430 category : TrackCategory . VesselGroups ,
403431 action : `${ editingVesselGroupId ? 'Edit' : 'Create new' } vessel group` ,
@@ -407,6 +435,8 @@ function VesselGroupModal(): React.ReactElement<any> {
407435 ] ) ,
408436 value : `number of vessels: ${ vessels . length } ` ,
409437 } )
438+ close ( )
439+ setButtonLoading ( '' )
410440 }
411441 } ,
412442 [
@@ -486,7 +516,17 @@ function VesselGroupModal(): React.ReactElement<any> {
486516 return (
487517 < Modal
488518 appSelector = { ROOT_DOM_ELEMENT }
489- title = { t ( ( t ) => t . vesselGroup . vesselGroup ) }
519+ title = {
520+ < div className = { styles . textAlign } >
521+ { t ( ( t ) => t . vesselGroup . vesselGroup ) }
522+ < IconButton
523+ size = "small"
524+ icon = "info"
525+ type = "default"
526+ tooltip = { t ( ( t ) => t . vesselGroup . vesselGroupTooltip ) }
527+ />
528+ </ div >
529+ }
490530 isOpen = { isModalOpen }
491531 className = { styles . modal }
492532 contentClassName = { styles . modalContainer }
@@ -504,23 +544,28 @@ function VesselGroupModal(): React.ReactElement<any> {
504544 />
505545 { ! fullModalLoading && ! hasVesselGroupsVessels && (
506546 < Fragment >
507- < MultiSelect
508- label = { t ( ( t ) => t . layer . sources ) }
509- placeholder = { getPlaceholderBySelections ( {
510- selection : sourcesSelected . map ( ( { id } ) => id ) ,
511- options : sourceOptions ,
512- } ) }
513- options = { sourceOptions }
514- selectedOptions = { sourcesSelected }
515- onSelect = { onSelectSourceClick }
516- onRemove = { sourcesSelected ?. length > 1 ? onRemoveSourceClick : undefined }
517- />
547+ < div >
548+ { isGFWUser && (
549+ < MultiSelect
550+ label = { t ( ( t ) => t . layer . sources ) }
551+ placeholder = { getPlaceholderBySelections ( {
552+ selection : sourcesSelected . map ( ( { id } ) => id ) ,
553+ options : sourceOptions ,
554+ } ) }
555+ options = { sourceOptions }
556+ selectedOptions = { sourcesSelected }
557+ onSelect = { onSelectSourceClick }
558+ onRemove = { sourcesSelected ?. length > 1 ? onRemoveSourceClick : undefined }
559+ />
560+ ) }
561+ </ div >
518562 < div >
519563 < InputDate
520564 value = { transmissionDateTo || '' }
521565 max = { AVAILABLE_END . slice ( 0 , 10 ) as string }
522566 min = { AVAILABLE_START . slice ( 0 , 10 ) as string }
523567 label = { t ( ( t ) => t . common . active_after ) }
568+ labelTooltip = { t ( ( t ) => t . vesselGroup . activeAfterTooltip ) }
524569 onChange = { ( e ) => {
525570 setTransmissionDateTo ( e . target . value )
526571 } }
@@ -535,6 +580,7 @@ function VesselGroupModal(): React.ReactElement<any> {
535580 max = { AVAILABLE_END . slice ( 0 , 10 ) as string }
536581 min = { AVAILABLE_START . slice ( 0 , 10 ) as string }
537582 label = { t ( ( t ) => t . common . active_before ) }
583+ labelTooltip = { t ( ( t ) => t . vesselGroup . activeBeforeTooltip ) }
538584 onChange = { ( e ) => {
539585 setTransmissionDateFrom ( e . target . value )
540586 } }
@@ -585,6 +631,21 @@ function VesselGroupModal(): React.ReactElement<any> {
585631 } as VesselGroup ) ,
586632 } ) }
587633 </ label >
634+ { unmatchedIDs && (
635+ < label className = { styles . textAlign } >
636+ < Icon icon = "warning" type = "warning" />
637+ { t ( ( t ) => t . vesselGroup . unmatchedIDs , {
638+ field : searchIdField ,
639+ ids : unmatchedIDs . join ( ', ' ) ,
640+ } ) }
641+ < IconButton
642+ size = "small"
643+ icon = "copy"
644+ tooltip = { t ( ( t ) => t . common . copy , { text : 'IDs' } ) }
645+ onClick = { ( ) => navigator . clipboard . writeText ( unmatchedIDs . join ( ', ' ) ) }
646+ />
647+ </ label >
648+ ) }
588649 < div className = { styles . vesselsTableContainer } >
589650 < VesselGroupVessels searchIdField = { searchIdField || 'imo' } />
590651 </ div >
@@ -608,7 +669,7 @@ function VesselGroupModal(): React.ReactElement<any> {
608669 < div className = { styles . footerMsg } >
609670 { error && < span className = { styles . errorMsg } > { error } </ span > }
610671 { datasetsWithoutRelatedEvents . length >= 1 && (
611- < div className = { styles . disclaimerFooter } >
672+ < div className = { styles . textAlign } >
612673 < Icon icon = "warning" type = "warning" />
613674 { t ( ( t ) => t . vesselGroup . disclaimerFeaturesNotAvailable , {
614675 features : t ( ( t ) => t . vesselGroup . disclaimerFeaturesNotAvailableGenericPrefix ) ,
0 commit comments