From 1d7afe43a64f2b47a5f36dc205f4d7cc1e1ca47f Mon Sep 17 00:00:00 2001
From: Camille Moinier <cmoinier@wrk208.wrk.prs.camptocamp.com>
Date: Fri, 13 Oct 2023 15:39:35 +0200
Subject: [PATCH] feat(me/dh): overview of my org records

---
 apps/metadata-editor-e2e/src/e2e/my-org.cy.ts |  44 ++++
 apps/metadata-editor/src/app/app.routes.ts    |   5 +
 .../my-org-users/my-org-users.component.css   |   7 +
 .../my-org-users/my-org-users.component.html  |   6 +
 .../my-org-users.component.spec.ts            | 131 +++++++++++
 .../my-org-users/my-org-users.component.ts    |  48 ++++
 .../my-org-records.component.html             |   8 +-
 .../my-org-records.component.spec.ts          | 208 +++++++++++-------
 .../my-org-records.component.ts               |  50 +++--
 .../app/records/records-list.component.html   |  55 ++++-
 .../src/app/records/records-list.component.ts |  12 +
 conf/default.toml                             |   2 +-
 docs/apps/editor.md                           |  13 +-
 .../src/lib/gn4/auth/auth.service.spec.ts     |   2 +
 .../src/lib/gn4/auth/auth.service.ts          |   5 +
 .../fixtures/src/lib/organisations.fixture.ts |   6 +
 libs/common/fixtures/src/lib/user.fixtures.ts |  31 +++
 libs/feature/catalog/src/index.ts             |   1 +
 .../src/lib/my-org/my-org.service.spec.ts     | 113 ++++++++++
 .../catalog/src/lib/my-org/my-org.service.ts  |  61 +++++
 .../organisations.component.spec.ts           |   2 +-
 .../record-table/record-table.component.html  |  38 +++-
 .../record-table/record-table.component.ts    |   2 +-
 translations/de.json                          |   7 +
 translations/en.json                          |   7 +
 translations/es.json                          |   7 +
 translations/fr.json                          |   7 +
 translations/it.json                          |   7 +
 translations/nl.json                          |   7 +
 translations/pt.json                          |   7 +
 30 files changed, 797 insertions(+), 102 deletions(-)
 create mode 100644 apps/metadata-editor-e2e/src/e2e/my-org.cy.ts
 create mode 100644 apps/metadata-editor/src/app/my-org-users/my-org-users.component.css
 create mode 100644 apps/metadata-editor/src/app/my-org-users/my-org-users.component.html
 create mode 100644 apps/metadata-editor/src/app/my-org-users/my-org-users.component.spec.ts
 create mode 100644 apps/metadata-editor/src/app/my-org-users/my-org-users.component.ts
 create mode 100644 libs/feature/catalog/src/lib/my-org/my-org.service.spec.ts
 create mode 100644 libs/feature/catalog/src/lib/my-org/my-org.service.ts

diff --git a/apps/metadata-editor-e2e/src/e2e/my-org.cy.ts b/apps/metadata-editor-e2e/src/e2e/my-org.cy.ts
new file mode 100644
index 0000000000..2362dbcc74
--- /dev/null
+++ b/apps/metadata-editor-e2e/src/e2e/my-org.cy.ts
@@ -0,0 +1,44 @@
+describe('my-org', () => {
+  beforeEach(() => {
+    cy.loginGN('barbie', 'p4ssworD_', false)
+    cy.visit(`/records/my-org`)
+    cy.get('md-editor-dashboard-menu').find('a').first().click()
+    cy.get('main').children('div').first().children('div').eq(1).as('linkGroup')
+  })
+  describe('my-org display', () => {
+    it('should show my-org name and logo', () => {
+      cy.get('h1').should('not.have.text', '')
+      cy.get('gn-ui-thumbnail')
+    })
+    it('should show the user and record count', () => {
+      cy.get('@linkGroup')
+        .find('a')
+        .children('span')
+        .first()
+        .should('not.have.text', '')
+      cy.get('@linkGroup')
+        .find('gn-ui-button')
+        .children('span')
+        .first()
+        .should('not.have.text', '')
+    })
+    it('should show my-org records', () => {
+      cy.get('.grid').should('have.length.above', 0)
+    })
+  })
+  describe('routing', () => {
+    it('should access the datahub with a filter', () => {
+      cy.get('@linkGroup').find('a').click()
+      cy.url().should('include', 'search/publisher=')
+    })
+    it('should access the user list page and show my-org users', () => {
+      cy.visit(`/records/my-org`)
+      cy.get('md-editor-dashboard-menu').find('a').first().click()
+      cy.get('@linkGroup').find('gn-ui-button').click()
+      cy.url().should('include', '/users/my-org')
+      cy.get('.grid').should('have.length.above', 0)
+      cy.get('h1').should('not.have.text', '')
+      cy.get('gn-ui-thumbnail')
+    })
+  })
+})
diff --git a/apps/metadata-editor/src/app/app.routes.ts b/apps/metadata-editor/src/app/app.routes.ts
index 8f95f45a5d..e6fa2b5e1a 100644
--- a/apps/metadata-editor/src/app/app.routes.ts
+++ b/apps/metadata-editor/src/app/app.routes.ts
@@ -10,6 +10,7 @@ import { MyRecordsComponent } from './records/my-records/my-records.component'
 import { MyDraftComponent } from './records/my-draft/my-draft.component'
 import { MyLibraryComponent } from './records/my-library/my-library.component'
 import { SearchRecordsComponent } from './records/search-records/search-records-list.component'
+import { MyOrgUsersComponent } from './my-org-users/my-org-users.component'
 
 export const appRoutes: Route[] = [
   { path: '', component: DashboardPageComponent, pathMatch: 'prefix' },
@@ -61,6 +62,10 @@ export const appRoutes: Route[] = [
       },
     ],
   },
