Browse Source

Merge branch 'develop' into qr-prototyping-cleanedup

pr-4468-qr-code-extraction
Daniel Spaude 2 years ago
parent
commit
7f768b7b5e
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  1. 9
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  2. 2
      packages/nc-gui/lang/ar.json
  3. 3
      packages/nc-gui/lang/bn_IN.json
  4. 2
      packages/nc-gui/lang/da.json
  5. 2
      packages/nc-gui/lang/de.json
  6. 2
      packages/nc-gui/lang/es.json
  7. 2
      packages/nc-gui/lang/fa.json
  8. 2
      packages/nc-gui/lang/fi.json
  9. 2
      packages/nc-gui/lang/fr.json
  10. 2
      packages/nc-gui/lang/he.json
  11. 2
      packages/nc-gui/lang/hi.json
  12. 2
      packages/nc-gui/lang/hr.json
  13. 2
      packages/nc-gui/lang/id.json
  14. 2
      packages/nc-gui/lang/it.json
  15. 2
      packages/nc-gui/lang/ja.json
  16. 2
      packages/nc-gui/lang/ko.json
  17. 2
      packages/nc-gui/lang/lv.json
  18. 2
      packages/nc-gui/lang/nl.json
  19. 2
      packages/nc-gui/lang/no.json
  20. 2
      packages/nc-gui/lang/pl.json
  21. 2
      packages/nc-gui/lang/pt.json
  22. 2
      packages/nc-gui/lang/pt_BR.json
  23. 2
      packages/nc-gui/lang/ru.json
  24. 2
      packages/nc-gui/lang/sl.json
  25. 2
      packages/nc-gui/lang/sv.json
  26. 2
      packages/nc-gui/lang/th.json
  27. 2
      packages/nc-gui/lang/tr.json
  28. 2
      packages/nc-gui/lang/uk.json
  29. 2
      packages/nc-gui/lang/vi.json
  30. 2
      packages/nc-gui/lang/zh-Hans.json
  31. 278
      packages/nc-gui/lang/zh-Hant.json
  32. 22
      packages/nc-gui/pages/index/index/[projectId].vue
  33. 19
      packages/nc-gui/pages/index/index/create.vue
  34. 5
      packages/nc-gui/pages/index/index/user.vue
  35. 51
      tests/playwright/pages/Account/ChangePassword.ts
  36. 3
      tests/playwright/pages/Account/Users.ts
  37. 10
      tests/playwright/pages/Account/index.ts
  38. 2
      tests/playwright/pages/Dashboard/Grid/index.ts
  39. 24
      tests/playwright/pages/Dashboard/Settings/Teams.ts
  40. 1
      tests/playwright/pages/Dashboard/TreeView.ts
  41. 1
      tests/playwright/pages/Dashboard/WebhookForm/index.ts
  42. 8
      tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  43. 25
      tests/playwright/pages/Dashboard/common/Toolbar/index.ts
  44. 32
      tests/playwright/pages/Dashboard/index.ts
  45. 50
      tests/playwright/pages/ProjectsPage/index.ts
  46. 2
      tests/playwright/tests/accountUserSettings.spec.ts
  47. 27
      tests/playwright/tests/authChangePassword.spec.ts
  48. 2
      tests/playwright/tests/metaSync.spec.ts
  49. 3
      tests/playwright/tests/toolbarOperations.spec.ts
  50. 7
      tests/playwright/tests/viewGridShare.spec.ts
  51. 9
      tests/playwright/tests/viewKanban.spec.ts

9
packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue

