geonetwork-ui 2.3.0-dev.c22cb78a → 2.3.0-dev.e07c5606

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.
Files changed (130) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +3 -3
  2. package/esm2022/libs/api/repository/src/lib/gn4/auth/avatar.service.interface.mjs +1 -1
  3. package/esm2022/libs/api/repository/src/lib/gn4/auth/gravatar.service.mjs +12 -1
  4. package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.mjs +36 -3
  5. package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +19 -7
  6. package/esm2022/libs/common/domain/src/lib/model/record/index.mjs +2 -1
  7. package/esm2022/libs/common/domain/src/lib/model/record/user-feedbacks.model.mjs +2 -0
  8. package/esm2022/libs/common/domain/src/lib/model/user/index.mjs +2 -0
  9. package/esm2022/libs/common/domain/src/lib/platform.service.interface.mjs +1 -1
  10. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.mjs +64 -0
  11. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +15 -6
  12. package/esm2022/libs/feature/editor/src/lib/components/wizard-field/wizard-field.component.mjs +1 -1
  13. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +9 -1
  14. package/esm2022/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.mjs +1 -1
  15. package/esm2022/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.mjs +1 -1
  16. package/esm2022/libs/feature/map/src/lib/add-layer-from-wms/add-layer-from-wms.component.mjs +1 -1
  17. package/esm2022/libs/feature/record/src/lib/feature-record.module.mjs +4 -4
  18. package/esm2022/libs/feature/record/src/lib/state/mdview.actions.mjs +22 -4
  19. package/esm2022/libs/feature/record/src/lib/state/mdview.effects.mjs +37 -10
  20. package/esm2022/libs/feature/record/src/lib/state/mdview.facade.mjs +22 -7
  21. package/esm2022/libs/feature/record/src/lib/state/mdview.reducer.mjs +52 -17
  22. package/esm2022/libs/feature/record/src/lib/state/mdview.selectors.mjs +18 -3
  23. package/esm2022/libs/feature/router/src/lib/default/state/router.effects.mjs +2 -2
  24. package/esm2022/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.mjs +2 -2
  25. package/esm2022/libs/ui/elements/src/lib/record-api-form/record-api-form.component.mjs +1 -1
  26. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +10 -2
  27. package/esm2022/libs/ui/elements/src/lib/user-feedback-item/time-since.pipe.mjs +59 -0
  28. package/esm2022/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.mjs +62 -0
  29. package/esm2022/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.mjs +17 -8
  30. package/esm2022/libs/ui/inputs/src/lib/text-area/text-area.component.mjs +27 -4
  31. package/esm2022/libs/ui/inputs/src/lib/text-input/text-input.component.mjs +19 -3
  32. package/esm2022/libs/ui/inputs/src/lib/ui-inputs.module.mjs +7 -6
  33. package/esm2022/translations/de.json +25 -0
  34. package/esm2022/translations/en.json +25 -0
  35. package/esm2022/translations/es.json +25 -0
  36. package/esm2022/translations/fr.json +25 -0
  37. package/esm2022/translations/it.json +25 -0
  38. package/esm2022/translations/nl.json +25 -0
  39. package/esm2022/translations/pt.json +25 -0
  40. package/fesm2022/geonetwork-ui.mjs +644 -68
  41. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  42. package/libs/api/repository/src/lib/gn4/auth/avatar.service.interface.d.ts +1 -0
  43. package/libs/api/repository/src/lib/gn4/auth/avatar.service.interface.d.ts.map +1 -1
  44. package/libs/api/repository/src/lib/gn4/auth/gravatar.service.d.ts +1 -0
  45. package/libs/api/repository/src/lib/gn4/auth/gravatar.service.d.ts.map +1 -1
  46. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts +6 -2
  47. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts.map +1 -1
  48. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +9 -6
  49. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
  50. package/libs/common/domain/src/lib/model/record/index.d.ts +1 -0
  51. package/libs/common/domain/src/lib/model/record/index.d.ts.map +1 -1
  52. package/libs/common/domain/src/lib/model/record/user-feedbacks.model.d.ts +15 -0
  53. package/libs/common/domain/src/lib/model/record/user-feedbacks.model.d.ts.map +1 -0
  54. package/libs/common/domain/src/lib/model/user/index.d.ts +2 -0
  55. package/libs/common/domain/src/lib/model/user/index.d.ts.map +1 -0
  56. package/libs/common/domain/src/lib/platform.service.interface.d.ts +3 -0
  57. package/libs/common/domain/src/lib/platform.service.interface.d.ts.map +1 -1
  58. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.d.ts +39 -0
  59. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.d.ts.map +1 -0
  60. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +3 -1
  61. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
  62. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  63. package/libs/feature/record/src/lib/feature-record.module.d.ts.map +1 -1
  64. package/libs/feature/record/src/lib/state/mdview.actions.d.ts +40 -6
  65. package/libs/feature/record/src/lib/state/mdview.actions.d.ts.map +1 -1
  66. package/libs/feature/record/src/lib/state/mdview.effects.d.ts +24 -4
  67. package/libs/feature/record/src/lib/state/mdview.effects.d.ts.map +1 -1
  68. package/libs/feature/record/src/lib/state/mdview.facade.d.ts +14 -4
  69. package/libs/feature/record/src/lib/state/mdview.facade.d.ts.map +1 -1
  70. package/libs/feature/record/src/lib/state/mdview.reducer.d.ts +8 -5
  71. package/libs/feature/record/src/lib/state/mdview.reducer.d.ts.map +1 -1
  72. package/libs/feature/record/src/lib/state/mdview.selectors.d.ts +12 -9
  73. package/libs/feature/record/src/lib/state/mdview.selectors.d.ts.map +1 -1
  74. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +16 -14
  75. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  76. package/libs/ui/elements/src/lib/user-feedback-item/time-since.pipe.d.ts +11 -0
  77. package/libs/ui/elements/src/lib/user-feedback-item/time-since.pipe.d.ts.map +1 -0
  78. package/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.d.ts +22 -0
  79. package/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.d.ts.map +1 -0
  80. package/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.d.ts +1 -1
  81. package/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.d.ts.map +1 -1
  82. package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts +7 -1
  83. package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts.map +1 -1
  84. package/libs/ui/inputs/src/lib/text-input/text-input.component.d.ts +4 -1
  85. package/libs/ui/inputs/src/lib/text-input/text-input.component.d.ts.map +1 -1
  86. package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts +35 -35
  87. package/package.json +1 -1
  88. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +2 -2
  89. package/src/libs/api/repository/src/lib/gn4/auth/avatar.service.interface.ts +1 -0
  90. package/src/libs/api/repository/src/lib/gn4/auth/gravatar.service.ts +12 -1
  91. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts +51 -1
  92. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +34 -7
  93. package/src/libs/common/domain/src/lib/model/record/index.ts +1 -0
  94. package/src/libs/common/domain/src/lib/model/record/user-feedbacks.model.ts +15 -0
  95. package/src/libs/common/domain/src/lib/platform.service.interface.ts +3 -0
  96. package/src/libs/common/fixtures/src/index.ts +8 -6
  97. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +1 -1
  98. package/src/libs/common/fixtures/src/lib/user-feedbacks.fixtures.ts +83 -0
  99. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.css +0 -0
  100. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.html +8 -0
  101. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.ts +64 -0
  102. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +28 -33
  103. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +11 -0
  104. package/src/libs/feature/editor/src/lib/fields.config.ts +8 -0
  105. package/src/libs/feature/record/src/lib/feature-record.module.ts +5 -2
  106. package/src/libs/feature/record/src/lib/state/mdview.actions.ts +51 -6
  107. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +82 -7
  108. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +49 -8
  109. package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +81 -24
  110. package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +40 -10
  111. package/src/libs/feature/router/src/lib/default/state/router.effects.ts +2 -2
  112. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +5 -0
  113. package/src/libs/ui/elements/src/lib/user-feedback-item/time-since.pipe.ts +54 -0
  114. package/src/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.css +0 -0
  115. package/src/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.html +75 -0
  116. package/src/libs/ui/elements/src/lib/user-feedback-item/user-feedback-item.component.ts +63 -0
  117. package/src/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts +13 -0
  118. package/src/libs/ui/inputs/src/lib/text-area/text-area.component.html +2 -1
  119. package/src/libs/ui/inputs/src/lib/text-area/text-area.component.ts +29 -0
  120. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +1 -1
  121. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +16 -1
  122. package/src/libs/ui/inputs/src/lib/ui-inputs.module.ts +1 -1
  123. package/translations/de.json +25 -0
  124. package/translations/en.json +25 -0
  125. package/translations/es.json +25 -0
  126. package/translations/fr.json +25 -0
  127. package/translations/it.json +25 -0
  128. package/translations/nl.json +25 -0
  129. package/translations/pt.json +25 -0
  130. package/translations/sk.json +25 -0
