Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22cf30b987 | ||
|
|
842e679cc6 | ||
|
|
a713f8d87f | ||
|
|
fd6d5f895d | ||
|
|
11628d19a3 | ||
|
|
46ba5fd16c | ||
|
|
4649736d49 | ||
|
|
51384bc085 | ||
|
|
abfa6200dd | ||
|
|
480e5796ec | ||
|
|
d8e2de48e6 | ||
|
|
b49e58dfc6 |
@@ -12057,17 +12057,17 @@ msgstr "Rekordtipe"
|
||||
msgid "Record-level"
|
||||
msgstr "Rekordvlak"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Rekords"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "نوع السجل"
|
||||
msgid "Record-level"
|
||||
msgstr "مستوى السجل"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "السجلات"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipus de registre"
|
||||
msgid "Record-level"
|
||||
msgstr "Nivell de registre"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Registres"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Typ záznamu"
|
||||
msgid "Record-level"
|
||||
msgstr "Na úrovni záznamu"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Záznamy"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Posttype"
|
||||
msgid "Record-level"
|
||||
msgstr "Postniveau"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Protokoller"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Datensatztyp"
|
||||
msgid "Record-level"
|
||||
msgstr "Datensatzebene"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Datensätze"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Τύπος εγγραφής"
|
||||
msgid "Record-level"
|
||||
msgstr "Επίπεδο εγγραφής"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Εγγραφές"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12075,17 +12075,17 @@ msgstr "Record Type"
|
||||
msgid "Record-level"
|
||||
msgstr "Record-level"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr "Record(s) selected"
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Records"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr "Records selected"
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipo de registro"
|
||||
msgid "Record-level"
|
||||
msgstr "Nivel de registro"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Registros"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tietuetyyppi"
|
||||
msgid "Record-level"
|
||||
msgstr "Tietuetaso"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Tietueet"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Type d'enregistrement"
|
||||
msgid "Record-level"
|
||||
msgstr "Niveau des enregistrements"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Enregistrements"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -12057,17 +12057,17 @@ msgstr "סוג רשומה"
|
||||
msgid "Record-level"
|
||||
msgstr "ברמת הרשומה"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "רישומים"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Rekordtípus"
|
||||
msgid "Record-level"
|
||||
msgstr "Rekordszintű"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Rekordok"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipo di record"
|
||||
msgid "Record-level"
|
||||
msgstr "Livello record"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Registri"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "レコードタイプ"
|
||||
msgid "Record-level"
|
||||
msgstr "レコードレベル"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "レコード"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "레코드 유형"
|
||||
msgid "Record-level"
|
||||
msgstr "레코드 수준"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "레코드"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Recordtype"
|
||||
msgid "Record-level"
|
||||
msgstr "Recordniveau"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Records"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Oppføringstype"
|
||||
msgid "Record-level"
|
||||
msgstr "Oppføringsnivå"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Poster"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Typ rekordu"
|
||||
msgid "Record-level"
|
||||
msgstr "Poziom rekordu"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Rekordy"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12052,17 +12052,17 @@ msgstr ""
|
||||
msgid "Record-level"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipo de registro"
|
||||
msgid "Record-level"
|
||||
msgstr "Nível de registro"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Registros"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipo de Registo"
|
||||
msgid "Record-level"
|
||||
msgstr "Nível de registo"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Registros"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Tipul înregistrării"
|
||||
msgid "Record-level"
|
||||
msgstr "Nivel de înregistrare"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Înregistrări"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
Binary file not shown.
@@ -12057,17 +12057,17 @@ msgstr "Тип записа"
|
||||
msgid "Record-level"
|
||||
msgstr "Ниво записа"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Рекорди"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12059,17 +12059,17 @@ msgstr "Posttyp"
|
||||
msgid "Record-level"
|
||||
msgstr "Postnivå"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Poster"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Kayıt Türü"
|
||||
msgid "Record-level"
|
||||
msgstr "Kayıt düzeyi"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Kayıtlar"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Тип запису"
|
||||
msgid "Record-level"
|
||||
msgstr "Рівень запису"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Записи"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "Loại bản ghi"
|
||||
msgid "Record-level"
|
||||
msgstr "Cấp bản ghi"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "Hồ Sơ"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "记录类型"
|
||||
msgid "Record-level"
|
||||
msgstr "记录级"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "记录"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
@@ -12057,17 +12057,17 @@ msgstr "記錄類型"
|
||||
msgid "Record-level"
|
||||
msgstr "記錄層級"
|
||||
|
||||
#. js-lingui-id: X5BjmT
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Record(s) selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: PWlTvh
|
||||
#: src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx
|
||||
msgid "Records"
|
||||
msgstr "紀錄"
|
||||
|
||||
#. js-lingui-id: J+Qyf2
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
#: src/modules/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
|
||||
msgid "Records selected"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: wRTiSD
|
||||
#: src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx
|
||||
msgid "Red"
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ const StyledFields = styled.div`
|
||||
`;
|
||||
|
||||
const StyledPropertyBoxContainer = styled.div`
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
|
||||
@@ -58,16 +58,15 @@ export const useCustomResolver = <
|
||||
pageSize,
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
loading: firstQueryLoading,
|
||||
fetchMore,
|
||||
error,
|
||||
} = useQuery<CustomResolverQueryResult<T>>(query, {
|
||||
const { data, loading, fetchMore, error } = useQuery<
|
||||
CustomResolverQueryResult<T>
|
||||
>(query, {
|
||||
client: apolloCoreClient,
|
||||
variables: queryVariables,
|
||||
});
|
||||
|
||||
const firstQueryLoading = loading && !data;
|
||||
|
||||
useSnackBarOnQueryError(error);
|
||||
|
||||
const fetchMoreRecords = async () => {
|
||||
|
||||
@@ -121,6 +121,24 @@ const MarkdownRenderer = lazy(async () => {
|
||||
li: ({ children }) => (
|
||||
<li>{processChildrenForRecordLinks(children)}</li>
|
||||
),
|
||||
h1: ({ children }) => (
|
||||
<h1>{processChildrenForRecordLinks(children)}</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<h2>{processChildrenForRecordLinks(children)}</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3>{processChildrenForRecordLinks(children)}</h3>
|
||||
),
|
||||
h4: ({ children }) => (
|
||||
<h4>{processChildrenForRecordLinks(children)}</h4>
|
||||
),
|
||||
h5: ({ children }) => (
|
||||
<h5>{processChildrenForRecordLinks(children)}</h5>
|
||||
),
|
||||
h6: ({ children }) => (
|
||||
<h6>{processChildrenForRecordLinks(children)}</h6>
|
||||
),
|
||||
a: ({ children, href, title, node: _node }) => (
|
||||
<a
|
||||
className="markdown-link"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useApolloClient } from '@apollo/client/react';
|
||||
import { MockedProvider } from '@apollo/client/testing/react';
|
||||
import { type ReactNode, act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
@@ -17,10 +11,8 @@ import {
|
||||
results,
|
||||
token,
|
||||
} from '@/auth/hooks/__mocks__/useAuth';
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { SnackBarComponentInstanceContext } from '@/ui/feedback/snack-bar-manager/contexts/SnackBarComponentInstanceContext';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { SupportDriver } from '~/generated-metadata/graphql';
|
||||
|
||||
const redirectSpy = jest.fn();
|
||||
|
||||
@@ -147,55 +139,15 @@ describe('useAuth', () => {
|
||||
});
|
||||
|
||||
it('should handle sign-out', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const client = useApolloClient();
|
||||
const workspaceAuthProviders = useAtomStateValue(
|
||||
workspaceAuthProvidersState,
|
||||
);
|
||||
const billing = useAtomStateValue(billingState);
|
||||
const isDeveloperDefaultSignInPrefilled = useAtomStateValue(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
);
|
||||
const supportChat = useAtomStateValue(supportChatState);
|
||||
const isMultiWorkspaceEnabled = useAtomStateValue(
|
||||
isMultiWorkspaceEnabledState,
|
||||
);
|
||||
return {
|
||||
...useAuth(),
|
||||
client,
|
||||
state: {
|
||||
workspaceAuthProviders,
|
||||
billing,
|
||||
isDeveloperDefaultSignInPrefilled,
|
||||
supportChat,
|
||||
isMultiWorkspaceEnabled,
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
sessionStorage.setItem('lingering-key', 'should-be-cleared');
|
||||
|
||||
const { signOut, client } = result.current;
|
||||
const { result } = renderHooks();
|
||||
|
||||
await act(async () => {
|
||||
await signOut();
|
||||
result.current.signOut();
|
||||
});
|
||||
|
||||
expect(sessionStorage.length).toBe(0);
|
||||
expect(client.cache.extract()).toEqual({});
|
||||
|
||||
const { state } = result.current;
|
||||
|
||||
expect(state.workspaceAuthProviders).toEqual(null);
|
||||
expect(state.billing).toBeNull();
|
||||
expect(state.isDeveloperDefaultSignInPrefilled).toBe(false);
|
||||
expect(state.supportChat).toEqual({
|
||||
supportDriver: SupportDriver.NONE,
|
||||
supportFrontChatId: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle credential sign-up', async () => {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
useApolloClient,
|
||||
useLazyQuery,
|
||||
useMutation,
|
||||
} from '@apollo/client/react';
|
||||
import { useLazyQuery, useMutation } from '@apollo/client/react';
|
||||
import { useCallback } from 'react';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
@@ -28,16 +24,7 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
|
||||
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useSignUpInNewWorkspace } from '@/auth/sign-in-up/hooks/useSignUpInNewWorkspace';
|
||||
import { useLoadMockedMetadata } from '@/metadata-store/hooks/useLoadMockedMetadata';
|
||||
import { preloadMockedMetadata } from '@/metadata-store/utils/preloadMockedMetadata';
|
||||
import { lastAuthenticatedMethodState } from '@/auth/states/lastAuthenticatedMethodState';
|
||||
import { loginTokenState } from '@/auth/states/loginTokenState';
|
||||
import {
|
||||
SignInUpStep,
|
||||
@@ -49,18 +36,13 @@ import {
|
||||
countAvailableWorkspaces,
|
||||
getFirstAvailableWorkspaces,
|
||||
} from '@/auth/utils/availableWorkspacesUtils';
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||
import { useOrigin } from '@/domain-manager/hooks/useOrigin';
|
||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useClearSseClient } from '@/sse-db-event/hooks/useClearSseClient';
|
||||
import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
@@ -78,8 +60,6 @@ export const useAuth = () => {
|
||||
);
|
||||
|
||||
const { origin } = useOrigin();
|
||||
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
||||
const isCaptchaScriptLoaded = useAtomStateValue(isCaptchaScriptLoadedState);
|
||||
const isMultiWorkspaceEnabled = useAtomStateValue(
|
||||
isMultiWorkspaceEnabledState,
|
||||
);
|
||||
@@ -87,9 +67,7 @@ export const useAuth = () => {
|
||||
isEmailVerificationRequiredState,
|
||||
);
|
||||
const { loadCurrentUser } = useLoadCurrentUser();
|
||||
const { clearSseClient } = useClearSseClient();
|
||||
|
||||
const { applyMockedMetadata } = useLoadMockedMetadata();
|
||||
const { createWorkspace } = useSignUpInNewWorkspace();
|
||||
|
||||
const setSignInUpStep = useSetAtomState(signInUpStepState);
|
||||
@@ -121,64 +99,17 @@ export const useAuth = () => {
|
||||
CheckUserExistsDocument,
|
||||
);
|
||||
|
||||
const client = useApolloClient();
|
||||
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const clearSession = useCallback(async () => {
|
||||
clearSseClient();
|
||||
store.set(isAppEffectRedirectEnabledState.atom, false);
|
||||
|
||||
const mockedData = await preloadMockedMetadata();
|
||||
|
||||
const authProvidersValue = store.get(workspaceAuthProvidersState.atom);
|
||||
const domainConfigurationValue = store.get(domainConfigurationState.atom);
|
||||
const workspacePublicDataValue = store.get(workspacePublicDataState.atom);
|
||||
const lastAuthenticatedMethod = store.get(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
);
|
||||
const isCaptchaScriptLoadedValue = store.get(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
);
|
||||
|
||||
const clearSession = useCallback(() => {
|
||||
sessionStorage.clear();
|
||||
clearSessionLocalStorageKeys();
|
||||
|
||||
store.set(workspaceAuthProvidersState.atom, authProvidersValue);
|
||||
store.set(workspacePublicDataState.atom, workspacePublicDataValue);
|
||||
store.set(domainConfigurationState.atom, domainConfigurationValue);
|
||||
store.set(isCaptchaScriptLoadedState.atom, isCaptchaScriptLoadedValue);
|
||||
store.set(lastAuthenticatedMethodState.atom, lastAuthenticatedMethod);
|
||||
|
||||
store.set(tokenPairState.atom, null);
|
||||
store.set(currentUserState.atom, null);
|
||||
store.set(currentWorkspaceState.atom, null);
|
||||
store.set(currentUserWorkspaceState.atom, null);
|
||||
store.set(currentWorkspaceMemberState.atom, null);
|
||||
store.set(currentWorkspaceMembersState.atom, []);
|
||||
store.set(availableWorkspacesState.atom, {
|
||||
availableWorkspacesForSignIn: [],
|
||||
availableWorkspacesForSignUp: [],
|
||||
});
|
||||
store.set(loginTokenState.atom, null);
|
||||
store.set(signInUpStepState.atom, SignInUpStep.Init);
|
||||
|
||||
applyMockedMetadata(mockedData);
|
||||
|
||||
await client.clearStore();
|
||||
setLastAuthenticateWorkspaceDomain(null);
|
||||
navigate(AppPath.SignInUp);
|
||||
store.set(isAppEffectRedirectEnabledState.atom, true);
|
||||
}, [
|
||||
clearSseClient,
|
||||
client,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
applyMockedMetadata,
|
||||
navigate,
|
||||
store,
|
||||
]);
|
||||
window.location.assign(AppPath.SignInUp);
|
||||
}, [store, setLastAuthenticateWorkspaceDomain]);
|
||||
|
||||
const handleSetAuthTokens = useCallback(
|
||||
(tokens: AuthTokenPair) => {
|
||||
@@ -475,11 +406,10 @@ export const useAuth = () => {
|
||||
[handleGetLoginTokenFromCredentials, handleGetAuthTokensFromLoginToken],
|
||||
);
|
||||
|
||||
const handleSignOut = useCallback(async () => {
|
||||
const handleSignOut = useCallback(() => {
|
||||
broadcastSignOutToOtherTabs();
|
||||
await clearSession();
|
||||
if (isCaptchaScriptLoaded) await requestFreshCaptchaToken();
|
||||
}, [clearSession, isCaptchaScriptLoaded, requestFreshCaptchaToken]);
|
||||
clearSession();
|
||||
}, [clearSession]);
|
||||
|
||||
const handleCredentialsSignUpInWorkspace = useCallback(
|
||||
async ({
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { useApolloClient } from '@apollo/client/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { useClearSseClient } from '@/sse-db-event/hooks/useClearSseClient';
|
||||
import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser';
|
||||
import { type AuthTokenPair } from '~/generated-metadata/graphql';
|
||||
|
||||
const IMPERSONATION_SESSION_KEY = 'impersonation_original_session';
|
||||
@@ -17,22 +12,27 @@ type StoredImpersonationSession = {
|
||||
returnPath: string;
|
||||
};
|
||||
|
||||
// Token swaps without a full reload would require enumerating every
|
||||
// user-scoped atom, localStorage entry, and Apollo cache key — brittle and
|
||||
// silently broken every time a new piece of user state is added. Instead,
|
||||
// set the cookie-backed token pair and let the browser re-bootstrap the app.
|
||||
const reloadWithSession = (returnPath: string) => {
|
||||
window.location.assign(returnPath);
|
||||
};
|
||||
|
||||
export const useImpersonationSession = () => {
|
||||
const store = useStore();
|
||||
const client = useApolloClient();
|
||||
const navigate = useNavigate();
|
||||
const { getAuthTokensFromLoginToken, signOut } = useAuth();
|
||||
const { clearSseClient } = useClearSseClient();
|
||||
const { loadCurrentUser } = useLoadCurrentUser();
|
||||
|
||||
const startImpersonating = useCallback(
|
||||
async (loginToken: string, returnPath?: string) => {
|
||||
const currentTokenPair = store.get(tokenPairState.atom);
|
||||
const targetPath = returnPath ?? window.location.pathname;
|
||||
|
||||
if (currentTokenPair) {
|
||||
const session: StoredImpersonationSession = {
|
||||
tokenPair: currentTokenPair,
|
||||
returnPath: returnPath ?? window.location.pathname,
|
||||
returnPath: targetPath,
|
||||
};
|
||||
sessionStorage.setItem(
|
||||
IMPERSONATION_SESSION_KEY,
|
||||
@@ -40,30 +40,25 @@ export const useImpersonationSession = () => {
|
||||
);
|
||||
}
|
||||
|
||||
clearSseClient();
|
||||
await client.clearStore();
|
||||
|
||||
store.set(isAppEffectRedirectEnabledState.atom, false);
|
||||
await getAuthTokensFromLoginToken(loginToken);
|
||||
store.set(isAppEffectRedirectEnabledState.atom, true);
|
||||
reloadWithSession(targetPath);
|
||||
},
|
||||
[store, client, clearSseClient, getAuthTokensFromLoginToken],
|
||||
[store, getAuthTokensFromLoginToken],
|
||||
);
|
||||
|
||||
const stopImpersonating = useCallback(async () => {
|
||||
const raw = sessionStorage.getItem(IMPERSONATION_SESSION_KEY);
|
||||
|
||||
if (!raw) {
|
||||
// No stored session — likely a cross-workspace tab opened via redirect.
|
||||
// Try closing the tab (works when opened via window.open or target=_blank).
|
||||
// Cross-workspace tab opened via redirect — no stored admin session
|
||||
// to restore. Close the tab; fall back to sign out if the browser
|
||||
// blocks window.close().
|
||||
window.close();
|
||||
// If window.close() was blocked by the browser, fall back to sign out.
|
||||
await signOut();
|
||||
return;
|
||||
}
|
||||
|
||||
let session: StoredImpersonationSession;
|
||||
|
||||
try {
|
||||
session = JSON.parse(raw);
|
||||
} catch {
|
||||
@@ -73,19 +68,9 @@ export const useImpersonationSession = () => {
|
||||
}
|
||||
|
||||
sessionStorage.removeItem(IMPERSONATION_SESSION_KEY);
|
||||
|
||||
clearSseClient();
|
||||
await client.clearStore();
|
||||
|
||||
store.set(isAppEffectRedirectEnabledState.atom, false);
|
||||
store.set(tokenPairState.atom, session.tokenPair);
|
||||
|
||||
await loadCurrentUser();
|
||||
|
||||
store.set(isAppEffectRedirectEnabledState.atom, true);
|
||||
|
||||
navigate(session.returnPath);
|
||||
}, [store, client, clearSseClient, loadCurrentUser, signOut, navigate]);
|
||||
reloadWithSession(session.returnPath);
|
||||
}, [store, signOut]);
|
||||
|
||||
const hasStoredSession = useCallback(() => {
|
||||
return sessionStorage.getItem(IMPERSONATION_SESSION_KEY) !== null;
|
||||
|
||||
+9
-15
@@ -1,8 +1,7 @@
|
||||
import { PinnedCommandMenuItemButtons } from '@/command-menu-item/display/components/PinnedCommandMenuItemButtons';
|
||||
import { RecordIndexCommandMenuDropdown } from '@/command-menu-item/components/RecordIndexCommandMenuDropdown';
|
||||
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
|
||||
import { PinnedCommandMenuItemButtons } from '@/command-menu-item/display/components/PinnedCommandMenuItemButtons';
|
||||
import { CommandMenuItemEditButton } from '@/command-menu-item/edit/components/CommandMenuItemEditButton';
|
||||
import { PinnedCommandMenuItemButtonsEditMode } from '@/command-menu-item/edit/components/PinnedCommandMenuItemButtonsEditMode';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
@@ -21,23 +20,18 @@ export const RecordIndexCommandMenu = () => {
|
||||
isLayoutCustomizationModeEnabledState,
|
||||
);
|
||||
|
||||
const showEditModePinnedButtons = isLayoutCustomizationModeEnabled;
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextStoreCurrentObjectMetadataItemId && (
|
||||
<>
|
||||
{!isMobile && showEditModePinnedButtons ? (
|
||||
<PinnedCommandMenuItemButtonsEditMode />
|
||||
) : (
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="button"
|
||||
containerType="index-page-header"
|
||||
>
|
||||
{!isMobile && <PinnedCommandMenuItemButtons />}
|
||||
</CommandMenuContextProvider>
|
||||
)}
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="button"
|
||||
containerType="index-page-header"
|
||||
isInPreviewMode={isLayoutCustomizationModeEnabled}
|
||||
>
|
||||
{!isMobile && <PinnedCommandMenuItemButtons />}
|
||||
</CommandMenuContextProvider>
|
||||
<CommandMenuContextProvider
|
||||
isInSidePanel={false}
|
||||
displayType="dropdownItem"
|
||||
|
||||
+6
@@ -4,7 +4,9 @@ import { CommandMenuItemEditButton } from '@/command-menu-item/edit/components/C
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
export const RecordShowCommandMenu = () => {
|
||||
@@ -23,6 +25,9 @@ export const RecordShowCommandMenu = () => {
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const isLayoutCustomizationModeEnabled = useAtomStateValue(
|
||||
isLayoutCustomizationModeEnabledState,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -32,6 +37,7 @@ export const RecordShowCommandMenu = () => {
|
||||
isInSidePanel={false}
|
||||
displayType="button"
|
||||
containerType="show-page-header"
|
||||
isInPreviewMode={isLayoutCustomizationModeEnabled}
|
||||
>
|
||||
{!isMobile && <PinnedCommandMenuItemButtons />}
|
||||
</CommandMenuContextProvider>
|
||||
|
||||
+1
@@ -125,6 +125,7 @@ export const StandalonePageCommandMenu = () => {
|
||||
containerType: 'standalone-page-header',
|
||||
commandMenuItems: filteredCommandMenuItems,
|
||||
commandMenuContextApi,
|
||||
isInPreviewMode: false,
|
||||
}}
|
||||
>
|
||||
{!isMobile && <PinnedCommandMenuItemButtons />}
|
||||
|
||||
+1
@@ -45,6 +45,7 @@ const meta: Meta<typeof RecordIndexCommandMenuDropdown> = {
|
||||
containerType: 'index-page-dropdown',
|
||||
commandMenuItems: createMockCommandMenuItems(),
|
||||
commandMenuContextApi: EMPTY_COMMAND_MENU_CONTEXT_API,
|
||||
isInPreviewMode: false,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
|
||||
+2
-1
@@ -3,13 +3,13 @@ import { Provider as JotaiProvider } from 'jotai';
|
||||
import { userEvent, within } from 'storybook/test';
|
||||
|
||||
import { RecordPageSidePanelCommandMenuDropdown } from '@/command-menu-item/components/RecordPageSidePanelCommandMenuDropdown';
|
||||
import { EMPTY_COMMAND_MENU_CONTEXT_API } from '@/command-menu-item/constants/EmptyCommandMenuContextApi';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { EMPTY_COMMAND_MENU_CONTEXT_API } from '@/command-menu-item/constants/EmptyCommandMenuContextApi';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
@@ -44,6 +44,7 @@ const meta: Meta<typeof RecordPageSidePanelCommandMenuDropdown> = {
|
||||
...EMPTY_COMMAND_MENU_CONTEXT_API,
|
||||
isInSidePanel: true,
|
||||
},
|
||||
isInPreviewMode: false,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
|
||||
@@ -9,6 +9,7 @@ export type CommandMenuContextType = {
|
||||
containerType: CommandMenuItemContainerType;
|
||||
commandMenuItems: CommandMenuItemFieldsFragment[];
|
||||
commandMenuContextApi: CommandMenuContextApi;
|
||||
isInPreviewMode: boolean;
|
||||
};
|
||||
|
||||
export const CommandMenuContext = createContext<CommandMenuContextType>({
|
||||
@@ -16,4 +17,5 @@ export const CommandMenuContext = createContext<CommandMenuContextType>({
|
||||
displayType: 'button',
|
||||
commandMenuItems: [],
|
||||
commandMenuContextApi: EMPTY_COMMAND_MENU_CONTEXT_API,
|
||||
isInPreviewMode: false,
|
||||
});
|
||||
|
||||
+6
-2
@@ -2,7 +2,7 @@ import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type CommandMenuContextType } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
|
||||
import { useCurrentCommandMenuContextApi } from '@/command-menu-item/hooks/useCurrentCommandMenuContextApi';
|
||||
|
||||
import { CommandMenuContextProviderContent } from './CommandMenuContextProviderContent';
|
||||
import { CommandMenuContextProviderWithWorkflowEnrichment } from './CommandMenuContextProviderWithWorkflowEnrichment';
|
||||
@@ -12,6 +12,7 @@ type CommandMenuContextProviderProps = {
|
||||
displayType: CommandMenuContextType['displayType'];
|
||||
containerType: CommandMenuContextType['containerType'];
|
||||
children: React.ReactNode;
|
||||
isInPreviewMode?: boolean;
|
||||
};
|
||||
|
||||
export const CommandMenuContextProvider = ({
|
||||
@@ -19,8 +20,9 @@ export const CommandMenuContextProvider = ({
|
||||
displayType,
|
||||
containerType,
|
||||
children,
|
||||
isInPreviewMode = false,
|
||||
}: CommandMenuContextProviderProps) => {
|
||||
const commandMenuContextApiFromHook = useCommandMenuContextApi();
|
||||
const commandMenuContextApiFromHook = useCurrentCommandMenuContextApi();
|
||||
|
||||
const commandMenuContextApi = isInSidePanel
|
||||
? { ...commandMenuContextApiFromHook, isInSidePanel: true }
|
||||
@@ -45,6 +47,7 @@ export const CommandMenuContextProvider = ({
|
||||
containerType={containerType}
|
||||
commandMenuContextApi={commandMenuContextApi}
|
||||
selectedWorkflowRecordIds={selectedWorkflowRecordIds}
|
||||
isInPreviewMode={isInPreviewMode}
|
||||
>
|
||||
{children}
|
||||
</CommandMenuContextProviderWithWorkflowEnrichment>
|
||||
@@ -56,6 +59,7 @@ export const CommandMenuContextProvider = ({
|
||||
displayType={displayType}
|
||||
containerType={containerType}
|
||||
commandMenuContextApi={commandMenuContextApi}
|
||||
isInPreviewMode={isInPreviewMode}
|
||||
>
|
||||
{children}
|
||||
</CommandMenuContextProviderContent>
|
||||
|
||||
+20
-2
@@ -2,10 +2,12 @@ import {
|
||||
CommandMenuContext,
|
||||
type CommandMenuContextType,
|
||||
} from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState';
|
||||
import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { doesCommandMenuItemMatchPageLayoutId } from '@/command-menu-item/utils/doesCommandMenuItemMatchPageLayoutId';
|
||||
import { doesCommandMenuItemMatchPageType } from '@/command-menu-item/utils/doesCommandMenuItemMatchPageType';
|
||||
import { doesCommandMenuItemMatchSelectionState } from '@/command-menu-item/utils/doesCommandMenuItemMatchSelectionState';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useMemo } from 'react';
|
||||
@@ -17,6 +19,7 @@ type CommandMenuContextProviderContentProps = {
|
||||
containerType: CommandMenuContextType['containerType'];
|
||||
children: React.ReactNode;
|
||||
commandMenuContextApi: CommandMenuContextApi;
|
||||
isInPreviewMode: boolean;
|
||||
};
|
||||
|
||||
export const CommandMenuContextProviderContent = ({
|
||||
@@ -24,19 +27,27 @@ export const CommandMenuContextProviderContent = ({
|
||||
containerType,
|
||||
children,
|
||||
commandMenuContextApi,
|
||||
isInPreviewMode,
|
||||
}: CommandMenuContextProviderContentProps) => {
|
||||
const commandMenuItems = useAtomStateValue(commandMenuItemsSelector);
|
||||
const commandMenuItemsDraft = useAtomStateValue(commandMenuItemsDraftState);
|
||||
const currentPageLayoutId = useAtomStateValue(currentPageLayoutIdState);
|
||||
|
||||
const filteredCommandMenuItems = useMemo(() => {
|
||||
const currentObjectMetadataItemId =
|
||||
commandMenuContextApi.objectMetadataItem.id;
|
||||
const hasSelectedRecords =
|
||||
commandMenuContextApi.numberOfSelectedRecords > 0;
|
||||
const commandMenuItemsToDisplay = isInPreviewMode
|
||||
? (commandMenuItemsDraft ?? commandMenuItems)
|
||||
: commandMenuItems;
|
||||
|
||||
return commandMenuItems
|
||||
return commandMenuItemsToDisplay
|
||||
.filter(
|
||||
doesCommandMenuItemMatchObjectMetadataId(currentObjectMetadataItemId),
|
||||
)
|
||||
.filter(doesCommandMenuItemMatchPageType(commandMenuContextApi.pageType))
|
||||
.filter(doesCommandMenuItemMatchSelectionState(hasSelectedRecords))
|
||||
.filter(doesCommandMenuItemMatchPageLayoutId(currentPageLayoutId))
|
||||
.filter((item) =>
|
||||
evaluateConditionalAvailabilityExpression(
|
||||
@@ -47,7 +58,13 @@ export const CommandMenuContextProviderContent = ({
|
||||
.sort(
|
||||
(firstItem, secondItem) => firstItem.position - secondItem.position,
|
||||
);
|
||||
}, [commandMenuItems, commandMenuContextApi, currentPageLayoutId]);
|
||||
}, [
|
||||
commandMenuContextApi,
|
||||
commandMenuItems,
|
||||
commandMenuItemsDraft,
|
||||
currentPageLayoutId,
|
||||
isInPreviewMode,
|
||||
]);
|
||||
|
||||
return (
|
||||
<CommandMenuContext.Provider
|
||||
@@ -56,6 +73,7 @@ export const CommandMenuContextProviderContent = ({
|
||||
containerType,
|
||||
commandMenuItems: filteredCommandMenuItems,
|
||||
commandMenuContextApi,
|
||||
isInPreviewMode,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
+3
@@ -12,6 +12,7 @@ type CommandMenuContextProviderWithWorkflowEnrichmentProps = {
|
||||
children: React.ReactNode;
|
||||
commandMenuContextApi: CommandMenuContextApi;
|
||||
selectedWorkflowRecordIds: string[];
|
||||
isInPreviewMode: boolean;
|
||||
};
|
||||
|
||||
export const CommandMenuContextProviderWithWorkflowEnrichment = ({
|
||||
@@ -20,6 +21,7 @@ export const CommandMenuContextProviderWithWorkflowEnrichment = ({
|
||||
children,
|
||||
commandMenuContextApi,
|
||||
selectedWorkflowRecordIds,
|
||||
isInPreviewMode,
|
||||
}: CommandMenuContextProviderWithWorkflowEnrichmentProps) => {
|
||||
const workflowsWithCurrentVersions = useWorkflowsWithCurrentVersions(
|
||||
selectedWorkflowRecordIds,
|
||||
@@ -54,6 +56,7 @@ export const CommandMenuContextProviderWithWorkflowEnrichment = ({
|
||||
displayType={displayType}
|
||||
containerType={containerType}
|
||||
commandMenuContextApi={enrichedCommandMenuContextApi}
|
||||
isInPreviewMode={isInPreviewMode}
|
||||
>
|
||||
{children}
|
||||
</CommandMenuContextProviderContent>
|
||||
|
||||
+22
-7
@@ -11,6 +11,7 @@ import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-lis
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
@@ -18,6 +19,14 @@ import { Loader } from 'twenty-ui/feedback';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { type CommandMenuItemFieldsFragment } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledPreviewWrapper = styled.div`
|
||||
cursor: not-allowed;
|
||||
|
||||
& * {
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
|
||||
type CommandMenuItemRendererProps = {
|
||||
item: CommandMenuItemFieldsFragment;
|
||||
};
|
||||
@@ -27,7 +36,8 @@ type CommandMenuItemButtonRendererProps = CommandMenuItemRendererProps;
|
||||
const CommandMenuItemButtonRenderer = ({
|
||||
item,
|
||||
}: CommandMenuItemButtonRendererProps) => {
|
||||
const { commandMenuContextApi } = useContext(CommandMenuContext);
|
||||
const { commandMenuContextApi, isInPreviewMode } =
|
||||
useContext(CommandMenuContext);
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const { iconKey, label, shortLabel } = interpolateCommandMenuItemFields(
|
||||
@@ -43,14 +53,19 @@ const CommandMenuItemButtonRenderer = ({
|
||||
label,
|
||||
});
|
||||
|
||||
const command = { key: item.id, label, shortLabel, Icon };
|
||||
|
||||
if (isInPreviewMode) {
|
||||
return (
|
||||
<StyledPreviewWrapper>
|
||||
<CommandMenuButton command={command} />
|
||||
</StyledPreviewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CommandMenuButton
|
||||
command={{
|
||||
key: item.id,
|
||||
label,
|
||||
shortLabel,
|
||||
Icon,
|
||||
}}
|
||||
command={command}
|
||||
onClick={disabled ? undefined : handleClick}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
+30
-10
@@ -2,6 +2,7 @@ import { PINNED_COMMAND_MENU_ITEMS_GAP } from '@/command-menu-item/display/const
|
||||
import { commandMenuPinnedInlineLayoutState } from '@/command-menu-item/display/states/commandMenuPinnedInlineLayoutState';
|
||||
import { getVisibleCommandMenuItemCountForContainerWidth } from '@/command-menu-item/display/utils/getVisibleCommandMenuItemCountForContainerWidth';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { isNumber } from '@sniptt/guards';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { type CommandMenuItemFieldsFragment } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -25,18 +26,37 @@ export const usePinnedCommandMenuItemsInlineLayout = ({
|
||||
[pinnedCommandMenuItems],
|
||||
);
|
||||
|
||||
const hasKnownPinnedInlineLayout = useMemo(
|
||||
() =>
|
||||
commandMenuPinnedInlineLayout.containerWidth > 0 &&
|
||||
pinnedCommandMenuItemKeysInDisplayOrder.every((commandMenuItemKey) =>
|
||||
isNumber(
|
||||
commandMenuPinnedInlineLayout.commandMenuItemWidthsByKey[
|
||||
commandMenuItemKey
|
||||
],
|
||||
),
|
||||
),
|
||||
[commandMenuPinnedInlineLayout, pinnedCommandMenuItemKeysInDisplayOrder],
|
||||
);
|
||||
|
||||
const visiblePinnedCommandMenuItemCount = useMemo(
|
||||
() =>
|
||||
getVisibleCommandMenuItemCountForContainerWidth({
|
||||
commandMenuItemKeysInDisplayOrder:
|
||||
pinnedCommandMenuItemKeysInDisplayOrder,
|
||||
commandMenuItemWidthsByKey:
|
||||
commandMenuPinnedInlineLayout.commandMenuItemWidthsByKey,
|
||||
commandMenuItemsContainerWidth:
|
||||
commandMenuPinnedInlineLayout.containerWidth,
|
||||
commandMenuItemsGapWidth: PINNED_COMMAND_MENU_ITEMS_GAP,
|
||||
}),
|
||||
[commandMenuPinnedInlineLayout, pinnedCommandMenuItemKeysInDisplayOrder],
|
||||
hasKnownPinnedInlineLayout
|
||||
? getVisibleCommandMenuItemCountForContainerWidth({
|
||||
commandMenuItemKeysInDisplayOrder:
|
||||
pinnedCommandMenuItemKeysInDisplayOrder,
|
||||
commandMenuItemWidthsByKey:
|
||||
commandMenuPinnedInlineLayout.commandMenuItemWidthsByKey,
|
||||
commandMenuItemsContainerWidth:
|
||||
commandMenuPinnedInlineLayout.containerWidth,
|
||||
commandMenuItemsGapWidth: PINNED_COMMAND_MENU_ITEMS_GAP,
|
||||
})
|
||||
: 0,
|
||||
[
|
||||
commandMenuPinnedInlineLayout,
|
||||
hasKnownPinnedInlineLayout,
|
||||
pinnedCommandMenuItemKeysInDisplayOrder,
|
||||
],
|
||||
);
|
||||
|
||||
const pinnedInlineCommandMenuItems = useMemo(
|
||||
|
||||
+3
-2
@@ -83,7 +83,7 @@ export const CommandMenuItemEditRecordSelectionDropdown = ({
|
||||
const TriggerIcon = isNoneSelected ? IconSquareX : IconSquareCheck;
|
||||
const triggerLabel = isNoneSelected
|
||||
? t`No record selected`
|
||||
: t`Records selected`;
|
||||
: t`Record(s) selected`;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@@ -108,6 +108,7 @@ export const CommandMenuItemEditRecordSelectionDropdown = ({
|
||||
</StyledClickableArea>
|
||||
}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownOffset={{ y: 4 }}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Medium}>
|
||||
<StyledDropdownMenuContainer
|
||||
@@ -122,7 +123,7 @@ export const CommandMenuItemEditRecordSelectionDropdown = ({
|
||||
/>
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconSquareCheck}
|
||||
text={t`Records selected`}
|
||||
text={t`Record(s) selected`}
|
||||
selected={!isNoneSelected}
|
||||
onClick={() => handleSelectMode('selection')}
|
||||
/>
|
||||
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
import { PinnedCommandMenuItemsInlineMeasurements } from '@/command-menu-item/display/components/PinnedCommandMenuItemsInlineMeasurements';
|
||||
import { PINNED_COMMAND_MENU_ITEMS_GAP } from '@/command-menu-item/display/constants/PinnedCommandMenuItemsGap';
|
||||
import { usePinnedCommandMenuItemsInlineLayout } from '@/command-menu-item/display/hooks/usePinnedCommandMenuItemsInlineLayout';
|
||||
import { interpolateCommandMenuItemFields } from '@/command-menu-item/display/utils/interpolateCommandMenuItemFields';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { CommandMenuButton } from '@/command-menu/components/CommandMenuButton';
|
||||
import { mainContextStoreHasSelectedRecordsSelector } from '@/context-store/states/selectors/mainContextStoreHasSelectedRecordsSelector';
|
||||
import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
import { styled } from '@linaria/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import { CommandMenuItemAvailabilityType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledCommandMenuItemContainer = styled(motion.div)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledItemsContainer = styled.div`
|
||||
display: flex;
|
||||
gap: ${PINNED_COMMAND_MENU_ITEMS_GAP}px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const PinnedCommandMenuItemButtonsEditMode = () => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const { getIcon } = useIcons();
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
|
||||
const currentObjectMetadataItemId =
|
||||
commandMenuContextApi.objectMetadataItem.id;
|
||||
|
||||
const commandMenuItemsDraft =
|
||||
useAtomStateValue(commandMenuItemsDraftState) ?? [];
|
||||
|
||||
const mainContextStoreHasSelectedRecords = useAtomStateValue(
|
||||
mainContextStoreHasSelectedRecordsSelector,
|
||||
);
|
||||
|
||||
const allowedAvailabilityTypes = useMemo(
|
||||
() =>
|
||||
new Set<CommandMenuItemAvailabilityType>([
|
||||
CommandMenuItemAvailabilityType.GLOBAL,
|
||||
CommandMenuItemAvailabilityType.GLOBAL_OBJECT_CONTEXT,
|
||||
mainContextStoreHasSelectedRecords
|
||||
? CommandMenuItemAvailabilityType.RECORD_SELECTION
|
||||
: CommandMenuItemAvailabilityType.FALLBACK,
|
||||
]),
|
||||
[mainContextStoreHasSelectedRecords],
|
||||
);
|
||||
|
||||
const pinnedCommandMenuItems = commandMenuItemsDraft
|
||||
.filter(
|
||||
doesCommandMenuItemMatchObjectMetadataId(currentObjectMetadataItemId),
|
||||
)
|
||||
.filter((item) => allowedAvailabilityTypes.has(item.availabilityType))
|
||||
.filter((item) => item.isPinned);
|
||||
|
||||
const {
|
||||
pinnedInlineCommandMenuItems,
|
||||
pinnedOverflowCommandMenuItems,
|
||||
onContainerDimensionChange,
|
||||
onCommandMenuItemDimensionChange,
|
||||
} = usePinnedCommandMenuItemsInlineLayout({
|
||||
pinnedCommandMenuItems,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<PinnedCommandMenuItemsInlineMeasurements
|
||||
pinnedCommandMenuItems={[
|
||||
...pinnedInlineCommandMenuItems,
|
||||
...pinnedOverflowCommandMenuItems,
|
||||
]}
|
||||
onPinnedCommandMenuItemDimensionChange={
|
||||
onCommandMenuItemDimensionChange
|
||||
}
|
||||
/>
|
||||
<StyledWrapper>
|
||||
<NodeDimension onDimensionChange={onContainerDimensionChange}>
|
||||
<StyledContainer>
|
||||
<StyledItemsContainer>
|
||||
{pinnedInlineCommandMenuItems.map((item) => {
|
||||
const { iconKey, label, shortLabel } =
|
||||
interpolateCommandMenuItemFields(item, commandMenuContextApi);
|
||||
|
||||
const Icon = getIcon(iconKey, COMMAND_MENU_DEFAULT_ICON);
|
||||
|
||||
return (
|
||||
<StyledCommandMenuItemContainer
|
||||
key={item.id}
|
||||
layout
|
||||
initial={{ width: 0, opacity: 0 }}
|
||||
animate={{ width: 'unset', opacity: 1 }}
|
||||
exit={{ width: 0, opacity: 0 }}
|
||||
transition={{
|
||||
duration: theme.animation.duration.instant,
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
>
|
||||
<CommandMenuButton
|
||||
command={{
|
||||
key: item.id,
|
||||
label,
|
||||
shortLabel,
|
||||
Icon,
|
||||
}}
|
||||
disabled
|
||||
/>
|
||||
</StyledCommandMenuItemContainer>
|
||||
);
|
||||
})}
|
||||
</StyledItemsContainer>
|
||||
</StyledContainer>
|
||||
</NodeDimension>
|
||||
</StyledWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+7
-36
@@ -1,15 +1,13 @@
|
||||
import { CommandMenuItemEditRecordSelectionDropdown } from '@/command-menu-item/edit/components/CommandMenuItemEditRecordSelectionDropdown';
|
||||
import { CommandMenuItemOptionsDropdown } from '@/command-menu-item/edit/components/CommandMenuItemOptionsDropdown';
|
||||
import { useEditableCommandMenuItems } from '@/command-menu-item/edit/hooks/useEditableCommandMenuItems';
|
||||
import { useReorderCommandMenuItemsInDraft } from '@/command-menu-item/edit/hooks/useReorderCommandMenuItemsInDraft';
|
||||
import { useResetCommandMenuItemsDraft } from '@/command-menu-item/edit/hooks/useResetCommandMenuItemsDraft';
|
||||
import { useUpdateCommandMenuItemInDraft } from '@/command-menu-item/edit/hooks/useUpdateCommandMenuItemInDraft';
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState';
|
||||
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
|
||||
import { useCurrentCommandMenuContextApi } from '@/command-menu-item/hooks/useCurrentCommandMenuContextApi';
|
||||
import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { groupCommandMenuItems } from '@/command-menu-item/utils/groupCommandMenuItems';
|
||||
import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId';
|
||||
import { mainContextStoreHasSelectedRecordsSelector } from '@/context-store/states/selectors/mainContextStoreHasSelectedRecordsSelector';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
||||
@@ -36,10 +34,7 @@ import {
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { MenuItem, MenuItemDraggable } from 'twenty-ui/navigation';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { type CommandMenuItemFieldsFragment } from '~/generated-metadata/graphql';
|
||||
import { normalizeSearchText } from '~/utils/normalizeSearchText';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@@ -66,7 +61,7 @@ const StyledContent = styled.div`
|
||||
export const SidePanelCommandMenuItemEditPage = () => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const commandMenuContextApi = useCommandMenuContextApi();
|
||||
const commandMenuContextApi = useCurrentCommandMenuContextApi();
|
||||
|
||||
const currentObjectMetadataItemId =
|
||||
commandMenuContextApi.objectMetadataItem.id;
|
||||
@@ -76,44 +71,20 @@ export const SidePanelCommandMenuItemEditPage = () => {
|
||||
const isRecordPage =
|
||||
commandMenuContextApi.pageType === ContextStorePageType.Record;
|
||||
|
||||
const isIndexPage =
|
||||
commandMenuContextApi.pageType === ContextStorePageType.Index;
|
||||
|
||||
const mainContextStoreHasSelectedRecords = useAtomStateValue(
|
||||
mainContextStoreHasSelectedRecordsSelector,
|
||||
);
|
||||
|
||||
const sidePanelSearch = useAtomStateValue(sidePanelSearchState);
|
||||
|
||||
const commandMenuItems = useAtomStateValue(commandMenuItemsSelector);
|
||||
const serverItemsById = new Map(
|
||||
commandMenuItems.map((item) => [item.id, item]),
|
||||
);
|
||||
const commandMenuItemsDraft =
|
||||
useAtomStateValue(commandMenuItemsDraftState) ?? [];
|
||||
const { updateCommandMenuItemInDraft } = useUpdateCommandMenuItemInDraft();
|
||||
const { reorderCommandMenuItemInDraft } = useReorderCommandMenuItemsInDraft();
|
||||
const { resetCommandMenuItemsDraft } = useResetCommandMenuItemsDraft();
|
||||
|
||||
const allowedAvailabilityTypes = new Set<CommandMenuItemAvailabilityType>([
|
||||
CommandMenuItemAvailabilityType.GLOBAL,
|
||||
...(isIndexPage || isRecordPage
|
||||
? [CommandMenuItemAvailabilityType.GLOBAL_OBJECT_CONTEXT]
|
||||
: []),
|
||||
...(mainContextStoreHasSelectedRecords
|
||||
? [CommandMenuItemAvailabilityType.RECORD_SELECTION]
|
||||
: []),
|
||||
]);
|
||||
|
||||
const filteredCommandMenuItems = commandMenuItemsDraft
|
||||
.filter(
|
||||
doesCommandMenuItemMatchObjectMetadataId(currentObjectMetadataItemId),
|
||||
)
|
||||
.filter((item) => allowedAvailabilityTypes.has(item.availabilityType))
|
||||
.sort((firstItem, secondItem) => firstItem.position - secondItem.position);
|
||||
const editableCommandMenuItems = useEditableCommandMenuItems();
|
||||
|
||||
const filteredCommandMenuItemIds = new Set(
|
||||
filteredCommandMenuItems.map((item) => item.id),
|
||||
editableCommandMenuItems.map((item) => item.id),
|
||||
);
|
||||
|
||||
const getDisplayLabel = (item: CommandMenuItemFieldsFragment) =>
|
||||
@@ -123,7 +94,7 @@ export const SidePanelCommandMenuItemEditPage = () => {
|
||||
}) ?? item.label;
|
||||
|
||||
const { pinned: allPinnedItems, other: allOtherItems } =
|
||||
groupCommandMenuItems(filteredCommandMenuItems);
|
||||
groupCommandMenuItems(editableCommandMenuItems);
|
||||
|
||||
const normalizedSearch =
|
||||
sidePanelSearch.length > 0
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState';
|
||||
import { useCurrentCommandMenuContextApi } from '@/command-menu-item/hooks/useCurrentCommandMenuContextApi';
|
||||
import { doesCommandMenuItemMatchObjectMetadataId } from '@/command-menu-item/utils/doesCommandMenuItemMatchObjectMetadataId';
|
||||
import { doesCommandMenuItemMatchPageLayoutId } from '@/command-menu-item/utils/doesCommandMenuItemMatchPageLayoutId';
|
||||
import { doesCommandMenuItemMatchPageType } from '@/command-menu-item/utils/doesCommandMenuItemMatchPageType';
|
||||
import { doesCommandMenuItemMatchSelectionState } from '@/command-menu-item/utils/doesCommandMenuItemMatchSelectionState';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useMemo } from 'react';
|
||||
import { CommandMenuItemAvailabilityType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useEditableCommandMenuItems = () => {
|
||||
const commandMenuContextApi = useCurrentCommandMenuContextApi();
|
||||
const commandMenuItemsDraft = useAtomStateValue(commandMenuItemsDraftState);
|
||||
const currentPageLayoutId = useAtomStateValue(currentPageLayoutIdState);
|
||||
|
||||
return useMemo(() => {
|
||||
const currentObjectMetadataItemId =
|
||||
commandMenuContextApi.objectMetadataItem.id;
|
||||
const hasSelectedRecords =
|
||||
commandMenuContextApi.numberOfSelectedRecords > 0;
|
||||
|
||||
return (commandMenuItemsDraft ?? [])
|
||||
.filter(
|
||||
doesCommandMenuItemMatchObjectMetadataId(currentObjectMetadataItemId),
|
||||
)
|
||||
.filter(doesCommandMenuItemMatchPageType(commandMenuContextApi.pageType))
|
||||
.filter(doesCommandMenuItemMatchSelectionState(hasSelectedRecords))
|
||||
.filter(
|
||||
(item) =>
|
||||
item.availabilityType !== CommandMenuItemAvailabilityType.FALLBACK,
|
||||
)
|
||||
.filter(doesCommandMenuItemMatchPageLayoutId(currentPageLayoutId))
|
||||
.sort(
|
||||
(firstItem, secondItem) => firstItem.position - secondItem.position,
|
||||
);
|
||||
}, [commandMenuItemsDraft, commandMenuContextApi, currentPageLayoutId]);
|
||||
};
|
||||
+1
@@ -70,6 +70,7 @@ const getWrapper =
|
||||
objectMetadataItem: {},
|
||||
objectMetadataLabel: '',
|
||||
},
|
||||
isInPreviewMode: false,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined, resolveObjectMetadataLabel } from 'twenty-shared/utils';
|
||||
|
||||
export const useCommandMenuContextApi = (): CommandMenuContextApi => {
|
||||
export const useCurrentCommandMenuContextApi = (): CommandMenuContextApi => {
|
||||
const store = useStore();
|
||||
|
||||
const contextStoreInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { doesCommandMenuItemMatchSelectionState } from '@/command-menu-item/utils/doesCommandMenuItemMatchSelectionState';
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const buildCommandMenuItem = (
|
||||
availabilityType: CommandMenuItemAvailabilityType,
|
||||
) =>
|
||||
({
|
||||
availabilityType,
|
||||
}) as CommandMenuItemFieldsFragment;
|
||||
|
||||
describe('doesCommandMenuItemMatchSelectionState', () => {
|
||||
it('should keep a non-record-selection item when no records are selected', () => {
|
||||
const item = buildCommandMenuItem(CommandMenuItemAvailabilityType.GLOBAL);
|
||||
|
||||
expect(doesCommandMenuItemMatchSelectionState(false)(item)).toBe(true);
|
||||
});
|
||||
|
||||
it('should hide a record-selection item when no records are selected', () => {
|
||||
const item = buildCommandMenuItem(
|
||||
CommandMenuItemAvailabilityType.RECORD_SELECTION,
|
||||
);
|
||||
|
||||
expect(doesCommandMenuItemMatchSelectionState(false)(item)).toBe(false);
|
||||
});
|
||||
|
||||
it('should keep a record-selection item when records are selected', () => {
|
||||
const item = buildCommandMenuItem(
|
||||
CommandMenuItemAvailabilityType.RECORD_SELECTION,
|
||||
);
|
||||
|
||||
expect(doesCommandMenuItemMatchSelectionState(true)(item)).toBe(true);
|
||||
});
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const doesCommandMenuItemMatchSelectionState =
|
||||
(hasSelectedRecords: boolean) => (item: CommandMenuItemFieldsFragment) =>
|
||||
item.availabilityType !==
|
||||
CommandMenuItemAvailabilityType.RECORD_SELECTION || hasSelectedRecords;
|
||||
+3
-2
@@ -1,11 +1,10 @@
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { EMPTY_COMMAND_MENU_CONTEXT_API } from '@/command-menu-item/constants/EmptyCommandMenuContextApi';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
|
||||
import { UpdateMultipleRecordsContainer } from '@/object-record/record-update-multiple/components/UpdateMultipleRecordsContainer';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useEffect } from 'react';
|
||||
import { ApolloClient, InMemoryCache } from '@apollo/client';
|
||||
import { MockLink } from '@apollo/client/testing';
|
||||
import {
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
type StoryObj,
|
||||
} from '@storybook/react-vite';
|
||||
import gql from 'graphql-tag';
|
||||
import { useEffect } from 'react';
|
||||
import { expect, userEvent, within } from 'storybook/test';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
@@ -80,6 +80,7 @@ const meta: Meta<typeof UpdateMultipleRecordsContainer> = {
|
||||
containerType: 'index-page-dropdown',
|
||||
displayType: 'dropdownItem',
|
||||
commandMenuContextApi: EMPTY_COMMAND_MENU_CONTEXT_API,
|
||||
isInPreviewMode: false,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import Linkify from 'linkify-react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledTextBody = styled(motion.div)`
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: ${themeCssVariables.spacing[4]};
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-line;
|
||||
|
||||
a {
|
||||
color: ${themeCssVariables.color.blue};
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration-color: ${themeCssVariables.color.blue};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type LinkifiedTextBodyProps = {
|
||||
body: string;
|
||||
isDisplayed: boolean;
|
||||
};
|
||||
|
||||
export const LinkifiedTextBody = ({
|
||||
body,
|
||||
isDisplayed,
|
||||
}: LinkifiedTextBodyProps) => {
|
||||
return (
|
||||
<AnimatedEaseInOut isOpen={isDisplayed} duration="fast">
|
||||
<StyledTextBody>
|
||||
<Linkify
|
||||
options={{
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer',
|
||||
}}
|
||||
>
|
||||
{body}
|
||||
</Linkify>
|
||||
</StyledTextBody>
|
||||
</AnimatedEaseInOut>
|
||||
);
|
||||
};
|
||||
@@ -11,7 +11,7 @@ const jestConfig = {
|
||||
testEnvironment: 'node',
|
||||
setupFilesAfterEnv: ['./setupTests.ts'],
|
||||
transformIgnorePatterns: [
|
||||
'/node_modules/(?!(file-type|@file-type|strtok3|token-types|@borewit|@tokenizer|uint8array-extras|read-next-line)/)',
|
||||
'/node_modules/(?!(file-type|@file-type|strtok3|token-types|@borewit|@tokenizer|uint8array-extras|read-next-line|digest-fetch|md5|js-sha256|js-sha512|base-64|charenc|crypt)/)',
|
||||
],
|
||||
testRegex: '.*\\.spec\\.ts$',
|
||||
transform: {
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
"typeorm": "../../node_modules/typeorm/.bin/typeorm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^3.0.83",
|
||||
"@ai-sdk/anthropic": "^3.0.46",
|
||||
"@ai-sdk/google": "^3.0.30",
|
||||
"@ai-sdk/mistral": "^3.0.20",
|
||||
"@ai-sdk/openai": "^3.0.30",
|
||||
"@ai-sdk/openai-compatible": "^2.0.40",
|
||||
"@ai-sdk/provider-utils": "^4.0.15",
|
||||
"@ai-sdk/xai": "^3.0.57",
|
||||
"@ai-sdk/amazon-bedrock": "^4.0.97",
|
||||
"@ai-sdk/anthropic": "^3.0.72",
|
||||
"@ai-sdk/google": "^3.0.65",
|
||||
"@ai-sdk/mistral": "^3.0.31",
|
||||
"@ai-sdk/openai": "^3.0.54",
|
||||
"@ai-sdk/openai-compatible": "^2.0.42",
|
||||
"@ai-sdk/provider-utils": "^4.0.24",
|
||||
"@ai-sdk/xai": "^3.0.84",
|
||||
"@aws-sdk/client-lambda": "3.1001.0",
|
||||
"@aws-sdk/client-s3": "3.1001.0",
|
||||
"@aws-sdk/client-sesv2": "3.1001.0",
|
||||
@@ -44,6 +44,7 @@
|
||||
"@graphql-tools/utils": "9.2.1",
|
||||
"@graphql-yoga/nestjs": "2.1.0",
|
||||
"@jrmdayn/googleapis-batcher": "^0.10.1",
|
||||
"@lifeomic/axios-fetch": "^3.1.0",
|
||||
"@lingui/conf": "5.1.2",
|
||||
"@lingui/core": "^5.1.2",
|
||||
"@lingui/format-po": "5.1.2",
|
||||
@@ -103,6 +104,7 @@
|
||||
"dataloader": "2.2.2",
|
||||
"date-fns": "2.30.0",
|
||||
"deep-equal": "2.2.3",
|
||||
"digest-fetch": "^3.1.1",
|
||||
"dompurify": "3.3.3",
|
||||
"dotenv": "16.4.5",
|
||||
"exa-js": "^2.11.0",
|
||||
@@ -178,7 +180,7 @@
|
||||
"temporal-polyfill": "^0.3.0",
|
||||
"transliteration": "2.3.5",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsdav": "^2.1.5",
|
||||
"tsdav": "^2.2.0",
|
||||
"tslib": "2.8.1",
|
||||
"type-fest": "4.10.1",
|
||||
"typeorm": "patch:typeorm@0.3.20#./patches/typeorm+0.3.20.patch",
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS usageEvent_v2 (
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
usageEvent_v2 (
|
||||
`timestamp` DateTime64 (3) NOT NULL,
|
||||
`workspaceId` String NOT NULL,
|
||||
`periodStart` DateTime64 (3),
|
||||
@@ -37,8 +37,9 @@ SELECT
|
||||
FROM
|
||||
usageEvent;
|
||||
|
||||
RENAME TABLE usageEvent TO usageEvent_old,
|
||||
usageEvent_v2 TO usageEvent;
|
||||
RENAME TABLE usageEvent TO usageEvent_old;
|
||||
|
||||
RENAME TABLE usageEvent_v2 TO usageEvent;
|
||||
|
||||
DROP TABLE IF EXISTS usageEvent_old;
|
||||
|
||||
@@ -50,4 +51,3 @@ ALTER TABLE usageEvent ADD PROJECTION IF NOT EXISTS billing_by_workspace_period
|
||||
GROUP BY
|
||||
workspaceId, periodStart
|
||||
);
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.2.0', 1777455269302)
|
||||
export class AddCacheTokensToAgentChatThreadFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatThread" ADD "totalCacheReadTokens" bigint NOT NULL DEFAULT 0`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatThread" ADD "totalCacheCreationTokens" bigint NOT NULL DEFAULT 0`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatThread" DROP COLUMN "totalCacheCreationTokens"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatThread" DROP COLUMN "totalCacheReadTokens"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+13
-2
@@ -1,7 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
|
||||
import { SetCalendarEventDescriptionDisplayedMaxRowsCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-workspace-command-1786000000000-set-calendar-event-description-displayed-max-rows.command';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [],
|
||||
imports: [
|
||||
ApplicationModule,
|
||||
WorkspaceCacheModule,
|
||||
WorkspaceIteratorModule,
|
||||
WorkspaceMigrationModule,
|
||||
],
|
||||
providers: [SetCalendarEventDescriptionDisplayedMaxRowsCommand],
|
||||
})
|
||||
export class V2_2_UpgradeVersionCommandModule {}
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import { Command } from 'nest-commander';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
type FieldMetadataSettings,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner';
|
||||
import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service';
|
||||
import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
const DISPLAYED_MAX_ROWS = 99;
|
||||
|
||||
@RegisteredWorkspaceCommand('2.2.0', 1786000000000)
|
||||
@Command({
|
||||
name: 'upgrade:2-2:set-calendar-event-description-displayed-max-rows',
|
||||
description:
|
||||
'Set displayedMaxRows setting on calendarEvent.description field',
|
||||
})
|
||||
export class SetCalendarEventDescriptionDisplayedMaxRowsCommand extends ActiveOrSuspendedWorkspaceCommandRunner {
|
||||
constructor(
|
||||
protected readonly workspaceIteratorService: WorkspaceIteratorService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {
|
||||
super(workspaceIteratorService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const isDryRun = options.dryRun ?? false;
|
||||
|
||||
const { flatFieldMetadataMaps } =
|
||||
await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatFieldMetadataMaps',
|
||||
]);
|
||||
|
||||
const descriptionField =
|
||||
findFlatEntityByUniversalIdentifier<FlatFieldMetadata>({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
universalIdentifier:
|
||||
STANDARD_OBJECTS.calendarEvent.fields.description.universalIdentifier,
|
||||
});
|
||||
|
||||
if (!descriptionField) {
|
||||
this.logger.log(
|
||||
`calendarEvent.description field not found for workspace ${workspaceId}, skipping`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const textSettings =
|
||||
descriptionField.settings as FieldMetadataSettings<FieldMetadataType.TEXT>;
|
||||
|
||||
if (textSettings?.displayedMaxRows === DISPLAYED_MAX_ROWS) {
|
||||
this.logger.log(
|
||||
`calendarEvent.description displayedMaxRows already set for workspace ${workspaceId}, skipping`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
this.logger.log(
|
||||
`[DRY RUN] Would set displayedMaxRows on calendarEvent.description for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { twentyStandardFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const fieldToUpdate = {
|
||||
...descriptionField,
|
||||
settings: {
|
||||
...textSettings,
|
||||
displayedMaxRows: DISPLAYED_MAX_ROWS,
|
||||
},
|
||||
};
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
fieldMetadata: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [fieldToUpdate],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
twentyStandardFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new Error(
|
||||
`Failed to set displayedMaxRows on calendarEvent.description for workspace ${workspaceId}: ${JSON.stringify(validateAndBuildResult, null, 2)}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Set displayedMaxRows on calendarEvent.description for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
@@ -18,6 +18,7 @@ import { AddConditionalAvailabilityExpressionToPageLayoutWidgetFastInstanceComma
|
||||
import { AddIsPreInstalledToApplicationRegistrationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-fast-1776886452831-add-is-pre-installed-to-application-registration';
|
||||
import { AddProviderExecutedToAgentMessagePartFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-fast-1777012800000-add-provider-executed-to-agent-message-part';
|
||||
import { BackfillPageLayoutWidgetPositionSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-slow-1795000002000-backfill-page-layout-widget-position';
|
||||
import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -38,4 +39,5 @@ export const INSTANCE_COMMANDS = [
|
||||
AddIsPreInstalledToApplicationRegistrationFastInstanceCommand,
|
||||
AddProviderExecutedToAgentMessagePartFastInstanceCommand,
|
||||
BackfillPageLayoutWidgetPositionSlowInstanceCommand,
|
||||
AddCacheTokensToAgentChatThreadFastInstanceCommand,
|
||||
];
|
||||
|
||||
+15
@@ -341,6 +341,10 @@ export class BillingUsageService {
|
||||
}
|
||||
|
||||
async hasAvailableCredits(workspaceId: string): Promise<boolean> {
|
||||
if (!this.twentyConfigService.get('IS_BILLING_ENABLED')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { billingSubscription: subscription } =
|
||||
await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'billingSubscription',
|
||||
@@ -369,6 +373,17 @@ export class BillingUsageService {
|
||||
return availableCredits > 0;
|
||||
}
|
||||
|
||||
async hasAvailableCreditsOrThrow(workspaceId: string): Promise<void> {
|
||||
const hasCredits = await this.hasAvailableCredits(workspaceId);
|
||||
|
||||
if (!hasCredits) {
|
||||
throw new BillingException(
|
||||
'Credits exhausted',
|
||||
BillingExceptionCode.BILLING_CREDITS_EXHAUSTED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getCurrentPeriodCreditsUsed(
|
||||
workspaceId: string,
|
||||
periodStart: Date,
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import { TimelineCalendarEventService } from './timeline-calendar-event.service'
|
||||
type MockWorkspaceRepository = Partial<
|
||||
WorkspaceRepository<CalendarEventWorkspaceEntity>
|
||||
> & {
|
||||
count: jest.Mock;
|
||||
find: jest.Mock;
|
||||
findAndCount: jest.Mock;
|
||||
};
|
||||
@@ -40,6 +41,7 @@ describe('TimelineCalendarEventService', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
mockCalendarEventRepository = {
|
||||
count: jest.fn().mockResolvedValue(1),
|
||||
find: jest.fn(),
|
||||
findAndCount: jest.fn(),
|
||||
};
|
||||
|
||||
+13
-3
@@ -55,6 +55,16 @@ export class TimelineCalendarEventService {
|
||||
'calendarEvent',
|
||||
);
|
||||
|
||||
const totalNumberOfCalendarEvents = await calendarEventRepository.count(
|
||||
{
|
||||
where: {
|
||||
calendarEventParticipants: {
|
||||
personId: Any(personIds),
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const calendarEventIds = await calendarEventRepository.find({
|
||||
where: {
|
||||
calendarEventParticipants: {
|
||||
@@ -76,12 +86,12 @@ export class TimelineCalendarEventService {
|
||||
|
||||
if (ids.length <= 0) {
|
||||
return {
|
||||
totalNumberOfCalendarEvents: 0,
|
||||
totalNumberOfCalendarEvents,
|
||||
timelineCalendarEvents: [],
|
||||
};
|
||||
}
|
||||
|
||||
const [events, total] = await calendarEventRepository.findAndCount({
|
||||
const [events] = await calendarEventRepository.findAndCount({
|
||||
where: {
|
||||
id: Any(ids),
|
||||
},
|
||||
@@ -241,7 +251,7 @@ export class TimelineCalendarEventService {
|
||||
});
|
||||
|
||||
return {
|
||||
totalNumberOfCalendarEvents: total,
|
||||
totalNumberOfCalendarEvents,
|
||||
timelineCalendarEvents,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -57,12 +57,24 @@ export class S3Driver implements StorageDriver {
|
||||
return;
|
||||
}
|
||||
|
||||
this.s3Client = new S3({ ...s3Options, region, endpoint });
|
||||
this.s3Client = new S3({
|
||||
...s3Options,
|
||||
region,
|
||||
endpoint,
|
||||
maxAttempts: 5,
|
||||
retryMode: 'adaptive',
|
||||
});
|
||||
this.bucketName = bucketName;
|
||||
|
||||
if (presignEnabled) {
|
||||
this.presignClient = presignEndpoint
|
||||
? new S3({ ...s3Options, region, endpoint: presignEndpoint })
|
||||
? new S3({
|
||||
...s3Options,
|
||||
region,
|
||||
endpoint: presignEndpoint,
|
||||
maxAttempts: 5,
|
||||
retryMode: 'adaptive',
|
||||
})
|
||||
: this.s3Client;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +121,14 @@ export class S3Driver implements StorageDriver {
|
||||
Bucket: this.bucketName,
|
||||
});
|
||||
|
||||
await this.s3Client.send(command);
|
||||
try {
|
||||
await this.s3Client.send(command);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`S3 writeFile failed for key "${params.filePath}": ${error.name ?? 'UnknownError'} - ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async createFolder(path: string) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user