@ -150,11 +150,16 @@ const emailField = (inputEl: typeof Input) => {
wrap-class-name="nc-modal-invite-user-and-share-base" wrap-class-name="nc-modal-invite-user-and-share-base"
@cancel="emit('closed')" @cancel="emit('closed')"
> >
<div class="flex flex-col"> <div class="flex flex-col" data-testid="invite-user-and-share-base-modal">
<div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full"> <div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full">
<a-typography-title class="select-none" :level="4"> {{ $t('activity.share') }}: {{ project.title }} </a-typography-title> <a-typography-title class="select-none" :level="4"> {{ $t('activity.share') }}: {{ project.title }} </a-typography-title>
<a-button type="text" class="!rounded-md mr-1 -mt-1.5" @click="emit('closed')"> <a-button
type="text"
class="!rounded-md mr-1 -mt-1.5"
data-testid="invite-user-and-share-base-modal-close-btn"
@click="emit('closed')"
>
<template #icon> <template #icon>
<MaterialSymbolsCloseRounded class="flex mx-auto" /> <MaterialSymbolsCloseRounded class="flex mx-auto" />
</template> </template>

2
packages/nc-gui/lang/ar.json

@ -368,6 +368,8 @@
"setPrimary": "تعيين كقيمة أساسية", "setPrimary": "تعيين كقيمة أساسية",
"addRow": "إضافة صف جديد", "addRow": "إضافة صف جديد",
"saveRow": "حفظ الصف", "saveRow": "حفظ الصف",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "إدراج صف جديد", "insertRow": "إدراج صف جديد",
"deleteRow": "حذف الصف", "deleteRow": "حذف الصف",
"deleteSelectedRow": "حذف الصفوف المحددة", "deleteSelectedRow": "حذف الصفوف المحددة",

3
packages/nc-gui/lang/bn_IN.json

@ -368,6 +368,8 @@
"setPrimary": "পথমিক মন হিট করন", "setPrimary": "পথমিক মন হিট করন",
"addRow": "নতন সিত করন", "addRow": "নতন সিত করন",
"saveRow": "সিরকষণ করন", "saveRow": "সিরকষণ করন",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "নতন সিন", "insertRow": "নতন সিন",
"deleteRow": "সিন", "deleteRow": "সিন",
"deleteSelectedRow": "নিিত সিিন", "deleteSelectedRow": "নিিত সিিন",
@ -601,7 +603,6 @@
"tableDeleted": "Deleted table successfully", "tableDeleted": "Deleted table successfully",
"generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base", "generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base",
"deleteViewConfirmation": "Are you sure you want to delete this view?", "deleteViewConfirmation": "Are you sure you want to delete this view?",
"deleteTokenConfirmation": "Are you sure you want to delete this token?",
"deleteTableConfirmation": "Do you want to delete the table", "deleteTableConfirmation": "Do you want to delete the table",
"showM2mTables": "Show M2M Tables", "showM2mTables": "Show M2M Tables",
"deleteKanbanStackConfirmation": "Deleting this stack will also remove the select option `{stackToBeDeleted}` from the `{groupingField}`. The records will move to the uncategorized stack." "deleteKanbanStackConfirmation": "Deleting this stack will also remove the select option `{stackToBeDeleted}` from the `{groupingField}`. The records will move to the uncategorized stack."

2
packages/nc-gui/lang/da.json

@ -368,6 +368,8 @@
"setPrimary": "Indstil som primær værdi", "setPrimary": "Indstil som primær værdi",
"addRow": "Tilføj ny række", "addRow": "Tilføj ny række",
"saveRow": "Gem ro", "saveRow": "Gem ro",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Indsæt ny række", "insertRow": "Indsæt ny række",
"deleteRow": "DELETE ROW.", "deleteRow": "DELETE ROW.",
"deleteSelectedRow": "Slet de valgte rækker", "deleteSelectedRow": "Slet de valgte rækker",

2
packages/nc-gui/lang/de.json

@ -369,6 +369,8 @@
"setPrimary": "Als Primärwert festlegen", "setPrimary": "Als Primärwert festlegen",
"addRow": "Neue Zeile hinzufügen", "addRow": "Neue Zeile hinzufügen",
"saveRow": "Zeile speichern", "saveRow": "Zeile speichern",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Neue Zeile einfügen", "insertRow": "Neue Zeile einfügen",
"deleteRow": "Zeile löschen", "deleteRow": "Zeile löschen",
"deleteSelectedRow": "Ausgewählte Zeilen löschen", "deleteSelectedRow": "Ausgewählte Zeilen löschen",

2
packages/nc-gui/lang/es.json

@ -368,6 +368,8 @@
"setPrimary": "Establecido como clave primaria", "setPrimary": "Establecido como clave primaria",
"addRow": "Añadir nueva fila", "addRow": "Añadir nueva fila",
"saveRow": "Grabar la fila", "saveRow": "Grabar la fila",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Insertar nueva fila", "insertRow": "Insertar nueva fila",
"deleteRow": "Borrar fila", "deleteRow": "Borrar fila",
"deleteSelectedRow": "Eliminar filas seleccionadas", "deleteSelectedRow": "Eliminar filas seleccionadas",

2
packages/nc-gui/lang/fa.json

@ -368,6 +368,8 @@
"setPrimary": "تنظیم به عنوان مقدار اولیه", "setPrimary": "تنظیم به عنوان مقدار اولیه",
"addRow": "اضافه کردن ردیف جدید", "addRow": "اضافه کردن ردیف جدید",
"saveRow": "دخیره ردیف", "saveRow": "دخیره ردیف",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "وارد کردن ردیف جدید", "insertRow": "وارد کردن ردیف جدید",
"deleteRow": "حذف ردیف جدید", "deleteRow": "حذف ردیف جدید",
"deleteSelectedRow": "حذف ردیفهای انتخاب شده", "deleteSelectedRow": "حذف ردیفهای انتخاب شده",

2
packages/nc-gui/lang/fi.json

@ -368,6 +368,8 @@
"setPrimary": "Aseta ensisijainen arvo", "setPrimary": "Aseta ensisijainen arvo",
"addRow": "Lisää uusi rivi", "addRow": "Lisää uusi rivi",
"saveRow": "Tallenna rivi", "saveRow": "Tallenna rivi",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Lisää uusi rivi", "insertRow": "Lisää uusi rivi",
"deleteRow": "Poista rivi", "deleteRow": "Poista rivi",
"deleteSelectedRow": "Poista valitut rivit", "deleteSelectedRow": "Poista valitut rivit",

2
packages/nc-gui/lang/fr.json

@ -368,6 +368,8 @@
"setPrimary": "Définir comme valeur primaire", "setPrimary": "Définir comme valeur primaire",
"addRow": "Ajouter une nouvelle ligne", "addRow": "Ajouter une nouvelle ligne",
"saveRow": "Enregistrer la ligne", "saveRow": "Enregistrer la ligne",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Insérer une nouvelle ligne", "insertRow": "Insérer une nouvelle ligne",
"deleteRow": "Supprimer la ligne", "deleteRow": "Supprimer la ligne",
"deleteSelectedRow": "Supprimer les lignes sélectionnées", "deleteSelectedRow": "Supprimer les lignes sélectionnées",

2
packages/nc-gui/lang/he.json

@ -368,6 +368,8 @@
"setPrimary": "להגדיר כערך ראשי", "setPrimary": "להגדיר כערך ראשי",
"addRow": "הוסף שורה חדשה", "addRow": "הוסף שורה חדשה",
"saveRow": "שמור שורה", "saveRow": "שמור שורה",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "הכנס שורה חדשה", "insertRow": "הכנס שורה חדשה",
"deleteRow": "מחק שורה", "deleteRow": "מחק שורה",
"deleteSelectedRow": "מחק את השורות שנבחרו", "deleteSelectedRow": "מחק את השורות שנבחרו",

2
packages/nc-gui/lang/hi.json

@ -368,6 +368,8 @@
"setPrimary": "पथमिक मय कप मट कर", "setPrimary": "पथमिक मय कप मट कर",
"addRow": "नई पि", "addRow": "नई पि",
"saveRow": "पि सह", "saveRow": "पि सह",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "नई पि", "insertRow": "नई पि",
"deleteRow": "पि हट", "deleteRow": "पि हट",
"deleteSelectedRow": "चयनित पि हट", "deleteSelectedRow": "चयनित पि हट",

2
packages/nc-gui/lang/hr.json

@ -368,6 +368,8 @@
"setPrimary": "Postavite kao primarnu vrijednost", "setPrimary": "Postavite kao primarnu vrijednost",
"addRow": "Dodaj novi red", "addRow": "Dodaj novi red",
"saveRow": "Spremanje retka", "saveRow": "Spremanje retka",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Umetnite novi red", "insertRow": "Umetnite novi red",
"deleteRow": "Brisanje retka", "deleteRow": "Brisanje retka",
"deleteSelectedRow": "Izbrišite odabrane retke", "deleteSelectedRow": "Izbrišite odabrane retke",

2
packages/nc-gui/lang/id.json

@ -368,6 +368,8 @@
"setPrimary": "Tetapkan sebagai nilai utama", "setPrimary": "Tetapkan sebagai nilai utama",
"addRow": "Tambahkan baris baru", "addRow": "Tambahkan baris baru",
"saveRow": "Hemat Baris", "saveRow": "Hemat Baris",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Masukkan baris baru.", "insertRow": "Masukkan baris baru.",
"deleteRow": "Hapus Baris", "deleteRow": "Hapus Baris",
"deleteSelectedRow": "Hapus baris yang dipilih", "deleteSelectedRow": "Hapus baris yang dipilih",

2
packages/nc-gui/lang/it.json

@ -368,6 +368,8 @@
"setPrimary": "Impostare come valore primario", "setPrimary": "Impostare come valore primario",
"addRow": "Aggiungi nuova riga", "addRow": "Aggiungi nuova riga",
"saveRow": "Salva riga", "saveRow": "Salva riga",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Inserisci nuova riga", "insertRow": "Inserisci nuova riga",
"deleteRow": "Elimina riga.", "deleteRow": "Elimina riga.",
"deleteSelectedRow": "Elimina righe selezionate", "deleteSelectedRow": "Elimina righe selezionate",

2
packages/nc-gui/lang/ja.json

@ -368,6 +368,8 @@
"setPrimary": "プライマリ値として設定", "setPrimary": "プライマリ値として設定",
"addRow": "行を追加", "addRow": "行を追加",
"saveRow": "行を保存", "saveRow": "行を保存",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "行を挿入", "insertRow": "行を挿入",
"deleteRow": "行を削除", "deleteRow": "行を削除",
"deleteSelectedRow": "選択行を削除", "deleteSelectedRow": "選択行を削除",

2
packages/nc-gui/lang/ko.json

@ -368,6 +368,8 @@
"setPrimary": "Primary value로 설정", "setPrimary": "Primary value로 설정",
"addRow": "행 추가", "addRow": "행 추가",
"saveRow": "행 저장", "saveRow": "행 저장",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "행 삽입", "insertRow": "행 삽입",
"deleteRow": "행 삭제", "deleteRow": "행 삭제",
"deleteSelectedRow": "선택한 행 삭제", "deleteSelectedRow": "선택한 행 삭제",

2
packages/nc-gui/lang/lv.json

@ -368,6 +368,8 @@
"setPrimary": "Uzstādīt kā primāro atslēgu", "setPrimary": "Uzstādīt kā primāro atslēgu",
"addRow": "Pievienot ierakstu", "addRow": "Pievienot ierakstu",
"saveRow": "Saglabāt ierakstu", "saveRow": "Saglabāt ierakstu",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Pievienot jaunu ierakstu", "insertRow": "Pievienot jaunu ierakstu",
"deleteRow": "Dzēst ierakstu", "deleteRow": "Dzēst ierakstu",
"deleteSelectedRow": "Dzēst izvēlētos ierakstus", "deleteSelectedRow": "Dzēst izvēlētos ierakstus",

2
packages/nc-gui/lang/nl.json

@ -368,6 +368,8 @@
"setPrimary": "Instellen als primaire waarde", "setPrimary": "Instellen als primaire waarde",
"addRow": "Nieuwe rij toevoegen", "addRow": "Nieuwe rij toevoegen",
"saveRow": "Sla rij op", "saveRow": "Sla rij op",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Voeg nieuwe rij toe", "insertRow": "Voeg nieuwe rij toe",
"deleteRow": "Verwijder rij", "deleteRow": "Verwijder rij",
"deleteSelectedRow": "Verwijder geselecteerde rijen", "deleteSelectedRow": "Verwijder geselecteerde rijen",

2
packages/nc-gui/lang/no.json

@ -368,6 +368,8 @@
"setPrimary": "Sett som primærverdi", "setPrimary": "Sett som primærverdi",
"addRow": "Legg til ny rad", "addRow": "Legg til ny rad",
"saveRow": "Lagre rad", "saveRow": "Lagre rad",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Sett inn ny rad", "insertRow": "Sett inn ny rad",
"deleteRow": "Slett rad", "deleteRow": "Slett rad",
"deleteSelectedRow": "Slett utvalgte rader", "deleteSelectedRow": "Slett utvalgte rader",

2
packages/nc-gui/lang/pl.json

@ -368,6 +368,8 @@
"setPrimary": "Ustaw jako wartość podstawowa", "setPrimary": "Ustaw jako wartość podstawowa",
"addRow": "Dodaj nowy rząd", "addRow": "Dodaj nowy rząd",
"saveRow": "Zapisz wiersz", "saveRow": "Zapisz wiersz",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Wstaw nowy rząd", "insertRow": "Wstaw nowy rząd",
"deleteRow": "Usuń rząd", "deleteRow": "Usuń rząd",
"deleteSelectedRow": "Usuń wybrane wiersze", "deleteSelectedRow": "Usuń wybrane wiersze",

2
packages/nc-gui/lang/pt.json

@ -368,6 +368,8 @@
"setPrimary": "Definido como valor primário", "setPrimary": "Definido como valor primário",
"addRow": "Adicionar nova linha", "addRow": "Adicionar nova linha",
"saveRow": "Salvar linha", "saveRow": "Salvar linha",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Insira a nova linha", "insertRow": "Insira a nova linha",
"deleteRow": "Excluir linha", "deleteRow": "Excluir linha",
"deleteSelectedRow": "Excluir linhas selecionadas", "deleteSelectedRow": "Excluir linhas selecionadas",

2
packages/nc-gui/lang/pt_BR.json

@ -368,6 +368,8 @@
"setPrimary": "Definido como valor primário", "setPrimary": "Definido como valor primário",
"addRow": "Adicionar nova linha", "addRow": "Adicionar nova linha",
"saveRow": "Salvar linha", "saveRow": "Salvar linha",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Insira a nova linha", "insertRow": "Insira a nova linha",
"deleteRow": "Excluir linha", "deleteRow": "Excluir linha",
"deleteSelectedRow": "Excluir linhas selecionadas", "deleteSelectedRow": "Excluir linhas selecionadas",

2
packages/nc-gui/lang/ru.json

@ -368,6 +368,8 @@
"setPrimary": "Установить в качестве основного значения", "setPrimary": "Установить в качестве основного значения",
"addRow": "Добавить новую строку", "addRow": "Добавить новую строку",
"saveRow": "Сохранить строку", "saveRow": "Сохранить строку",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Вставить новый строк", "insertRow": "Вставить новый строк",
"deleteRow": "Удалить строку", "deleteRow": "Удалить строку",
"deleteSelectedRow": "Удалить выбранные строки", "deleteSelectedRow": "Удалить выбранные строки",

2
packages/nc-gui/lang/sl.json

@ -368,6 +368,8 @@
"setPrimary": "Kot primarna vrednost", "setPrimary": "Kot primarna vrednost",
"addRow": "Dodaj novo vrstico", "addRow": "Dodaj novo vrstico",
"saveRow": "Shrani vrstico", "saveRow": "Shrani vrstico",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Vstavite novo vrstico", "insertRow": "Vstavite novo vrstico",
"deleteRow": "Izbriši vrstico", "deleteRow": "Izbriši vrstico",
"deleteSelectedRow": "Izbrišite izbrane vrstice", "deleteSelectedRow": "Izbrišite izbrane vrstice",

2
packages/nc-gui/lang/sv.json

@ -368,6 +368,8 @@
"setPrimary": "Ange som primärt värde", "setPrimary": "Ange som primärt värde",
"addRow": "Lägg till ny rad", "addRow": "Lägg till ny rad",
"saveRow": "Spara rad", "saveRow": "Spara rad",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Infoga ny rad", "insertRow": "Infoga ny rad",
"deleteRow": "Radera raden", "deleteRow": "Radera raden",
"deleteSelectedRow": "Ta bort valda rader", "deleteSelectedRow": "Ta bort valda rader",

2
packages/nc-gui/lang/th.json

@ -368,6 +368,8 @@
"setPrimary": "ตงคาเปนคาปฐมภ", "setPrimary": "ตงคาเปนคาปฐมภ",
"addRow": "เพมแถวใหม", "addRow": "เพมแถวใหม",
"saveRow": "บนทกแถว", "saveRow": "บนทกแถว",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "แทรกแถวใหม", "insertRow": "แทรกแถวใหม",
"deleteRow": "ลบแถว", "deleteRow": "ลบแถว",
"deleteSelectedRow": "ลบแถวทเลอก", "deleteSelectedRow": "ลบแถวทเลอก",

2
packages/nc-gui/lang/tr.json

@ -368,6 +368,8 @@
"setPrimary": "Birincil değer yap", "setPrimary": "Birincil değer yap",
"addRow": "Yeni satır ekle", "addRow": "Yeni satır ekle",
"saveRow": "Satırı kaydet", "saveRow": "Satırı kaydet",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Yeni Satır Ekle", "insertRow": "Yeni Satır Ekle",
"deleteRow": "Satırı Sil", "deleteRow": "Satırı Sil",
"deleteSelectedRow": "Seçilen Satırları Sil", "deleteSelectedRow": "Seçilen Satırları Sil",

2
packages/nc-gui/lang/uk.json

@ -368,6 +368,8 @@
"setPrimary": "Встановлено як первинне значення", "setPrimary": "Встановлено як первинне значення",
"addRow": "Додати новий рядок", "addRow": "Додати новий рядок",
"saveRow": "Рятувати рядок", "saveRow": "Рятувати рядок",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Вставте новий рядок", "insertRow": "Вставте новий рядок",
"deleteRow": "Видалити рядок", "deleteRow": "Видалити рядок",
"deleteSelectedRow": "Видалити вибрані рядки", "deleteSelectedRow": "Видалити вибрані рядки",

2
packages/nc-gui/lang/vi.json

@ -368,6 +368,8 @@
"setPrimary": "Đặt dưới dạng giá trị chính", "setPrimary": "Đặt dưới dạng giá trị chính",
"addRow": "Thêm hàng mới", "addRow": "Thêm hàng mới",
"saveRow": "Lưu hàng.", "saveRow": "Lưu hàng.",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "Chèn hàng mới", "insertRow": "Chèn hàng mới",
"deleteRow": "Xóa hàng", "deleteRow": "Xóa hàng",
"deleteSelectedRow": "Xóa các hàng đã chọn", "deleteSelectedRow": "Xóa các hàng đã chọn",

2
packages/nc-gui/lang/zh-Hans.json

@ -368,6 +368,8 @@
"setPrimary": "设置为主要值", "setPrimary": "设置为主要值",
"addRow": "添加新行", "addRow": "添加新行",
"saveRow": "保存行", "saveRow": "保存行",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"insertRow": "插入新行", "insertRow": "插入新行",
"deleteRow": "删除行", "deleteRow": "删除行",
"deleteSelectedRow": "删除所选行", "deleteSelectedRow": "删除所选行",

278
packages/nc-gui/lang/zh-Hant.json

@ -6,7 +6,7 @@
"close": "關閉", "close": "關閉",
"yes": "是", "yes": "是",
"no": "否", "no": "否",
"ok": "OK", "ok": "確認",
"and": "和", "and": "和",
"or": "或", "or": "或",
"add": "新增", "add": "新增",
@ -59,23 +59,23 @@
"confirm": "確認", "confirm": "確認",
"generate": "Generate", "generate": "Generate",
"copy": "複製", "copy": "複製",
"misc": "其他", "misc": "Miscellaneous",
"lock": "鎖定", "lock": "鎖定",
"unlock": "解鎖", "unlock": "解鎖",
"credentials": "憑證", "credentials": "憑證",
"help": "幫助", "help": "幫助",
"questions": "問題", "questions": "問題",
"reachOut": "Reach out here", "reachOut": "Reach out here",
"betaNote": "此功能還在測試中", "betaNote": "此功能目前是 Beta 測試版",
"moreInfo": "More information can be found here", "moreInfo": "更多資訊能在這裡找到",
"logs": "Logs", "logs": "日誌",
"groupingField": "Grouping Field" "groupingField": "Grouping Field"
}, },
"objects": { "objects": {
"project": "專案", "project": "項目",
"projects": "全部專案", "projects": "項目",
"table": "資料表", "table": "表",
"tables": "全部資料表", "tables": "表",
"field": "欄位", "field": "欄位",
"fields": "欄位", "fields": "欄位",
"column": "列", "column": "列",
@ -86,8 +86,8 @@
"records": "記錄", "records": "記錄",
"webhook": "Webhook", "webhook": "Webhook",
"webhooks": "Webhook", "webhooks": "Webhook",
"view": "檢視", "view": "檢視",
"views": "所有檢視", "views": "檢視",
"viewType": { "viewType": {
"grid": "網格", "grid": "網格",
"gallery": "圖庫", "gallery": "圖庫",
@ -100,19 +100,19 @@
"role": "角色", "role": "角色",
"roles": "角色", "roles": "角色",
"roleType": { "roleType": {
"owner": "有者", "owner": "有者",
"creator": "創造者", "creator": "創造者",
"editor": "編輯", "editor": "編輯",
"commenter": "評論者", "commenter": "評論者",
"viewer": "檢視者", "viewer": "檢視者",
"orgLevelCreator": "組織級建立者", "orgLevelCreator": "組織級建立者",
"orgLevelViewer": "組織級檢視者" "orgLevelViewer": "組織級檢視者"
}, },
"sqlVIew": "SQL View" "sqlVIew": "SQL View"
}, },
"datatype": { "datatype": {
"ID": "ID", "ID": "ID",
"ForeignKey": "外", "ForeignKey": "外鑰匙",
"SingleLineText": "單行文本", "SingleLineText": "單行文本",
"LongText": "長篇文章", "LongText": "長篇文章",
"Attachment": "附件", "Attachment": "附件",
@ -134,8 +134,8 @@
"Rating": "評分", "Rating": "評分",
"Formula": "公式", "Formula": "公式",
"Rollup": "捲起", "Rollup": "捲起",
"Count": "數", "Count": "數",
"Lookup": "查找", "Lookup": "抬頭",
"DateTime": "日期時間", "DateTime": "日期時間",
"CreateTime": "創建時間", "CreateTime": "創建時間",
"LastModifiedTime": "最後修改時間", "LastModifiedTime": "最後修改時間",
@ -192,11 +192,11 @@
"teamAndSettings": "團隊 & 設定", "teamAndSettings": "團隊 & 設定",
"apiDocs": "API 說明文件", "apiDocs": "API 說明文件",
"importFromAirtable": "從 Airtable 匯入", "importFromAirtable": "從 Airtable 匯入",
"generateToken": "Generate Token", "generateToken": "產生 Token",
"APIsAndSupport": "APIs 與支援", "APIsAndSupport": "APIs & Support",
"helpCenter": "幫助中心", "helpCenter": "幫助中心",
"swaggerDocumentation": "Swagger 文件", "swaggerDocumentation": "Swagger 文件",
"quickImportFrom": "Quick Import From", "quickImportFrom": "快速匯入從",
"quickImport": "快速匯入", "quickImport": "快速匯入",
"advancedSettings": "進階設定", "advancedSettings": "進階設定",
"codeSnippet": "程式碼片段" "codeSnippet": "程式碼片段"
@ -204,15 +204,15 @@
"labels": { "labels": {
"createdBy": "Created By", "createdBy": "Created By",
"notifyVia": "透過...通知", "notifyVia": "透過...通知",
"projName": "專案名", "projName": "項目名",
"tableName": "資料表名稱", "tableName": "表名稱",
"viewName": "查看名稱", "viewName": "查看名稱",
"viewLink": "查看鏈接", "viewLink": "查看鏈接",
"columnName": "欄位名稱", "columnName": "名稱",
"columnType": "欄位類型", "columnType": "類型",
"roleName": "角色名稱", "roleName": "角色名稱",
"roleDescription": "角色描述", "roleDescription": "角色描述",
"databaseType": "數據庫類別", "databaseType": "鍵入數據庫",
"lengthValue": "長度/值", "lengthValue": "長度/值",
"dbType": "資料庫類型", "dbType": "資料庫類型",
"sqliteFile": "SQLite 檔案", "sqliteFile": "SQLite 檔案",
@ -221,7 +221,7 @@
"username": "使用者名稱", "username": "使用者名稱",
"password": "密碼", "password": "密碼",
"schemaName": "Schema 名稱", "schemaName": "Schema 名稱",
"database": "資料庫", "database": "數據庫",
"action": "行動", "action": "行動",
"actions": "行動", "actions": "行動",
"operation": "操作", "operation": "操作",
@ -231,7 +231,7 @@
"authentication": "驗證", "authentication": "驗證",
"token": "權杖", "token": "權杖",
"where": "在哪裡", "where": "在哪裡",
"cache": "快取", "cache": "緩存",
"chat": "聊天", "chat": "聊天",
"email": "電子郵件", "email": "電子郵件",
"storage": "貯存", "storage": "貯存",
@ -249,13 +249,13 @@
"requriedCa": "必填 - CA", "requriedCa": "必填 - CA",
"requriedIdentity": "必填 - IDENTITY", "requriedIdentity": "必填 - IDENTITY",
"inflection": { "inflection": {
"tableName": "屈折 - 資料表名稱", "tableName": "屈折 - 表名稱",
"columnName": "屈折 - 欄位名稱" "columnName": "屈折 - 欄位名稱"
}, },
"community": { "community": {
"starUs1": "在 Github 上", "starUs1": "在 Github 上",
"starUs2": "幫我們按讚", "starUs2": "幫我們按讚",
"bookDemo": "預免費 Demo", "bookDemo": "預免費 Demo",
"getAnswered": "解惑您的問題", "getAnswered": "解惑您的問題",
"joinDiscord": "加入 Discord", "joinDiscord": "加入 Discord",
"joinCommunity": "加入 NocoDB 社群", "joinCommunity": "加入 NocoDB 社群",
@ -265,10 +265,10 @@
"docReference": "文件參考文獻", "docReference": "文件參考文獻",
"selectUserRole": "選擇使用者角色", "selectUserRole": "選擇使用者角色",
"childTable": "子表格", "childTable": "子表格",
"childColumn": "子欄", "childColumn": "子欄",
"onUpdate": "更新", "onUpdate": "更新",
"onDelete": "在刪除", "onDelete": "在刪除",
"account": "帳戶", "account": "Account",
"language": "語言", "language": "語言",
"primaryColor": "Primary Color", "primaryColor": "Primary Color",
"accentColor": "Accent Color", "accentColor": "Accent Color",
@ -277,13 +277,13 @@
"apiKey": "API Key", "apiKey": "API Key",
"sharedBase": "Shared Base", "sharedBase": "Shared Base",
"importData": "匯入資料", "importData": "匯入資料",
"importSecondaryViews": "匯入 Secondary Views", "importSecondaryViews": "Import Secondary Views",
"importRollupColumns": "匯入 Rollup 欄位", "importRollupColumns": "Import Rollup Columns",
"importLookupColumns": "匯入 Lookup 欄位", "importLookupColumns": "Import Lookup Columns",
"importAttachmentColumns": "匯入 Attachment 欄位", "importAttachmentColumns": "Import Attachment Columns",
"importFormulaColumns": "Import Formula Columns", "importFormulaColumns": "Import Formula Columns",
"noData": "目前沒有資料", "noData": "沒有資料",
"goToDashboard": "Go to Dashboard", "goToDashboard": "前往儀表板",
"importing": "匯入中", "importing": "匯入中",
"flattenNested": "Flatten Nested", "flattenNested": "Flatten Nested",
"downloadAllowed": "允許下載", "downloadAllowed": "允許下載",
@ -291,16 +291,16 @@
"primaryKey": "主鍵", "primaryKey": "主鍵",
"hasMany": "has many", "hasMany": "has many",
"belongsTo": "belongs to", "belongsTo": "belongs to",
"manyToMany": "have many to many relation", "manyToMany": "有多對多關聯",
"extraConnectionParameters": "Extra connection parameters", "extraConnectionParameters": "Extra connection parameters",
"commentsOnly": "Comments only", "commentsOnly": "Comments only",
"documentation": "文件", "documentation": "Documentation",
"subscribeNewsletter": "Subscribe to our weekly newsletter", "subscribeNewsletter": "Subscribe to our weekly newsletter",
"signUpWithGoogle": "使用 Google 帳號註冊", "signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "使用 Google 帳號登入", "signInWithGoogle": "Sign in with Google",
"agreeToTos": "By signing up, you agree to the Terms of Service", "agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!", "welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url" "inviteOnlySignup": "只接受使用邀請連結進行註冊"
}, },
"activity": { "activity": {
"createProject": "建立專案", "createProject": "建立專案",
@ -313,7 +313,7 @@
"deleteProject": "刪除專案", "deleteProject": "刪除專案",
"refreshProject": "重新整理專案", "refreshProject": "重新整理專案",
"saveProject": "儲存專案", "saveProject": "儲存專案",
"deleteKanbanStack": "刪除 stack?", "deleteKanbanStack": "Delete stack?",
"createProjectExtended": { "createProjectExtended": {
"extDB": "連線至外部資料庫來建立", "extDB": "連線至外部資料庫來建立",
"excel": "從 Excel 建立專案", "excel": "從 Excel 建立專案",
@ -332,10 +332,10 @@
"projInfo": "複製專案資訊", "projInfo": "複製專案資訊",
"themes": "主題" "themes": "主題"
}, },
"sort": "排序", "sort": "種類",
"addSort": "加排序選項", "addSort": "加排序選項",
"filter": "篩選", "filter": "篩選",
"addFilter": "加過濾器", "addFilter": "加過濾器",
"share": "分享", "share": "分享",
"shareBase": { "shareBase": {
"disable": "禁用共享基礎", "disable": "禁用共享基礎",
@ -352,7 +352,7 @@
"deleteUser": "從專案中刪除使用者", "deleteUser": "從專案中刪除使用者",
"resendInvite": "重新發送邀請電子郵件", "resendInvite": "重新發送邀請電子郵件",
"copyInviteURL": "複製邀請連結", "copyInviteURL": "複製邀請連結",
"copyPasswordResetURL": "Copy password reset URL", "copyPasswordResetURL": "複製重設密碼連結",
"newRole": "新角色", "newRole": "新角色",
"reloadRoles": "重新載入角色", "reloadRoles": "重新載入角色",
"nextPage": "下一頁", "nextPage": "下一頁",
@ -360,14 +360,16 @@
"nextRecord": "下一步記錄", "nextRecord": "下一步記錄",
"previousRecord": "之前的紀錄", "previousRecord": "之前的紀錄",
"copyApiURL": "複製 API 網址", "copyApiURL": "複製 API 網址",
"createTable": "建立資料表", "createTable": "表創造",
"refreshTable": "表刷新", "refreshTable": "表刷新",
"renameTable": "重命名資料表", "renameTable": "重命名",
"deleteTable": "刪除資料表", "deleteTable": "刪除",
"addField": "將新欄位增加到此資料表", "addField": "將新字段添加到此表",
"setPrimary": "設置為主要值", "setPrimary": "設置為主要值",
"addRow": "新增行", "addRow": "新增行",
"saveRow": "儲存行", "saveRow": "儲存行",
"saveAndExit": "儲存並結束",
"saveAndStay": "Save & Stay",
"insertRow": "插入新行", "insertRow": "插入新行",
"deleteRow": "刪除行", "deleteRow": "刪除行",
"deleteSelectedRow": "刪除所選行", "deleteSelectedRow": "刪除所選行",
@ -382,19 +384,19 @@
"clearMetadata": "清除中繼資料", "clearMetadata": "清除中繼資料",
"exportToFile": "匯出為檔案", "exportToFile": "匯出為檔案",
"changePwd": "更改密碼", "changePwd": "更改密碼",
"createView": "建立檢視", "createView": "建立檢視",
"shareView": "分享檢視", "shareView": "分享檢視",
"listSharedView": "共享視圖列表", "listSharedView": "共享視圖列表",
"ListView": "檢視表清單", "ListView": "檢視表清單",
"copyView": "複製檢視", "copyView": "複製檢視",
"renameView": "重新命名檢視", "renameView": "重新命名檢視",
"deleteView": "刪除檢視", "deleteView": "刪除檢視",
"createGrid": "創建網格視圖", "createGrid": "創建網格視圖",
"createGallery": "創建畫廊視圖", "createGallery": "創建畫廊視圖",
"createCalendar": "創建日曆視圖", "createCalendar": "創建日曆視圖",
"createKanban": "創建尋呼視圖", "createKanban": "創建尋呼視圖",
"createForm": "創建表單視圖", "createForm": "創建表單視圖",
"showSystemFields": "顯示系統欄位", "showSystemFields": "顯示系統字段",
"copyUrl": "複製網址", "copyUrl": "複製網址",
"openTab": "開啟新分頁", "openTab": "開啟新分頁",
"iFrame": "複製嵌入式 HTML 程式碼", "iFrame": "複製嵌入式 HTML 程式碼",
@ -412,9 +414,9 @@
"sponsorUs": "贊助我們", "sponsorUs": "贊助我們",
"sendEmail": "傳送電子郵件", "sendEmail": "傳送電子郵件",
"addUserToProject": "Add user to project", "addUserToProject": "Add user to project",
"getApiSnippet": "Get API Snippet", "getApiSnippet": "取得 API 程式碼片段",
"clearCell": "Clear cell", "clearCell": "清除儲存格",
"addFilterGroup": "Add Filter Group", "addFilterGroup": "增加過濾組",
"linkRecord": "Link record", "linkRecord": "Link record",
"addNewRecord": "Add new record", "addNewRecord": "Add new record",
"useConnectionUrl": "Use Connection URL", "useConnectionUrl": "Use Connection URL",
@ -425,7 +427,7 @@
"showColumns": "顯示欄位", "showColumns": "顯示欄位",
"showPkAndFk": "顯示主鍵與外鍵", "showPkAndFk": "顯示主鍵與外鍵",
"showSqlViews": "Show SQL Views", "showSqlViews": "Show SQL Views",
"showMMTables": "Show Many to Many tables", "showMMTables": "顯示多對多資料表",
"showJunctionTableNames": "Show Junction Table Names" "showJunctionTableNames": "Show Junction Table Names"
}, },
"kanban": { "kanban": {
@ -446,13 +448,13 @@
"dark": "它確實有黑色(^⇧b)", "dark": "它確實有黑色(^⇧b)",
"light": "它是黑色嗎?(^⇧b)" "light": "它是黑色嗎?(^⇧b)"
}, },
"addTable": "建立資料表", "addTable": "添加新表",
"inviteMore": "邀請更多使用者", "inviteMore": "邀請更多用戶",
"toggleNavDraw": "切換導航抽屜", "toggleNavDraw": "切換導航抽屜",
"reloadApiToken": "重新載入 API 權杖", "reloadApiToken": "重新載入 API 權杖",
"generateNewApiToken": "產生新 API 權杖", "generateNewApiToken": "產生新 API 權杖",
"addRole": "添加新角色", "addRole": "添加新角色",
"reloadList": "重新載列表", "reloadList": "重新載列表",
"metaSync": "同步中繼資料", "metaSync": "同步中繼資料",
"sqlMigration": "重新加載遷移", "sqlMigration": "重新加載遷移",
"updateRestart": "更新並重新啟動", "updateRestart": "更新並重新啟動",
@ -468,15 +470,15 @@
"projName": "輸入專案名稱", "projName": "輸入專案名稱",
"password": { "password": {
"enter": "輸入密碼", "enter": "輸入密碼",
"current": "前密碼", "current": "前密碼",
"new": "新密碼", "new": "新密碼",
"save": "儲存密碼", "save": "儲存密碼",
"confirm": "確認新密碼" "confirm": "確認新密碼"
}, },
"searchProjectTree": "搜索專案樹", "searchProjectTree": "搜索",
"searchFields": "搜索欄位", "searchFields": "搜索字段",
"searchColumn": "搜索{search}列", "searchColumn": "搜索{search}列",
"searchApps": "搜索應用程", "searchApps": "搜索應用程",
"searchModels": "搜索模型", "searchModels": "搜索模型",
"noItemsFound": "未找到任何項目", "noItemsFound": "未找到任何項目",
"defaultValue": "預設值", "defaultValue": "預設值",
@ -487,13 +489,13 @@
"msg": { "msg": {
"info": { "info": {
"roles": { "roles": {
"orgCreator": "建立者可以建立專案與存取任何受邀請的專案.", "orgCreator": "建立者可以建立專案與存取任何受邀請的專案",
"orgViewer": "檢視者不可建立新專案但可以存取任何受邀請的專案." "orgViewer": "建立者不能建立專案但可以存取任何受邀請的專案"
}, },
"footerInfo": "每頁行駛", "footerInfo": "每頁行駛",
"upload": "選擇檔案以上傳", "upload": "選擇檔案以上傳",
"upload_sub": "或拖放檔案", "upload_sub": "或拖放檔案",
"excelSupport": "支:.xls,.xlsx,.xlsm,.ods,.ots", "excelSupport": "支:.xls,.xlsx,.xlsm,.ods,.ots",
"excelURL": "輸入 Excel 檔案 URL", "excelURL": "輸入 Excel 檔案 URL",
"csvURL": "輸入 CSV 檔案 URL", "csvURL": "輸入 CSV 檔案 URL",
"footMsg": "要解析為推斷數據類型的行數", "footMsg": "要解析為推斷數據類型的行數",
@ -506,10 +508,10 @@
"startProject": "你想啟動這個專案嗎?", "startProject": "你想啟動這個專案嗎?",
"restartProject": "你想重新啟動專案嗎?", "restartProject": "你想重新啟動專案嗎?",
"deleteProject": "你想刪除這個專案嗎?", "deleteProject": "你想刪除這個專案嗎?",
"shareBasePrivate": "產生公開享的 Readonly Base", "shareBasePrivate": "產生公開享的 Readonly Base",
"shareBasePublic": "網路上的任何人都可以查看", "shareBasePublic": "網路上的任何人都可以查看",
"userInviteNoSMTP": "看起來你還沒有配置郵件!請複上面的邀請鏈接並將其發送給", "userInviteNoSMTP": "看起來你還沒有配置郵件!請複上面的邀請鏈接並將其發送給",
"dragDropHide": "在此處拖放欄位以隱藏", "dragDropHide": "在此處拖放字段以隱藏",
"formInput": "輸入表單輸入標籤", "formInput": "輸入表單輸入標籤",
"formHelpText": "添加一些幫助文本", "formHelpText": "添加一些幫助文本",
"onlyCreator": "僅建立者可見", "onlyCreator": "僅建立者可見",
@ -520,10 +522,10 @@
"privateLinkAdditionalInfo": "具有私有連結的人只能看到此檢視表中可見的儲存格", "privateLinkAdditionalInfo": "具有私有連結的人只能看到此檢視表中可見的儲存格",
"afterFormSubmitted": "表格提交後", "afterFormSubmitted": "表格提交後",
"apiOptions": "存取專案方式", "apiOptions": "存取專案方式",
"submitAnotherForm": "顯示 '提交另一個表格' 按鈕", "submitAnotherForm": "顯示“提交另一個表格”按鈕",
"showBlankForm": "5 秒後顯示空白表格", "showBlankForm": "5 秒後顯示空白表格",
"emailForm": "發電子郵件給我", "emailForm": "發電子郵件給我",
"showSysFields": "顯示系統欄位", "showSysFields": "顯示系統字段",
"filterAutoApply": "自動申請", "filterAutoApply": "自動申請",
"showMessage": "顯示此消息", "showMessage": "顯示此消息",
"viewNotShared": "當前視圖不共享!", "viewNotShared": "當前視圖不共享!",
@ -531,9 +533,9 @@
"collabView": "具有編輯權限或更高的合作者可以更改視圖配置。", "collabView": "具有編輯權限或更高的合作者可以更改視圖配置。",
"lockedView": "沒有人可以編輯視圖配置,直到它被解鎖。", "lockedView": "沒有人可以編輯視圖配置,直到它被解鎖。",
"personalView": "只有您可以編輯視圖配置。默認情況下,其他合作者的個人視圖隱藏。", "personalView": "只有您可以編輯視圖配置。默認情況下,其他合作者的個人視圖隱藏。",
"ownerDesc": "可以添加/刪除創建者。和完整編輯資料庫結構和欄位。", "ownerDesc": "可以添加/刪除創建者。和完整編輯數據庫結構和字段。",
"creatorDesc": "可以完全編輯資料庫結構和值。", "creatorDesc": "可以完全編輯數據庫結構和值。",
"editorDesc": "可以編輯記錄但無法更改資料庫/欄位的結構。", "editorDesc": "可以編輯記錄但無法更改數據庫/字段的結構。",
"commenterDesc": "可以查看和評論記錄,但無法編輯任何內容", "commenterDesc": "可以查看和評論記錄,但無法編輯任何內容",
"viewerDesc": "可以查看記錄但無法編輯任何內容", "viewerDesc": "可以查看記錄但無法編輯任何內容",
"addUser": "新增使用者", "addUser": "新增使用者",
@ -551,7 +553,7 @@
}, },
"sponsor": { "sponsor": {
"header": "你可以幫助我們!", "header": "你可以幫助我們!",
"message": "我們是一個小型團隊,全職打造 NocoDB 並且開源程式碼。我們相信像 NocoDB 這樣的工具應該在網際網路上自由提供給每位問題解決者。" "message": "我們是一支小型團隊,全職工作,使Nocodb開放來源。我們相信一個像Nocodb這樣的工具應該在互聯網上的每個問題求解器上自由提供。"
}, },
"loginMsg": "登入 NocoDB", "loginMsg": "登入 NocoDB",
"passwordRecovery": { "passwordRecovery": {
@ -577,7 +579,7 @@
"tablesMetadataInSync": "表元數據同步", "tablesMetadataInSync": "表元數據同步",
"addMultipleUsers": "您可以添加多個逗號(,)分隔的電子郵件", "addMultipleUsers": "您可以添加多個逗號(,)分隔的電子郵件",
"enterTableName": "輸入表名", "enterTableName": "輸入表名",
"addDefaultColumns": "建立預設欄位", "addDefaultColumns": "添加默認列",
"tableNameInDb": "數據庫中保存的表名", "tableNameInDb": "數據庫中保存的表名",
"airtable": { "airtable": {
"credentials": "Where to find this?" "credentials": "Where to find this?"
@ -591,18 +593,18 @@
"copiedToClipboard": "複製到剪貼簿", "copiedToClipboard": "複製到剪貼簿",
"requriedFieldsCantBeMoved": "Required field can't be moved", "requriedFieldsCantBeMoved": "Required field can't be moved",
"updateNotAllowedWithoutPK": "Update not allowed for table which doesn't have primary key", "updateNotAllowedWithoutPK": "Update not allowed for table which doesn't have primary key",
"autoIncFieldNotEditable": "Auto increment field is not editable", "autoIncFieldNotEditable": "自增欄位不可編輯",
"editingPKnotSupported": "Editing primary key not supported", "editingPKnotSupported": "不支援編輯主鍵",
"deletedCache": "Deleted cache successfully", "deletedCache": "刪除快取成功",
"cacheEmpty": "快取是空的", "cacheEmpty": "快取是空的",
"exportedCache": "Exported Cache Successfully", "exportedCache": "Exported Cache Successfully",
"valueAlreadyInList": "This value is already in the list", "valueAlreadyInList": "此值已在列表中",
"noColumnsToUpdate": "No columns to update", "noColumnsToUpdate": "No columns to update",
"tableDeleted": "Deleted table successfully", "tableDeleted": "刪除資料表成功",
"generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base", "generatePublicShareableReadonlyBase": "Generate publicly shareable readonly base",
"deleteViewConfirmation": "Are you sure you want to delete this view?", "deleteViewConfirmation": "是否確定要刪除此檢視?",
"deleteTableConfirmation": "Do you want to delete the table", "deleteTableConfirmation": "你想刪除此資料表",
"showM2mTables": "顯示 M2M 資料表", "showM2mTables": "顯示多對多資料表",
"deleteKanbanStackConfirmation": "Deleting this stack will also remove the select option `{stackToBeDeleted}` from the `{groupingField}`. The records will move to the uncategorized stack." "deleteKanbanStackConfirmation": "Deleting this stack will also remove the select option `{stackToBeDeleted}` from the `{groupingField}`. The records will move to the uncategorized stack."
}, },
"error": { "error": {
@ -619,51 +621,51 @@
"passwdRequired": "密碼為必填", "passwdRequired": "密碼為必填",
"passwdLength": "您的密碼應至少有 8 個字元", "passwdLength": "您的密碼應至少有 8 個字元",
"passwdMismatch": "密碼不匹配", "passwdMismatch": "密碼不匹配",
"completeRuleSet": "At least 8 characters with one Uppercase, one number and one special character", "completeRuleSet": "密碼必須含有至少 8 個字元,其中有一個大寫字母、一個數字和一個特殊字元",
"atLeast8Char": "At least 8 characters", "atLeast8Char": "至少 8 個字元",
"atLeastOneUppercase": "One Uppercase letter", "atLeastOneUppercase": "一個大寫字母",
"atLeastOneNumber": "One Number", "atLeastOneNumber": "一個數字",
"atLeastOneSpecialChar": "One special character", "atLeastOneSpecialChar": "一個特殊字元",
"allowedSpecialCharList": "Allowed special character list" "allowedSpecialCharList": "允許特殊字元列表"
}, },
"invalidURL": "無效的 URL", "invalidURL": "無效的連結",
"internalError": "Some internal error occurred", "internalError": "發生內部錯誤",
"templateGeneratorNotFound": "Template Generator cannot be found!", "templateGeneratorNotFound": "Template Generator cannot be found!",
"fileUploadFailed": "檔案上傳失敗", "fileUploadFailed": "上傳文件失敗",
"primaryColumnUpdateFailed": "Failed to update primary column", "primaryColumnUpdateFailed": "Failed to update primary column",
"formDescriptionTooLong": "Data too long for Form Description", "formDescriptionTooLong": "表單描述資料過長",
"columnsRequired": "Following columns are required", "columnsRequired": "Following columns are required",
"selectAtleastOneColumn": "At least one column has to be selected", "selectAtleastOneColumn": "至少必須選擇一個欄位",
"columnDescriptionNotFound": "Cannot find the destination column for", "columnDescriptionNotFound": "Cannot find the destination column for",
"duplicateMappingFound": "Duplicate mapping found, please remove one of the mapping", "duplicateMappingFound": "Duplicate mapping found, please remove one of the mapping",
"nullValueViolatesNotNull": "Null value violates not-null constraint", "nullValueViolatesNotNull": "Null 值違反不可為 Null 限制條件",
"sourceHasInvalidNumbers": "Source data contains some invalid numbers", "sourceHasInvalidNumbers": "來源資料包含無效的數字",
"sourceHasInvalidBoolean": "Source data contains some invalid boolean values", "sourceHasInvalidBoolean": "來源資料包含無效的布林值",
"invalidForm": "無效的表", "invalidForm": "無效的表",
"formValidationFailed": "Form validation failed", "formValidationFailed": "Form validation failed",
"youHaveBeenSignedOut": "You have been signed out", "youHaveBeenSignedOut": "您已登出",
"failedToLoadList": "Failed to load list", "failedToLoadList": "Failed to load list",
"failedToLoadChildrenList": "Failed to load children list", "failedToLoadChildrenList": "Failed to load children list",
"deleteFailed": "Delete failed", "deleteFailed": "刪除失敗",
"unlinkFailed": "Unlink failed", "unlinkFailed": "Unlink failed",
"rowUpdateFailed": "Row update failed", "rowUpdateFailed": "資料更新失敗",
"deleteRowFailed": "Failed to delete row", "deleteRowFailed": "刪除資料失敗",
"setFormDataFailed": "Failed to set form data", "setFormDataFailed": "Failed to set form data",
"formViewUpdateFailed": "Failed to update form view", "formViewUpdateFailed": "Failed to update form view",
"tableNameRequired": "Table name is required", "tableNameRequired": "資料表名稱必填",
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _", "nameShouldStartWithAnAlphabetOr_": "名稱必須用 英文字母 或 _ 當開頭",
"followingCharactersAreNotAllowed": "Following characters are not allowed", "followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required", "columnNameRequired": "欄位名稱必填",
"projectNameExceeds50Characters": "Project name exceeds 50 characters", "projectNameExceeds50Characters": "專案名稱超過 50 個字元",
"projectNameCannotStartWithSpace": "Project name cannot start with space", "projectNameCannotStartWithSpace": "專案名稱不能有空白開頭",
"requiredField": "Required field", "requiredField": "必填欄位",
"ipNotAllowed": "IP not allowed", "ipNotAllowed": "IP not allowed",
"targetFileIsNotAnAcceptedFileType": "Target file is not an accepted file type", "targetFileIsNotAnAcceptedFileType": "Target file is not an accepted file type",
"theAcceptedFileTypeIsCsv": "The accepted file type is .csv", "theAcceptedFileTypeIsCsv": "The accepted file type is .csv",
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots", "theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key 不可為空", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} 不可為空.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible"
}, },
"toast": { "toast": {
@ -685,37 +687,37 @@
}, },
"success": { "success": {
"updatedUIACL": "Updated UI ACL for tables successfully", "updatedUIACL": "Updated UI ACL for tables successfully",
"pluginUninstalled": "Plugin uninstalled successfully", "pluginUninstalled": "外掛移除安裝成功",
"pluginSettingsSaved": "Plugin settings saved successfully", "pluginSettingsSaved": "外掛設定儲存成功",
"pluginTested": "Successfully tested plugin settings", "pluginTested": "外掛設定測試成功",
"tableRenamed": "資料表重新命名成功", "tableRenamed": "資料表重新命名成功",
"viewDeleted": "View deleted successfully", "viewDeleted": "檢視刪除成功",
"primaryColumnUpdated": "Successfully updated as primary column", "primaryColumnUpdated": "Successfully updated as primary column",
"tableDataExported": "Successfully exported all table data", "tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated", "updated": "成功更新",
"sharedViewDeleted": "Deleted shared view successfully", "sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully", "userDeleted": "使用者已成功删除",
"viewRenamed": "View renamed successfully", "viewRenamed": "檢視重新命名成功",
"tokenGenerated": "Token generated successfully", "tokenGenerated": "Token 產生成功",
"tokenDeleted": "Token deleted successfully", "tokenDeleted": "Token 刪除成功",
"userAddedToProject": "成功增加使用者到專案", "userAddedToProject": "專案增加使用者成功",
"userAdded": "成功增加使用者", "userAdded": "增加使用者成功",
"userDeletedFromProject": "Successfully deleted user from project", "userDeletedFromProject": "專案移除使用者成功",
"inviteEmailSent": "Invite Email sent successfully", "inviteEmailSent": "邀請郵件發送成功",
"inviteURLCopied": "Invite URL 複製到剪貼簿", "inviteURLCopied": "邀請連結已複製到剪貼簿",
"passwordResetURLCopied": "Password reset URL copied to 剪貼簿", "passwordResetURLCopied": "密碼重置連結已複製到剪貼簿",
"shareableURLCopied": "Copied shareable base URL to 剪貼簿!", "shareableURLCopied": "Copied shareable base URL to clipboard!",
"embeddableHTMLCodeCopied": "Copied embeddable HTML code!", "embeddableHTMLCodeCopied": "Copied embeddable HTML code!",
"userDetailsUpdated": "Successfully updated the user details", "userDetailsUpdated": "成功更新使用者資料",
"tableDataImported": "Successfully imported table data", "tableDataImported": "成功匯入資料表資料",
"webhookUpdated": "Webhook details updated successfully", "webhookUpdated": "Webhook details updated successfully",
"webhookDeleted": "Hook deleted successfully", "webhookDeleted": "Hook deleted successfully",
"webhookTested": "Webhook tested successfully", "webhookTested": "Webhook tested successfully",
"columnUpdated": "欄位已更新", "columnUpdated": "欄位已更新",
"columnCreated": "欄位已建立", "columnCreated": "欄位已建立",
"passwordChanged": "密碼變更成功. 請重新登入.", "passwordChanged": "密碼已更新,請重新登入。",
"settingsSaved": "設定儲存成功", "settingsSaved": "設定已成功儲存",
"roleUpdated": "角色更新成功" "roleUpdated": "角色已成功更新"
} }
} }
} }

22
packages/nc-gui/pages/index/index/[projectId].vue

@ -1,6 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import type { VNodeRef } from '@vue/runtime-core'
import { import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
message, message,
@ -8,14 +9,13 @@ import {
projectTitleValidator, projectTitleValidator,
reactive, reactive,
ref, ref,
tryOnMounted,
useProject, useProject,
useRoute, useRoute,
} from '#imports' } from '#imports'
const route = useRoute() const route = useRoute()
const { project, loadProject, updateProject, isLoading, projectLoadedHook } = useProject() const { loadProject, updateProject, isLoading } = useProject()
loadProject(false) loadProject(false)
@ -43,21 +43,7 @@ const renameProject = async () => {
} }
} }
// select and focus title field on load const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
projectLoadedHook(async () => {
formState.title = project.value.title as string
tryOnMounted(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.focus()
input.setSelectionRange(0, formState.title?.length)
}, 150)
})
})
</script> </script>
<template> <template>
@ -89,7 +75,7 @@ projectLoadedHook(async () => {
@finish="renameProject" @finish="renameProject"
> >
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules"> <a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" /> <a-input :ref="focus" v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item> </a-form-item>
<div class="text-center"> <div class="text-center">

19
packages/nc-gui/pages/index/index/create.vue

@ -1,11 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import type { VNodeRef } from '@vue/runtime-core'
import { import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
message, message,
navigateTo, navigateTo,
nextTick,
onMounted,
projectTitleValidator, projectTitleValidator,
reactive, reactive,
ref, ref,
@ -47,19 +46,7 @@ const createProject = async () => {
} }
} }
// select and focus title field on load const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
onMounted(async () => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
})
</script> </script>
<template> <template>
@ -88,7 +75,7 @@ onMounted(async () => {
@finish="createProject" @finish="createProject"
> >
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="m-10"> <a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="m-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" /> <a-input :ref="focus" v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item> </a-form-item>
<div class="text-center"> <div class="text-center">

5
packages/nc-gui/pages/index/index/user.vue

@ -68,7 +68,10 @@ const resetError = () => {
</script> </script>
<template> <template>
<div class="relative flex flex-col justify-center gap-2 w-full p-8 md:(bg-white rounded-lg border-1 border-gray-200 shadow)"> <div
class="relative flex flex-col justify-center gap-2 w-full p-8 md:(bg-white rounded-lg border-1 border-gray-200 shadow)"
data-testid="user-change-password"
>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" /> <LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<div <div

51
tests/playwright/pages/Account/ChangePassword.ts

@ -0,0 +1,51 @@
import { expect, Page } from '@playwright/test';
import BasePage from '../Base';
export class ChangePasswordPage extends BasePage {
constructor(rootPage: Page) {
super(rootPage);
}
get() {
return this.rootPage.getByTestId('nc-user-settings-form');
}
async changePassword({
oldPass,
newPass,
repeatPass,
networkValidation,
}: {
oldPass: string;
newPass: string;
repeatPass: string;
networkValidation?: boolean;
}) {
const currentPassword = this.get().locator('input[data-testid="nc-user-settings-form__current-password"]');
const newPassword = this.get().locator('input[data-testid="nc-user-settings-form__new-password"]');
const confirmPassword = this.get().locator('input[data-testid="nc-user-settings-form__new-password-repeat"]');
await currentPassword.fill(oldPass);
await newPassword.fill(newPass);
await confirmPassword.fill(repeatPass);
const submitChangePassword = this.get().locator('button[data-testid="nc-user-settings-form__submit"]').click();
if (networkValidation) {
await this.waitForResponse({
uiAction: submitChangePassword,
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: 'api/v1/auth/password/change',
});
} else {
await submitChangePassword;
}
}
async verifyFormError({ error }: { error: string }) {
await expect(this.get().getByTestId('nc-user-settings-form__error')).toHaveText(error);
}
async verifyPasswordDontMatchError() {
await expect(this.rootPage.locator('.ant-form-item-explain-error')).toHaveText('Passwords do not match');
}
}

3
tests/playwright/pages/Account/Users.ts

@ -1,10 +1,12 @@
import { Locator } from '@playwright/test'; import { Locator } from '@playwright/test';
import BasePage from '../Base'; import BasePage from '../Base';
import { ChangePasswordPage } from './ChangePassword';
import { AccountPage } from './index'; import { AccountPage } from './index';
export class AccountUsersPage extends BasePage { export class AccountUsersPage extends BasePage {
readonly inviteUserBtn: Locator; readonly inviteUserBtn: Locator;
readonly inviteUserModal: Locator; readonly inviteUserModal: Locator;
readonly changePasswordPage: ChangePasswordPage;
private accountPage: AccountPage; private accountPage: AccountPage;
constructor(accountPage: AccountPage) { constructor(accountPage: AccountPage) {
@ -12,6 +14,7 @@ export class AccountUsersPage extends BasePage {
this.accountPage = accountPage; this.accountPage = accountPage;
this.inviteUserBtn = this.get().locator(`[data-testid="nc-super-user-invite"]`); this.inviteUserBtn = this.get().locator(`[data-testid="nc-super-user-invite"]`);
this.inviteUserModal = accountPage.rootPage.locator(`.nc-modal-invite-user`); this.inviteUserModal = accountPage.rootPage.locator(`.nc-modal-invite-user`);
this.changePasswordPage = new ChangePasswordPage(this.rootPage);
} }
async goto() { async goto() {

10
tests/playwright/pages/Account/index.ts

@ -1,9 +1,19 @@
import { Page } from '@playwright/test'; import { Page } from '@playwright/test';
import BasePage from '../Base'; import BasePage from '../Base';
import { AccountSettingsPage } from './Settings';
import { AccountTokenPage } from './Token';
import { AccountUsersPage } from './Users';
export class AccountPage extends BasePage { export class AccountPage extends BasePage {
readonly settings: AccountSettingsPage;
readonly token: AccountTokenPage;
readonly users: AccountUsersPage;
constructor(page: Page) { constructor(page: Page) {
super(page); super(page);
this.settings = new AccountSettingsPage(this);
this.token = new AccountTokenPage(this);
this.users = new AccountUsersPage(this);
} }
get() { get() {

2
tests/playwright/pages/Dashboard/Grid/index.ts

@ -135,7 +135,7 @@ export class GridPage extends BasePage {
} }
async deleteRow(index: number) { async deleteRow(index: number) {
await this.get().locator(`td[data-testid="cell-Title-${index}"]`).click({ await this.get().getByTestId(`cell-Title-${index}`).click({
button: 'right', button: 'right',
}); });

24
tests/playwright/pages/Dashboard/Settings/Teams.ts

@ -1,32 +1,31 @@
import { expect, Locator } from '@playwright/test'; import { Locator } from '@playwright/test';
import { SettingsPage } from '.'; import { SettingsPage } from '.';
import BasePage from '../../Base'; import BasePage from '../../Base';
import { writeFileAsync } from 'xlsx';
import { ToolbarPage } from '../common/Toolbar';
export class TeamsPage extends BasePage { export class TeamsPage extends BasePage {
private readonly settings: SettingsPage; private readonly settings: SettingsPage;
readonly inviteTeamBtn: Locator; private readonly inviteTeamBtn: Locator;
readonly inviteTeamModal: Locator; private readonly inviteTeamModal: Locator;
constructor(settings: SettingsPage) { constructor(settings: SettingsPage) {
super(settings.rootPage); super(settings.rootPage);
this.settings = settings; this.settings = settings;
this.inviteTeamBtn = this.get().locator(`button:has-text("Invite Team")`); this.inviteTeamBtn = this.get().locator(`button:has-text("Invite Team")`);
this.inviteTeamModal = this.rootPage.locator(`.nc-modal-invite-user-and-share-base`); this.inviteTeamModal = this.rootPage.getByTestId('invite-user-and-share-base-modal');
} }
get() { get() {
return this.settings.get().locator(`[data-testid="nc-settings-subtab-Users Management"]`); return this.settings.get().getByTestId('nc-settings-subtab-Users Management');
} }
// Prefixing to differentiate between emails created by the tests which are deleted after the test run
prefixEmail(email: string) { prefixEmail(email: string) {
const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0'; const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0';
return `nc_test_${parallelId}_${email}`; return `nc_test_${parallelId}_${email}`;
} }
getSharedBaseSubModal() { getSharedBaseSubModal() {
return this.rootPage.locator(`[data-testid="nc-share-base-sub-modal"]`); return this.rootPage.getByTestId('nc-share-base-sub-modal');
} }
async invite({ email, role }: { email: string; role: string }) { async invite({ email, role }: { email: string; role: string }) {
@ -44,8 +43,8 @@ export class TeamsPage extends BasePage {
} }
async closeInvite() { async closeInvite() {
// two btn-icon-only in invite modal: close & copy url // todo: Fix the case where there is ghost dom for previous modal
await this.inviteTeamModal.locator(`button.ant-btn-icon-only:visible`).first().click(); await this.inviteTeamModal.getByTestId('invite-user-and-share-base-modal-close-btn').last().click();
} }
async inviteMore() { async inviteMore() {
@ -53,7 +52,7 @@ export class TeamsPage extends BasePage {
} }
async toggleSharedBase({ toggle }: { toggle: boolean }) { async toggleSharedBase({ toggle }: { toggle: boolean }) {
const toggleBtn = await this.getSharedBaseSubModal().locator(`.nc-disable-shared-base`); const toggleBtn = this.getSharedBaseSubModal().locator(`.nc-disable-shared-base`);
const toggleBtnText = await toggleBtn.first().innerText(); const toggleBtnText = await toggleBtn.first().innerText();
const disabledBase = toggleBtnText.includes('Disable'); const disabledBase = toggleBtnText.includes('Disable');
@ -76,8 +75,7 @@ export class TeamsPage extends BasePage {
} }
async getSharedBaseUrl() { async getSharedBaseUrl() {
const url = await this.getSharedBaseSubModal().locator(`.nc-url:visible`).innerText(); return await this.getSharedBaseSubModal().locator(`.nc-url:visible`).textContent();
return url;
} }
async sharedBaseActions({ action }: { action: string }) { async sharedBaseActions({ action }: { action: string }) {

1
tests/playwright/pages/Dashboard/TreeView.ts

@ -57,6 +57,7 @@ export class TreeViewPage extends BasePage {
responseJsonMatcher: json => json.title === title && json.type === 'table', responseJsonMatcher: json => json.title === title && json.type === 'table',
}); });
// Tab render is slow for playwright
await this.dashboard.waitForTabRender({ title }); await this.dashboard.waitForTabRender({ title });
} }

1
tests/playwright/pages/Dashboard/WebhookForm/index.ts

@ -23,7 +23,6 @@ export class WebhookFormPage extends BasePage {
return this.dashboard.get().locator(`.nc-drawer-webhook-body`); return this.dashboard.get().locator(`.nc-drawer-webhook-body`);
} }
// todo: Removing opening webhook drawer logic as it belongs to `Toolbar` page
async create({ title, event, url = 'http://localhost:9090/hook' }: { title: string; event: string; url?: string }) { async create({ title, event, url = 'http://localhost:9090/hook' }: { title: string; event: string; url?: string }) {
await this.toolbar.clickActions(); await this.toolbar.clickActions();
await this.toolbar.actions.click('Webhooks'); await this.toolbar.actions.click('Webhooks');

8
tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts

@ -40,11 +40,6 @@ export class ToolbarFilterPage extends BasePage {
value: string; value: string;
isLocallySaved: boolean; isLocallySaved: boolean;
}) { }) {
await this.toolbar.clickFilter();
// todo: If the filter menu is open for the first time for the table, there can will be a api call which will re render the filter menu
await this.rootPage.waitForTimeout(1000);
await this.get().locator(`button:has-text("Add Filter")`).first().click(); await this.get().locator(`button:has-text("Add Filter")`).first().click();
await this.rootPage.locator('.nc-filter-field-select').last().click(); await this.rootPage.locator('.nc-filter-field-select').last().click();
@ -82,9 +77,6 @@ export class ToolbarFilterPage extends BasePage {
requestUrlPathToMatch: isLocallySaved ? `/api/v1/db/public/` : `/api/v1/db/data/noco/`, requestUrlPathToMatch: isLocallySaved ? `/api/v1/db/public/` : `/api/v1/db/data/noco/`,
}); });
await this.toolbar.parent.dashboard.waitForLoaderToDisappear(); await this.toolbar.parent.dashboard.waitForLoaderToDisappear();
await this.toolbar.clickFilter();
await this.toolbar.parent.waitLoading(); await this.toolbar.parent.waitLoading();
} }

25
tests/playwright/pages/Dashboard/common/Toolbar/index.ts

@ -69,13 +69,30 @@ export class ToolbarPage extends BasePage {
if (menuOpen) await this.sort.get().waitFor({ state: 'hidden' }); if (menuOpen) await this.sort.get().waitFor({ state: 'hidden' });
} }
async clickFilter() { async clickFilter({
// `networkValidation` is used to verify that api calls are made when the button is clicked
// which happens when the filter is opened for the first time
networkValidation,
}: { networkValidation?: boolean } = {}) {
const menuOpen = await this.filter.get().isVisible(); const menuOpen = await this.filter.get().isVisible();
await this.get().locator(`button.nc-filter-menu-btn`).click(); const clickFilterAction = this.get().locator(`button.nc-filter-menu-btn`).click();
// Wait for the menu to close // Wait for the menu to close
if (menuOpen) await this.filter.get().waitFor({ state: 'hidden' }); if (menuOpen) {
await clickFilterAction;
await this.filter.get().waitFor({ state: 'hidden' });
} else {
if (networkValidation) {
// Since on opening filter menu, api is called to fetch filter options, and will rerender the menu
await this.waitForResponse({
uiAction: clickFilterAction,
requestUrlPathToMatch: '/api/v1/db',
httpMethodsToMatch: ['GET'],
});
} else {
await clickFilterAction;
}
}
} }
async clickShareView() { async clickShareView() {

32
tests/playwright/pages/Dashboard/index.ts

@ -61,7 +61,7 @@ export class DashboardPage extends BasePage {
} }
async gotoSettings() { async gotoSettings() {
await this.rootPage.locator('[data-testid="nc-project-menu"]').click(); await this.rootPage.getByTestId('nc-project-menu').click();
await this.rootPage.locator('div.nc-project-menu-item:has-text(" Team & Settings")').click(); await this.rootPage.locator('div.nc-project-menu-item:has-text(" Team & Settings")').click();
} }
@ -79,9 +79,6 @@ export class DashboardPage extends BasePage {
} }
async clickHome() { async clickHome() {
// todo: Fast page transition breaks the vue router
await this.rootPage.waitForTimeout(2000);
await this.rootPage.getByTestId('nc-noco-brand-icon').click(); await this.rootPage.getByTestId('nc-noco-brand-icon').click();
const projectsPage = new ProjectsPage(this.rootPage); const projectsPage = new ProjectsPage(this.rootPage);
await projectsPage.waitToBeRendered(); await projectsPage.waitToBeRendered();
@ -124,32 +121,9 @@ export class DashboardPage extends BasePage {
} }
} }
async openPasswordChangeModal() {
// open change password portal
await this.rootPage.locator('.nc-menu-accounts').click();
await this.rootPage
.locator('.nc-dropdown-user-accounts-menu')
.getByTestId('nc-menu-accounts__user-settings')
.click();
}
// todo: Move this to a seperate page
async changePassword({ oldPass, newPass, repeatPass }: { oldPass: string; newPass: string; repeatPass: string }) {
// change password
const currentPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__current-password"]');
const newPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password"]');
const confirmPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password-repeat"]');
await currentPassword.fill(oldPass);
await newPassword.fill(newPass);
await confirmPassword.fill(repeatPass);
await this.rootPage.locator('button[data-testid="nc-user-settings-form__submit"]').click();
}
async signOut() { async signOut() {
await this.rootPage.locator('[data-testid="nc-project-menu"]').click(); await this.rootPage.getByTestId('nc-project-menu').click();
const projMenu = await this.rootPage.locator('.nc-dropdown-project-menu'); const projMenu = this.rootPage.locator('.nc-dropdown-project-menu');
await projMenu.locator('[data-menu-id="account"]:visible').click(); await projMenu.locator('[data-menu-id="account"]:visible').click();
await this.rootPage.locator('div.nc-project-menu-item:has-text("Sign Out"):visible').click(); await this.rootPage.locator('div.nc-project-menu-item:has-text("Sign Out"):visible').click();
await this.rootPage.locator('[data-testid="nc-form-signin"]:visible').waitFor(); await this.rootPage.locator('[data-testid="nc-form-signin"]:visible').waitFor();

50
tests/playwright/pages/ProjectsPage/index.ts

@ -28,7 +28,7 @@ export class ProjectsPage extends BasePage {
}) { }) {
if (!withoutPrefix) name = this.prefixTitle(name); if (!withoutPrefix) name = this.prefixTitle(name);
await this.rootPage.locator('.nc-new-project-menu').click(); await this.get().locator('.nc-new-project-menu').click();
const createProjectMenu = await this.rootPage.locator('.nc-dropdown-create-project'); const createProjectMenu = await this.rootPage.locator('.nc-dropdown-create-project');
@ -38,20 +38,18 @@ export class ProjectsPage extends BasePage {
await createProjectMenu.locator(`.ant-dropdown-menu-title-content`).nth(1).click(); await createProjectMenu.locator(`.ant-dropdown-menu-title-content`).nth(1).click();
} }
// todo: Fast page transition breaks the vue router
await this.rootPage.waitForTimeout(2000);
await this.rootPage.locator(`.nc-metadb-project-name`).waitFor(); await this.rootPage.locator(`.nc-metadb-project-name`).waitFor();
await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name); await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name);
await this.rootPage.waitForTimeout(2000); const createProjectSubmitAction = this.rootPage.locator(`button:has-text("Create")`).click();
await this.waitForResponse({
await this.rootPage.locator(`button:has-text("Create")`).click({ uiAction: createProjectSubmitAction,
delay: 2000, httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/projects/',
}); });
// fix me! wait for page to be rendered completely // wait for dashboard to render
await this.rootPage.waitForTimeout(2000); await this.rootPage.locator('.nc-container').waitFor({ state: 'visible' });
} }
async checkProjectCreateButton({ exists = true }) { async checkProjectCreateButton({ exists = true }) {
@ -91,9 +89,6 @@ export class ProjectsPage extends BasePage {
withoutPrefix?: boolean; withoutPrefix?: boolean;
waitForAuthTab?: boolean; waitForAuthTab?: boolean;
}) { }) {
// todo: Fast page transition breaks the vue router
await this.rootPage.waitForTimeout(2000);
if (!withoutPrefix) title = this.prefixTitle(title); if (!withoutPrefix) title = this.prefixTitle(title);
let project: any; let project: any;
@ -138,7 +133,13 @@ export class ProjectsPage extends BasePage {
if (!withoutPrefix) title = this.prefixTitle(title); if (!withoutPrefix) title = this.prefixTitle(title);
await this.get().locator(`[data-testid="delete-project-${title}"]`).click(); await this.get().locator(`[data-testid="delete-project-${title}"]`).click();
await this.rootPage.locator(`button:has-text("Yes")`).click();
const deleteProjectAction = this.rootPage.locator(`button:has-text("Yes")`).click();
await this.waitForResponse({
uiAction: deleteProjectAction,
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/projects/',
});
await this.get().locator('.ant-table-row', { hasText: title }).waitFor({ state: 'hidden' }); await this.get().locator('.ant-table-row', { hasText: title }).waitFor({ state: 'hidden' });
} }
@ -161,9 +162,6 @@ export class ProjectsPage extends BasePage {
}); });
await projRow.locator('.nc-action-btn').nth(0).click(); await projRow.locator('.nc-action-btn').nth(0).click();
// todo: Fast page transition breaks the vue router
await this.rootPage.waitForTimeout(2000);
await project.locator('input.nc-metadb-project-name').fill(newTitle); await project.locator('input.nc-metadb-project-name').fill(newTitle);
// press enter to save // press enter to save
const submitAction = project.locator('input.nc-metadb-project-name').press('Enter'); const submitAction = project.locator('input.nc-metadb-project-name').press('Enter');
@ -172,9 +170,6 @@ export class ProjectsPage extends BasePage {
requestUrlPathToMatch: 'api/v1/db/meta/projects/', requestUrlPathToMatch: 'api/v1/db/meta/projects/',
httpMethodsToMatch: ['PATCH'], httpMethodsToMatch: ['PATCH'],
}); });
// todo: vue navigation breaks if page changes very quickly
await this.rootPage.waitForTimeout(1000);
} }
async openLanguageMenu() { async openLanguageMenu() {
@ -187,10 +182,23 @@ export class ProjectsPage extends BasePage {
} }
async verifyLanguage(param: { json: any }) { async verifyLanguage(param: { json: any }) {
const title = await this.rootPage.locator(`.nc-project-page-title`); const title = this.rootPage.locator(`.nc-project-page-title`);
const menu = this.rootPage.locator(`.nc-new-project-menu`); const menu = this.rootPage.locator(`.nc-new-project-menu`);
await expect(title).toHaveText(param.json.title.myProject); await expect(title).toHaveText(param.json.title.myProject);
await expect(menu).toHaveText(param.json.title.newProj); await expect(menu).toHaveText(param.json.title.newProj);
await this.rootPage.locator(`[placeholder="${param.json.activity.searchProject}"]`).waitFor(); await this.rootPage.locator(`[placeholder="${param.json.activity.searchProject}"]`).waitFor();
} }
async openPasswordChangeModal() {
// open change password portal
await this.rootPage.locator('.nc-menu-accounts').click();
await this.rootPage
.locator('.nc-dropdown-user-accounts-menu')
.getByTestId('nc-menu-accounts__user-settings')
.click();
}
async waitForRender() {
await this.rootPage.locator('.nc-project-page-title:has-text("My Projects")').waitFor();
}
} }