@@ -1,61 +1,118 @@
1
1
  import { Action, createReducer, on } from '@ngrx/store'
2
- import * as MdViewActions from './mdview.actions'
2
+ import * as MetadataViewActions from './mdview.actions'
3
3
  import { DatavizConfigurationModel } from '../../../../../../libs/common/domain/src/lib/model/dataviz/dataviz-configuration.model'
4
- import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
4
+ import {
5
+ CatalogRecord,
6
+ UserFeedback,
7
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
5
8
 
6
- export const MD_VIEW_FEATURE_STATE_KEY = 'mdView'
9
+ export const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView'
7
10
 
8
- export interface MdViewState {
11
+ export interface MetadataViewState {
9
12
  loadingFull: boolean
10
13
  error: { notFound?: boolean; otherError?: string } | null
11
14
  metadata?: Partial<CatalogRecord>
12
15
  related?: CatalogRecord[]
16
+ userFeedbacks?: UserFeedback[]
17
+ allUserFeedbacksLoading: boolean
18
+ addUserFeedbackLoading: boolean
13
19
  chartConfig?: DatavizConfigurationModel
14
20
  }
15
21
 
16
- export const initialMdviewState: MdViewState = {
22
+ export const initialMetadataViewState: MetadataViewState = {
17
23
  error: null,
18
24
  loadingFull: false,
25
+ allUserFeedbacksLoading: false,
26
+ addUserFeedbackLoading: false,
19
27
  }
20
28
 
21
- const mdViewReducer = createReducer(
22
- initialMdviewState,
23
- on(MdViewActions.loadFullMetadata, (state) => ({
29
+ const metadataViewReducer = createReducer(
30
+ initialMetadataViewState,
31
+
32
+ /*
33
+ Metadata reducers
34
+ */
35
+ on(MetadataViewActions.loadFullMetadata, (state) => ({
24
36
  ...state,
25
37
  error: null,
26
38
  loadingFull: true,
27
39
  })),
28
- on(MdViewActions.setIncompleteMetadata, (state, { incomplete }) => ({
40
+ on(MetadataViewActions.setIncompleteMetadata, (state, { incomplete }) => ({
29
41
  ...state,
30
42
  error: null,
31
43
  metadata: incomplete,
32
44
  })),
33
- on(MdViewActions.loadFullSuccess, (state, { full }) => ({
45
+ on(MetadataViewActions.loadFullMetadataSuccess, (state, { full }) => ({
34
46
  ...state,
35
47
  error: null,
36
48
  metadata: full,
37
49
  loadingFull: false,
38
50
  })),
39
- on(MdViewActions.loadFullFailure, (state, { otherError, notFound }) => ({
40
- ...state,
41
- error: { otherError, notFound },
42
- loadingFull: false,
43
- })),
44
- on(MdViewActions.setRelated, (state, { related }) => ({
51
+ on(
52
+ MetadataViewActions.loadFullMetadataFailure,
53
+ (state, { otherError, notFound }) => ({
54
+ ...state,
55
+ error: { otherError, notFound },
56
+ loadingFull: false,
57
+ })
58
+ ),
59
+ on(MetadataViewActions.closeMetadata, (state) => {
60
+ const { metadata, related, userFeedbacks, ...stateWithoutMetadata } = state
61
+ return stateWithoutMetadata
62
+ }),
63
+
64
+ /*
65
+ Related reducers
66
+ */
67
+ on(MetadataViewActions.setRelated, (state, { related }) => ({
45
68
  ...state,
46
69
  related,
47
70
  })),
48
- on(MdViewActions.setChartConfig, (state, { chartConfig }) => ({
71
+
72
+ /*
73
+ ChartConfig reducers
74
+ */
75
+ on(MetadataViewActions.setChartConfig, (state, { chartConfig }) => ({
49
76
  ...state,
50
77
  chartConfig,
51
78
  })),
52
- on(MdViewActions.close, (state) => {
53
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
- const { metadata, related, ...stateWithoutMd } = state
55
- return stateWithoutMd
56
- })
79
+
80
+ /*
81
+ UserFeedbacks reducers
82
+ */
83
+ on(MetadataViewActions.loadUserFeedbacks, (state) => ({
84
+ ...state,
85
+ error: null,
86
+ allUserFeedbacksLoading: true,
87
+ })),
88
+ on(MetadataViewActions.addUserFeedback, (state) => ({
89
+ ...state,
90
+ addUserFeedbackLoading: true,
91
+ })),
92
+ on(
93
+ MetadataViewActions.loadUserFeedbacksSuccess,
94
+ (state, { userFeedbacks }) => ({
95
+ ...state,
96
+ error: null,
97
+ userFeedbacks: userFeedbacks,
98
+ addUserFeedbackLoading: false,
99
+ allUserFeedbacksLoading: false,
100
+ })
101
+ ),
102
+ on(
103
+ MetadataViewActions.loadUserFeedbacksFailure,
104
+ (state, { otherError, notFound }) => ({
105
+ ...state,
106
+ error: { otherError, notFound },
107
+ addUserFeedbackLoading: false,
108
+ allUserFeedbacksLoading: false,
109
+ })
110
+ )
57
111
  )
58
112
 
59
- export function reducer(state: MdViewState | undefined, action: Action) {
60
- return mdViewReducer(state, action)
113
+ export function reducer(
114
+ metadataViewState: MetadataViewState | undefined,
115
+ action: Action
116
+ ) {
117
+ return metadataViewReducer(metadataViewState, action)
61
118
  }
@@ -1,36 +1,66 @@
1
1
  import { createFeatureSelector, createSelector } from '@ngrx/store'
2
- import { MD_VIEW_FEATURE_STATE_KEY, MdViewState } from './mdview.reducer'
2
+ import {
3
+ METADATA_VIEW_FEATURE_STATE_KEY,
4
+ MetadataViewState,
5
+ } from './mdview.reducer'
3
6
 
4
- export const getMdViewState = createFeatureSelector<MdViewState>(
5
- MD_VIEW_FEATURE_STATE_KEY
7
+ export const getMdViewState = createFeatureSelector<MetadataViewState>(
8
+ METADATA_VIEW_FEATURE_STATE_KEY
6
9
  )
7
10
 
11
+ /*
12
+ Metadata selectors
13
+ */
8
14
  export const getMetadataUuid = createSelector(
9
15
  getMdViewState,
10
- (state: MdViewState) =>
16
+ (state: MetadataViewState) =>
11
17
  state.metadata ? state.metadata.uniqueIdentifier : null
12
18
  )
13
19
  export const getMetadata = createSelector(
14
20
  getMdViewState,
15
- (state: MdViewState) => state.metadata
21
+ (state: MetadataViewState) => state.metadata
16
22
  )
17
23
  export const getMetadataIsIncomplete = createSelector(
18
24
  getMdViewState,
19
- (state: MdViewState) => (state.metadata ? state.loadingFull : null)
25
+ (state: MetadataViewState) => (state.metadata ? state.loadingFull : null)
20
26
  )
21
27
  export const getMetadataIsLoading = createSelector(
22
28
  getMdViewState,
23
- (state: MdViewState) => state.loadingFull
29
+ (state: MetadataViewState) => state.loadingFull
24
30
  )
25
31
  export const getMetadataError = createSelector(
26
32
  getMdViewState,
27
- (state: MdViewState) => state.error
33
+ (state: MetadataViewState) => state.error
28
34
  )
35
+
36
+ /*
37
+ Related selectors
38
+ */
29
39
  export const getRelated = createSelector(
30
40
  getMdViewState,
31
- (state: MdViewState) => state.related
41
+ (state: MetadataViewState) => state.related
32
42
  )
43
+
44
+ /*
45
+ Metadata selectors
46
+ */
33
47
  export const getChartConfig = createSelector(
34
48
  getMdViewState,
35
- (state: MdViewState) => state.chartConfig
49
+ (state: MetadataViewState) => state.chartConfig
50
+ )
51
+
52
+ /*
53
+ UserFeedback selectors
54
+ */
55
+ export const getUserFeedbacks = createSelector(
56
+ getMdViewState,
57
+ (state: MetadataViewState) => state.userFeedbacks
58
+ )
59
+ export const getAllUserFeedbacksLoading = createSelector(
60
+ getMdViewState,
61
+ (state: MetadataViewState) => state.allUserFeedbacksLoading
62
+ )
63
+ export const getAddUserFeedbacksLoading = createSelector(
64
+ getMdViewState,
65
+ (state: MetadataViewState) => state.addUserFeedbackLoading
36
66
  )
@@ -15,7 +15,7 @@ import {
15
15
  } from '../../../../../../../libs/common/domain/src/lib/model/search'
16
16
  import { Actions, createEffect, ofType } from '@ngrx/effects'
17
17
  import { navigation } from '@ngrx/router-store/data-persistence'
18
- import { of, pairwise, startWith, withLatestFrom } from 'rxjs'
18
+ import { of, pairwise, startWith } from 'rxjs'
19
19
  import { map, mergeMap, tap } from 'rxjs/operators'
20
20
  import * as RouterActions from './router.actions'
21
21
  import { RouterFacade } from './router.facade'
@@ -141,7 +141,7 @@ export class RouterEffects {
141
141
  navigateToSearch$ = createEffect(() =>
142
142
  this._actions$.pipe(
143
143
  navigation(this.routerConfig.searchRouteComponent, {
144
- run: () => MdViewActions.close(),
144
+ run: () => MdViewActions.closeMetadata(),
145
145
  })
146
146
  )
147
147
  )
@@ -31,6 +31,8 @@ import { MaxLinesComponent } from './max-lines/max-lines.component'
31
31
  import { RecordApiFormComponent } from './record-api-form/record-api-form.component'
32
32
  import { MarkdownParserComponent } from './markdown-parser/markdown-parser.component'
33
33
  import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-overlay-preview.component'
34
+ import { UserFeedbackItemComponent } from './user-feedback-item/user-feedback-item.component'
35
+ import { TimeSincePipe } from './user-feedback-item/time-since.pipe'
34
36
 
35
37
  @NgModule({
36
38
  imports: [
@@ -47,6 +49,7 @@ import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-over
47
49
  NgOptimizedImage,
48
50
  MarkdownParserComponent,
49
51
  ThumbnailComponent,
52
+ TimeSincePipe,
50
53
  ],
51
54
  declarations: [
52
55
  MetadataInfoComponent,
@@ -68,6 +71,7 @@ import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-over
68
71
  PaginationButtonsComponent,
69
72
  MaxLinesComponent,
70
73
  RecordApiFormComponent,
74
+ UserFeedbackItemComponent,
71
75
  ImageOverlayPreviewComponent,
72
76
  ],
73
77
  exports: [
@@ -91,6 +95,7 @@ import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-over
91
95
  MaxLinesComponent,
92
96
  RecordApiFormComponent,
93
97
  MarkdownParserComponent,
98
+ UserFeedbackItemComponent,
94
99
  ImageOverlayPreviewComponent,
95
100
  ],
96
101
  })
@@ -0,0 +1,54 @@
1
+ import { Pipe, PipeTransform } from '@angular/core'
2
+ import { TranslateService } from '@ngx-translate/core'
3
+ import { formatDistance } from 'date-fns'
4
+ import { de, enUS, es, fr, it, nl, pt, sk } from 'date-fns/locale'
5
+
6
+ @Pipe({
7
+ name: 'timeSince',
8
+ standalone: true,
9
+ })
10
+ export class TimeSincePipe implements PipeTransform {
11
+ constructor(private translate: TranslateService) {}
12
+
13
+ transform(value: Date): string {
14
+ if (isNaN(value.getTime())) {
15
+ throw new Error('Invalid Date')
16
+ }
17
+
18
+ const maintenant = new Date()
19
+ let locale: Locale
20
+
21
+ switch (this.translate.currentLang) {
22
+ case 'fr':
23
+ locale = fr
24
+ break
25
+ case 'de':
26
+ locale = de
27
+ break
28
+ case 'es':
29
+ locale = es
30
+ break
31
+ case 'it':
32
+ locale = it
33
+ break
34
+ case 'nl':
35
+ locale = nl
36
+ break
37
+ case 'pt':
38
+ locale = pt
39
+ break
40
+ case 'sk':
41
+ locale = sk
42
+ break
43
+ case 'en':
44
+ default:
45
+ locale = enUS
46
+ break
47
+ }
48
+
49
+ return formatDistance(value, maintenant, {
50
+ addSuffix: true,
51
+ locale: locale,
52
+ })
53
+ }
54
+ }
@@ -0,0 +1,75 @@
1
+ <div
2
+ *ngIf="userFeedbackParent.published"
3
+ class="flex flex-col bg-white rounded w-full"
4
+ [ngClass]="[isAnAnswer ? 'ps-4 ' : 'p-4']"
5
+ >
6
+ <div class="flex flex-row">
7
+ <div class="avatar">
8
+ <img
9
+ class="rounded-full"
10
+ [src]="userFeedbackParent.avatarUrl"
11
+ alt="avatar"
12
+ />
13
+ </div>
14
+ <div class="p-4 flex flex-col">
15
+ <span>{{ userFeedbackParent.authorName }}</span>
16
+ <span> {{ userFeedbackParent.date | timeSince }}</span>
17
+ </div>
18
+ </div>
19
+ <div data-cy="commentText" class="mt-4 whitespace-pre-line">
20
+ {{ userFeedbackParent.comment }}
21
+ </div>
22
+ <div
23
+ class="w-full"
24
+ *ngFor="let userFeedBacksAnswer of userFeedBacksAnswers; let last = last"
25
+ >
26
+ <hr class="-mx-4 my-6" />
27
+ <gn-ui-user-feedback-item
28
+ [userFeedbackParent]="userFeedBacksAnswer"
29
+ [isLastComment]="last"
30
+ ></gn-ui-user-feedback-item>
31
+ </div>
32
+
33
+ <div *ngIf="isActiveUserEditor" class="mt-2 flex flex-col">
34
+ <hr class="-mx-4 my-4" />
35
+ <div
36
+ id="new-comment-buttons"
37
+ class="flex flex-row gap-2 items-center justify-end"
38
+ >
39
+ <gn-ui-text-area
40
+ [disabled]="isAddUserFeedbackLoading"
41
+ [(value)]="newAnswer"
42
+ (valueChange)="onNewAnswerValueChange()"
43
+ (keyup.control.enter)="publishNewAnswer()"
44
+ [placeholder]="
45
+ 'record.metadata.userFeedbacks.newAnswer.placeholder' | translate
46
+ "
47
+ class="grow"
48
+ extraClass="bg-transparent border-0 placeholder-primary-darker text-primary-darker h-9"
49
+ ></gn-ui-text-area>
50
+ <div *ngIf="!isAnswerEmpty" class="flex flex-row justify-end">
51
+ <gn-ui-button
52
+ [disabled]="isAddUserFeedbackLoading"
53
+ [type]="'outline'"
54
+ (buttonClick)="publishNewAnswer()"
55
+ [title]="
56
+ 'record.metadata.userFeedbacks.newAnswer.buttonTitle' | translate
57
+ "
58
+ extraClass="!p-[0.5em] text-primary-darker border-primary-darker h-9"
59
+ >
60
+ <mat-icon
61
+ class="material-symbols-outlined"
62
+ *ngIf="!isAddUserFeedbackLoading"
63
+ >
64
+ send
65
+ </mat-icon>
66
+ <ng-container *ngIf="isAddUserFeedbackLoading">
67
+ <div class="flex justify-center w-full">
68
+ <gn-ui-spinning-loader></gn-ui-spinning-loader>
69
+ </div>
70
+ </ng-container>
71
+ </gn-ui-button>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
@@ -0,0 +1,63 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ OnInit,
7
+ Output,
8
+ } from '@angular/core'
9
+ import {
10
+ UserFeedback,
11
+ UserFeedbackViewModel,
12
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
13
+ import { UserModel } from '../../../../../../libs/common/domain/src/lib/model/user'
14
+
15
+ @Component({
16
+ selector: 'gn-ui-user-feedback-item',
17
+ templateUrl: './user-feedback-item.component.html',
18
+ styleUrls: ['./user-feedback-item.component.css'],
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ })
21
+ export class UserFeedbackItemComponent implements OnInit {
22
+ @Input() userFeedbackParent: UserFeedbackViewModel
23
+ @Input() userFeedBacksAnswers: UserFeedbackViewModel[]
24
+ @Input() isActiveUserEditor: boolean
25
+ @Input() activeUser: UserModel
26
+ @Input() isLastComment: boolean
27
+ @Input() isAddUserFeedbackLoading: boolean
28
+
29
+ @Output() newUserFeedbackAnswer = new EventEmitter<UserFeedback>()
30
+
31
+ isAnAnswer = false
32
+ newAnswer = ''
33
+ isAnswerEmpty = true
34
+
35
+ ngOnInit(): void {
36
+ this.isAnAnswer = !!this.userFeedbackParent.parentUuid
37
+ }
38
+
39
+ onNewAnswerValueChange() {
40
+ this.isAnswerEmpty = this.newAnswer.length === 0
41
+ }
42
+
43
+ publishNewAnswer() {
44
+ if (this.newAnswer.trim() === '') return
45
+
46
+ const newAnswer: UserFeedback = {
47
+ ...this.userFeedbackParent,
48
+ uuid: undefined,
49
+ published: true,
50
+ comment: this.newAnswer,
51
+ parentUuid: this.userFeedbackParent.uuid,
52
+ authorUserId: this.activeUser?.id,
53
+ authorEmail: this.activeUser?.email,
54
+ date: new Date(),
55
+ authorName: `${this.activeUser?.name} ${this.activeUser?.surname}`,
56
+ }
57
+
58
+ this.newUserFeedbackAnswer.emit(newAnswer)
59
+
60
+ this.newAnswer = ''
61
+ this.onNewAnswerValueChange()
62
+ }
63
+ }
@@ -2,6 +2,7 @@ import {
2
2
  CdkConnectedOverlay,
3
3
  CdkOverlayOrigin,
4
4
  ConnectedPosition,
5
+ OverlayModule,
5
6
  } from '@angular/cdk/overlay'
6
7
  import {
7
8
  ChangeDetectionStrategy,
@@ -17,6 +18,10 @@ import {
17
18
  } from '@angular/core'
18
19
  import { firstValueFrom } from 'rxjs'
19
20
  import { DropdownChoice } from './dropdown-selector.model'
21
+ import { CommonModule } from '@angular/common'
22
+ import { TranslateModule } from '@ngx-translate/core'
23
+ import { MatIconModule } from '@angular/material/icon'
24
+ import { ButtonComponent } from '../button/button.component'
20
25
 
21
26
  const DEFAULT_ROW_NUMBERS = 6
22
27
 
@@ -25,6 +30,14 @@ const DEFAULT_ROW_NUMBERS = 6
25
30
  templateUrl: './dropdown-selector.component.html',
26
31
  styleUrls: ['./dropdown-selector.component.css'],
27
32
  changeDetection: ChangeDetectionStrategy.OnPush,
33
+ standalone: true,
34
+ imports: [
35
+ CommonModule,
36
+ ButtonComponent,
37
+ OverlayModule,
38
+ MatIconModule,
39
+ TranslateModule,
40
+ ],
28
41
  })
29
42
  export class DropdownSelectorComponent implements OnInit {
30
43
  @Input() title: string
@@ -2,11 +2,12 @@
2
2
  <textarea
3
3
  #input
4
4
  name="textArea"
5
+ [disabled]="disabled"
5
6
  [placeholder]="placeholder"
6
7
  [value]="value"
7
8
  (change)="handleChange($event)"
8
9
  (input)="handleChange($event)"
9
- class="w-full pt-2 pl-2 resize-none border border-gray-800 rounded italic leading-tight focus:outline-none focus:bg-background focus:border-primary"
10
+ [class]="classList"
10
11
  [attr.required]="required || null"
11
12
  ></textarea>
12
13
  </div>
@@ -15,7 +15,12 @@ import { distinctUntilChanged } from 'rxjs/operators'
15
15
  standalone: true,
16
16
  })
17
17
  export class TextAreaComponent implements AfterViewInit {
18
+ private readonly baseClasses: string
19
+ private readonly disabledClasses: string
20
+
18
21
  @Input() value = ''
22
+ @Input() disabled = false
23
+ @Input() extraClass = ''
19
24
  @Input() placeholder: string
20
25
  @Input() required = false
21
26
 
@@ -24,6 +29,30 @@ export class TextAreaComponent implements AfterViewInit {
24
29
 
25
30
  @ViewChild('input') input
26
31
 
32
+ constructor() {
33
+ this.baseClasses = [
34
+ 'w-full',
35
+ 'pt-2',
36
+ 'pl-2',
37
+ 'resize-none',
38
+ 'border',
39
+ 'border-gray-800',
40
+ 'rounded italic',
41
+ 'leading-tight',
42
+ 'focus:outline-none',
43
+ 'focus:bg-background',
44
+ 'focus:border-primary',
45
+ ].join(' ')
46
+
47
+ this.disabledClasses = ['cursor-not-allowed'].join(' ')
48
+ }
49
+
50
+ get classList() {
51
+ return `${this.baseClasses} ${this.extraClass} ${
52
+ this.disabled ? this.disabledClasses : ''
53
+ }`
54
+ }
55
+
27
56
  ngAfterViewInit() {
28
57
  this.checkRequired(this.input.nativeElement.value)
29
58
  }
@@ -1,6 +1,6 @@
1
1
  <input
2
2
  #input
3
- class="appearance-none border border-gray-300 rounded w-full p-2 text-gray-700 leading-tight focus:outline-none focus:border-primary"
3
+ [class]="classList"
4
4
  type="text"
5
5
  [value]="value"
6
6
  (change)="handleChange($event)"
@@ -14,14 +14,29 @@ import { Subject } from 'rxjs'
14
14
  styleUrls: ['./text-input.component.css'],
15
15
  })
16
16
  export class TextInputComponent implements AfterViewInit {
17
+ private readonly baseClass = [
18
+ 'appearance-none',
19
+ 'border border-gray-300',
20
+ 'rounded w-full',
21
+ 'p-2',
22
+ 'text-gray-700',
23
+ 'leading-tight',
24
+ 'focus:outline-none',
25
+ 'focus:border-primary',
26
+ ].join(' ')
27
+
17
28
  @Input() value = ''
29
+ @Input() extraClass = ''
18
30
  @Input() hint: string
19
31
  @Input() required = false
20
32
  rawChange = new Subject<string>()
21
33
  @Output() valueChange = this.rawChange.pipe(distinctUntilChanged())
22
-
23
34
  @ViewChild('input') input
24
35
 
36
+ get classList() {
37
+ return `${this.baseClass} ${this.extraClass}`
38
+ }
39
+
25
40
  ngAfterViewInit() {
26
41
  this.checkRequired(this.input.nativeElement.value)
27
42
  }
@@ -36,7 +36,6 @@ import { ImageInputComponent } from './image-input/image-input.component'
36
36
 
37
37
  @NgModule({
38
38
  declarations: [
39
- DropdownSelectorComponent,
40
39
  AutocompleteComponent,
41
40
  TextInputComponent,
42
41
  DragAndDropFileInputComponent,
@@ -73,6 +72,7 @@ import { ImageInputComponent } from './image-input/image-input.component'
73
72
  TextAreaComponent,
74
73
  ButtonComponent,
75
74
  ImageInputComponent,
75
+ DropdownSelectorComponent,
76
76
  ],
77
77
  exports: [
78
78
  DropdownSelectorComponent,
@@ -1,4 +1,5 @@
1
1
  {
2
+ "button.login": "",
2
3
  "catalog.figures.datasets": "{count, plural, =0{Datensätze} one{Datensatz} other{Datensätze}}",
3
4
  "catalog.figures.organisations": "{count, plural, =0{Organisationen} one{Organisation} other{Organisationen}}",
4
5
  "chart.aggregation.average": "Durchschnitt",
@@ -144,6 +145,16 @@
144
145
  "downloads.format.unknown": "unbekannt",
145
146
  "downloads.wfs.featuretype.not.found": "Der Layer wurde nicht gefunden",
146
147
  "dropFile": "Datei ablegen",
148
+ "editor.record.form.license": "Lizenz",
149
+ "editor.record.form.license.cc-by": "",
150
+ "editor.record.form.license.cc-by-sa": "",
151
+ "editor.record.form.license.cc-zero": "",
152
+ "editor.record.form.license.etalab": "",
153
+ "editor.record.form.license.etalab-v2": "",
154
+ "editor.record.form.license.odbl": "",
155
+ "editor.record.form.license.odc-by": "",
156
+ "editor.record.form.license.pddl": "",
157
+ "editor.record.form.license.unknown": "Unbekannt oder nicht vorhanden",
147
158
  "editor.record.loadError.body": "",
148
159
  "editor.record.loadError.closeMessage": "",
149
160
  "editor.record.loadError.title": "",
@@ -278,6 +289,14 @@
278
289
  "record.metadata.quality.updateFrequency.failed": "Aktualisierungsfrequenz nicht angegeben",
279
290
  "record.metadata.quality.updateFrequency.success": "Aktualisierungsfrequenz angegeben",
280
291
  "record.metadata.related": "Ähnliche Datensätze",
292
+ "record.metadata.userFeedbacks": "",
293
+ "record.metadata.userFeedbacks.anonymousUser": "",
294
+ "record.metadata.userFeedbacks.sortSelector.label": "",
295
+ "record.metadata.userFeedbacks.sortSelector.choices.newestFirst": "",
296
+ "record.metadata.userFeedbacks.sortSelector.choices.oldestFirst": "",
297
+ "record.metadata.userFeedbacks.newComment.placeholder": "",
298
+ "record.metadata.userFeedbacks.newAnswer.placeholder": "",
299
+ "record.metadata.userFeedbacks.newAnswer.buttonTitle": "",
281
300
  "record.metadata.sheet": "Weitere Informationen verfügbar unter:",
282
301
  "record.metadata.status": "Status",
283
302
  "record.metadata.technical": "Technische Informationen",
@@ -351,6 +370,12 @@
351
370
  "table.loading.data": "Daten werden geladen...",
352
371
  "table.object.count": "Objekte in diesem Datensatz",
353
372
  "table.select.data": "Datenquelle",
373
+ "timeSincePipe.lessThanAMinute": "",
374
+ "timeSincePipe.minutesAgo": "",
375
+ "timeSincePipe.hoursAgo": "",
376
+ "timeSincePipe.daysAgo": "",
377
+ "timeSincePipe.monthsAgo": "",
378
+ "timeSincePipe.yearsAgo": "",
354
379
  "tooltip.html.copy": "HTML kopieren",
355
380
  "tooltip.id.copy": "Eindeutige Kennung kopieren",
356
381
  "tooltip.url.copy": "URL kopieren",