+  {
+    path: 'users/my-org',
+    component: MyOrgUsersComponent,
+  },
   { path: 'sign-in', component: SignInPageComponent },
   { path: 'create', component: CreatePageComponent },
   {
diff --git a/apps/metadata-editor/src/app/my-org-users/my-org-users.component.css b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.css
new file mode 100644
index 0000000000..8318f27f3e
--- /dev/null
+++ b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.css
@@ -0,0 +1,7 @@
+.record-table-col {
+  @apply px-5 py-3 items-center truncate;
+}
+
+.record-table-header {
+  @apply record-table-col capitalize;
+}
diff --git a/apps/metadata-editor/src/app/my-org-users/my-org-users.component.html b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.html
new file mode 100644
index 0000000000..1ecec479eb
--- /dev/null
+++ b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.html
@@ -0,0 +1,6 @@
+<md-editor-records-list
+  [users]="orgData.userList"
+  [logo]="orgData.logoUrl"
+  [title]="orgData.orgName"
+>
+</md-editor-records-list>
diff --git a/apps/metadata-editor/src/app/my-org-users/my-org-users.component.spec.ts b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.spec.ts
new file mode 100644
index 0000000000..897fa36d43
--- /dev/null
+++ b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.spec.ts
@@ -0,0 +1,131 @@
+import { MyOrgUsersComponent } from './my-org-users.component'
+import { BehaviorSubject, of } from 'rxjs'
+import { MyOrgService } from '@geonetwork-ui/feature/catalog'
+import {
+  ORGANISATIONS_FIXTURE,
+  USER_FIXTURE_ANON,
+  USERS_FIXTURE,
+} from '@geonetwork-ui/common/fixtures'
+import { AuthService } from '@geonetwork-ui/api/repository/gn4'
+import { SearchFacade } from '@geonetwork-ui/feature/search'
+
+describe('MyOrgUsersComponent', () => {
+  let component: MyOrgUsersComponent
+  let searchFacade: SearchFacade
+  let myOrgService: MyOrgService
+  let authService: AuthService
+
+  beforeEach(() => {
+    const user = USER_FIXTURE_ANON()
+    const allUsers = USERS_FIXTURE()
+
+    const myOrgServiceMock = {
+      myOrgData$: of({
+        orgName: 'wizard-org',
+        logoUrl: 'https://my-geonetwork.org/logo11.png',
+        recordCount: 10,
+        userCount: 3,
+        userList: [
+          {
+            id: '161',
+            profile: 'Administrator',
+            username: 'ghost16',
+            name: 'Ghost',
+            surname: 'Old',
+            email: 'old.ghost@wiz.fr',
+            organisation: 'wizard-org',
+            profileIcon:
+              'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
+          },
+          {
+            id: '3',
+            profile: 'Editor',
+            username: 'voldy63',
+            name: 'Lord',
+            surname: 'Voldemort',
+            email: 'lord.voldy@wiz.com',
+            organisation: 'wizard-org',
+          },
+          {
+            id: '4',
+            profile: 'Editor',
+            username: 'al.dumble98',
+            name: 'Albus',
+            surname: 'Dumbledore',
+            email: 'albus.dumble@wiz.com',
+            organisation: 'wizard-org',
+          },
+        ],
+      }),
+    }
+
+    const authServiceMock = {
+      user$: new BehaviorSubject(user),
+      allUsers$: new BehaviorSubject(allUsers),
+    }
+
+    const organisationsServiceMock = {
+      organisations$: of(ORGANISATIONS_FIXTURE),
+    }
+
+    const searchFacadeMock = {
+      resetSearch: jest.fn(),
+    }
+
+    myOrgService = myOrgServiceMock as any
+    authService = authServiceMock as any
+    searchFacade = searchFacadeMock as any
+
+    component = new MyOrgUsersComponent(myOrgService, searchFacade)
+  })
+
+  it('should create', () => {
+    expect(component).toBeTruthy()
+  })
+
+  describe('Get organization users info', () => {
+    it('should get the org name', () => {
+      expect(component.orgData.orgName).toEqual('wizard-org')
+    })
+
+    it('should get the org logo', () => {
+      expect(component.orgData.logoUrl).toEqual(
+        'https://my-geonetwork.org/logo11.png'
+      )
+    })
+
+    it('should get the list of users', () => {
+      expect(component.orgData.userList).toEqual([
+        {
+          id: '161',
+          profile: 'Administrator',
+          username: 'ghost16',
+          name: 'Ghost',
+          surname: 'Old',
+          email: 'old.ghost@wiz.fr',
+          organisation: 'wizard-org',
+          profileIcon:
+            'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
+        },
+        {
+          id: '3',
+          profile: 'Editor',
+          username: 'voldy63',
+          name: 'Lord',
+          surname: 'Voldemort',
+          email: 'lord.voldy@wiz.com',
+          organisation: 'wizard-org',
+        },
+        {
+          id: '4',
+          profile: 'Editor',
+          username: 'al.dumble98',
+          name: 'Albus',
+          surname: 'Dumbledore',
+          email: 'albus.dumble@wiz.com',
+          organisation: 'wizard-org',
+        },
+      ])
+    })
+  })
+})
diff --git a/apps/metadata-editor/src/app/my-org-users/my-org-users.component.ts b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.ts
new file mode 100644
index 0000000000..aae4f1a49f
--- /dev/null
+++ b/apps/metadata-editor/src/app/my-org-users/my-org-users.component.ts
@@ -0,0 +1,48 @@
+import { Component, OnDestroy } from '@angular/core'
+import { RecordsListComponent } from '../records/records-list.component'
+import { UiInputsModule } from '@geonetwork-ui/ui/inputs'
+import { TranslateModule } from '@ngx-translate/core'
+import { CommonModule } from '@angular/common'
+import { MyOrgService } from '@geonetwork-ui/feature/catalog'
+import { SearchFacade } from '@geonetwork-ui/feature/search'
+import { UserApiModel } from '@geonetwork-ui/data-access/gn4'
+
+@Component({
+  selector: 'md-editor-my-org-users',
+  templateUrl: './my-org-users.component.html',
+  styleUrls: ['./my-org-users.component.css'],
+  standalone: true,
+  imports: [
+    RecordsListComponent,
+    UiInputsModule,
+    TranslateModule,
+    CommonModule,
+  ],
+})
+export class MyOrgUsersComponent implements OnDestroy {
+  orgData: {
+    orgName: string
+    logoUrl: string
+    recordCount: number
+    userCount: number
+    userList: UserApiModel[]
+  }
+
+  private myOrgDataSubscription
+
+  constructor(
+    private myOrgRecordsService: MyOrgService,
+    public searchFacade: SearchFacade
+  ) {
+    this.searchFacade.resetSearch()
+    this.myOrgDataSubscription = this.myOrgRecordsService.myOrgData$.subscribe(
+      (data) => {
+        this.orgData = data
+      }
+    )
+  }
+
+  ngOnDestroy() {
+    this.myOrgDataSubscription.unsubscribe()
+  }
+}
diff --git a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.html b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.html
index 200b0ba65e..f5365b2f10 100644
--- a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.html
+++ b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.html
@@ -1,2 +1,8 @@
-<md-editor-records-list [title]="'dashboard.records.myOrg' | translate">
+<md-editor-records-list
+  [logo]="orgData?.logoUrl"
+  [title]="orgData?.orgName"
+  [recordCount]="orgData?.recordCount"
+  [userCount]="orgData?.userCount"
+  [linkToDatahub]="getDatahubUrl()"
+>
 </md-editor-records-list>
diff --git a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.spec.ts b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.spec.ts
index 7ca9a17948..63e0629753 100644
--- a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.spec.ts
+++ b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.spec.ts
@@ -1,108 +1,164 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing'
 import { MyOrgRecordsComponent } from './my-org-records.component'
-import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search'
-import { Component, importProvidersFrom } from '@angular/core'
-import { TranslateModule } from '@ngx-translate/core'
-import { RecordsListComponent } from '../records-list.component'
+import { BehaviorSubject, of } from 'rxjs'
+import { MyOrgService } from '@geonetwork-ui/feature/catalog'
 import {
-  FILTERS_AGGREGATION,
-  USER_FIXTURE,
+  ORGANISATIONS_FIXTURE,
+  USER_FIXTURE_ANON,
+  USERS_FIXTURE,
 } from '@geonetwork-ui/common/fixtures'
-import { BehaviorSubject, of } from 'rxjs'
-import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
 import { AuthService } from '@geonetwork-ui/api/repository/gn4'
+import { SearchFacade } from '@geonetwork-ui/feature/search'
+import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
+import { EditorRouterService } from '../../router.service'
 
-const user = USER_FIXTURE()
-const filters = FILTERS_AGGREGATION
+const orgDataMock = {
+  orgName: 'wizard-org',
+  logoUrl: 'https://my-geonetwork.org/logo11.png',
+  recordCount: 10,
+  userCount: 3,
+  userList: [
+    {
+      id: '161',
+      profile: 'Administrator',
+      username: 'ghost16',
+      name: 'Ghost',
+      surname: 'Old',
+      email: 'old.ghost@wiz.fr',
+      organisation: 'wizard-org',
+      profileIcon:
+        'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
+    },
+    {
+      id: '3',
+      profile: 'Editor',
+      username: 'voldy63',
+      name: 'Lord',
+      surname: 'Voldemort',
+      email: 'lord.voldy@wiz.com',
+      organisation: 'wizard-org',
+    },
+    {
+      id: '4',
+      profile: 'Editor',
+      username: 'al.dumble98',
+      name: 'Albus',
+      surname: 'Dumbledore',
+      email: 'albus.dumble@wiz.com',
+      organisation: 'wizard-org',
+    },
+  ],
+}
 
-class AuthServiceMock {
-  user$ = new BehaviorSubject(user)
-  authReady = jest.fn(() => this._authSubject$)
-  _authSubject$ = new BehaviorSubject({})
+const myOrgServiceMock = {
+  myOrgData$: of(orgDataMock),
 }
-class OrganisationsServiceMock {
-  getFiltersForOrgs = jest.fn(() => new BehaviorSubject(filters))
-  organisationsCount$ = of(456)
+
+const user = USER_FIXTURE_ANON()
+const allUsers = USERS_FIXTURE()
+
+const authServiceMock = {
+  user$: new BehaviorSubject(user),
+  allUsers$: new BehaviorSubject(allUsers),
 }
 
-class searchServiceMock {
-  updateSearchFilters = jest.fn()
-  setSearch = jest.fn()
-  setSortBy = jest.fn()
-  setSortAndFilters = jest.fn()
+const organisationsServiceMock = {
+  organisations$: of(ORGANISATIONS_FIXTURE),
 }
 
-class SearchFacadeMock {
-  resetSearch = jest.fn()
-  setFilters = jest.fn()
+const searchFacadeMock = {
+  resetSearch: jest.fn(),
 }
 
-@Component({
-  // eslint-disable-next-line
-  selector: 'md-editor-records-list',
-  template: '',
-  standalone: true,
-})
-export class MockRecordsListComponent {}
+const routeServiceMock = {
+  getDatahubSearchRoute: jest.fn(),
+}
 
 describe('MyOrgRecordsComponent', () => {
   let component: MyOrgRecordsComponent
-  let fixture: ComponentFixture<MyOrgRecordsComponent>
   let searchFacade: SearchFacade
-  let orgService: OrganizationsServiceInterface
+  let myOrgService: MyOrgService
+  let authService: AuthService
+  let orgServiceInterface: OrganizationsServiceInterface
+  let routerService: EditorRouterService
 
   beforeEach(() => {
-    TestBed.configureTestingModule({
-      providers: [
-        importProvidersFrom(TranslateModule.forRoot()),
-        {
-          provide: SearchFacade,
-          useClass: SearchFacadeMock,
-        },
-        { provide: AuthService, useClass: AuthServiceMock },
-        {
-          provide: OrganizationsServiceInterface,
-          useClass: OrganisationsServiceMock,
-        },
-        {
-          provide: SearchFacade,
-          useClass: SearchFacadeMock,
-        },
-        {
-          provide: SearchService,
-          useClass: searchServiceMock,
-        },
-      ],
-    }).overrideComponent(MyOrgRecordsComponent, {
-      remove: {
-        imports: [RecordsListComponent],
-      },
-      add: {
-        imports: [MockRecordsListComponent],
-      },
-    })
-    searchFacade = TestBed.inject(SearchFacade)
-    orgService = TestBed.inject(OrganizationsServiceInterface)
-    fixture = TestBed.createComponent(MyOrgRecordsComponent)
-    component = fixture.componentInstance
-    fixture.detectChanges()
+    orgServiceInterface = organisationsServiceMock as any
+    myOrgService = myOrgServiceMock as any
+    authService = authServiceMock as any
+    searchFacade = searchFacadeMock as any
+    routerService = routeServiceMock as any
+
+    component = new MyOrgRecordsComponent(
+      myOrgService,
+      searchFacade,
+      orgServiceInterface,
+      routerService
+    )
   })
 
   it('should create', () => {
     expect(component).toBeTruthy()
   })
 
-  describe('filters', () => {
-    it('clears filters on init', () => {
-      expect(searchFacade.resetSearch).toHaveBeenCalled()
+  describe('Get organization users info', () => {
+    it('should get the org name', () => {
+      expect(component.orgData.orgName).toEqual('wizard-org')
     })
-    it('filters by user organisation on init', () => {
-      expect(orgService.getFiltersForOrgs).toHaveBeenCalledWith([
+
+    it('should get the org logo', () => {
+      expect(component.orgData.logoUrl).toEqual(
+        'https://my-geonetwork.org/logo11.png'
+      )
+    })
+
+    it('should get the list of users', () => {
+      expect(component.orgData.userList).toEqual([
         {
-          name: user.organisation,
+          id: '161',
+          profile: 'Administrator',
+          username: 'ghost16',
+          name: 'Ghost',
+          surname: 'Old',
+          email: 'old.ghost@wiz.fr',
+          organisation: 'wizard-org',
+          profileIcon:
+            'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
+        },
+        {
+          id: '3',
+          profile: 'Editor',
+          username: 'voldy63',
+          name: 'Lord',
+          surname: 'Voldemort',
+          email: 'lord.voldy@wiz.com',
+          organisation: 'wizard-org',
+        },
+        {
+          id: '4',
+          profile: 'Editor',
+          username: 'al.dumble98',
+          name: 'Albus',
+          surname: 'Dumbledore',
+          email: 'albus.dumble@wiz.com',
+          organisation: 'wizard-org',
         },
       ])
-      expect(searchFacade.setFilters).toHaveBeenCalledWith(filters)
     })
   })
+  it('should generate the correct Datahub URL', () => {
+    // Mock the router method and set orgData
+    component.router.getDatahubSearchRoute = () => 'http://example.com'
+    component.orgData = {
+      orgName: 'TestOrg',
+      logoUrl: '',
+      recordCount: 5,
+      userCount: 3,
+      userList: [],
+    }
+
+    const datahubUrl = component.getDatahubUrl()
+
+    // Assert that the generated URL contains the orgName
+    expect(datahubUrl).toContain('publisher=TestOrg')
+  })
 })
diff --git a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.ts b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.ts
index 7a2141398e..16fbbfebd0 100644
--- a/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.ts
+++ b/apps/metadata-editor/src/app/records/my-org-records/my-org-records.component.ts
@@ -2,11 +2,12 @@ import { Component, OnDestroy } from '@angular/core'
 import { CommonModule } from '@angular/common'
 import { TranslateModule } from '@ngx-translate/core'
 import { RecordsListComponent } from '../records-list.component'
+import { MyOrgService } from '@geonetwork-ui/feature/catalog'
 import { SearchFacade } from '@geonetwork-ui/feature/search'
-import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
 import { Organization } from '@geonetwork-ui/common/domain/record'
-import { Subscription } from 'rxjs'
-import { AuthService } from '@geonetwork-ui/api/repository/gn4'
+import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
+import { UserApiModel } from '@geonetwork-ui/data-access/gn4'
+import { EditorRouterService } from '../../router.service'
 
 @Component({
   selector: 'md-editor-my-org-records',
@@ -16,29 +17,48 @@ import { AuthService } from '@geonetwork-ui/api/repository/gn4'
   imports: [CommonModule, TranslateModule, RecordsListComponent],
 })
 export class MyOrgRecordsComponent implements OnDestroy {
-  subscriptionAuthService: Subscription
-  subscriptionOrgService: Subscription
+  orgData: {
+    orgName: string
+    logoUrl: string
+    recordCount: number
+    userCount: number
+    userList: UserApiModel[]
+  }
+
+  public myOrgDataSubscription
 
   constructor(
+    private myOrgRecordsService: MyOrgService,
     public searchFacade: SearchFacade,
-    private authService: AuthService,
-    private orgService: OrganizationsServiceInterface
+    public orgService: OrganizationsServiceInterface,
+    public router: EditorRouterService
   ) {
     this.searchFacade.resetSearch()
-
-    this.subscriptionAuthService = this.authService.user$.subscribe((user) => {
-      this.searchByOrganisation({ name: user?.organisation })
-    })
+    this.myOrgDataSubscription = this.myOrgRecordsService.myOrgData$.subscribe(
+      (data) => {
+        this.orgData = data
+        this.searchByOrganisation({ name: data.orgName })
+      }
+    )
   }
 
   searchByOrganisation(organisation: Organization) {
-    this.subscriptionOrgService = this.orgService
+    this.orgService
       .getFiltersForOrgs([organisation])
       .subscribe((filters) => this.searchFacade.setFilters(filters))
   }
 
-  ngOnDestroy(): void {
-    this.subscriptionAuthService.unsubscribe()
-    this.subscriptionOrgService.unsubscribe()
+  getDatahubUrl(): string {
+    const url = new URL(
+      this.router.getDatahubSearchRoute(),
+      window.location.toString()
+    )
+
+    url.searchParams.append('publisher', this.orgData?.orgName)
+    return url.toString()
+  }
+
+  ngOnDestroy() {
+    this.myOrgDataSubscription.unsubscribe()
   }
 }
diff --git a/apps/metadata-editor/src/app/records/records-list.component.html b/apps/metadata-editor/src/app/records/records-list.component.html
index 06e70f9220..ef2f49181f 100644
--- a/apps/metadata-editor/src/app/records/records-list.component.html
+++ b/apps/metadata-editor/src/app/records/records-list.component.html
@@ -1,23 +1,68 @@
 <main class="grow bg-gray-100 p-12 flex flex-col justify-between gap-12">
-  <div class="flex justify-center gap-8 items-baseline">
-    <h1 class="text-[56px] font-title grow">{{ title }}</h1>
+  <div class="flex gap-8 justify-between">
+    <div class="flex content-start gap-5">
+      <div
+        *ngIf="logo"
+        class="flex-shrink-0 bg-gray-100 rounded-lg overflow-hidden border border-gray-300 h-20 w-20"
+      >
+        <gn-ui-thumbnail
+          class="relative"
+          [thumbnailUrl]="logo"
+          [fit]="'contain'"
+        >
+        </gn-ui-thumbnail>
+      </div>
+      <div class="flex flex-row gap-3 items-center">
+        <h1 class="text-[56px] font-title grow">{{ title }}</h1>
+        np
+        <h1 *ngIf="users" class="text-[56px] font-title grow" translate>
+          dashboard.records.users
+        </h1>
+      </div>
+    </div>
     <a
       class="text-[30px] hover:underline"
-      *ngIf="linkToDatahub"
+      *ngIf="linkToDatahub && !recordCount"
       [href]="linkToDatahub"
       target="_blank"
     >
       {{ searchFacade.resultsHits$ | async }}
       <span>{{ 'record.metadata.publications' | translate }}</span>
     </a>
+    <div
+      *ngIf="recordCount"
+      class="flex flex-col justify-items-start gap-3 items-center"
+    >
+      <a
+        *ngIf="linkToDatahub"
+        [href]="linkToDatahub"
+        target="_blank"
+        class="flex justify-center gap-2 items-center hover:text-gray-900 text-gray-800 cursor-pointer"
+        ><span>{{ recordCount }}</span>
+        <span translate>dashboard.records.publishedRecords</span></a
+      >
+      <gn-ui-button
+        (click)="showUsers(logo, title)"
+        class="flex justify-center gap-2 items-center hover:text-gray-900 text-gray-800 cursor-pointer"
+        ><span>{{ userCount }}</span>
+        <span translate>dashboard.records.users</span></gn-ui-button
+      >
+    </div>
   </div>
   <div
     class="shadow rounded bg-white grow"
-    *ngIf="searchFacade.results$ | async as results"
+    *ngIf="users ? users : (searchFacade.results$ | async) as results"
   >
+    <div
+      class="px-5 py-5 flex justify-center gap-8 items-baseline"
+      *ngIf="results.length === 0"
+    >
+      <span *ngIf="users" translate>dashboard.records.noUser</span>
+      <span *ngIf="!users" translate> dashboard.records.noRecord</span>
+    </div>
     <gn-ui-record-table
       [records]="results"
-      [totalHits]="searchFacade.resultsHits$ | async"
+      [totalHits]="users ? users.length : (searchFacade.resultsHits$ | async)"
       (recordSelect)="editRecord($event)"
       (sortByChange)="setSortBy($event)"
       [sortBy]="searchFacade.sortBy$ | async"
diff --git a/apps/metadata-editor/src/app/records/records-list.component.ts b/apps/metadata-editor/src/app/records/records-list.component.ts
index dc7f42a5f5..580b2ec9a7 100644
--- a/apps/metadata-editor/src/app/records/records-list.component.ts
+++ b/apps/metadata-editor/src/app/records/records-list.component.ts
@@ -36,6 +36,10 @@ const includes = [
 })
 export class RecordsListComponent {
   @Input() title: string
+  @Input() logo: string
+  @Input() recordCount: number
+  @Input() userCount: number
+  @Input() users
   @Input() linkToDatahub?: string
 
   constructor(
@@ -46,6 +50,10 @@ export class RecordsListComponent {
     this.searchFacade.setPageSize(15).setConfigRequestFields(includes)
   }
 
+  getRecords() {
+    return this.users ? this.users : this.searchFacade.results$
+  }
+
   paginate(page: number) {
     this.searchService.setPage(page)
   }
@@ -60,4 +68,8 @@ export class RecordsListComponent {
   setSortBy(newSortBy: SortByField) {
     this.searchService.setSortBy(newSortBy)
   }
+
+  showUsers() {
+    this.router.navigate(['/users/my-org'])
+  }
 }
diff --git a/conf/default.toml b/conf/default.toml
index 3f985d2546..20448ee63f 100644
--- a/conf/default.toml
+++ b/conf/default.toml
@@ -72,7 +72,7 @@ background_color = "#fdfbff"
 # filter_geometry_data = '{ "coordinates": [...], "type": "Polygon" }'
 
 # The advanced search filters available to the user can be customized with this setting.
-# The following fields can be used for filtering: 'publisher', 'format', 'publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'isSpatial', 'license'
+# The following fields can be used for filtering: 'publisher', 'format', 'publicationYear', 'standard', 'inspireKeyword', 'topic', 'isSpatial', 'license'
 # any other field will be ignored
 # advanced_filters = ['publisher', 'format', 'publicationYear', 'topic', 'isSpatial', 'license']
 
diff --git a/docs/apps/editor.md b/docs/apps/editor.md
index e53d148ff2..0e67017ee6 100644
--- a/docs/apps/editor.md
+++ b/docs/apps/editor.md
@@ -4,6 +4,17 @@ outline: deep
 
 # Editor
 
-## Chapter 1
+## My organization
+
+The "my organization" tab contains filtered records owned by the organization of the logged in user. Note that this page will not display any records if no user is logged in.
+The page is made of :
+
+- The organization name and logo, fetched from `organisations$` in the `OrganizationServiceInterface`.
+- A table with the filtered records. The table is from the component `md-editor-records-list`, which does the fetching of the records.
+- Two links :
+  - The first link is the count of published records for this organization. It leads to the datahub, where the filter by organization will be activated to only show the user's organization. The filter is set through the URL directly with the name from `organisations$`.
+  - The second link is the count of users for this organization. It leads to a new page in the dashboard. The page is also made of the organization's name and logo, and of a table presenting the users and their details. These users are fetched from the observables `user$` (logged in user), and `allUsers$` (all users of geonetwork) in the `AuthService`. `allUsers$` are then filtered by their organization to be displayed here. The table in this page is also from the component `md-editor-records-list`, which detects if an input `users` (containing the list of filtered users) was received and creates the table accordingly.
+
+It's important to know that a user with an organization must be logged in for this component to work. If not, or in the case where the organization doesn't own any records, a message will be displayed instead of the table, to inform the user.
 
 ## Chapter 2
diff --git a/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts b/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
index cf31d96fc7..c587381c57 100644
--- a/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
+++ b/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
@@ -4,6 +4,7 @@ import { TestBed } from '@angular/core/testing'
 import { MeApiService } from '@geonetwork-ui/data-access/gn4'
 import { TranslateService } from '@ngx-translate/core'
 import { AvatarServiceInterface } from './avatar.service.interface'
+import { HttpClientTestingModule } from '@angular/common/http/testing'
 
 const userMock = {
   id: '21737',
@@ -64,6 +65,7 @@ describe('AuthService', () => {
           useClass: AvatarServiceInterfaceMock,
         },
       ],
+      imports: [HttpClientTestingModule],
     })
   })
 
diff --git a/libs/api/repository/src/lib/gn4/auth/auth.service.ts b/libs/api/repository/src/lib/gn4/auth/auth.service.ts
index 820c89107b..b901e4fdbb 100644
--- a/libs/api/repository/src/lib/gn4/auth/auth.service.ts
+++ b/libs/api/repository/src/lib/gn4/auth/auth.service.ts
@@ -2,6 +2,8 @@ import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'
 import {
   MeApiService,
   MeResponseApiModel,
+  UserApiModel,
+  UsersApiService,
 } from '@geonetwork-ui/data-access/gn4'
 import { LANG_2_TO_3_MAPPER } from '@geonetwork-ui/util/i18n'
 import { UserModel } from '@geonetwork-ui/common/domain/user.model'
@@ -19,6 +21,7 @@ export const LOGIN_URL = new InjectionToken<string>('loginUrl')
 export class AuthService {
   authReady$: Observable<UserModel>
   user$: Observable<UserModel>
+  allUsers$: Observable<UserApiModel[]>
   isAnonymous$ = this.authReady().pipe(map((user) => !user || !('id' in user)))
 
   baseLoginUrl = this.baseLoginUrlToken || DEFAULT_GN4_LOGIN_URL
@@ -42,6 +45,7 @@ export class AuthService {
     @Inject(LOGIN_URL)
     private baseLoginUrlToken: string,
     private meApi: MeApiService,
+    private usersApi: UsersApiService,
     private translateService: TranslateService,
     private avatarService: AvatarServiceInterface
   ) {
@@ -49,6 +53,7 @@ export class AuthService {
       map((apiUser) => this.mapToUserModel(apiUser)),
       shareReplay({ bufferSize: 1, refCount: true })
     )
+    this.allUsers$ = this.usersApi.getUsers().pipe(shareReplay())
   }
 
   // TODO: refactor authReady
diff --git a/libs/common/fixtures/src/lib/organisations.fixture.ts b/libs/common/fixtures/src/lib/organisations.fixture.ts
index f78bd303cb..833d53a5b7 100644
--- a/libs/common/fixtures/src/lib/organisations.fixture.ts
+++ b/libs/common/fixtures/src/lib/organisations.fixture.ts
@@ -68,4 +68,10 @@ export const ORGANISATIONS_FIXTURE: Organization[] = deepFreeze([
     logoUrl: new URL('https://my-geonetwork.org/logo10.png'),
     recordCount: 2,
   },
+  {
+    name: 'wizard-org',
+    description: 'another org for testing',
+    logoUrl: new URL('https://my-geonetwork.org/logo11.png'),
+    recordCount: 2,
+  },
 ])
diff --git a/libs/common/fixtures/src/lib/user.fixtures.ts b/libs/common/fixtures/src/lib/user.fixtures.ts
index a1e9cea331..654fc14d21 100644
--- a/libs/common/fixtures/src/lib/user.fixtures.ts
+++ b/libs/common/fixtures/src/lib/user.fixtures.ts
@@ -12,8 +12,21 @@ export const USER_FIXTURE = (): UserModel => ({
     'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
 })
 
+export const USER_FIXTURE_ANON = (): UserModel => ({
+  id: '161',
+  profile: 'Administrator',
+  username: 'ghost16',
+  name: 'Ghost',
+  surname: 'Old',
+  email: 'old.ghost@wiz.fr',
+  organisation: 'wizard-org',
+  profileIcon:
+    'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp',
+})
+
 export const USERS_FIXTURE = (): UserModel[] => [
   USER_FIXTURE(),
+  USER_FIXTURE_ANON(),
   {
     id: '1',
     profile: 'Editor',
@@ -32,4 +45,22 @@ export const USERS_FIXTURE = (): UserModel[] => [
     email: 't.trinity@matrix.com',
     organisation: 'The matrix',
   },
+  {
+    id: '3',
+    profile: 'Editor',
+    username: 'voldy63',
+    name: 'Lord',
+    surname: 'Voldemort',
+    email: 'lord.voldy@wiz.com',
+    organisation: 'wizard-org',
+  },
+  {
+    id: '4',
+    profile: 'Editor',
+    username: 'al.dumble98',
+    name: 'Albus',
+    surname: 'Dumblerdore',
+    email: 'albus.dumble@wiz.com',
+    organisation: 'wizard-org',
+  },
 ]
diff --git a/libs/feature/catalog/src/index.ts b/libs/feature/catalog/src/index.ts
index 38ad1c9e7a..fee6dd1389 100644
--- a/libs/feature/catalog/src/index.ts
+++ b/libs/feature/catalog/src/index.ts
@@ -5,3 +5,4 @@ export * from './lib/feature-catalog.module'
 export * from './lib/sources/sources.service'
 export * from './lib/sources/sources.model'
 export * from './lib/records/records.service'
+export * from './lib/my-org/my-org.service'
diff --git a/libs/feature/catalog/src/lib/my-org/my-org.service.spec.ts b/libs/feature/catalog/src/lib/my-org/my-org.service.spec.ts
new file mode 100644
index 0000000000..e4def91fa1
--- /dev/null
+++ b/libs/feature/catalog/src/lib/my-org/my-org.service.spec.ts
@@ -0,0 +1,113 @@
+import { TestBed } from '@angular/core/testing'
+import { MyOrgService } from './my-org.service'
+import { AuthService } from '@geonetwork-ui/api/repository/gn4'
+import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
+import { BehaviorSubject, Observable, of, Subject } from 'rxjs'
+import { UserApiModel } from '@geonetwork-ui/data-access/gn4'
+import { UserModel } from '@geonetwork-ui/common/domain/user.model'
+import { HttpClientTestingModule } from '@angular/common/http/testing'
+import { TranslateService } from '@ngx-translate/core'
+import { AvatarServiceInterface } from '@geonetwork-ui/api/repository/gn4'
+
+const translateServiceMock = {
+  currentLang: 'fr',
+}
+
+class AvatarServiceInterfaceMock {
+  placeholder = 'http://placeholder.com'
+  getProfileIcon = (hash: string) => `${hash}`
+}
+
+const orgs = [
+  { name: 'Géo2France', logoUrl: { href: 'logo-url' }, recordCount: 10 },
+]
+const orgs$ = of(orgs)
+
+class orgServiceMock {
+  organisations$ = orgs$
+}
+
+describe('MyOrgService', () => {
+  let myOrgService: MyOrgService
+  let authService: AuthService
+  let orgService: OrganizationsServiceInterface
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        MyOrgService,
+        {
+          provide: TranslateService,
+          useValue: translateServiceMock,
+        },
+        {
+          provide: AvatarServiceInterface,
+          useClass: AvatarServiceInterfaceMock,
+        },
+        { provide: OrganizationsServiceInterface, useClass: orgServiceMock },
+        AuthService,
+      ],
+      imports: [HttpClientTestingModule],
+    })
+    myOrgService = TestBed.inject(MyOrgService)
+    authService = TestBed.inject(AuthService)
+    orgService = TestBed.inject(OrganizationsServiceInterface)
+  })
+
+  it('should be created', () => {
+    expect(myOrgService).toBeTruthy()
+  })
+
+  it('should update myOrgDataSubject when authService user$ emits a user', () => {
+    const userSubject = new BehaviorSubject<UserModel | null>(null)
+    const user: UserModel = {
+      organisation: 'Géo2France',
+      id: '2',
+      profile: 'profile',
+      username: 'username',
+      name: 'name',
+      surname: 'surname',
+      email: 'email@email',
+      profileIcon: 'icon.com',
+    }
+    authService.user$ = userSubject.asObservable()
+
+    userSubject.next(user)
+
+    myOrgService.myOrgData$.subscribe((data) => {
+      expect(data.orgName).toEqual('Géo2France')
+    })
+  })
+
+  it('should update myOrgDataSubject when orgService organisations$ emits organizations', () => {
+    const orgsSubject = new BehaviorSubject<any[]>([])
+    const orgs = [
+      { name: 'Géo2France', logoUrl: { href: 'logo-url' }, recordCount: 10 },
+    ]
+    orgService.organisations$ = orgsSubject.asObservable()
+
+    orgsSubject.next(orgs)
+
+    myOrgService.myOrgData$.subscribe((data) => {
+      expect(data.orgName).toEqual('Géo2France')
+      expect(data.logoUrl).toEqual('logo-url')
+      expect(data.recordCount).toEqual(10)
+    })
+  })
+
+  it('should update myOrgDataSubject when authService allUsers$ emits users', () => {
+    const allUsersSubject = new BehaviorSubject<UserApiModel[]>([])
+    const users: UserApiModel[] = [
+      { organisation: 'Géo2France' },
+      { organisation: 'Géo2France' },
+    ]
+    authService.allUsers$ = allUsersSubject.asObservable()
+
+    allUsersSubject.next(users)
+
+    myOrgService.myOrgData$.subscribe((data) => {
+      expect(data.orgName).toEqual('Géo2France')
+      expect(data.userList.length).toEqual(2)
+    })
+  })
+})
diff --git a/libs/feature/catalog/src/lib/my-org/my-org.service.ts b/libs/feature/catalog/src/lib/my-org/my-org.service.ts
new file mode 100644
index 0000000000..b2250696fb
--- /dev/null
+++ b/libs/feature/catalog/src/lib/my-org/my-org.service.ts
@@ -0,0 +1,61 @@
+import { Injectable } from '@angular/core'
+import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
+import { AuthService } from '@geonetwork-ui/api/repository/gn4'
+import { BehaviorSubject, combineLatest, map, Observable } from 'rxjs'
+import { UserApiModel } from '@geonetwork-ui/data-access/gn4'
+
+@Injectable({
+  providedIn: 'root',
+})
+export class MyOrgService {
+  myOrgData$: Observable<{
+    orgName: string
+    logoUrl: string
+    recordCount: number
+    userCount: number
+    userList: UserApiModel[]
+  }>
+
+  private myOrgDataSubject = new BehaviorSubject<{
+    orgName: string
+    logoUrl: string
+    recordCount: number
+    userCount: number
+    userList: UserApiModel[]
+  }>({
+    orgName: '',
+    logoUrl: '',
+    recordCount: 0,
+    userCount: 0,
+    userList: [],
+  })
+
+  constructor(
+    private authService: AuthService,
+    private orgService: OrganizationsServiceInterface
+  ) {
+    this.myOrgData$ = combineLatest([
+      this.authService.user$,
+      this.authService.allUsers$,
+      this.orgService.organisations$,
+    ]).pipe(
+      map(([user, allUsers, orgs]) => {
+        const orgName = user.organisation
+        const org = orgs.find((org) => org.name === orgName)
+        const logoUrl = org?.logoUrl?.href.toString()
+        const recordCount = org?.recordCount
+        const userList = allUsers.filter(
+          (user) => user.organisation === orgName
+        )
+        const userCount = userList.length
+        return {
+          orgName,
+          logoUrl,
+          recordCount,
+          userList,
+          userCount,
+        }
+      })
+    )
+  }
+}
diff --git a/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts b/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts
index 7ff3a3fca1..d5ef56539e 100644
--- a/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts
+++ b/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts
@@ -148,7 +148,7 @@ describe('OrganisationsComponent', () => {
           expect(
             orgPreviewComponents[orgPreviewComponents.length - 1].organisation
               .name
-          ).toEqual('J Data Org')
+          ).toEqual('wizard-org')
         })
       })
     })
diff --git a/libs/ui/search/src/lib/record-table/record-table.component.html b/libs/ui/search/src/lib/record-table/record-table.component.html
index e93f1977c4..6343d7a8a1 100644
--- a/libs/ui/search/src/lib/record-table/record-table.component.html
+++ b/libs/ui/search/src/lib/record-table/record-table.component.html
@@ -11,8 +11,41 @@
       results.records.hits.displayedOn
     </div>
 
+    <div
+      class="grid grid-cols-[repeat(3,minmax(0,max-content))] gap-x-4 gap-y-1"
+      *ngIf="records[0].name"
+    >
+      <div class="contents text-sm">
+        <div class="record-table-header text-gray-400 flex gap-1">
+          <span translate>dashboard.records.userDetail</span>
+        </div>
+        <div class="record-table-header text-gray-400 flex gap-1">
+          <span translate>dashboard.records.username</span>
+        </div>
+        <div class="record-table-header text-gray-400 flex gap-1">
+          <span translate>dashboard.records.userEmail</span>
+        </div>
+      </div>
+      <div
+        class="contents hover:text-gray-900 text-gray-800 cursor-pointer"
+        (click)="recordSelect.emit(record)"
+        *ngFor="let record of records"
+      >
+        <div class="record-table-col text-16">
+          {{ record.name }}
+        </div>
+        <div class="record-table-col text-16">
+          {{ record.username }}
+        </div>
+        <div class="record-table-col text-16">
+          {{ record.emailAddresses[0] }}
+        </div>
+      </div>
+    </div>
+
     <div
       class="grid grid-cols-[repeat(5,minmax(0,max-content))] gap-x-4 gap-y-1"
+      *ngIf="!records[0].name"
     >
       <div class="contents text-sm">
         <div class="record-table-header text-gray-400 flex gap-1">
@@ -130,7 +163,10 @@
           >
             {{ formats[1] }}
           </span>
-          <div class="flex-shrink-0" *ngIf="formats.slice(2).length > 0">
+          <div
+            class="flex-shrink-0"
+            *ngIf="!record.name && formats.slice(2).length > 0"
+          >
             <span>+{{ formats.slice(2).length }}</span>
           </div>
         </div>
diff --git a/libs/ui/search/src/lib/record-table/record-table.component.ts b/libs/ui/search/src/lib/record-table/record-table.component.ts
index e98926e42a..2be118f1fb 100644
--- a/libs/ui/search/src/lib/record-table/record-table.component.ts
+++ b/libs/ui/search/src/lib/record-table/record-table.component.ts
@@ -14,7 +14,7 @@ import { SortByField } from '@geonetwork-ui/common/domain/search'
   styleUrls: ['./record-table.component.css'],
 })
 export class RecordTableComponent {
-  @Input() records: CatalogRecord[] = []
+  @Input() records: any[] = []
   @Input() totalHits?: number
   @Input() sortBy?: SortByField
   @Output() recordSelect = new EventEmitter<CatalogRecord>()
diff --git a/translations/de.json b/translations/de.json
index 51fa939bdd..c6703b3d0b 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "",
   "dashboard.records.myOrg": "",
   "dashboard.records.myRecords": "",
+  "dashboard.records.noRecord": "",
+  "dashboard.records.noUser": "",
+  "dashboard.records.publishedRecords": "",
   "dashboard.records.search": "",
+  "dashboard.records.users": "",
+  "dashboard.records.username": "",
+  "dashboard.records.userDetail": "",
+  "dashboard.records.userEmail": "",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "Dateiformat-Erkennung",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "Sammeln von Datensatzinformationen",
   "datafeeder.analysisProgressBar.illustration.samplingData": "Datenauswahl",
diff --git a/translations/en.json b/translations/en.json
index 865a83bed5..a8f6d85feb 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "My library",
   "dashboard.records.myOrg": "Organization",
   "dashboard.records.myRecords": "My Records",
+  "dashboard.records.noRecord": "No record for this organization",
+  "dashboard.records.noUser": "No users for this organization",
+  "dashboard.records.publishedRecords": "published records",
   "dashboard.records.search": "Search for \"{searchText}\"",
+  "dashboard.records.users": "users",
+  "dashboard.records.username": "Username",
+  "dashboard.records.userDetail": "Name",
+  "dashboard.records.userEmail": "Email",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "File format \n detection",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "Gathering dataset \n information",
   "datafeeder.analysisProgressBar.illustration.samplingData": "Sampling \n data",
diff --git a/translations/es.json b/translations/es.json
index ee50a1f071..deb48dee7a 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "",
   "dashboard.records.myOrg": "",
   "dashboard.records.myRecords": "",
+  "dashboard.records.noRecord": "",
+  "dashboard.records.noUser": "",
+  "dashboard.records.publishedRecords": "",
   "dashboard.records.search": "",
+  "dashboard.records.users": "",
+  "dashboard.records.username": "",
+  "dashboard.records.userDetail": "",
+  "dashboard.records.userEmail": "",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "",
   "datafeeder.analysisProgressBar.illustration.samplingData": "",
diff --git a/translations/fr.json b/translations/fr.json
index 8c8b9880bd..b433aaa0f9 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "Ma bibliothèque",
   "dashboard.records.myOrg": "Mon organisation",
   "dashboard.records.myRecords": "Mes fiches publiées",
+  "dashboard.records.noRecord": "Aucun jeu de données pour cette organisation",
+  "dashboard.records.noUser": "Aucun utilisateur pour cette organisation",
+  "dashboard.records.publishedRecords": "données publiées",
   "dashboard.records.search": "Résultats pour \"{searchText}\"",
+  "dashboard.records.users": "utilisateurs",
+  "dashboard.records.username": "Nom d'utilisateur",
+  "dashboard.records.userDetail": "Nom",
+  "dashboard.records.userEmail": "Email",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "Détection du \n format de fichier",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "Récupération des informations \n sur le jeu de données",
   "datafeeder.analysisProgressBar.illustration.samplingData": "Sampling \n des données",
diff --git a/translations/it.json b/translations/it.json
index 97f1920271..db14efc962 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "",
   "dashboard.records.myOrg": "",
   "dashboard.records.myRecords": "",
+  "dashboard.records.noRecord": "",
+  "dashboard.records.noUser": "",
+  "dashboard.records.publishedRecords": "",
   "dashboard.records.search": "",
+  "dashboard.records.users": "",
+  "dashboard.records.username": "",
+  "dashboard.records.userDetail": "",
+  "dashboard.records.userEmail": "",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "",
   "datafeeder.analysisProgressBar.illustration.samplingData": "",
diff --git a/translations/nl.json b/translations/nl.json
index 665755a10b..4af1cd9267 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "",
   "dashboard.records.myOrg": "",
   "dashboard.records.myRecords": "",
+  "dashboard.records.noRecord": "",
+  "dashboard.records.noUser": "",
+  "dashboard.records.publishedRecords": "",
   "dashboard.records.search": "",
+  "dashboard.records.users": "",
+  "dashboard.records.username": "",
+  "dashboard.records.userDetail": "",
+  "dashboard.records.userEmail": "",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "",
   "datafeeder.analysisProgressBar.illustration.samplingData": "",
diff --git a/translations/pt.json b/translations/pt.json
index dbcdc6ec2a..d4a4d66612 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -22,7 +22,14 @@
   "dashboard.records.myLibrary": "",
   "dashboard.records.myOrg": "",
   "dashboard.records.myRecords": "",
+  "dashboard.records.noRecord": "",
+  "dashboard.records.noUser": "",
+  "dashboard.records.publishedRecords": "",
   "dashboard.records.search": "",
+  "dashboard.records.users": "",
+  "dashboard.records.username": "",
+  "dashboard.records.userDetail": "",
+  "dashboard.records.userEmail": "",
   "datafeeder.analysisProgressBar.illustration.fileFormatDetection": "",
   "datafeeder.analysisProgressBar.illustration.gatheringDatasetInformation": "",
   "datafeeder.analysisProgressBar.illustration.samplingData": "",