Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b7b33fe18 | ||
|
|
f1125d0894 | ||
|
|
6b3064e2ba | ||
|
|
a321e24839 | ||
|
|
3717df34be | ||
|
|
140dceebd1 | ||
|
|
4d2ceaf70a | ||
|
|
6b49a14b9f | ||
|
|
62b347fc74 | ||
|
|
cf4b4455d3 | ||
|
|
268fccca29 | ||
|
|
c938fbf4d6 | ||
|
|
eca92ca559 |
@@ -23,9 +23,11 @@ jobs:
|
||||
packages/twenty-sdk/**
|
||||
packages/twenty-client-sdk/**
|
||||
packages/twenty-shared/**
|
||||
packages/twenty-server/**
|
||||
!packages/twenty-sdk/package.json
|
||||
!packages/twenty-client-sdk/package.json
|
||||
!packages/twenty-shared/package.json
|
||||
!packages/twenty-server/package.json
|
||||
|
||||
example-app-postcard:
|
||||
needs: changed-files-check
|
||||
@@ -83,6 +85,31 @@ jobs:
|
||||
working-directory: packages/twenty-apps/examples/postcard
|
||||
run: npx vitest run
|
||||
|
||||
- name: Configure remote for SDK CLI
|
||||
run: |
|
||||
mkdir -p ~/.twenty
|
||||
cat > ~/.twenty/config.json <<EOF
|
||||
{
|
||||
"version": 1,
|
||||
"remotes": {
|
||||
"target": {
|
||||
"apiUrl": "${TWENTY_API_URL}",
|
||||
"apiKey": "${TWENTY_API_KEY}",
|
||||
"accessToken": "${TWENTY_API_KEY}"
|
||||
}
|
||||
},
|
||||
"defaultRemote": "target"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Deploy postcard app (registry install path)
|
||||
working-directory: packages/twenty-apps/examples/postcard
|
||||
run: node ${{ github.workspace }}/packages/twenty-sdk/dist/cli.cjs deploy --remote target
|
||||
|
||||
- name: Install postcard app (registry install path)
|
||||
working-directory: packages/twenty-apps/examples/postcard
|
||||
run: node ${{ github.workspace }}/packages/twenty-sdk/dist/cli.cjs install --remote target
|
||||
|
||||
ci-example-app-postcard-status-check:
|
||||
if: always() && !cancelled()
|
||||
timeout-minutes: 5
|
||||
|
||||
@@ -771,6 +771,7 @@ type ViewFilter {
|
||||
viewFilterGroupId: UUID
|
||||
positionInViewFilterGroup: Float
|
||||
subFieldName: String
|
||||
relationTargetFieldMetadataId: UUID
|
||||
viewId: UUID!
|
||||
workspaceId: UUID!
|
||||
createdAt: DateTime!
|
||||
@@ -3416,6 +3417,7 @@ input CreateViewFilterInput {
|
||||
viewFilterGroupId: UUID
|
||||
positionInViewFilterGroup: Float
|
||||
subFieldName: String
|
||||
relationTargetFieldMetadataId: UUID
|
||||
viewId: UUID!
|
||||
}
|
||||
|
||||
@@ -3434,6 +3436,7 @@ input UpdateViewFilterInputUpdates {
|
||||
viewFilterGroupId: UUID
|
||||
positionInViewFilterGroup: Float
|
||||
subFieldName: String
|
||||
relationTargetFieldMetadataId: UUID
|
||||
}
|
||||
|
||||
input DeleteViewFilterInput {
|
||||
@@ -3522,6 +3525,7 @@ input UpsertViewWidgetViewFilterInput {
|
||||
viewFilterGroupId: UUID
|
||||
positionInViewFilterGroup: Float
|
||||
subFieldName: String
|
||||
relationTargetFieldMetadataId: UUID
|
||||
}
|
||||
|
||||
input UpsertViewWidgetViewFilterGroupInput {
|
||||
|
||||
@@ -523,6 +523,7 @@ export interface ViewFilter {
|
||||
viewFilterGroupId?: Scalars['UUID']
|
||||
positionInViewFilterGroup?: Scalars['Float']
|
||||
subFieldName?: Scalars['String']
|
||||
relationTargetFieldMetadataId?: Scalars['UUID']
|
||||
viewId: Scalars['UUID']
|
||||
workspaceId: Scalars['UUID']
|
||||
createdAt: Scalars['DateTime']
|
||||
@@ -3451,6 +3452,7 @@ export interface ViewFilterGenqlSelection{
|
||||
viewFilterGroupId?: boolean | number
|
||||
positionInViewFilterGroup?: boolean | number
|
||||
subFieldName?: boolean | number
|
||||
relationTargetFieldMetadataId?: boolean | number
|
||||
viewId?: boolean | number
|
||||
workspaceId?: boolean | number
|
||||
createdAt?: boolean | number
|
||||
@@ -5973,7 +5975,7 @@ export interface CreateViewFilterGroupInput {id?: (Scalars['UUID'] | null),paren
|
||||
|
||||
export interface UpdateViewFilterGroupInput {id?: (Scalars['UUID'] | null),parentViewFilterGroupId?: (Scalars['UUID'] | null),logicalOperator?: (ViewFilterGroupLogicalOperator | null),positionInViewFilterGroup?: (Scalars['Float'] | null),viewId?: (Scalars['UUID'] | null)}
|
||||
|
||||
export interface CreateViewFilterInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],operand?: (ViewFilterOperand | null),value: Scalars['JSON'],viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null),viewId: Scalars['UUID']}
|
||||
export interface CreateViewFilterInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],operand?: (ViewFilterOperand | null),value: Scalars['JSON'],viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null),relationTargetFieldMetadataId?: (Scalars['UUID'] | null),viewId: Scalars['UUID']}
|
||||
|
||||
export interface UpdateViewFilterInput {
|
||||
/** The id of the view filter to update */
|
||||
@@ -5981,7 +5983,7 @@ id: Scalars['UUID'],
|
||||
/** The view filter to update */
|
||||
update: UpdateViewFilterInputUpdates}
|
||||
|
||||
export interface UpdateViewFilterInputUpdates {fieldMetadataId?: (Scalars['UUID'] | null),operand?: (ViewFilterOperand | null),value?: (Scalars['JSON'] | null),viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null)}
|
||||
export interface UpdateViewFilterInputUpdates {fieldMetadataId?: (Scalars['UUID'] | null),operand?: (ViewFilterOperand | null),value?: (Scalars['JSON'] | null),viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null),relationTargetFieldMetadataId?: (Scalars['UUID'] | null)}
|
||||
|
||||
export interface DeleteViewFilterInput {
|
||||
/** The id of the view filter to delete. */
|
||||
@@ -6013,7 +6015,7 @@ viewFieldId?: (Scalars['UUID'] | null),
|
||||
/** The field metadata id. Used to create a new view field when viewFieldId is not provided. */
|
||||
fieldMetadataId?: (Scalars['UUID'] | null),isVisible: Scalars['Boolean'],position: Scalars['Float'],size?: (Scalars['Float'] | null)}
|
||||
|
||||
export interface UpsertViewWidgetViewFilterInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],operand?: (ViewFilterOperand | null),value: Scalars['JSON'],viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null)}
|
||||
export interface UpsertViewWidgetViewFilterInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],operand?: (ViewFilterOperand | null),value: Scalars['JSON'],viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null),relationTargetFieldMetadataId?: (Scalars['UUID'] | null)}
|
||||
|
||||
export interface UpsertViewWidgetViewFilterGroupInput {id?: (Scalars['UUID'] | null),parentViewFilterGroupId?: (Scalars['UUID'] | null),logicalOperator?: (ViewFilterGroupLogicalOperator | null),positionInViewFilterGroup?: (Scalars['Float'] | null)}
|
||||
|
||||
|
||||
@@ -1521,6 +1521,9 @@ export default {
|
||||
"subFieldName": [
|
||||
1
|
||||
],
|
||||
"relationTargetFieldMetadataId": [
|
||||
3
|
||||
],
|
||||
"viewId": [
|
||||
3
|
||||
],
|
||||
@@ -8929,6 +8932,9 @@ export default {
|
||||
"subFieldName": [
|
||||
1
|
||||
],
|
||||
"relationTargetFieldMetadataId": [
|
||||
3
|
||||
],
|
||||
"viewId": [
|
||||
3
|
||||
],
|
||||
@@ -8966,6 +8972,9 @@ export default {
|
||||
"subFieldName": [
|
||||
1
|
||||
],
|
||||
"relationTargetFieldMetadataId": [
|
||||
3
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
@@ -9154,6 +9163,9 @@ export default {
|
||||
"subFieldName": [
|
||||
1
|
||||
],
|
||||
"relationTargetFieldMetadataId": [
|
||||
3
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: var(--t-background-tertiary);
|
||||
color: var(--t-font-color-primary);
|
||||
}
|
||||
|
||||
html {
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "En"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Enige {fieldLabel}-veld"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Wagwoord is gestel"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Wagwoord is opgedateer"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Wagwoord moet minstens 8 karakters wees"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "و"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "أي حقل {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "تم تعيين كلمة المرور"
|
||||
msgid "Password has been updated"
|
||||
msgstr "تم تحديث كلمة السر"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "يجب أن تكون كلمة المرور 8 أحرف على الأقل"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "I"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Qualsevol camp {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "La contrasenya s'ha establert"
|
||||
msgid "Password has been updated"
|
||||
msgstr "La contrasenya s'ha actualitzat"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "La contrasenya ha de tenir almenys 8 caràcters"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "A"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Libovolné pole {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Heslo bylo nastaveno"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Heslo bylo aktualizováno"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Heslo musí mít alespoň 8 znaků"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Og"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Ethvert {fieldLabel}-felt"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Adgangskoden er blevet sat"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Password er blevet opdateret"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Adgangskode skal være min. 8 tegn"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Und"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Beliebiges {fieldLabel}-Feld"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Das Passwort wurde aktualisiert"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Das Passwort wurde aktualisiert"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Passwort muss mind. 8 Zeichen lang sein"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Και"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Οποιοδήποτε πεδίο {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Ο κωδικός πρόσβασης έχει ρυθμιστεί"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Ο κωδικός έχει ενημερωθεί"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Ο κωδικός πρόσβασης πρέπει να είναι τουλάχιστον 8 χαρακτήρες"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1958,7 +1958,7 @@ msgid "And"
|
||||
msgstr "And"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Any {fieldLabel} field"
|
||||
|
||||
@@ -11819,11 +11819,11 @@ msgstr "Password has been set"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Password has been updated"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Password must be min. 8 characters"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr "Password must be between 8 and 50 characters"
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Y"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Cualquier campo {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "La contraseña ha sido establecida"
|
||||
msgid "Password has been updated"
|
||||
msgstr "La contraseña ha sido actualizada"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "La contraseña debe tener al menos 8 caracteres"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Ja"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Mikä tahansa {fieldLabel}-kenttä"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Salasana on asetettu"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Salasana on päivitetty"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Salasanassa on oltava vähintään 8 merkkiä"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Et"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Tout champ {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Le mot de passe a été défini"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Le mot de passe a été mis à jour"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Le mot de passe doit comporter au minimum 8 caractères"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
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
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "וגם"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "כל שדה {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "הסיסמה עודכנה"
|
||||
msgid "Password has been updated"
|
||||
msgstr "הסיסמה עודכנה"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "הסיסמה חייבת להיות באורך מינימום 8 תווים"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "És"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Bármely {fieldLabel} mező"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "A jelszót beállították"
|
||||
msgid "Password has been updated"
|
||||
msgstr "A jelszót frissítették"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "A jelszónak legalább 8 karakter hosszúnak kell lennie"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "E"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Qualsiasi campo {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "La password è stata impostata"
|
||||
msgid "Password has been updated"
|
||||
msgstr "La password è stata aggiornata"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "La password deve essere di almeno 8 caratteri"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "および"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "任意の {fieldLabel} フィールド"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "パスワードが設定されました"
|
||||
msgid "Password has been updated"
|
||||
msgstr "パスワードが更新されました"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "パスワードは8文字以上である必要があります"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "및"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "모든 {fieldLabel} 필드"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "비밀번호가 설정되었습니다"
|
||||
msgid "Password has been updated"
|
||||
msgstr "비밀번호가 업데이트되었습니다"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "비밀번호는 최소 8자여야 합니다"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "En"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Elk {fieldLabel}-veld"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Wachtwoord is ingesteld"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Wachtwoord is geüpdated"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Wachtwoord moet min. 8 tekens bevatten"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Og"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Ethvert {fieldLabel}-felt"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Passordet har blitt satt"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Passordet har blitt oppdatert"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Passord må være minst 8 tegn"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "I"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Dowolne pole {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Hasło zostało ustawione"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Hasło zostało zaktualizowane"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Hasło musi mieć min. 8 znaków"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1958,7 +1958,7 @@ msgid "And"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr ""
|
||||
|
||||
@@ -11796,10 +11796,10 @@ msgstr ""
|
||||
msgid "Password has been updated"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "E"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Qualquer campo {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "A senha foi definida"
|
||||
msgid "Password has been updated"
|
||||
msgstr "A senha foi atualizada"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "A senha deve ter no mínimo 8 caracteres"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "E"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Qualquer campo {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "A palavra-passe foi definida"
|
||||
msgid "Password has been updated"
|
||||
msgstr "A palavra-passe foi atualizada"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "A palavra-passe deve ter no mínimo 8 caracteres"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Și"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Orice câmp {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Parola a fost setată"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Parola a fost actualizată"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Parola trebuie să aibă minimum 8 caractere"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
Binary file not shown.
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "И"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Било које поље {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Лозинка је постављена"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Лозинка је ажурирана"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Лозинка мора имати најмање 8 знакова"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Och"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Vilket {fieldLabel}-fält som helst"
|
||||
|
||||
@@ -11803,11 +11803,11 @@ msgstr "Lösenordet har ställts in"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Lösenordet har uppdaterats"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Lösenord måste vara minst 8 tecken"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Ve"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Herhangi bir {fieldLabel} alanı"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Parola ayarlandı"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Parola güncellendi"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Parola en az 8 karakter olmalıdır"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Та"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Будь-яке поле {fieldLabel}"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Пароль було встановлено"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Пароль було оновлено"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Пароль має містити щонайменше 8 символів"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "Và"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "Bất kỳ trường {fieldLabel} nào"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "Mật khẩu đã được thiết lập"
|
||||
msgid "Password has been updated"
|
||||
msgstr "Mật khẩu đã được cập nhật"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "Mật khẩu phải có tối thiểu 8 ký tự"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "且"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "任意 {fieldLabel} 字段"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "密码已更新"
|
||||
msgid "Password has been updated"
|
||||
msgstr "密码已更新"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "密码长度至少为 8 个字符"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -1963,7 +1963,7 @@ msgid "And"
|
||||
msgstr "和"
|
||||
|
||||
#. js-lingui-id: KYvJeB
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx
|
||||
#: src/modules/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu.tsx
|
||||
msgid "Any {fieldLabel} field"
|
||||
msgstr "任何 {fieldLabel} 欄位"
|
||||
|
||||
@@ -11801,11 +11801,11 @@ msgstr "密碼已設定"
|
||||
msgid "Password has been updated"
|
||||
msgstr "密碼已更新"
|
||||
|
||||
#. js-lingui-id: 1oqxe6
|
||||
#. js-lingui-id: BfLK2u
|
||||
#: src/pages/auth/PasswordReset.tsx
|
||||
#: src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
|
||||
msgid "Password must be min. 8 characters"
|
||||
msgstr "密碼至少需 8 個字元"
|
||||
msgid "Password must be between 8 and 50 characters"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: mi6Rel
|
||||
#: src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts
|
||||
|
||||
@@ -26,7 +26,10 @@ const makeValidationSchema = (signInUpStep: SignInUpStep) =>
|
||||
signInUpStep === SignInUpStep.Password
|
||||
? z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, t`Password must be min. 8 characters`)
|
||||
.regex(
|
||||
PASSWORD_REGEX,
|
||||
t`Password must be between 8 and 50 characters`,
|
||||
)
|
||||
: z.string().optional(),
|
||||
captchaToken: z.string().default(''),
|
||||
})
|
||||
|
||||
@@ -8,4 +8,12 @@ describe('PASSWORD_REGEX', () => {
|
||||
expect(PASSWORD_REGEX.test(validPassword)).toBe(true);
|
||||
expect(PASSWORD_REGEX.test(invalidPassword)).toBe(false);
|
||||
});
|
||||
|
||||
it('should match passwords with at most 50 characters', () => {
|
||||
const validPassword = 'a'.repeat(50);
|
||||
const invalidPassword = 'a'.repeat(51);
|
||||
|
||||
expect(PASSWORD_REGEX.test(validPassword)).toBe(true);
|
||||
expect(PASSWORD_REGEX.test(invalidPassword)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const PASSWORD_REGEX = /^.{8,}$/;
|
||||
export const PASSWORD_REGEX = /^.{8,50}$/;
|
||||
|
||||
+5
@@ -89,12 +89,17 @@ export const buildHeadlessCommandContextApi = ({
|
||||
? (currentWorkspaceMember?.timeZone ?? systemTimeZone)
|
||||
: systemTimeZone;
|
||||
|
||||
const flattenedFieldMetadataItems = objectMetadataItems.flatMap(
|
||||
(objectMetadataItem) => objectMetadataItem.fields,
|
||||
);
|
||||
|
||||
const graphqlFilter = isDefined(objectMetadataItem)
|
||||
? computeContextStoreFilters({
|
||||
contextStoreTargetedRecordsRule: targetedRecordsRule,
|
||||
contextStoreFilters: filters,
|
||||
contextStoreFilterGroups: filterGroups,
|
||||
objectMetadataItem,
|
||||
flattenedFieldMetadataItems,
|
||||
filterValueDependencies: {
|
||||
currentWorkspaceMemberId: currentWorkspaceMember?.id,
|
||||
timeZone: userTimezone,
|
||||
|
||||
+1
@@ -72,6 +72,7 @@ export const useFindManyRecordsSelectedInContextStore = ({
|
||||
contextStoreFilters,
|
||||
contextStoreFilterGroups,
|
||||
objectMetadataItem,
|
||||
flattenedFieldMetadataItems: allFieldMetadataItems,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
});
|
||||
|
||||
+2
@@ -29,6 +29,7 @@ describe('computeContextStoreFilters', () => {
|
||||
contextStoreFilters: [],
|
||||
contextStoreFilterGroups: [],
|
||||
objectMetadataItem: personObjectMetadataItem,
|
||||
flattenedFieldMetadataItems: personObjectMetadataItem.fields,
|
||||
filterValueDependencies: mockFilterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue: '',
|
||||
});
|
||||
@@ -73,6 +74,7 @@ describe('computeContextStoreFilters', () => {
|
||||
contextStoreFilters,
|
||||
contextStoreFilterGroups: [],
|
||||
objectMetadataItem: personObjectMetadataItem,
|
||||
flattenedFieldMetadataItems: personObjectMetadataItem.fields,
|
||||
filterValueDependencies: mockFilterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue: '',
|
||||
});
|
||||
|
||||
+12
-2
@@ -1,7 +1,9 @@
|
||||
import { type ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { augmentFieldsWithRelationTargets } from '@/object-record/record-filter/utils/augmentFieldsWithRelationTargets';
|
||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||
import {
|
||||
type RecordFilterValueDependencies,
|
||||
@@ -17,6 +19,7 @@ type ComputeContextStoreFiltersProps = {
|
||||
contextStoreFilters: RecordFilter[];
|
||||
contextStoreFilterGroups: RecordFilterGroup[];
|
||||
objectMetadataItem: EnrichedObjectMetadataItem;
|
||||
flattenedFieldMetadataItems: FieldMetadataItem[];
|
||||
filterValueDependencies: RecordFilterValueDependencies;
|
||||
contextStoreAnyFieldFilterValue: string;
|
||||
};
|
||||
@@ -26,6 +29,7 @@ export const computeContextStoreFilters = ({
|
||||
contextStoreFilters,
|
||||
contextStoreFilterGroups,
|
||||
objectMetadataItem,
|
||||
flattenedFieldMetadataItems,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
}: ComputeContextStoreFiltersProps) => {
|
||||
@@ -37,12 +41,18 @@ export const computeContextStoreFilters = ({
|
||||
fields: objectMetadataItem.fields,
|
||||
});
|
||||
|
||||
const fields = augmentFieldsWithRelationTargets({
|
||||
baseFields: objectMetadataItem?.fields ?? [],
|
||||
recordFilters: contextStoreFilters,
|
||||
allFieldMetadataItems: flattenedFieldMetadataItems,
|
||||
});
|
||||
|
||||
if (contextStoreTargetedRecordsRule.mode === 'exclusion') {
|
||||
queryFilter = makeAndFilterVariables([
|
||||
recordGqlFilterForAnyFieldFilter,
|
||||
computeRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
fields: objectMetadataItem?.fields ?? [],
|
||||
fields,
|
||||
recordFilters: contextStoreFilters,
|
||||
recordFilterGroups: contextStoreFilterGroups,
|
||||
}),
|
||||
@@ -71,7 +81,7 @@ export const computeContextStoreFilters = ({
|
||||
},
|
||||
computeRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
fields: objectMetadataItem?.fields ?? [],
|
||||
fields,
|
||||
recordFilters: contextStoreFilters,
|
||||
recordFilterGroups: contextStoreFilterGroups,
|
||||
}),
|
||||
|
||||
+6
-6
@@ -1,6 +1,10 @@
|
||||
import { SORTABLE_FIELD_METADATA_TYPES } from '@/object-metadata/constants/SortableFieldMetadataTypes';
|
||||
import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField';
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { isManyToOneRelationField } from '@/object-metadata/utils/isManyToOneRelationField';
|
||||
import {
|
||||
type FieldMetadataType,
|
||||
type RelationType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
type SortableFieldInput = {
|
||||
isSystem?: boolean | null;
|
||||
@@ -17,13 +21,9 @@ export const filterSortableFieldMetadataItems = (field: SortableFieldInput) => {
|
||||
field.type,
|
||||
);
|
||||
|
||||
const isRelationFieldSortable =
|
||||
field.type === FieldMetadataType.RELATION &&
|
||||
field.relation?.type === RelationType.MANY_TO_ONE;
|
||||
|
||||
return (
|
||||
!isHiddenSystemField(field) &&
|
||||
isFieldActive &&
|
||||
(isFieldTypeSortable || isRelationFieldSortable)
|
||||
(isFieldTypeSortable || isManyToOneRelationField(field))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
type FieldWithRelation = {
|
||||
type: FieldMetadataType;
|
||||
relation?: { type: RelationType } | null;
|
||||
};
|
||||
|
||||
export const isManyToOneRelationField = <T extends FieldWithRelation>(
|
||||
field: T,
|
||||
): field is T & { relation: NonNullable<T['relation']> } =>
|
||||
field.type === FieldMetadataType.RELATION &&
|
||||
field.relation?.type === RelationType.MANY_TO_ONE;
|
||||
+53
-52
@@ -1,13 +1,6 @@
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||
import { useSelectFieldUsedInAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown';
|
||||
import { useApplyAdvancedFilterCompositeSubField } from '@/object-record/advanced-filter/hooks/useApplyAdvancedFilterCompositeSubField';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||
@@ -20,21 +13,26 @@ import { type CompositeFieldSubFieldName } from '@/settings/data-model/types/Com
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconChevronLeft, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
type AdvancedFilterSubFieldSelectMenuProps = {
|
||||
type AdvancedFilterCompositeSubFieldSelectMenuProps = {
|
||||
recordFilterId: string;
|
||||
};
|
||||
|
||||
export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
export const AdvancedFilterCompositeSubFieldSelectMenu = ({
|
||||
recordFilterId,
|
||||
}: AdvancedFilterSubFieldSelectMenuProps) => {
|
||||
}: AdvancedFilterCompositeSubFieldSelectMenuProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const fieldMetadataItemUsedInDropdown = useAtomComponentSelectorValue(
|
||||
@@ -54,21 +52,20 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
const { closeAdvancedFilterFieldSelectDropdown } =
|
||||
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||
|
||||
const { selectFieldUsedInAdvancedFilterDropdown } =
|
||||
useSelectFieldUsedInAdvancedFilterDropdown();
|
||||
const { applyAdvancedFilterCompositeSubField } =
|
||||
useApplyAdvancedFilterCompositeSubField();
|
||||
|
||||
const handleSelectFilter = (
|
||||
selectedFieldMetadataItem: FieldMetadataItem | null | undefined,
|
||||
subFieldName?: CompositeFieldSubFieldName | null | undefined,
|
||||
) => {
|
||||
if (!isDefined(selectedFieldMetadataItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectFieldUsedInAdvancedFilterDropdown({
|
||||
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
||||
const handleSelectFilter = ({
|
||||
fieldMetadataItem,
|
||||
subFieldName,
|
||||
}: {
|
||||
fieldMetadataItem: FieldMetadataItem;
|
||||
subFieldName?: CompositeFieldSubFieldName | null;
|
||||
}) => {
|
||||
applyAdvancedFilterCompositeSubField({
|
||||
sourceFieldMetadataItem: fieldMetadataItem,
|
||||
subFieldName: subFieldName ?? null,
|
||||
recordFilterId,
|
||||
subFieldName,
|
||||
});
|
||||
|
||||
closeAdvancedFilterFieldSelectDropdown();
|
||||
@@ -132,36 +129,42 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
||||
>
|
||||
{compositeFieldTypeIsFilterableByAnySubField && (
|
||||
<SelectableListItem
|
||||
itemId="-1"
|
||||
key={`select-filter-${-1}`}
|
||||
onEnter={() => {
|
||||
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
{compositeFieldTypeIsFilterableByAnySubField &&
|
||||
isDefined(fieldMetadataItemUsedInDropdown) && (
|
||||
<SelectableListItem
|
||||
itemId="-1"
|
||||
key={`select-filter-${-1}`}
|
||||
testId={`select-filter-${-1}`}
|
||||
focused={selectedItemId === '-1'}
|
||||
onClick={() => {
|
||||
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
||||
onEnter={() => {
|
||||
handleSelectFilter({
|
||||
fieldMetadataItem: fieldMetadataItemUsedInDropdown,
|
||||
});
|
||||
}}
|
||||
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
|
||||
text={t`Any ${fieldLabel} field`}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
>
|
||||
<MenuItem
|
||||
key={`select-filter-${-1}`}
|
||||
testId={`select-filter-${-1}`}
|
||||
focused={selectedItemId === '-1'}
|
||||
onClick={() => {
|
||||
handleSelectFilter({
|
||||
fieldMetadataItem: fieldMetadataItemUsedInDropdown,
|
||||
});
|
||||
}}
|
||||
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
|
||||
text={t`Any ${fieldLabel} field`}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{subFieldsAreFilterable &&
|
||||
isDefined(fieldMetadataItemUsedInDropdown) &&
|
||||
subFieldNames.map((subFieldName, index) => (
|
||||
<SelectableListItem
|
||||
itemId={subFieldName}
|
||||
key={`select-filter-${index}`}
|
||||
onEnter={() => {
|
||||
handleSelectFilter(
|
||||
fieldMetadataItemUsedInDropdown,
|
||||
handleSelectFilter({
|
||||
fieldMetadataItem: fieldMetadataItemUsedInDropdown,
|
||||
subFieldName,
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
@@ -169,12 +172,10 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
key={`select-filter-${index}`}
|
||||
testId={`select-filter-${index}`}
|
||||
onClick={() => {
|
||||
if (isDefined(fieldMetadataItemUsedInDropdown)) {
|
||||
handleSelectFilter(
|
||||
fieldMetadataItemUsedInDropdown,
|
||||
subFieldName,
|
||||
);
|
||||
}
|
||||
handleSelectFilter({
|
||||
fieldMetadataItem: fieldMetadataItemUsedInDropdown,
|
||||
subFieldName,
|
||||
});
|
||||
}}
|
||||
text={getCompositeSubFieldLabel(
|
||||
objectFilterDropdownSubMenuFieldType,
|
||||
@@ -182,7 +183,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
)}
|
||||
LeftIcon={getIcon(
|
||||
ICON_NAME_BY_SUB_FIELD[subFieldName] ??
|
||||
fieldMetadataItemUsedInDropdown?.icon,
|
||||
fieldMetadataItemUsedInDropdown.icon,
|
||||
)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
+29
-12
@@ -1,7 +1,9 @@
|
||||
import { AdvancedFilterCompositeSubFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterCompositeSubFieldSelectMenu';
|
||||
import { AdvancedFilterFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu';
|
||||
import { AdvancedFilterSubFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu';
|
||||
import { AdvancedFilterRelationTargetFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterRelationTargetFieldSelectMenu';
|
||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { objectFilterDropdownIsSelectingRelationTargetFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingRelationTargetFieldComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
type AdvancedFilterFieldSelectDropdownContentProps = {
|
||||
recordFilterId: string;
|
||||
@@ -10,16 +12,31 @@ type AdvancedFilterFieldSelectDropdownContentProps = {
|
||||
export const AdvancedFilterFieldSelectDropdownContent = ({
|
||||
recordFilterId,
|
||||
}: AdvancedFilterFieldSelectDropdownContentProps) => {
|
||||
const [objectFilterDropdownIsSelectingCompositeField] = useAtomComponentState(
|
||||
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||
);
|
||||
const objectFilterDropdownIsSelectingCompositeField =
|
||||
useAtomComponentStateValue(
|
||||
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||
);
|
||||
|
||||
const shouldShowCompositeSelectionSubMenu =
|
||||
objectFilterDropdownIsSelectingCompositeField;
|
||||
const objectFilterDropdownIsSelectingRelationTargetField =
|
||||
useAtomComponentStateValue(
|
||||
objectFilterDropdownIsSelectingRelationTargetFieldComponentState,
|
||||
);
|
||||
|
||||
return shouldShowCompositeSelectionSubMenu ? (
|
||||
<AdvancedFilterSubFieldSelectMenu recordFilterId={recordFilterId} />
|
||||
) : (
|
||||
<AdvancedFilterFieldSelectMenu recordFilterId={recordFilterId} />
|
||||
);
|
||||
if (objectFilterDropdownIsSelectingRelationTargetField) {
|
||||
return (
|
||||
<AdvancedFilterRelationTargetFieldSelectMenu
|
||||
recordFilterId={recordFilterId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (objectFilterDropdownIsSelectingCompositeField) {
|
||||
return (
|
||||
<AdvancedFilterCompositeSubFieldSelectMenu
|
||||
recordFilterId={recordFilterId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <AdvancedFilterFieldSelectMenu recordFilterId={recordFilterId} />;
|
||||
};
|
||||
|
||||
+45
-12
@@ -11,17 +11,20 @@ import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { AdvancedFilterFieldSelectSearchInput } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectSearchInput';
|
||||
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||
import { useSelectFieldUsedInAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown';
|
||||
import { useApplyAdvancedFilterSourceField } from '@/object-record/advanced-filter/hooks/useApplyAdvancedFilterSourceField';
|
||||
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
|
||||
import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||
import { objectFilterDropdownIsSelectingRelationTargetFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingRelationTargetFieldComponentState';
|
||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||
import { isCompositeFilterableFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFilterableFieldType';
|
||||
import { isManyToOneRelationField } from '@/object-metadata/utils/isManyToOneRelationField';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
||||
import { usePushFocusForLeafFieldValuePicker } from '@/object-record/advanced-filter/hooks/usePushFocusForLeafFieldValuePicker';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
@@ -87,8 +90,8 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
advancedFilterFieldSelectDropdownId,
|
||||
);
|
||||
|
||||
const { selectFieldUsedInAdvancedFilterDropdown } =
|
||||
useSelectFieldUsedInAdvancedFilterDropdown();
|
||||
const { applyAdvancedFilterSourceField } =
|
||||
useApplyAdvancedFilterSourceField();
|
||||
|
||||
const [, setObjectFilterDropdownSubMenuFieldType] = useAtomComponentState(
|
||||
objectFilterDropdownSubMenuFieldTypeComponentState,
|
||||
@@ -99,10 +102,18 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||
);
|
||||
|
||||
const [, setObjectFilterDropdownIsSelectingRelationTargetField] =
|
||||
useAtomComponentState(
|
||||
objectFilterDropdownIsSelectingRelationTargetFieldComponentState,
|
||||
);
|
||||
|
||||
const setFieldMetadataItemIdUsedInDropdown = useSetAtomComponentState(
|
||||
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const { pushFocusForLeafFieldValuePicker } =
|
||||
usePushFocusForLeafFieldValuePicker();
|
||||
|
||||
const handleFieldMetadataItemSelect = (
|
||||
selectedFieldMetadataItem: FieldMetadataItem,
|
||||
) => {
|
||||
@@ -112,19 +123,41 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
selectedFieldMetadataItem.type,
|
||||
);
|
||||
|
||||
selectFieldUsedInAdvancedFilterDropdown({
|
||||
fieldMetadataItemId: selectedFieldMetadataItem.id,
|
||||
const isRelationTraversalField = isManyToOneRelationField(
|
||||
selectedFieldMetadataItem,
|
||||
);
|
||||
|
||||
const compositeSubMenuFieldType =
|
||||
!isRelationTraversalField && isCompositeFilterableFieldType(filterType)
|
||||
? filterType
|
||||
: null;
|
||||
|
||||
// For sub-menu paths (composite or relation traversal) we only stage
|
||||
// the source field in the dropdown state and open the sub-menu —
|
||||
// upsertRecordFilter happens in the sub-menu's hook once the user
|
||||
// makes the final choice. Otherwise navigating back from the sub-menu
|
||||
// would leave an orphan partial filter in the chip list.
|
||||
if (isRelationTraversalField) {
|
||||
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
||||
setObjectFilterDropdownIsSelectingRelationTargetField(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (compositeSubMenuFieldType !== null) {
|
||||
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
||||
setObjectFilterDropdownSubMenuFieldType(compositeSubMenuFieldType);
|
||||
setObjectFilterDropdownIsSelectingCompositeField(true);
|
||||
return;
|
||||
}
|
||||
|
||||
applyAdvancedFilterSourceField({
|
||||
sourceFieldMetadataItem: selectedFieldMetadataItem,
|
||||
recordFilterId,
|
||||
});
|
||||
|
||||
if (isCompositeFilterableFieldType(filterType)) {
|
||||
setObjectFilterDropdownSubMenuFieldType(filterType);
|
||||
pushFocusForLeafFieldValuePicker(selectedFieldMetadataItem);
|
||||
|
||||
setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id);
|
||||
setObjectFilterDropdownIsSelectingCompositeField(true);
|
||||
} else {
|
||||
closeAdvancedFilterFieldSelectDropdown();
|
||||
}
|
||||
closeAdvancedFilterFieldSelectDropdown();
|
||||
};
|
||||
|
||||
const shouldShowVisibleFields = visibleFieldMetadataItems.length > 0;
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isManyToOneRelationField } from '@/object-metadata/utils/isManyToOneRelationField';
|
||||
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
|
||||
import { useApplyAdvancedFilterRelationTargetField } from '@/object-record/advanced-filter/hooks/useApplyAdvancedFilterRelationTargetField';
|
||||
import { usePushFocusForLeafFieldValuePicker } from '@/object-record/advanced-filter/hooks/usePushFocusForLeafFieldValuePicker';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { objectFilterDropdownIsSelectingRelationTargetFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingRelationTargetFieldComponentState';
|
||||
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconChevronLeft, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
type AdvancedFilterRelationTargetFieldSelectMenuProps = {
|
||||
recordFilterId: string;
|
||||
};
|
||||
|
||||
export const AdvancedFilterRelationTargetFieldSelectMenu = ({
|
||||
recordFilterId,
|
||||
}: AdvancedFilterRelationTargetFieldSelectMenuProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const sourceFieldMetadataItem = useAtomComponentSelectorValue(
|
||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownIsSelectingRelationTargetField =
|
||||
useSetAtomComponentState(
|
||||
objectFilterDropdownIsSelectingRelationTargetFieldComponentState,
|
||||
);
|
||||
|
||||
const { closeAdvancedFilterFieldSelectDropdown } =
|
||||
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||
|
||||
const { applyAdvancedFilterRelationTargetField } =
|
||||
useApplyAdvancedFilterRelationTargetField();
|
||||
|
||||
const { pushFocusForLeafFieldValuePicker } =
|
||||
usePushFocusForLeafFieldValuePicker();
|
||||
|
||||
const { advancedFilterFieldSelectDropdownId } =
|
||||
useAdvancedFilterFieldSelectDropdown(recordFilterId);
|
||||
|
||||
const selectedItemId = useAtomComponentStateValue(
|
||||
selectedItemIdComponentState,
|
||||
advancedFilterFieldSelectDropdownId,
|
||||
);
|
||||
|
||||
const targetObjectMetadataId =
|
||||
isDefined(sourceFieldMetadataItem) &&
|
||||
isManyToOneRelationField(sourceFieldMetadataItem)
|
||||
? sourceFieldMetadataItem.relation.targetObjectMetadata.id
|
||||
: null;
|
||||
|
||||
const { filterableFieldMetadataItems: allTargetFields } =
|
||||
useFilterableFieldMetadataItems(targetObjectMetadataId ?? '');
|
||||
|
||||
// The backend supports a single hop only. Exclude many-to-one relations
|
||||
// from the target list so the user can't compose multi-hop traversals
|
||||
// (e.g. Person → Company → ParentCompany) that the dispatcher would
|
||||
// collapse back to a filter-by-id on the intermediate relation.
|
||||
const relationTargetFields = allTargetFields.filter(
|
||||
(field) => !isManyToOneRelationField(field),
|
||||
);
|
||||
|
||||
if (
|
||||
!isDefined(sourceFieldMetadataItem) ||
|
||||
!isManyToOneRelationField(sourceFieldMetadataItem)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleSubMenuBack = () => {
|
||||
setObjectFilterDropdownIsSelectingRelationTargetField(false);
|
||||
};
|
||||
|
||||
const handleSelectTargetField = (
|
||||
relationTargetFieldMetadataItem: FieldMetadataItem,
|
||||
) => {
|
||||
applyAdvancedFilterRelationTargetField({
|
||||
sourceFieldMetadataItem,
|
||||
relationTargetFieldMetadataItem,
|
||||
recordFilterId,
|
||||
});
|
||||
|
||||
pushFocusForLeafFieldValuePicker(relationTargetFieldMetadataItem);
|
||||
|
||||
setObjectFilterDropdownIsSelectingRelationTargetField(false);
|
||||
closeAdvancedFilterFieldSelectDropdown();
|
||||
};
|
||||
|
||||
const selectableItemIdArray = relationTargetFields.map((field) => field.id);
|
||||
|
||||
return (
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
onClick={handleSubMenuBack}
|
||||
Icon={IconChevronLeft}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{sourceFieldMetadataItem.label}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
focusId={advancedFilterFieldSelectDropdownId}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
||||
>
|
||||
{relationTargetFields.map((targetField, index) => (
|
||||
<SelectableListItem
|
||||
itemId={targetField.id}
|
||||
key={`select-filter-relation-${index}`}
|
||||
onEnter={() => {
|
||||
handleSelectTargetField(targetField);
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === targetField.id}
|
||||
key={`select-filter-relation-${index}`}
|
||||
testId={`select-filter-relation-${index}`}
|
||||
onClick={() => {
|
||||
handleSelectTargetField(targetField);
|
||||
}}
|
||||
text={targetField.label}
|
||||
LeftIcon={getIcon(targetField.icon)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
+10
@@ -6,6 +6,7 @@ import { shouldShowFilterTextInput } from '@/object-record/advanced-filter/utils
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
@@ -55,6 +56,12 @@ export const AdvancedFilterValueInput = ({
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setRelationTargetFieldMetadataIdUsedInDropdown =
|
||||
useSetAtomComponentState(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownCurrentRecordFilter = useSetAtomComponentState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
dropdownId,
|
||||
@@ -74,6 +81,9 @@ export const AdvancedFilterValueInput = ({
|
||||
const handleFilterValueDropdownOpen = () => {
|
||||
setObjectFilterDropdownCurrentRecordFilter(recordFilter);
|
||||
setFieldMetadataItemIdUsedInDropdown(recordFilter.fieldMetadataId);
|
||||
setRelationTargetFieldMetadataIdUsedInDropdown(
|
||||
recordFilter.relationTargetFieldMetadataId ?? null,
|
||||
);
|
||||
};
|
||||
|
||||
const filterType = recordFilter.type;
|
||||
|
||||
+31
-71
@@ -1,31 +1,30 @@
|
||||
import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useGetInitialFilterValue } from '@/object-record/object-filter-dropdown/hooks/useGetInitialFilterValue';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { isCompositeTypeNonFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeNonFilterableByAnySubField';
|
||||
import { type CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type SelectFilterParams = {
|
||||
fieldMetadataItemId: string;
|
||||
type ApplyAdvancedFilterCompositeSubFieldParams = {
|
||||
sourceFieldMetadataItem: FieldMetadataItem;
|
||||
subFieldName: CompositeFieldSubFieldName | null;
|
||||
recordFilterId: string;
|
||||
subFieldName?: CompositeFieldSubFieldName | null | undefined;
|
||||
};
|
||||
|
||||
export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
// Creates an advanced filter from a composite source field together with
|
||||
// the user's chosen sub-field (e.g. address city, name first name). The
|
||||
// filter type is still driven by the source field's composite type.
|
||||
export const useApplyAdvancedFilterCompositeSubField = () => {
|
||||
const setSelectedOperandInDropdown = useSetAtomComponentState(
|
||||
selectedOperandInDropdownComponentState,
|
||||
);
|
||||
@@ -38,54 +37,34 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
objectFilterDropdownSearchInputComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useAtomComponentStateValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const { getFieldMetadataItemByIdOrThrow } =
|
||||
useGetFieldMetadataItemByIdOrThrow();
|
||||
|
||||
const setSubFieldNameUsedInDropdown = useSetAtomComponentState(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setRelationTargetFieldMetadataIdUsedInDropdown =
|
||||
useSetAtomComponentState(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownCurrentRecordFilter = useSetAtomComponentState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useAtomComponentStateValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||
const { getInitialFilterValue } = useGetInitialFilterValue();
|
||||
|
||||
const selectFieldUsedInAdvancedFilterDropdown = ({
|
||||
fieldMetadataItemId,
|
||||
recordFilterId,
|
||||
const applyAdvancedFilterCompositeSubField = ({
|
||||
sourceFieldMetadataItem,
|
||||
subFieldName,
|
||||
}: SelectFilterParams) => {
|
||||
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId);
|
||||
recordFilterId,
|
||||
}: ApplyAdvancedFilterCompositeSubFieldParams) => {
|
||||
setFieldMetadataItemIdUsedInDropdown(sourceFieldMetadataItem.id);
|
||||
|
||||
const { fieldMetadataItem } =
|
||||
getFieldMetadataItemByIdOrThrow(fieldMetadataItemId);
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
fieldMetadataItem.type === 'RELATION' ||
|
||||
fieldMetadataItem.type === 'SELECT'
|
||||
) {
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: fieldMetadataItem.id,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: fieldMetadataItem.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
||||
const filterType = getFilterTypeFromFieldType(sourceFieldMetadataItem.type);
|
||||
|
||||
const firstOperand = getRecordFilterOperands({
|
||||
filterType,
|
||||
@@ -109,30 +88,9 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
(recordFilter) => recordFilter.id === recordFilterId,
|
||||
);
|
||||
|
||||
const isCompositeFilterOnAnySubField =
|
||||
isCompositeFieldType(filterType) && !isDefined(subFieldName);
|
||||
const compositeFilterNonFilterableByAnySubField =
|
||||
isCompositeTypeNonFilterableByAnySubField(filterType);
|
||||
|
||||
let subFieldNameForNonFilterableWithAny:
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined
|
||||
| null = subFieldName;
|
||||
|
||||
if (
|
||||
isCompositeFilterOnAnySubField &&
|
||||
compositeFilterNonFilterableByAnySubField
|
||||
) {
|
||||
subFieldNameForNonFilterableWithAny =
|
||||
getDefaultSubFieldNameForCompositeFilterableFieldType(filterType);
|
||||
}
|
||||
|
||||
const subFieldNameToUse =
|
||||
subFieldName ?? subFieldNameForNonFilterableWithAny;
|
||||
|
||||
const newAdvancedFilter = {
|
||||
id: recordFilterId,
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
fieldMetadataId: sourceFieldMetadataItem.id,
|
||||
displayValue,
|
||||
operand: firstOperand,
|
||||
value,
|
||||
@@ -140,11 +98,13 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
positionInRecordFilterGroup:
|
||||
existingRecordFilter?.positionInRecordFilterGroup,
|
||||
type: filterType,
|
||||
label: fieldMetadataItem.label,
|
||||
subFieldName: subFieldNameToUse,
|
||||
label: sourceFieldMetadataItem.label,
|
||||
subFieldName,
|
||||
relationTargetFieldMetadataId: null,
|
||||
} satisfies RecordFilter;
|
||||
|
||||
setSubFieldNameUsedInDropdown(subFieldNameToUse);
|
||||
setSubFieldNameUsedInDropdown(subFieldName);
|
||||
setRelationTargetFieldMetadataIdUsedInDropdown(null);
|
||||
|
||||
setObjectFilterDropdownSearchInput('');
|
||||
|
||||
@@ -153,6 +113,6 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
selectFieldUsedInAdvancedFilterDropdown,
|
||||
applyAdvancedFilterCompositeSubField,
|
||||
};
|
||||
};
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useGetInitialFilterValue } from '@/object-record/object-filter-dropdown/hooks/useGetInitialFilterValue';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ApplyAdvancedFilterRelationTargetFieldParams = {
|
||||
sourceFieldMetadataItem: FieldMetadataItem;
|
||||
relationTargetFieldMetadataItem: FieldMetadataItem;
|
||||
recordFilterId: string;
|
||||
};
|
||||
|
||||
// Creates an advanced filter that traverses a relation: the source is the
|
||||
// relation field, the filter operates on a target field of the related
|
||||
// object. Filter type comes from the target field; the label combines
|
||||
// both labels so the chip reads "Source → Target".
|
||||
export const useApplyAdvancedFilterRelationTargetField = () => {
|
||||
const setSelectedOperandInDropdown = useSetAtomComponentState(
|
||||
selectedOperandInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setFieldMetadataItemIdUsedInDropdown = useSetAtomComponentState(
|
||||
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownSearchInput = useSetAtomComponentState(
|
||||
objectFilterDropdownSearchInputComponentState,
|
||||
);
|
||||
|
||||
const setSubFieldNameUsedInDropdown = useSetAtomComponentState(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setRelationTargetFieldMetadataIdUsedInDropdown =
|
||||
useSetAtomComponentState(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownCurrentRecordFilter = useSetAtomComponentState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useAtomComponentStateValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||
const { getInitialFilterValue } = useGetInitialFilterValue();
|
||||
|
||||
const applyAdvancedFilterRelationTargetField = ({
|
||||
sourceFieldMetadataItem,
|
||||
relationTargetFieldMetadataItem,
|
||||
recordFilterId,
|
||||
}: ApplyAdvancedFilterRelationTargetFieldParams) => {
|
||||
setFieldMetadataItemIdUsedInDropdown(sourceFieldMetadataItem.id);
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
relationTargetFieldMetadataItem.type,
|
||||
);
|
||||
|
||||
const firstOperand = getRecordFilterOperands({
|
||||
filterType,
|
||||
subFieldName: null,
|
||||
})?.[0];
|
||||
|
||||
if (!isDefined(firstOperand)) {
|
||||
throw new Error(
|
||||
`No valid operand found for relation-traversal filter type: ${filterType}`,
|
||||
);
|
||||
}
|
||||
|
||||
setSelectedOperandInDropdown(firstOperand);
|
||||
|
||||
const { value, displayValue } = getInitialFilterValue(
|
||||
filterType,
|
||||
firstOperand,
|
||||
);
|
||||
|
||||
const existingRecordFilter = currentRecordFilters.find(
|
||||
(recordFilter) => recordFilter.id === recordFilterId,
|
||||
);
|
||||
|
||||
const newAdvancedFilter = {
|
||||
id: recordFilterId,
|
||||
fieldMetadataId: sourceFieldMetadataItem.id,
|
||||
displayValue,
|
||||
operand: firstOperand,
|
||||
value,
|
||||
recordFilterGroupId: existingRecordFilter?.recordFilterGroupId,
|
||||
positionInRecordFilterGroup:
|
||||
existingRecordFilter?.positionInRecordFilterGroup,
|
||||
type: filterType,
|
||||
label: `${sourceFieldMetadataItem.label} → ${relationTargetFieldMetadataItem.label}`,
|
||||
subFieldName: null,
|
||||
relationTargetFieldMetadataId: relationTargetFieldMetadataItem.id,
|
||||
} satisfies RecordFilter;
|
||||
|
||||
setSubFieldNameUsedInDropdown(null);
|
||||
setRelationTargetFieldMetadataIdUsedInDropdown(
|
||||
relationTargetFieldMetadataItem.id,
|
||||
);
|
||||
|
||||
setObjectFilterDropdownSearchInput('');
|
||||
|
||||
setObjectFilterDropdownCurrentRecordFilter(newAdvancedFilter);
|
||||
upsertRecordFilter(newAdvancedFilter);
|
||||
};
|
||||
|
||||
return {
|
||||
applyAdvancedFilterRelationTargetField,
|
||||
};
|
||||
};
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useGetInitialFilterValue } from '@/object-record/object-filter-dropdown/hooks/useGetInitialFilterValue';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ApplyAdvancedFilterSourceFieldParams = {
|
||||
sourceFieldMetadataItem: FieldMetadataItem;
|
||||
recordFilterId: string;
|
||||
};
|
||||
|
||||
// Creates a leaf advanced filter from a source field selection. Composite
|
||||
// and many-to-one relation sources are handled by their own specialized
|
||||
// hooks (useApplyAdvancedFilterCompositeSubField,
|
||||
// useApplyAdvancedFilterRelationTargetField) — callers branch to those
|
||||
// before reaching this hook.
|
||||
export const useApplyAdvancedFilterSourceField = () => {
|
||||
const setSelectedOperandInDropdown = useSetAtomComponentState(
|
||||
selectedOperandInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setFieldMetadataItemIdUsedInDropdown = useSetAtomComponentState(
|
||||
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownSearchInput = useSetAtomComponentState(
|
||||
objectFilterDropdownSearchInputComponentState,
|
||||
);
|
||||
|
||||
const setSubFieldNameUsedInDropdown = useSetAtomComponentState(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setRelationTargetFieldMetadataIdUsedInDropdown =
|
||||
useSetAtomComponentState(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownCurrentRecordFilter = useSetAtomComponentState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useAtomComponentStateValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||
const { getInitialFilterValue } = useGetInitialFilterValue();
|
||||
|
||||
const applyAdvancedFilterSourceField = ({
|
||||
sourceFieldMetadataItem,
|
||||
recordFilterId,
|
||||
}: ApplyAdvancedFilterSourceFieldParams) => {
|
||||
setFieldMetadataItemIdUsedInDropdown(sourceFieldMetadataItem.id);
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(sourceFieldMetadataItem.type);
|
||||
|
||||
const firstOperand = getRecordFilterOperands({
|
||||
filterType,
|
||||
subFieldName: null,
|
||||
})?.[0];
|
||||
|
||||
if (!isDefined(firstOperand)) {
|
||||
throw new Error(`No valid operand found for filter type: ${filterType}`);
|
||||
}
|
||||
|
||||
setSelectedOperandInDropdown(firstOperand);
|
||||
|
||||
const { value, displayValue } = getInitialFilterValue(
|
||||
filterType,
|
||||
firstOperand,
|
||||
);
|
||||
|
||||
const existingRecordFilter = currentRecordFilters.find(
|
||||
(recordFilter) => recordFilter.id === recordFilterId,
|
||||
);
|
||||
|
||||
const newAdvancedFilter = {
|
||||
id: recordFilterId,
|
||||
fieldMetadataId: sourceFieldMetadataItem.id,
|
||||
displayValue,
|
||||
operand: firstOperand,
|
||||
value,
|
||||
recordFilterGroupId: existingRecordFilter?.recordFilterGroupId,
|
||||
positionInRecordFilterGroup:
|
||||
existingRecordFilter?.positionInRecordFilterGroup,
|
||||
type: filterType,
|
||||
label: sourceFieldMetadataItem.label,
|
||||
subFieldName: null,
|
||||
relationTargetFieldMetadataId: null,
|
||||
} satisfies RecordFilter;
|
||||
|
||||
setSubFieldNameUsedInDropdown(null);
|
||||
setRelationTargetFieldMetadataIdUsedInDropdown(null);
|
||||
|
||||
setObjectFilterDropdownSearchInput('');
|
||||
|
||||
setObjectFilterDropdownCurrentRecordFilter(newAdvancedFilter);
|
||||
upsertRecordFilter(newAdvancedFilter);
|
||||
};
|
||||
|
||||
return {
|
||||
applyAdvancedFilterSourceField,
|
||||
};
|
||||
};
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
|
||||
// RELATION and SELECT leaf fields open a value picker keyed by the field
|
||||
// id once the user finishes choosing the field — push it on the focus
|
||||
// stack so the picker's keyboard hotkeys are active when it opens. Other
|
||||
// field types use input-style pickers that don't rely on the focus stack.
|
||||
export const usePushFocusForLeafFieldValuePicker = () => {
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const pushFocusForLeafFieldValuePicker = (
|
||||
fieldMetadataItem: FieldMetadataItem,
|
||||
) => {
|
||||
if (
|
||||
fieldMetadataItem.type !== 'RELATION' &&
|
||||
fieldMetadataItem.type !== 'SELECT'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: fieldMetadataItem.id,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: fieldMetadataItem.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return { pushFocusForLeafFieldValuePicker };
|
||||
};
|
||||
+4
-3
@@ -1,6 +1,7 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { isManyToOneRelationField } from '@/object-metadata/utils/isManyToOneRelationField';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
|
||||
@@ -27,9 +28,9 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
||||
|
||||
const Icon = getIcon(fieldMetadataItemToSelect.icon);
|
||||
|
||||
const shouldShowSubMenu = isCompositeFieldType(
|
||||
fieldMetadataItemToSelect.type,
|
||||
);
|
||||
const shouldShowSubMenu =
|
||||
isCompositeFieldType(fieldMetadataItemToSelect.type) ||
|
||||
isManyToOneRelationField(fieldMetadataItemToSelect);
|
||||
|
||||
const handleClick = () => {
|
||||
resetSelectedItem();
|
||||
|
||||
+33
-6
@@ -1,7 +1,10 @@
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
|
||||
import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
|
||||
@@ -13,6 +16,7 @@ import { DropdownMenuInnerSelect } from '@/ui/layout/dropdown/components/Dropdow
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
|
||||
@@ -32,11 +36,34 @@ export const ObjectFilterDropdownInnerSelectOperandDropdown = () => {
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const operandsForFilterType = isDefined(fieldMetadataItemUsedInDropdown)
|
||||
const relationTargetFieldMetadataIdUsedInDropdown =
|
||||
useAtomComponentStateValue(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
|
||||
// The target field may have been deleted from the workspace since the
|
||||
// filter was saved — return null and let the parent skip rendering
|
||||
// rather than throwing.
|
||||
const relationTargetFieldMetadataItem = isDefined(
|
||||
relationTargetFieldMetadataIdUsedInDropdown,
|
||||
)
|
||||
? getFieldMetadataItemById({
|
||||
fieldMetadataId: relationTargetFieldMetadataIdUsedInDropdown,
|
||||
objectMetadataItems,
|
||||
}).fieldMetadataItem
|
||||
: null;
|
||||
|
||||
const effectiveFieldMetadataItem = isDefined(
|
||||
relationTargetFieldMetadataIdUsedInDropdown,
|
||||
)
|
||||
? relationTargetFieldMetadataItem
|
||||
: fieldMetadataItemUsedInDropdown;
|
||||
|
||||
const operandsForFilterType = isDefined(effectiveFieldMetadataItem)
|
||||
? getRecordFilterOperands({
|
||||
filterType: getFilterTypeFromFieldType(
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
),
|
||||
filterType: getFilterTypeFromFieldType(effectiveFieldMetadataItem.type),
|
||||
subFieldName: subFieldNameUsedInDropdown,
|
||||
})
|
||||
: [];
|
||||
@@ -70,13 +97,13 @@ export const ObjectFilterDropdownInnerSelectOperandDropdown = () => {
|
||||
|
||||
if (
|
||||
!isDefined(selectedOperandInDropdown) ||
|
||||
!isDefined(fieldMetadataItemUsedInDropdown)
|
||||
!isDefined(effectiveFieldMetadataItem)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
effectiveFieldMetadataItem.type,
|
||||
);
|
||||
|
||||
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
|
||||
|
||||
+9
@@ -5,6 +5,7 @@ import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/ob
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||
import { objectFilterDropdownIsSelectingRelationTargetFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingRelationTargetFieldComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
@@ -45,6 +46,12 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const objectFilterDropdownIsSelectingRelationTargetField =
|
||||
useAtomComponentStateCallbackState(
|
||||
objectFilterDropdownIsSelectingRelationTargetFieldComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const objectFilterDropdownCurrentRecordFilter =
|
||||
useAtomComponentStateCallbackState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
@@ -56,6 +63,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
|
||||
store.set(selectedOperandInDropdown, null);
|
||||
store.set(objectFilterDropdownFilterIsSelected, false);
|
||||
store.set(objectFilterDropdownIsSelectingCompositeField, false);
|
||||
store.set(objectFilterDropdownIsSelectingRelationTargetField, false);
|
||||
store.set(fieldMetadataItemIdUsedInDropdown, null);
|
||||
store.set(objectFilterDropdownCurrentRecordFilter, null);
|
||||
store.set(objectFilterDropdownAnyFieldSearchIsSelected, false);
|
||||
@@ -64,6 +72,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
|
||||
selectedOperandInDropdown,
|
||||
objectFilterDropdownFilterIsSelected,
|
||||
objectFilterDropdownIsSelectingCompositeField,
|
||||
objectFilterDropdownIsSelectingRelationTargetField,
|
||||
fieldMetadataItemIdUsedInDropdown,
|
||||
objectFilterDropdownCurrentRecordFilter,
|
||||
objectFilterDropdownAnyFieldSearchIsSelected,
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
|
||||
|
||||
export const objectFilterDropdownIsSelectingRelationTargetFieldComponentState =
|
||||
createAtomComponentState<boolean>({
|
||||
key: 'objectFilterDropdownIsSelectingRelationTargetFieldComponentState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
|
||||
});
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||
import { type CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType';
|
||||
import { type ObjectFilterDropdownSubMenuFieldType } from '@/object-record/record-filter/types/ObjectFilterDropdownSubMenuFieldType';
|
||||
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
|
||||
|
||||
export const objectFilterDropdownSubMenuFieldTypeComponentState =
|
||||
createAtomComponentState<CompositeFilterableFieldType | null>({
|
||||
createAtomComponentState<ObjectFilterDropdownSubMenuFieldType | null>({
|
||||
key: 'objectFilterDropdownSubMenuFieldTypeComponentState',
|
||||
defaultValue: null,
|
||||
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
|
||||
|
||||
export const relationTargetFieldMetadataIdUsedInDropdownComponentState =
|
||||
createAtomComponentState<string | null>({
|
||||
key: 'relationTargetFieldMetadataIdUsedInDropdownComponentState',
|
||||
defaultValue: null,
|
||||
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
|
||||
});
|
||||
+19
-6
@@ -1,3 +1,4 @@
|
||||
import { flattenedFieldMetadataItemsSelector } from '@/object-metadata/states/flattenedFieldMetadataItemsSelector';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarMonthDaysRange } from '@/object-record/record-calendar/month/hooks/useRecordCalendarMonthDaysRange';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
@@ -6,8 +7,10 @@ import { anyFieldFilterValueComponentState } from '@/object-record/record-filter
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { augmentFieldsWithRelationTargets } from '@/object-record/record-filter/utils/augmentFieldsWithRelationTargets';
|
||||
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type Temporal } from 'temporal-polyfill';
|
||||
@@ -46,6 +49,10 @@ export const useRecordCalendarQueryDateRangeFilter = (
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const flattenedFieldMetadataItems = useAtomStateValue(
|
||||
flattenedFieldMetadataItemsSelector,
|
||||
);
|
||||
|
||||
const anyFieldFilterValue = useAtomComponentStateValue(
|
||||
anyFieldFilterValueComponentState,
|
||||
viewBarInstanceId,
|
||||
@@ -94,15 +101,21 @@ export const useRecordCalendarQueryDateRangeFilter = (
|
||||
displayValue: `${lastDayOfLastWeek.toString()}`,
|
||||
};
|
||||
|
||||
const calendarRecordFilters = [
|
||||
...currentRecordFilters,
|
||||
dateRangeFilterAfter,
|
||||
dateRangeFilterBefore,
|
||||
];
|
||||
|
||||
const dateRangeFilter = computeRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilters: [
|
||||
...currentRecordFilters,
|
||||
dateRangeFilterAfter,
|
||||
dateRangeFilterBefore,
|
||||
],
|
||||
recordFilters: calendarRecordFilters,
|
||||
recordFilterGroups: currentRecordFilterGroups,
|
||||
fields: objectMetadataItem.fields,
|
||||
fields: augmentFieldsWithRelationTargets({
|
||||
baseFields: objectMetadataItem.fields,
|
||||
recordFilters: calendarRecordFilters,
|
||||
allFieldMetadataItems: flattenedFieldMetadataItems,
|
||||
}),
|
||||
});
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
|
||||
+2
-1
@@ -156,7 +156,8 @@ export const useGetRecordFilterDisplayValue = () => {
|
||||
}
|
||||
|
||||
const { fieldMetadataItem } = getFieldMetadataItemByIdOrThrow(
|
||||
recordFilter.fieldMetadataId,
|
||||
recordFilter.relationTargetFieldMetadataId ??
|
||||
recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
const fieldMetadataItemOptions = fieldMetadataItem.options;
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { type CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType';
|
||||
|
||||
export type ObjectFilterDropdownSubMenuFieldType = CompositeFilterableFieldType;
|
||||
@@ -24,7 +24,7 @@ export type RecordFilter = {
|
||||
positionInRecordFilterGroup?: number | null;
|
||||
label: string;
|
||||
subFieldName?: CompositeFieldSubFieldName | null | undefined;
|
||||
// RLS-specific: when set, filter compares against current user's field value
|
||||
relationTargetFieldMetadataId?: string | null;
|
||||
rlsDynamicValue?: RLSDynamicValue | null;
|
||||
};
|
||||
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
// Relation-traversal filters reference target fields that live on a
|
||||
// different object than the one being queried, so they aren't in the
|
||||
// source object's own field list. The shared GraphQL filter dispatcher
|
||||
// looks the target up by id in `fieldMetadataItems` and drops the filter
|
||||
// when it can't find it — this helper merges the resolved target fields
|
||||
// in from the workspace-wide flat list so callers don't silently lose
|
||||
// relation-traversal filters.
|
||||
export const augmentFieldsWithRelationTargets = ({
|
||||
baseFields,
|
||||
recordFilters,
|
||||
allFieldMetadataItems,
|
||||
}: {
|
||||
baseFields: FieldMetadataItem[];
|
||||
recordFilters: Pick<RecordFilter, 'relationTargetFieldMetadataId'>[];
|
||||
allFieldMetadataItems: FieldMetadataItem[];
|
||||
}): FieldMetadataItem[] => {
|
||||
const targetFieldIds = new Set(
|
||||
recordFilters
|
||||
.map((filter) => filter.relationTargetFieldMetadataId)
|
||||
.filter(isDefined),
|
||||
);
|
||||
|
||||
if (targetFieldIds.size === 0) {
|
||||
return baseFields;
|
||||
}
|
||||
|
||||
const baseFieldIds = new Set(baseFields.map((field) => field.id));
|
||||
const additionalTargetFields = allFieldMetadataItems.filter(
|
||||
(field) => targetFieldIds.has(field.id) && !baseFieldIds.has(field.id),
|
||||
);
|
||||
|
||||
if (additionalTargetFields.length === 0) {
|
||||
return baseFields;
|
||||
}
|
||||
|
||||
return [...baseFields, ...additionalTargetFields];
|
||||
};
|
||||
+7
@@ -6,11 +6,13 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { flattenedFieldMetadataItemsSelector } from '@/object-metadata/states/flattenedFieldMetadataItemsSelector';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -52,11 +54,16 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const flattenedFieldMetadataItems = useAtomStateValue(
|
||||
flattenedFieldMetadataItemsSelector,
|
||||
);
|
||||
|
||||
const computedFilter = computeContextStoreFilters({
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
contextStoreFilterGroups,
|
||||
objectMetadataItem,
|
||||
flattenedFieldMetadataItems,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
});
|
||||
|
||||
+7
@@ -7,6 +7,7 @@ import { contextStoreFilterGroupsComponentState } from '@/context-store/states/c
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { flattenedFieldMetadataItemsSelector } from '@/object-metadata/states/flattenedFieldMetadataItemsSelector';
|
||||
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize';
|
||||
@@ -18,6 +19,7 @@ import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -87,6 +89,10 @@ export const useRecordIndexLazyFetchRecords = ({
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const flattenedFieldMetadataItems = useAtomStateValue(
|
||||
flattenedFieldMetadataItemsSelector,
|
||||
);
|
||||
|
||||
const findManyRecordsParams = useFindManyRecordIndexTableParams(
|
||||
objectMetadataItem.nameSingular,
|
||||
recordIndexId,
|
||||
@@ -101,6 +107,7 @@ export const useRecordIndexLazyFetchRecords = ({
|
||||
contextStoreFilters,
|
||||
contextStoreFilterGroups,
|
||||
objectMetadataItem,
|
||||
flattenedFieldMetadataItems,
|
||||
filterValueDependencies,
|
||||
contextStoreAnyFieldFilterValue,
|
||||
});
|
||||
|
||||
+12
-1
@@ -1,14 +1,17 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { flattenedFieldMetadataItemsSelector } from '@/object-metadata/states/flattenedFieldMetadataItemsSelector';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { augmentFieldsWithRelationTargets } from '@/object-record/record-filter/utils/augmentFieldsWithRelationTargets';
|
||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import {
|
||||
combineFilters,
|
||||
computeRecordGqlOperationFilter,
|
||||
@@ -47,8 +50,16 @@ export const useFindManyRecordIndexTableParams = (
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const flattenedFieldMetadataItems = useAtomStateValue(
|
||||
flattenedFieldMetadataItemsSelector,
|
||||
);
|
||||
|
||||
const currentFilters = computeRecordGqlOperationFilter({
|
||||
fields: objectMetadataItem?.fields ?? [],
|
||||
fields: augmentFieldsWithRelationTargets({
|
||||
baseFields: objectMetadataItem?.fields ?? [],
|
||||
recordFilters: currentRecordFilters,
|
||||
allFieldMetadataItems: flattenedFieldMetadataItems,
|
||||
}),
|
||||
recordFilterGroups: currentRecordFilterGroups,
|
||||
recordFilters: currentRecordFilters,
|
||||
filterValueDependencies,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user