diff --git a/e2e/project-admin/project-admin.spec.ts b/e2e/project-admin/project-admin.spec.ts new file mode 100644 index 0000000000..b7b57a2508 --- /dev/null +++ b/e2e/project-admin/project-admin.spec.ts @@ -0,0 +1,240 @@ +// spec: .specs/FR-2209-project-admin-management/spec.md +// Stories 1-4 (admin scope + members page) plus ProjectSelect badge. +// Full validation requires sibling stack PRs (#6652 serving, #6654 vfolder, +// #6655 sessions, and #6656 header switch confirm) to be merged — tests +// that exercise features exclusively on those branches are marked with +// `test.fixme` with an explanatory comment. +import { + loginAsCreatedAccount, + navigateTo, + webuiEndpoint, +} from '../utils/test-util'; +import { test, expect, Page } from '@playwright/test'; + +/** + * Credentials for the dedicated project-admin test account. + * + * Resolved exclusively from env vars (`E2E_PROJECT_ADMIN_EMAIL` and + * `E2E_PROJECT_ADMIN_PASSWORD`). No hardcoded fallbacks — real test-server + * credentials must not be committed. When either var is missing, every test + * in this file is skipped via `test.skip` with an explanatory message. + * + * The referenced account must be a project admin of the project named + * `E2E_PROJECT_ADMIN_PROJECT` (defaults to `default`) on the test cluster. + */ +const projectAdminEmail = process.env.E2E_PROJECT_ADMIN_EMAIL; +const projectAdminPassword = process.env.E2E_PROJECT_ADMIN_PASSWORD; +const hasProjectAdminCredentials = Boolean( + projectAdminEmail && projectAdminPassword, +); +const SKIP_REASON = + 'requires E2E_PROJECT_ADMIN_EMAIL and E2E_PROJECT_ADMIN_PASSWORD env vars'; + +const DEFAULT_PROJECT_NAME = process.env.E2E_PROJECT_ADMIN_PROJECT || 'default'; + +async function loginAsProjectAdmin( + page: Page, + request: Parameters[1], +): Promise { + // `test.skip` at the describe-level guards entry, so by the time this + // runs both values are defined. The non-null assertions keep the type + // narrow without re-introducing any fallback literal. + await loginAsCreatedAccount( + page, + request, + projectAdminEmail!, + projectAdminPassword!, + ); +} + +test.beforeEach(() => { + // Skip all tests in this file if the project-admin credentials are not + // supplied via env vars. We intentionally avoid hardcoded fallbacks so + // real cluster credentials cannot be committed to source control. + test.skip(!hasProjectAdminCredentials, SKIP_REASON); +}); + +test.describe( + 'FR-2209 Project Admin - menu visibility', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('project admin can see the Admin category in the sider', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + + // The project-admin tier surfaces a subset of admin pages + // (Sessions, Serving, Data, Members). Presence of any one of them in + // the sider is sufficient to confirm the admin category is mounted. + await expect( + page.getByRole('menuitem', { name: 'Sessions' }).first(), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: 'Project Members' }), + ).toBeVisible(); + }); + }, +); + +test.describe( + 'FR-2209 Story 1 - admin sessions scope', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('project admin can navigate to Admin Sessions page', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + await navigateTo(page, '/admin-session'); + + // Assert the page rendered (breadcrumb or heading). The exact + // per-project filter is enforced on the backend side by PR #6655. + await expect(page).toHaveURL(/\/admin-session/); + await expect( + page.getByTestId('webui-breadcrumb').getByText(/Sessions/i), + ).toBeVisible(); + }); + + // Requires sibling PR #6655 (FR-2554): backend + UI scope sessions list + // to the project-admin's project only. Until #6655 merges, the list is + // unscoped and a content-based assertion would be meaningless. + test.fixme('admin sessions list only contains the project-admin project (requires #6655)', async () => { + // Pending: assert every row's project column equals `default`. + }); + }, +); + +test.describe( + 'FR-2209 Story 2 - admin serving scope', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('project admin can navigate to Admin Serving page', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + await navigateTo(page, '/admin-serving'); + + await expect(page).toHaveURL(/\/admin-serving/); + }); + + // The "Start Service" action must be hidden for project-admins in + // admin-serving. The button is conditionally rendered by PR #6652. + test.fixme('"Start Service" button is hidden on Admin Serving for project admins (requires #6652)', async () => { + // Pending: assert getByRole('button', { name: 'Start Service' }) + // is hidden on /admin-serving for a project-admin session. + }); + }, +); + +test.describe( + 'FR-2209 Story 3 - admin vfolder scope', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('project admin can navigate to Admin Data page', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + await navigateTo(page, '/admin-data'); + + await expect(page).toHaveURL(/\/admin-data/); + }); + + // Create Folder modal should lock the type and project fields for + // project admins, and only project-scoped folders should appear in + // the list. That gating lives on PR #6654. + test.fixme('Create Folder modal locks type and project for project admins (requires #6654)', async () => { + // Pending: open Create Folder modal and assert type + project + // fields are disabled and prefilled with the current project. + }); + }, +); + +test.describe( + 'FR-2209 Story 4 - project members page', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('project admin sees read-only members table with Name/Email/Role columns', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + await navigateTo(page, '/admin-members'); + + await expect(page).toHaveURL(/\/admin-members/); + + // Card title / page heading + await expect( + page.getByText('Project Members', { exact: true }).first(), + ).toBeVisible(); + + // Table column headers — BAITable surfaces them as + // `role="columnheader"` on the underlying . + await expect( + page.getByRole('columnheader', { name: 'Name', exact: true }), + ).toBeVisible(); + await expect( + page.getByRole('columnheader', { name: 'Email', exact: true }), + ).toBeVisible(); + await expect( + page.getByRole('columnheader', { name: 'Role', exact: true }), + ).toBeVisible(); + + // Read-only view: there should be no Add Member / Remove Member + // controls on the page. Project-admins manage members exclusively + // via the RBAC Management page per the FR-2209 spec. + await expect( + page.getByRole('button', { name: /Add Member/i }), + ).toHaveCount(0); + await expect( + page.getByRole('button', { name: /Remove Member/i }), + ).toHaveCount(0); + }); + }, +); + +test.describe( + 'FR-2209 ProjectSelect - Project Admin badge', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + test('"Project Admin" badge appears next to the project in the ProjectSelect dropdown', async ({ + page, + request, + }) => { + await loginAsProjectAdmin(page, request); + await page.goto(webuiEndpoint); + + // Open the project selector in the header (rendered as a + // Select-like button with the current project name). + const selectorTrigger = page + .getByRole('combobox') + .filter({ hasText: DEFAULT_PROJECT_NAME }) + .first(); + await expect(selectorTrigger).toBeVisible(); + await selectorTrigger.click(); + + // Badge is rendered as a Tag inside the option label. + await expect( + page.getByText('Project Admin', { exact: true }).first(), + ).toBeVisible(); + }); + }, +); + +test.describe( + 'FR-2209 PR-1b - admin-mode switch confirm', + { tag: ['@project-admin', '@rbac', '@functional'] }, + () => { + // Header-level switch-out-of-admin confirm dialog is introduced in + // PR #6656 (FR-2553) which is a sibling of this PR's base chain. + // The i18n keys and logic are not present here yet. + test.fixme('selecting a non-admin project in admin mode prompts a confirm modal (requires #6656)', async () => { + // Pending: open header project selector, pick a project where + // the user is NOT admin, assert confirm modal appears with the + // SwitchOutOfAdminConfirmTitle/Content copy. Click Cancel and + // assert the selector restores the previous project. + }); + }, +); diff --git a/resources/i18n/de.json b/resources/i18n/de.json index 77c2d10d86..4ef993fe78 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "Ungültige UUID.", "ResourcePolicy": "Ressourcenrichtlinie" }, + "projectMembers": { + "Email": "E-Mail", + "Name": "Name", + "PageTitle": "Projektmitglieder", + "Role": "Rolle", + "RoleAdmin": "Administrator", + "RoleMember": "Mitglied" + }, + "projectSelect": { + "ProjectAdminBadge": "Projektadministrator" + }, "rbac": { "Active": "Aktiv", "Assign": "Zuweisen", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "Datenschutz-Bestimmungen", "ProfileUpdated": "Das Profil wurde erfolgreich aktualisiert.", "Project": "Projekt", + "ProjectMembers": "Projektmitglieder", "Projects": "Projekte", "RBACManagement": "RBAC-Verwaltung", "Remaining": "Übrig", diff --git a/resources/i18n/el.json b/resources/i18n/el.json index ffc915365a..c04931bebb 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "Μη έγκυρο UUID.", "ResourcePolicy": "Πολιτική πόρων" }, + "projectMembers": { + "Email": "Email", + "Name": "Όνομα", + "PageTitle": "Μέλη Έργου", + "Role": "Ρόλος", + "RoleAdmin": "Διαχειριστής", + "RoleMember": "Μέλος" + }, + "projectSelect": { + "ProjectAdminBadge": "Διαχειριστής Έργου" + }, "rbac": { "Active": "Ενεργός", "Assign": "Ανάθεση", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Πολιτική απορρήτου", "ProfileUpdated": "Το προφίλ ενημερώθηκε με επιτυχία.", "Project": "Έργο", + "ProjectMembers": "Μέλη Έργου", "Projects": "Έργα", "RBACManagement": "Διαχείριση RBAC", "Remaining": "Παραμένων", diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 757c49f849..b9bff05455 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "UUID no válido.", "ResourcePolicy": "Política de recursos" }, + "projectMembers": { + "Email": "Correo electrónico", + "Name": "Nombre", + "PageTitle": "Miembros del proyecto", + "Role": "Rol", + "RoleAdmin": "Administrador", + "RoleMember": "Miembro" + }, + "projectSelect": { + "ProjectAdminBadge": "Administrador del proyecto" + }, "rbac": { "Active": "Activo", "Assign": "Asignar", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Política de privacidad", "ProfileUpdated": "El perfil se ha actualizado correctamente.", "Project": "Proyecto", + "ProjectMembers": "Miembros del proyecto", "Projects": "Proyectos", "RBACManagement": "Gestión RBAC", "Remaining": "Restante", diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index 6250f45cb7..5d29998a49 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "Virheellinen UUID.", "ResourcePolicy": "Resurssipolitiikka" }, + "projectMembers": { + "Email": "Sähköposti", + "Name": "Nimi", + "PageTitle": "Projektin jäsenet", + "Role": "Rooli", + "RoleAdmin": "Ylläpitäjä", + "RoleMember": "Jäsen" + }, + "projectSelect": { + "ProjectAdminBadge": "Projektin ylläpitäjä" + }, "rbac": { "Active": "Aktiivinen", "Assign": "Määritä", @@ -2946,6 +2957,7 @@ "PrivacyPolicy": "Tietosuojakäytäntö", "ProfileUpdated": "Profiili on päivitetty onnistuneesti.", "Project": "Hanke", + "ProjectMembers": "Projektin jäsenet", "Projects": "Hankkeet", "RBACManagement": "RBAC-hallinta", "Remaining": "Jäljellä oleva", diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index 2ac3857772..6ae210b9ed 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -1682,6 +1682,17 @@ "ProjectIDFilterRuleMessage": "UUID invalide.", "ResourcePolicy": "Politique de ressources" }, + "projectMembers": { + "Email": "E-mail", + "Name": "Nom", + "PageTitle": "Membres du projet", + "Role": "Rôle", + "RoleAdmin": "Administrateur", + "RoleMember": "Membre" + }, + "projectSelect": { + "ProjectAdminBadge": "Administrateur de projet" + }, "rbac": { "Active": "Actif", "Assign": "Attribuer", @@ -2949,6 +2960,7 @@ "PrivacyPolicy": "Politique de confidentialité", "ProfileUpdated": "Le profil a été mis à jour avec succès.", "Project": "Projet", + "ProjectMembers": "Membres du projet", "Projects": "Projets", "RBACManagement": "Gestion RBAC", "Remaining": "Restant", diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 9129897d8e..2fcd87003b 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -1682,6 +1682,17 @@ "ProjectIDFilterRuleMessage": "UUID tidak valid.", "ResourcePolicy": "Kebijakan Sumber Daya" }, + "projectMembers": { + "Email": "Email", + "Name": "Nama", + "PageTitle": "Anggota Proyek", + "Role": "Peran", + "RoleAdmin": "Admin", + "RoleMember": "Anggota" + }, + "projectSelect": { + "ProjectAdminBadge": "Admin Proyek" + }, "rbac": { "Active": "Aktif", "Assign": "Tetapkan", @@ -2949,6 +2960,7 @@ "PrivacyPolicy": "Kebijakan Privasi", "ProfileUpdated": "Profil berhasil diperbarui.", "Project": "Proyek", + "ProjectMembers": "Anggota Proyek", "Projects": "Proyek", "RBACManagement": "Manajemen RBAC", "Remaining": "Tersisa", diff --git a/resources/i18n/it.json b/resources/i18n/it.json index 3a4f4dcfb9..8d045881c5 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "UUID non valido.", "ResourcePolicy": "Politica delle risorse" }, + "projectMembers": { + "Email": "Email", + "Name": "Nome", + "PageTitle": "Membri del progetto", + "Role": "Ruolo", + "RoleAdmin": "Amministratore", + "RoleMember": "Membro" + }, + "projectSelect": { + "ProjectAdminBadge": "Amministratore del progetto" + }, "rbac": { "Active": "Attivo", "Assign": "Assegna", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "politica sulla riservatezza", "ProfileUpdated": "Il profilo è stato aggiornato con successo.", "Project": "Progetto", + "ProjectMembers": "Membri del progetto", "Projects": "Progetti", "RBACManagement": "Gestione RBAC", "Remaining": "Rimanente", diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index 56663e5477..5baf16d6e2 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "無効なUUIDです。", "ResourcePolicy": "リソースポリシー" }, + "projectMembers": { + "Email": "メール", + "Name": "名前", + "PageTitle": "プロジェクトメンバー", + "Role": "役割", + "RoleAdmin": "管理者", + "RoleMember": "メンバー" + }, + "projectSelect": { + "ProjectAdminBadge": "プロジェクト管理者" + }, "rbac": { "Active": "アクティブ", "Assign": "割り当て", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "個人情報保護方針", "ProfileUpdated": "プロフィールが正常に更新されました。", "Project": "プロジェクト", + "ProjectMembers": "プロジェクトメンバー", "Projects": "プロジェクト", "RBACManagement": "RBAC管理", "Remaining": "残り", diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index 167d8d9030..3b8502be3b 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -1680,6 +1680,17 @@ "ProjectIDFilterRuleMessage": "UUID хүчин төгөлдөргүй.", "ResourcePolicy": "Ресурсийн бодлого" }, + "projectMembers": { + "Email": "И-мэйл", + "Name": "Нэр", + "PageTitle": "Төслийн гишүүд", + "Role": "Үүрэг", + "RoleAdmin": "Админ", + "RoleMember": "Гишүүн" + }, + "projectSelect": { + "ProjectAdminBadge": "Төслийн админ" + }, "rbac": { "Active": "Идэвхтэй", "Assign": "Оноох", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "Нууцлалын бодлого", "ProfileUpdated": "Профайл амжилттай шинэчлэгдлээ.", "Project": "Төсөл", + "ProjectMembers": "Төслийн гишүүд", "Projects": "Төслүүд", "RBACManagement": "RBAC удирдлага", "Remaining": "Үлдсэн", diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index eaaf9d2588..13043a1105 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "UUID tidak sah.", "ResourcePolicy": "Dasar Sumber" }, + "projectMembers": { + "Email": "E-mel", + "Name": "Nama", + "PageTitle": "Ahli Projek", + "Role": "Peranan", + "RoleAdmin": "Pentadbir", + "RoleMember": "Ahli" + }, + "projectSelect": { + "ProjectAdminBadge": "Pentadbir Projek" + }, "rbac": { "Active": "Aktif", "Assign": "Tugaskan", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Dasar Privasi", "ProfileUpdated": "Profil telah berjaya dikemas kini.", "Project": "Projek", + "ProjectMembers": "Ahli Projek", "Projects": "Projek", "RBACManagement": "Pengurusan RBAC", "Remaining": "Tinggal", diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index 1e28955c48..77b9c11b02 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -1682,6 +1682,17 @@ "ProjectIDFilterRuleMessage": "Nieprawidłowy UUID.", "ResourcePolicy": "Polityka zasobów" }, + "projectMembers": { + "Email": "E-mail", + "Name": "Nazwa", + "PageTitle": "Członkowie projektu", + "Role": "Rola", + "RoleAdmin": "Administrator", + "RoleMember": "Członek" + }, + "projectSelect": { + "ProjectAdminBadge": "Administrator projektu" + }, "rbac": { "Active": "Aktywny", "Assign": "Przypisz", @@ -2949,6 +2960,7 @@ "PrivacyPolicy": "Polityka prywatności", "ProfileUpdated": "Profil został pomyślnie zaktualizowany.", "Project": "Projekt", + "ProjectMembers": "Członkowie projektu", "Projects": "Projekty", "RBACManagement": "Zarządzanie RBAC", "Remaining": "Pozostały", diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 58a35c380c..926486e7dc 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "UUID inválido.", "ResourcePolicy": "Política de Recursos" }, + "projectMembers": { + "Email": "E-mail", + "Name": "Nome", + "PageTitle": "Membros do projeto", + "Role": "Função", + "RoleAdmin": "Administrador", + "RoleMember": "Membro" + }, + "projectSelect": { + "ProjectAdminBadge": "Administrador do projeto" + }, "rbac": { "Active": "Ativo", "Assign": "Atribuir", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Política de Privacidade", "ProfileUpdated": "O perfil foi atualizado com sucesso.", "Project": "Projeto", + "ProjectMembers": "Membros do projeto", "Projects": "Projetos", "RBACManagement": "Gerenciamento RBAC", "Remaining": "Restante", diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 062bc154a0..e4794c9459 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "UUID inválido.", "ResourcePolicy": "Política de Recursos" }, + "projectMembers": { + "Email": "E-mail", + "Name": "Nome", + "PageTitle": "Membros do projeto", + "Role": "Função", + "RoleAdmin": "Administrador", + "RoleMember": "Membro" + }, + "projectSelect": { + "ProjectAdminBadge": "Administrador do projeto" + }, "rbac": { "Active": "Ativo", "Assign": "Atribuir", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "Política de Privacidade", "ProfileUpdated": "O perfil foi atualizado com sucesso.", "Project": "Projeto", + "ProjectMembers": "Membros do projeto", "Projects": "Projetos", "RBACManagement": "Gestão RBAC", "Remaining": "Restante", diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index cf1969b9fa..5dec9bef12 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "Неверный UUID.", "ResourcePolicy": "Политика ресурсов" }, + "projectMembers": { + "Email": "Эл. почта", + "Name": "Имя", + "PageTitle": "Участники проекта", + "Role": "Роль", + "RoleAdmin": "Администратор", + "RoleMember": "Участник" + }, + "projectSelect": { + "ProjectAdminBadge": "Администратор проекта" + }, "rbac": { "Active": "Активный", "Assign": "Назначить", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Политика конфиденциальности", "ProfileUpdated": "Профиль успешно обновлён.", "Project": "Проект", + "ProjectMembers": "Участники проекта", "Projects": "Проекты", "RBACManagement": "Управление RBAC", "Remaining": "Оставшийся", diff --git a/resources/i18n/th.json b/resources/i18n/th.json index cacc28470f..bb9b5c4426 100644 --- a/resources/i18n/th.json +++ b/resources/i18n/th.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "UUID ไม่ถูกต้อง.", "ResourcePolicy": "นโยบายทรัพยากร" }, + "projectMembers": { + "Email": "อีเมล", + "Name": "ชื่อ", + "PageTitle": "สมาชิกโครงการ", + "Role": "บทบาท", + "RoleAdmin": "ผู้ดูแล", + "RoleMember": "สมาชิก" + }, + "projectSelect": { + "ProjectAdminBadge": "ผู้ดูแลโครงการ" + }, "rbac": { "Active": "ใช้งานอยู่", "Assign": "มอบหมาย", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "นโยบายความเป็นส่วนตัว", "ProfileUpdated": "อัปเดตโปรไฟล์สำเร็จแล้ว", "Project": "โครงการ", + "ProjectMembers": "สมาชิกโครงการ", "Projects": "โครงการ", "RBACManagement": "การจัดการ RBAC", "Remaining": "ที่เหลืออยู่", diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 1dd8461d0f..c38544c342 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -1679,6 +1679,17 @@ "ProjectIDFilterRuleMessage": "Geçersiz UUID.", "ResourcePolicy": "Kaynak Politikası" }, + "projectMembers": { + "Email": "E-posta", + "Name": "Ad", + "PageTitle": "Proje Üyeleri", + "Role": "Rol", + "RoleAdmin": "Yönetici", + "RoleMember": "Üye" + }, + "projectSelect": { + "ProjectAdminBadge": "Proje Yöneticisi" + }, "rbac": { "Active": "Aktif", "Assign": "Ata", @@ -2945,6 +2956,7 @@ "PrivacyPolicy": "Gizlilik Politikası", "ProfileUpdated": "Profil başarıyla güncellendi.", "Project": "Proje", + "ProjectMembers": "Proje Üyeleri", "Projects": "Projeler", "RBACManagement": "RBAC Yönetimi", "Remaining": "Geriye kalan", diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index ddbc037c77..4ae912efc0 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "UUID không hợp lệ.", "ResourcePolicy": "Chính sách tài nguyên" }, + "projectMembers": { + "Email": "Email", + "Name": "Tên", + "PageTitle": "Thành viên dự án", + "Role": "Vai trò", + "RoleAdmin": "Quản trị viên", + "RoleMember": "Thành viên" + }, + "projectSelect": { + "ProjectAdminBadge": "Quản trị viên dự án" + }, "rbac": { "Active": "Hoạt động", "Assign": "Phân công", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "Chính sách bảo mật", "ProfileUpdated": "Hồ sơ đã được cập nhật thành công.", "Project": "Dự án", + "ProjectMembers": "Thành viên dự án", "Projects": "Dự án", "RBACManagement": "Quản lý RBAC", "Remaining": "Còn lại", diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index 42c24f3f04..7042f77012 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -1681,6 +1681,17 @@ "ProjectIDFilterRuleMessage": "无效的 UUID。", "ResourcePolicy": "资源策略" }, + "projectMembers": { + "Email": "邮箱", + "Name": "姓名", + "PageTitle": "项目成员", + "Role": "角色", + "RoleAdmin": "管理员", + "RoleMember": "成员" + }, + "projectSelect": { + "ProjectAdminBadge": "项目管理员" + }, "rbac": { "Active": "活跃", "Assign": "分配", @@ -2947,6 +2958,7 @@ "PrivacyPolicy": "隐私政策", "ProfileUpdated": "个人资料已成功更新。", "Project": "项目", + "ProjectMembers": "项目成员", "Projects": "项目", "RBACManagement": "RBAC管理", "Remaining": "其余的", diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index 9a83bd0fc7..63e1fe2f71 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -1683,6 +1683,17 @@ "ProjectIDFilterRuleMessage": "無效的 UUID。", "ResourcePolicy": "資源策略" }, + "projectMembers": { + "Email": "電子郵件", + "Name": "姓名", + "PageTitle": "專案成員", + "Role": "角色", + "RoleAdmin": "管理員", + "RoleMember": "成員" + }, + "projectSelect": { + "ProjectAdminBadge": "專案管理員" + }, "rbac": { "Active": "活躍", "Assign": "分配", @@ -2949,6 +2960,7 @@ "PrivacyPolicy": "隱私政策", "ProfileUpdated": "個人資料已成功更新。", "Project": "項目", + "ProjectMembers": "專案成員", "Projects": "項目", "RBACManagement": "RBAC管理", "Remaining": "其餘的",