2
tests/playwright/tests/accountUserSettings.spec.ts

@ -13,7 +13,7 @@ test.describe('App settings', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
context = await setup({ page }); context = await setup({ page });
accountPage = new AccountPage(page); accountPage = new AccountPage(page);
accountSettingsPage = new AccountSettingsPage(accountPage); accountSettingsPage = accountPage.settings;
}); });
test('Toggle invite only signup', async () => { test('Toggle invite only signup', async () => {

27
tests/playwright/tests/authChangePassword.spec.ts

@ -4,17 +4,24 @@ import setup from '../setup';
import { LoginPage } from '../pages/LoginPage'; import { LoginPage } from '../pages/LoginPage';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings'; import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import { SignupPage } from '../pages/SignupPage'; import { SignupPage } from '../pages/SignupPage';
import { ProjectsPage } from '../pages/ProjectsPage';
import { AccountPage } from '../pages/Account';
test.describe('Auth', () => { test.describe('Auth', () => {
let context: any;
let dashboard: DashboardPage; let dashboard: DashboardPage;
let settings: SettingsPage; let settings: SettingsPage;
let context: any;
let signupPage: SignupPage; let signupPage: SignupPage;
let projectsPage: ProjectsPage;
let accountPage: AccountPage;
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
context = await setup({ page }); context = await setup({ page });
dashboard = new DashboardPage(page, context.project); dashboard = new DashboardPage(page, context.project);
signupPage = new SignupPage(page); signupPage = new SignupPage(page);
projectsPage = new ProjectsPage(page);
accountPage = new AccountPage(page);
settings = dashboard.settings; settings = dashboard.settings;
}); });
@ -37,31 +44,31 @@ test.describe('Auth', () => {
password: 'Password123.', password: 'Password123.',
}); });
await dashboard.openPasswordChangeModal(); await projectsPage.openPasswordChangeModal();
// Existing active pass incorrect // Existing active pass incorrect
await dashboard.changePassword({ await accountPage.users.changePasswordPage.changePassword({
oldPass: '123456789', oldPass: '123456789',
newPass: '123456789', newPass: '123456789',
repeatPass: '123456789', repeatPass: '123456789',
}); });
await dashboard.rootPage await accountPage.users.changePasswordPage.verifyFormError({ error: 'Current password is wrong' });
.locator('[data-testid="nc-user-settings-form__error"]:has-text("Current password is wrong")')
.waitFor();
// New pass and repeat pass mismatch // New pass and repeat pass mismatch
await dashboard.changePassword({ await accountPage.users.changePasswordPage.changePassword({
oldPass: 'Password123.', oldPass: 'Password123.',
newPass: '123456789', newPass: '123456789',
repeatPass: '987654321', repeatPass: '987654321',
networkValidation: false,
}); });
await dashboard.rootPage.locator('.ant-form-item-explain-error:has-text("Passwords do not match")').waitFor(); await accountPage.users.changePasswordPage.verifyPasswordDontMatchError();
// All good // All good
await dashboard.changePassword({ await accountPage.users.changePasswordPage.changePassword({
oldPass: 'Password123.', oldPass: 'Password123.',
newPass: 'NewPasswordConfigured', newPass: 'NewPasswordConfigured',
repeatPass: 'NewPasswordConfigured', repeatPass: 'NewPasswordConfigured',
networkValidation: true,
}); });
const loginPage = new LoginPage(page); const loginPage = new LoginPage(page);
@ -69,6 +76,6 @@ test.describe('Auth', () => {
await loginPage.fillPassword('NewPasswordConfigured'); await loginPage.fillPassword('NewPasswordConfigured');
await loginPage.submit(); await loginPage.submit();
await page.locator('.nc-project-page-title:has-text("My Projects")').waitFor(); await projectsPage.waitForRender();
}); });
}); });

2
tests/playwright/tests/metaSync.spec.ts

@ -260,12 +260,14 @@ test.describe('Meta sync', () => {
isLocallySaved: false, isLocallySaved: false,
}); });
await dashboard.grid.toolbar.clickFilter();
await dashboard.grid.toolbar.filter.addNew({ await dashboard.grid.toolbar.filter.addNew({
columnTitle: 'Col1', columnTitle: 'Col1',
opType: '>=', opType: '>=',
value: '5', value: '5',
isLocallySaved: false, isLocallySaved: false,
}); });
await dashboard.grid.toolbar.clickFilter();
await dashboard.grid.verifyRowCount({ count: 5 }); await dashboard.grid.verifyRowCount({ count: 5 });
}); });

3
tests/playwright/tests/toolbarOperations.spec.ts

@ -56,12 +56,15 @@ test.describe('Toolbar operations (GRID)', () => {
await validateFirstRow('Afghanistan'); await validateFirstRow('Afghanistan');
// Filter column // Filter column
await toolbar.clickFilter();
await toolbar.filter.addNew({ await toolbar.filter.addNew({
columnTitle: 'Country', columnTitle: 'Country',
value: 'India', value: 'India',
opType: 'is equal', opType: 'is equal',
isLocallySaved: false, isLocallySaved: false,
}); });
await toolbar.clickFilter();
await validateFirstRow('India'); await validateFirstRow('India');
// Reset filter // Reset filter

7
tests/playwright/tests/viewGridShare.spec.ts

@ -39,12 +39,14 @@ test.describe('Shared view', () => {
isLocallySaved: false, isLocallySaved: false,
}); });
// filter // filter
await dashboard.grid.toolbar.clickFilter();
await dashboard.grid.toolbar.filter.addNew({ await dashboard.grid.toolbar.filter.addNew({
columnTitle: 'Address', columnTitle: 'Address',
value: 'Ab', value: 'Ab',
opType: 'is like', opType: 'is like',
isLocallySaved: false, isLocallySaved: false,
}); });
await dashboard.grid.toolbar.clickFilter();
// share with password disabled, download enabled // share with password disabled, download enabled
await dashboard.grid.toolbar.clickShareView(); await dashboard.grid.toolbar.clickShareView();
@ -106,12 +108,14 @@ test.describe('Shared view', () => {
}); });
if (isMysql(context)) { if (isMysql(context)) {
await sharedPage.grid.toolbar.clickFilter();
await sharedPage.grid.toolbar.filter.addNew({ await sharedPage.grid.toolbar.filter.addNew({
columnTitle: 'District', columnTitle: 'District',
value: 'Ta', value: 'Ta',
opType: 'is like', opType: 'is like',
isLocallySaved: true, isLocallySaved: true,
}); });
await sharedPage.grid.toolbar.clickFilter();
} }
await sharedPage.grid.toolbar.fields.toggle({ title: 'LastUpdate', isLocallySaved: true }); await sharedPage.grid.toolbar.fields.toggle({ title: 'LastUpdate', isLocallySaved: true });
expectedColumns[6].isVisible = false; expectedColumns[6].isVisible = false;
@ -191,12 +195,15 @@ test.describe('Shared view', () => {
title: 'New Column', title: 'New Column',
isVisible: true, isVisible: true,
}); });
await sharedPage2.grid.toolbar.clickFilter();
await sharedPage2.grid.toolbar.filter.addNew({ await sharedPage2.grid.toolbar.filter.addNew({
columnTitle: 'Country', columnTitle: 'Country',
value: 'New Country', value: 'New Country',
opType: 'is like', opType: 'is like',
isLocallySaved: true, isLocallySaved: true,
}); });
await sharedPage2.grid.toolbar.clickFilter();
await sharedPage2.grid.cell.verify({ await sharedPage2.grid.cell.verify({
index: 0, index: 0,
columnHeader: 'Country', columnHeader: 'Country',

9
tests/playwright/tests/viewKanban.spec.ts

@ -142,12 +142,17 @@ test.describe('View', () => {
}); });
// verify filter // verify filter
await toolbar.clickFilter({
networkValidation: true,
});
await toolbar.filter.addNew({ await toolbar.filter.addNew({
columnTitle: 'Title', columnTitle: 'Title',
opType: 'is like', opType: 'is like',
value: 'BA', value: 'BA',
isLocallySaved: false, isLocallySaved: false,
}); });
await toolbar.clickFilter();
// verify card order // verify card order
const order4 = [ const order4 = [
['BAKED CLEOPATRA', 'BALLROOM MOCKINGBIRD'], ['BAKED CLEOPATRA', 'BALLROOM MOCKINGBIRD'],
@ -188,12 +193,16 @@ test.describe('View', () => {
isAscending: false, isAscending: false,
isLocallySaved: false, isLocallySaved: false,
}); });
await toolbar.clickFilter();
await toolbar.filter.addNew({ await toolbar.filter.addNew({
columnTitle: 'Title', columnTitle: 'Title',
opType: 'is like', opType: 'is like',
value: 'BA', value: 'BA',
isLocallySaved: false, isLocallySaved: false,
}); });
await toolbar.clickFilter();
await toolbar.fields.hideAll(); await toolbar.fields.hideAll();
await toolbar.fields.toggle({ title: 'Title' }); await toolbar.fields.toggle({ title: 'Title' });

Loading…
Cancel
Save