Compare commits

..
67 Commits
Author SHA1 Message Date
Charles BochetandGitHub 83fc434c5d Fix workspace invite onboarding loop (#16444)
- The issue is that the listener is not triggered if they are no change
to the workspaceMember (which happens when name is already filled
through GoogleSSO)

Fixes https://github.com/twentyhq/twenty/issues/16440
2025-12-09 19:18:17 +01:00
nitinandGitHub 3e9820fa8d fix line chart's no data calculations and update tests (#16441)
closes
https://discord.com/channels/1130383047699738754/1447976382528360520
2025-12-09 23:03:32 +05:30
adb38631b3 i18n - docs translations (#16435)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-09 17:22:08 +01:00
Charles BochetandGitHub aa519e7d81 Fix messaging errors on message folder list (#16404)
This is a known quirk of many Google APIs, including Gmail. Some error
responses use numeric code fields (e.g., 403), while others may return
them as strings (e.g., "403"). This depends on which internal service
returns the error and the language client you’re using.
2025-12-09 17:01:25 +01:00
Charles BochetandGitHub a18203934c Fix flat entity maps date serialization (#16420)
Changes:
- as we store date in redis as serialized, let's make all flatEntity
dates as string. This requires changing FlatEntity types and making sure
that entity are converted to flatEntity and flatEntity to dtos
2025-12-09 16:41:50 +01:00
608e5751a9 i18n - translations (#16433)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-09 16:21:37 +01:00
nitinandGitHub 7af3da9393 [Dashboards] add ratio aggregate on aggregate chart (#16413)
closes https://github.com/twentyhq/core-team-issues/issues/1960
video QA


https://github.com/user-attachments/assets/e90cee44-5edc-430d-b003-03ab4ea921f7
2025-12-09 15:23:21 +01:00
Thomas TrompetteandGitHub bd49fd8d5f Remove object relation schema from step output (#16428)
Fix https://github.com/twentyhq/core-team-issues/issues/1931

On trigger database event and step record crud, we still display the
full object for relation in the variable picker (Account Owner in
screenshot).
<img width="259" height="189" alt="Capture d’écran 2025-12-09 à 14 43
06"
src="https://github.com/user-attachments/assets/1d7fdebf-9183-4197-929a-7b7d719c465a"
/>

But as we do not enrich the data anymore, those fields are actually
empty. We should instead display AccountOwnerId and, if the user needs
the full Account owner, a search can be used.
<img width="259" height="189" alt="Capture d’écran 2025-12-09 à 14 42
37"
src="https://github.com/user-attachments/assets/87b46762-3204-4d25-9b6a-077d6b8567bd"
/>
2025-12-09 14:10:12 +00:00
Paul RastoinandGitHub e758f78caf Finalize standard index as code declaration (#16429)
# Introduction
Following https://github.com/twentyhq/twenty/pull/16426
Related https://github.com/twentyhq/core-team-issues/issues/1995
2025-12-09 14:05:27 +00:00
Thomas TrompetteandGitHub 3ba7a070d9 Fix is empty and non empty filter for numbers (#16424)
isNonEmptyString does not work for a number. We need to properly handle
null and undefined
2025-12-09 14:55:14 +01:00
Paul RastoinandGitHub e8d4f11d8e Standard index as code introduction + type refactor (#16426)
# Introduction
Related https://github.com/twentyhq/core-team-issues/issues/1995

In this PR we're introducing the standard index tools for their
declaration
Also adding a common typing in order to have a simily consistency across
standard builders

Will implement remaining standard index builder in an other PR
2025-12-09 14:52:25 +01:00
Paul RastoinandGitHub 4996f3dd28 Finalize twenty standard app as workspace migration object and fields (#16353)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1995
In this PR we're fixing the remaining object/fields validation errors
resulting from standard objects and fields now passing a validation that
wasn't when using the sync metadata

## Key Changes
- **Field naming**: Renamed `iCalUID` to `iCalUid` for consistent
camelCase convention across calendar events
- **Enum standardization**: Uppercased enum values for message channels
(email→EMAIL), message participants (from→FROM, to→TO, cc→CC, bcc→BCC),
and message direction (incoming→INCOMING, outgoing→OUTGOING)
- **Label simplification**: Removed example values from workspace member
number format labels for cleaner UI
- **Migration infrastructure**: Added `isSystemBuild` flag throughout
field metadata service pipeline to allow system-level updates of
standard fields that bypass normal restrictions

## Migrating the existing data
We've created an upgrade command that will identify using the existing
object and field standard id field that needs to be updated, even though
the sync metadata still in usage could have fix them ( and the goal is
to deprecate it by the end of the sprint )
We will call the updateOneField for each of them, we're passing by the
field service in order to battle test what are going to be the temporary
way to handle standard migrations when we will start deprecating the
sync metadata but haven't still refactored the v2 workspace migration to
be workspace agnostic

## Twenty eng migration
Tested the whole migration + upgrade on twenty eng
Here are generated workspace migration
Records are handled natively gracefully too

### ICalUid
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "iCalUID",
            "to": "iCalUid",
            "property": "name"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Incoming Outgoing
None as already caps in database somehow
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [],
    "workspaceId": ""
  }
}
```

### EMAIL
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'email'",
            "to": "'EMAIL'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "email"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "sms"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "EMAIL"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "SMS"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": "e"
  }
}
```

### MessageParticipantRole
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'from'",
            "to": "'FROM'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "from"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "to"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "cc"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "bcc"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "FROM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "TO"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "CC"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "BCC"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Workspace member number format labels
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot (1,234.56)",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma (1 234,56)",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma (1.234,56)",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot (1'234.56)",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "to": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```
2025-12-09 14:09:48 +01:00
EtienneandGitHub be91d870f9 Fix - revert standard sync logic on index + fix command (#16421) 2025-12-09 12:54:01 +00:00
2009112712 i18n - docs translations (#16423)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-09 13:23:10 +01:00
82ee097bdc i18n - translations (#16422)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-09 12:32:54 +01:00
e5fea85351 fix : slash menu overflow causing page scroll (#16303)
Issue : #15794 

## Context
As shown in the linked issue, slash menu dropdown for rich text editor
at bottom of notes editor screen extended beyond the page container,
causing main page to scroll.

## Changes Made
Added floating UI's `flip()` middleware to the component in
`CustomSlashMenu.tsx` so that the menu automatically flips upward when
detecting not enough space for dropdown. This prevents overflow beyond
the page container.

Additionally, added offset to menu when flipping up to prevent
overlapping of the current line / caret.

## Results


https://github.com/user-attachments/assets/47a79468-779c-4fd7-8e22-2b496456ea92

---------

Co-authored-by: Hyeonjae Park <hyeonjaep@Hyeonjaes-MacBook-Pro.local>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2025-12-09 10:52:58 +00:00
nitinandGitHub 7e8151355d [Dashboards] add No data layer on charts (#16397)
video qa


https://github.com/user-attachments/assets/01c790de-eae7-406e-9308-af036f53ab2e
2025-12-09 10:47:42 +00:00
MarieandGitHub 790a8d6f93 Fixes on views (#16407)
Fixes

- Fixes https://github.com/twentyhq/twenty/issues/15640 : we have some
viewGroups that were in the past wrongly migrated; eg deleteing an
enum's option did not lead to deleting the associated viewGroup. We have
not cleaned that (we could do a command), but we can identify them and
choose not to display them - otherwise currently they are shown as a
duplicate of "No value" column
- Fixes buggy edge case introduced by
https://github.com/twentyhq/twenty/pull/16382 : after updating a view
from Table/Calendar to Kanban, then visiting another view, when coming
back to the newly updated kanban view the view groups were empty. This
is due to to the view groups being optimistically created with
on-the-fly computed ids when the view is updated, while we call
refreshCoreViewsByObjectMetadataId() after. The view groups we get from
the refresh obviously have different ids. It is not possible to indicate
the ids of the view group through the update of the view as they are a
side effect of the view update.
2025-12-09 10:42:00 +00:00
EtienneandGitHub ec2b6ae691 Fix storybook test (#16416) 2025-12-09 10:29:23 +00:00
ae2919e564 fix: migrate wildcard routes to named parameters (#16380)
Closes #16189

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-12-09 09:52:32 +01:00
Alex GaleyandGitHub b578ea5951 Fireflies app reached beta : utilities to ingest one or many meetings - No production webhook without headers forwarding (#16410)
# Fireflies
Automatically (with cli util : webhook not working and no cron added)
captures meeting notes with AI-generated summaries and insights from
Fireflies.ai into your Twenty CRM.

## Current Status
- Doesn't work with Fireflies webhook yet due to missing headers
forwarding in twenty serverless func
- Meeting ingestion utility script are available for individual meeting
insertion and historical meetings with filters with yarn meeting:all
- You have to push the secrets as application.config.ts values (despite
env variables in .env in docker compose or container I couldn't push the
secrets with the cli)

## Current Platform Limitation (Headers)

- Twenty serverless route triggers currently do **not forward HTTP
headers** to functions. Fireflies signatures sent in headers are
stripped, so header-based verification does not work in production.
- Workaround: the provided test script also includes the signature
inside the payload; the handler falls back to that payload signature.
Use this only for testing until header forwarding is supported.

## Utilities for meeting insertion (workarounds)

- Ingest a specific Fireflies meeting into Twenty:
`yarn meeting:ingest <meetingId>` or `MEETING_ID=... yarn
meeting:ingest`

- Fetch all/historical Fireflies meetings into Twenty:
`yarn meeting:all [--from 2024-01-01] [--to 2024-02-01] [--organizer
a@x.com] [--participant b@x.com] [--channel <channelId>] [--mine]
[--dry-run]`

  - Filters (combine as needed):
    - `--from` / `--to`: ISO or date string range filter
    - `--organizer` / `--participant`: comma-separated emails
    - `--channel`: Fireflies channel id
    - `--mine`: only meetings for the current Fireflies user
  - Controls:
    - `--dry-run`: list and transform without writing to Twenty
    - `--page-size`: pagination size (default 50)
    - `--max-records`: stop after N transcripts (default 500)
    
    
    I am closing previous #16378  as this one includes it all
2025-12-09 09:27:01 +01:00
95c69507aa i18n - docs translations (#16409)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 21:21:35 +01:00
0b8984116b i18n - translations (#16408)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 20:20:32 +01:00
Abdul RahmanandGitHub 77e592502c fix: improve CRON schedule validation and display (#16360)
Fixes multiple issues with CRON schedule input validation and execution
time display.

### Issues Fixed

1. **UTC label placement** - Added "UTC" suffix to specific times (e.g.,
"at 09:30 UTC") but not to interval descriptions (e.g., "every hour")

2. **Upcoming execution time calculation** - Fixed incorrect execution
times for malformed CRON expressions by implementing auto-correction

### Changes

- Created `normalizeCronExpression` utility to standardize cron
expressions before parsing
- Updated `formatTime` to support optional UTC suffix
- Enhanced `getHoursDescription` to append UTC to specific times
- Added comprehensive test coverage (102 tests passing)

### Before

- `"1 /3 * * *"` showed daily executions at same time (incorrect)
- `"9 * * *"` showed same time repeated 3 times (incorrect)
- No UTC labels on schedule descriptions (confusing)

### After

- All malformed expressions auto-corrected and show correct execution
times
- UTC labels clearly indicate timezone for specific times
- User-friendly error messages for truly invalid patterns

Closes #15870
2025-12-09 00:09:11 +05:30
f80b2f2c38 i18n - docs translations (#16406)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 19:22:30 +01:00
07cfaa78ef Fix unique standard field (#16371)
Fixes https://github.com/twentyhq/twenty/issues/15925

- update field metadata update logic
- uniformize the way index are named
- command to migrate v1-named unique index
- add integration testing

---------

Co-authored-by: prastoin <paul@twenty.com>
2025-12-08 18:05:28 +00:00
950f452fef i18n - translations (#16405)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 19:01:09 +01:00
4fe9e57847 fix(http-request-action): pretty-print JSON errors in test HTTP requeset hook (#16387)
Before:
<img width="415" height="319" alt="Screenshot from 2025-12-08 09-00-48"
src="https://github.com/user-attachments/assets/bd99cf74-d278-4344-a3c2-e7740d7d058b"
/>

After:
<img width="399" height="371" alt="Screenshot from 2025-12-08 08-59-05"
src="https://github.com/user-attachments/assets/5c75bded-1632-4a9a-9b51-ed3c83f36b3b"
/>

fixes #16243

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-12-08 18:23:55 +01:00
Lucky GoyalandGitHub d7dbb87a8f use micros converters in FormCurrencyFieldInput (#16330)
Fixes: #15887

The currency fields were expecting values in micros (multiplied by
10^6), but
this requirement was not indicated anywhere in the workflow UI. Users
had to
manually add a code node to multiply amounts by 10^6 before using the
new
currency value.

This change integrates the micros conversion logic directly into
FormCurrencyFieldInput by using convertCurrencyAmountToCurrencyMicros
and
convertCurrencyMicrosToCurrencyAmount utilities. This allows users to
input
human-readable decimal amounts (e.g., 3.21 for $3.21) in the form, which
are
automatically converted to micros internally for storage and processing.

This eliminates the need for users to add separate code nodes for
conversion
and improves the user experience by making the expected input format
clear.
2025-12-08 17:50:50 +01:00
9efeb180d6 i18n - docs translations (#16402)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 17:22:01 +01:00
Thomas TrompetteandGitHub 1c14567bf1 Workflow statuses update on record table - use cache instead of web sockets (#16391)
Updated the 3 hooks - activate, deactivate and discard draft
(deleteVersion) - so those updates the cache.
Removed the update listeners.

Also wrapped a few actions to unsure workflowId is not undefined when
received by useWorkflowWithCurrentVersion hook. Otherwise, useFindRecord
with by performed with skip true, disconnecting tmp from the cache and
making it miss a statuses update.



https://github.com/user-attachments/assets/5fc355e8-cf18-4881-855b-744eb253b79e
2025-12-08 17:17:13 +01:00
Thomas TrompetteandGitHub ac89b5aff6 Improve object record changed performances (#16398)
Using deepEqual, average over 3 calls:
- version trigger 1.53ms
- version steps 38.3ms
- run state 372.8 ms

Using stringified hash, average over 3 calls:
- version trigger 0.07ms
- version steps 0.74 ms
- run state 0.521ms

Short fastDeepEqual
- version trigger 0.028ms
- version steps 0.197ms
- run state 0.214ms

Lib fastDeepEqual
- version trigger 0.038ms
- version steps 0.187ms
- run state 0.230ms
2025-12-08 17:06:53 +01:00
WeikoandGitHub 859004f4fc Refactor global datasource part 2 (#16399)
## Context
Deprecating TwentyORMManager in favor of TwentyORMGlobalManager
(temporarily, as this will simplify the ultimate goal to later replace
all usages with the new TwentyORMGlobalManagerV2 which will have a
similar signature)
This means this PR had to refactor a bit of code to pass down the
workspaceId when not available directly as it is now a requirement,
meaning we also deprecated scopedWorkspaceContextFactory to have a less
obscure way to fetch the workspaceId and have something more
declarative.

Step 3 will be to update TwentyORMGlobalManager to use a featureFlag
toggling and use the new GlobalWorkspaceOrmManager internally using the
new cache service

Step 4 will be to remove the feature flag and pg_pool patch
2025-12-08 17:05:00 +01:00
c2d3fbcd21 i18n - translations (#16400)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 15:49:13 +01:00
Raphaël BosiandGitHub 5fb7e76005 Migrate page layout to v2 (#16364) 2025-12-08 14:11:24 +00:00
MarieandGitHub 86df188e83 [fix] Fix switch view type from "Layout" switcher (#16382)
In this previous work aiming at eliminating the creation of viewGroups
being separated from the views, I removed the creation of viewGroups
from the FE at view creation, but forgot to do it at view update when
the user chooses "change Layout" option (from Table/Kanban/Calendar to
another).

In this PR
- I removed creation, deletion and destroy of viewGroups from
usePersistViewGroupRecords. I left update because it useful for
visibility and position
- since usePersistViewGroups record was also handling optimistic, I
moved optimistic logic to a new hook useViewsSideEffectsOnViewGroups. I
realized that so far we optimistic is only useful for creation. For the
update though we do need to compute the view groups that will be created
to be able to call loadRecordIndexState, so I created a function for
that, but I did not seek to perform real optimistic, with writing and
deleting groups from the cache as it was a bit heavier and not in use
for now and I want to merge this asap as it fixes the creation of
viewGroups.
2025-12-08 15:05:05 +01:00
Charles BochetandGitHub 2790d5dd93 Message fixes (#16389)
In this PR:
- add more logs to troubleshoot issues in production
- use messageChannelSyncStatus in messageSync service instead of making
a manual update
2025-12-08 14:56:43 +01:00
neo773andGitHub 5f4c7e016c refactor imap gql resolver (#16375) 2025-12-08 14:54:24 +01:00
Baptiste DevessierandGitHub 51c0a8dd86 [Page layouts] Sync tabs with URL hash (#16341)
There are three identified scenarios.

## First scenario

The user uses a desktop. They click on a company, which is opened in the
side panel. They click on a tab, like "Tasks". The user chooses to open
the record in fullscreen. The "Tasks" tab is opened by default in the
fullscreen record page.

If the user refreshes the page, the default tab is shown: "Timeline".
**This was the behavior on the show pages, and I kept it.** If we wanted
to show the "Tasks" tab here, we would have to put the id of the "Tasks"
tab in the URL hash when we open the record in fullscreen and an active
tab id is already set.


https://github.com/user-attachments/assets/205776ac-9ad7-4e79-af9a-6b44360a5d77

## Second scenario

The user uses a desktop or a mobile device and interacts with a company
record. They click on a tab. If they refresh, the selected tab will be
displayed by default.

### Desktop


https://github.com/user-attachments/assets/d4e1908b-a591-42f6-bb82-5aec592e2778

### Mobile


https://github.com/user-attachments/assets/7bab73cc-a2ff-4ce8-9bc6-325efef2d500

## Third scenario

The user uses a desktop. They click on a dashboard, which is opened in
fullscreen mode by default. They select tabs. If they refresh, the last
selected tab is opened by default.

When the user turns edit mode on, **the url hash is removed**. The last
selected tab remains selected. If the user selects another tab, the url
hash isn't set, but the newly selected tab is displayed correctly. If
the user saves their changes, the selected tab remains selected.
However, if they refresh the page, the default tab replaces the
previously selected tab.

Not setting the url hash when editing a page layout simplifies the code.
I'm okay with this tradeoff as the feature is primarily meant to let
users share specific tabs of their records.

**This behavior will also be used for record page layout once edit mode
is supported.**


https://github.com/user-attachments/assets/869657a1-6895-4ade-816c-972c77aaab9e

Closes https://github.com/twentyhq/core-team-issues/issues/1784
2025-12-08 10:47:46 +01:00
martmullandGitHub 7f1e69740a 1895 extensibility v1 application tokens (#16365)
First PR to implement application tokens
- add new application role in twenty-server
- move duplicated constants and types to twenty-shared
- will  add role configuration utils into twenty-sdk in another PR
2025-12-08 10:42:46 +01:00
Félix MalfaitandGitHub 1f9a6b067a fix: make impersonation audit logging non-blocking (#16390)
## Summary

This PR makes the Clickhouse audit logging in the impersonation flow
**non-blocking** (fire-and-forget).

## Problem

When the Clickhouse server is unavailable or slow, the impersonation
feature becomes blocked because all audit logging calls use `await`,
causing the entire impersonation flow to hang.

## Solution

Remove the `await` keyword from all audit logging calls in the
impersonation flow. The `ClickHouseService.insert()` method already
handles errors internally (catches and logs them), so there's no risk of
unhandled promise rejections.

## Changes

- **`impersonation.service.ts`**: 4 audit calls made non-blocking
- **`auth.resolver.ts`**: 5 audit calls made non-blocking  
- **`auth.service.ts`**: 2 audit calls made non-blocking

## Testing

- Impersonation flow continues to work when Clickhouse is available
- Impersonation flow no longer blocks when Clickhouse is unavailable
2025-12-08 08:48:44 +00:00
Paul RastoinandGitHub 3e57aa14d3 Refactor page layout tab and widgets migration (#16367)
# Introduction
Making columns nullable instead of required + metadata on the fly
migration in typeorm migration that could affect other breaking change
migrations to be run
2025-12-06 10:23:24 +01:00
Félix MalfaitandGitHub 223082a4da refactor(twenty-server): consolidate AI tool provider architecture (#16355)
## Summary

Consolidates the AI tool provider architecture by creating a single
`ToolProviderService` as the entry point for all tool generation. This
removes multiple intermediate services and simplifies the codebase.

## Changes

### New Architecture
- **`ToolProviderService`**: Single service for all tool generation
with:
  - `getTools(spec)` - Get tools by category with permissions
  - `getToolByType(type)` - Get specific tool for workflow execution
  
- **`ToolCategory` enum**: Declarative specification of tool types:
  - `DATABASE_CRUD` - Record CRUD operations
  - `ACTION` - HTTP requests, email sending, article search
  - `WORKFLOW` - Workflow management tools
  - `METADATA` - Object/field metadata tools
  - `NATIVE_MODEL` - Model-specific tools (e.g., web search)

- **`ToolSpecification` type**: Clean API for requesting tools with
permissions

### Removed
- `AiToolsModule` - No longer needed
- `ToolService` - Logic inlined into ToolProviderService
- `ToolAdapterService` - Logic inlined into ToolProviderService
- `ToolRegistryService` - Logic inlined into ToolProviderService

### Updated
- All consumers (agents, chat, MCP, workflows) now use
`ToolProviderService`
- Test files updated accordingly

## Stats
- **547 insertions, 1146 deletions** (net ~600 lines removed)
- 4 services deleted
- 1 module deleted

## Testing
- [x] Typecheck passes
- [x] Lint passes
2025-12-06 06:51:47 +01:00
nitinandGitHub 43ed4963d6 [Dashboards] Improve tooltip animations (#16357)
before - 


https://github.com/user-attachments/assets/069a8737-fcc9-4186-bdbf-e9ed0dfa41a8


after - 


https://github.com/user-attachments/assets/dbc8604c-dda0-46a9-a67b-f59cc91d7f53
2025-12-05 18:28:26 +01:00
Abdul RahmanandGitHub b706664c99 Show add step button on trigger node without hover (#16361)
https://github.com/user-attachments/assets/3407bbea-356b-4508-b931-27b45818a339

Closes #14136
2025-12-05 18:28:13 +01:00
Paul RastoinandGitHub c8f541618d Refactor validate build and run for configuration to be less verbose and more reliable (#16343)
# Introduction
Refactored the api `validateBuildAndRunWorkspaceMigration` to be require
less configuration but to infer required args dynamically depending on
provided metadata maps to compare

## `inferDeletionFromMissingEntities`
Is not dynamically computed avoiding any miss configuration issue and
any missleading devxp

## Maps computation

Making only one call to redis to build both dependency and to be
compared entity maps. It does not matter to avoid passing a about to
compared flat entity maps to could also be a depedency, it's handled
directly in the builder setup optimistic cache logic

Please note that the flat maps used for the service input transpilation
might differ from the one that we will dynamically compute and inject in
the builder. Leading to do 2 redis calls but also race condition prone
validation error
We prefer that this occurs at the builder rather than at the runner
level as the pg instance is not cache and reflect the real state of a
given workspace
In a nutshell, there's a possible race condition between cache
invalidation and computation in both service input transpilers and
builder but we're totally ok with that
2025-12-05 15:41:56 +00:00
fee3e6b7a1 fix folder actions (#16344)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-12-05 16:19:50 +01:00
Thomas TrompetteandGitHub de4e78e5b2 Fix workflow relation variables (#16362)
display many to one relations instead of one to many
2025-12-05 16:11:37 +01:00
Baptiste DevessierandGitHub 2466d81ace [Record Page Layouts] Pin record header (#16339)
https://github.com/user-attachments/assets/df988bf2-d2d6-49a4-b062-bc7ec49e1fad

Closes https://github.com/twentyhq/core-team-issues/issues/1806
2025-12-05 16:10:08 +01:00
MarieandGitHub 58b8b49d1d Fix flaky storybook test (#16358)
RecordTable story was flaky with this kind of error
[(1)](https://www.apollographql.com/docs/react/errors#%7B%22version%22%3A%223.13.8%22%2C%22message%22%3A13%2C%22args%22%3A%5B%22addressStreet2%22%2C%22%7B%5Cn%20%20%5C%22__typename%5C%22%3A%20%5C%22Address%5C%22%2C%5Cn%20%20%5C%22addressLine1%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressLine2%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressCity%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressState%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressCountry%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressPostcode%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%5C%22addressLat%5C%22%3A%20null%2C%5Cn%20%20%5C%22addressLng%5C%22%3A%20null%5Cn%7D%22%5D%7D):
_Missing field 'addressStreet2' while writing result { "typename":
"Address", "addressLine1": "", "addressLine2": "", "addressCity": "",
"addressState": "", "addressCountry": "", "addressPostcode": "",
"addressLat": null, "addressLng": null }_ or
[(2)](https://www.apollographql.com/docs/react/errors#%7B%22version%22%3A%223.13.8%22%2C%22message%22%3A13%2C%22args%22%3A%5B%22deletedAt%22%2C%22%7B%5Cn%20%20%5C%22__typename%5C%22%3A%20%5C%22Company%5C%22%2C%5Cn%20%20%5C%22id%5C%22%3A%20%5C%2220202020-5d81-46d6-bf83-f7fd33ea6102%5C%22%2C%5Cn%20%20%5C%22employees%5C%22%3A%20null%2C%5Cn%20%20%5C%22createdAt%5C%22%3A%20%5C%222025-01-06T08%3A30%3A15.412Z%5C%22%2C%5Cn%20%20%5C%22updatedAt%5C%22%3A%20%5C%222025-01-06T14%3A45%3A22.412Z%5C%22%2C%5Cn%20%20%5C%22name%5C%22%3A%20%5C%22Facebook%5C%22%2C%5Cn%20%20%5C%22idealCustomerProfile%5C%22%3A%20false%2C%5Cn%20%20%5C%22accountOwner%5C%22%3A%20null%2C%5Cn%20%20%5C%22accountOwnerId%5C%22%3A%20null%2C%5Cn%20%20%5C%22domainName%5C%22%3A%20%7B%5Cn%20%20%20%20%5C%22__typename%5C%22%3A%20%5C%22Links%5C%22%2C%5Cn%20%20%20%20%5C%22primaryLinkUrl%5C%22%3A%20%5C%22https%3A%2F%2Ffacebook.com%5C%22%2C%5Cn%20%20%20%20%5C%22primaryLinkLabel%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22secondaryLinks%5C%22%3A%20%5B%5D%5Cn%20%20%7D%2C%5Cn%20%20%5C%22address%5C%22%3A%20%7B%5Cn%20%20%20%20%5C%22addressStreet1%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressStreet2%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressCity%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressState%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressCountry%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressPostcode%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22addressLat%5C%22%3A%20null%2C%5Cn%20%20%20%20%5C%22addressLng%5C%22%3A%20null%5Cn%20%20%7D%2C%5Cn%20%20%5C%22previousEmployees%5C%22%3A%20null%2C%5Cn%20%20%5C%22annualRecurringRevenue%5C%22%3A%20%7B%5Cn%20%20%20%20%5C%22__typename%5C%22%3A%20%5C%22Currency%5C%22%2C%5Cn%20%20%20%20%5C%22amountMicros%5C%22%3A%20null%2C%5Cn%20%20%20%20%5C%22currencyCode%5C%22%3A%20%5C%22%5C%22%5Cn%20%20%7D%2C%5Cn%20%20%5C%22position%5C%22%3A%202%2C%5Cn%20%20%5C%22xLink%5C%22%3A%20%7B%5Cn%20%20%20%20%5C%22__typename%5C%22%3A%20%5C%22Links%5C%22%2C%5Cn%20%20%20%20%5C%22primaryLinkLabel%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22primaryLinkUrl%5C%22%3A%20%5C%22%5C%22%2C%5Cn%20%20%20%20%5C%22secondaryLinks%5C%22%3A%20%5B%5D%5Cn%20%20%7D%2C%5Cn%20%20%5C%22linkedinLink%5C%22%3A%20%7B%5Cn%20%20%20%20%5C%22__typename%5C%22%3A%20%5C%22Links%5C%22%2C%5Cn%20%20%20%20%5C%22primary%22%5D%7D):
_Missing field 'deletedAt' while writing result..._

What the issue is and why is it flaky?
<img width="463" height="525" alt="image"
src="https://github.com/user-attachments/assets/af82a5ae-bce8-4e2f-a25b-b6f027082bc1"
/>
flakines may be due to timing of when the error is thrown vs when test
assertions run
2025-12-05 15:27:25 +01:00
a9033a3045 i18n - docs translations (#16359)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-05 15:21:01 +01:00
MarieandGitHub 7ce22d5c7e breaking (soft) - Migrate viewGroup.fieldMetadataId -> view.mainGroupByFieldMetadataId (2/3) (#16277)
Should be merged once https://github.com/twentyhq/twenty/pull/16206 has
been released + command run to prod

In this PR
- Remove usage of viewGroup.fieldMetadataId, both in BE and FE states. 
- But we still need to properly populate it until we fully remove
viewGroup.fieldMetadataId from db and ORM entity (upcoming 3rd PR out of
3). fieldMetadataId was removed from CoreViewGroup type and
CreateViewGroupInput and is determined BE-side based on the associated
view's mainGroupByFieldMetadataId. **I expect this means a downtime on
viewGroup creation, until both FE and BE are deployed and cache is
flushed.** This seems acceptable to me as it only regards viewGroup
creation.
    - this information is replaced by view.mainGroupByFieldMetadataID
- Handle view group creation, update and deletion in the BE as a
side-effect of a view creation, update or deletion. Optimistic effects
are still used
- Add validation at view creation or update regarding
mainGroupByFieldMetadata

Left to do in 3rd PR
- Remove viewGroup.fieldMetadataId from db and ORM entity
- Restore feature allowing to update an existing grouped view's group by
field (already OK on BE side but need to rebuild FE optimistic)
2025-12-05 14:00:03 +00:00
17d88a074b i18n - translations (#16356)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-05 14:40:04 +01:00
Raphaël BosiandGitHub 077be7644c Migrate page layout widget to v2 of the API (#16323) 2025-12-05 13:04:06 +00:00
Charles BochetandGitHub 56e66315cd Fix messaging direct import (#16354)
Mainly clarifying naming (scheduleMessageImport was actually tagging as
PENDING)
+ tagging as SCHEDULED in case of direct import (we skip PENDING state)
2025-12-05 10:48:09 +01:00
Charles BochetandGitHub 10b5e156e5 Improvement on messaging (#16351)
In this PR:
- change messaging / calendar stale duration check to 30minutes (cron is
running every 1h, duration check was 1h, so evaluation was flaky)
- when temporary error (throttling), preserve syncStageStartedAt as this
is necessary to assess exponential throttling
2025-12-05 00:04:07 +01:00
Félix MalfaitandGitHub 2803292521 feat: add Metadata Builder agent for data model management (#16350)
## Summary

This PR adds a new **Metadata Builder** AI agent that specializes in
managing the workspace data model (creating objects, adding fields,
etc.).

## Changes

### New Files
- `data-model-manager-role.ts` - New standard role with `DATA_MODEL`
permission flag
- `metadata-builder-agent.ts` - New standard agent for data model
management

### Modified Files
- **ChatToolsProviderService**: Refactored to consolidate all
permission-based tools into a single `getChatTools()` method. Now
injects both workflow tools and metadata tools based on permissions.
- **AgentChatRoutingService**: Updated to use the new consolidated
`getChatTools()` method
- **AiChatModule**: Added imports for `ObjectMetadataModule` and
`FieldMetadataModule`
- **Router system prompt**: Added metadata-builder agent selection rules
with clear distinction between schema operations vs data operations
- **Metadata tools factories**: Improved error messages to show detailed
validation errors instead of generic messages

### Refactoring
- Renamed `index.ts` files to `standard-agent-definitions.ts` and
`standard-role-definitions.ts` to follow naming conventions
- Renamed exports from `standardAgentDefinitions` to
`STANDARD_AGENT_DEFINITIONS` (SCREAMING_SNAKE_CASE)

## Key Features

1. **Metadata Builder Agent** can:
   - Create new custom objects
   - Add fields to existing objects
   - Update object and field properties
   - Create relations between objects

2. **Permission-based tool injection**: Tools are automatically injected
based on the `DATA_MODEL` permission flag

3. **Improved routing**: The router now correctly distinguishes between:
   - "Create an object called Project" → metadata-builder (schema)
   - "Create a company called Acme" → data-manipulator (data)

4. **Better error messages**: Validation errors now show detailed
messages like:
   ```
   Validation errors:
   [objectMetadata] Name must be in camelCase format
   [objectMetadata] Label is required
   ```
2025-12-04 23:26:29 +01:00
Félix MalfaitandGitHub 34d7d82099 refactor(mcp): call metadata services directly instead of REST layer (#16349)
## Summary

Refactors MCP metadata tools to call underlying services directly
instead of going through the REST layer. This makes MCP a pure
presentation layer.

### Changes

**Created:**
-
`packages/twenty-server/src/engine/metadata-modules/metadata-tools/metadata-tools.module.ts`
- Module that exports MetadataToolsFactory
-
`packages/twenty-server/src/engine/metadata-modules/metadata-tools/services/metadata-tools.factory.ts`
- Factory that generates 8 metadata tools using Zod schemas:
- `get-object-metadata`, `create-object-metadata`,
`update-object-metadata`, `delete-object-metadata`
- `get-field-metadata`, `create-field-metadata`,
`update-field-metadata`, `delete-field-metadata`

**Modified:**
-
`packages/twenty-server/src/engine/api/mcp/services/mcp-metadata.service.ts`
- Uses new factory instead of REST-based services
- `packages/twenty-server/src/engine/api/mcp/mcp.module.ts` - Imports
MetadataToolsModule, removes old service imports

**Deleted:**
-
`packages/twenty-server/src/engine/api/mcp/services/tools/create.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/update.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/delete.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/get.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/mcp-metadata-tools.service.ts`

### Architecture Improvement

**Before:**
```
MCP Tool → MetadataQueryBuilderFactory → RestApiService → GraphQL API → Service
```

**After:**
```
MCP Tool → Service (ObjectMetadataService / FieldMetadataService)
```

This follows the pattern established by `direct-record-tools.factory.ts`
and workflow tools.
2025-12-04 22:21:16 +01:00
aa0471ca1f i18n - translations (#16348)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 21:21:31 +01:00
Félix MalfaitandGitHub 9cecbaebc3 refactor(workflow-tools): reorganize to one file per tool with co-located schemas (#16313)
## Summary

Reorganizes workflow tools to improve maintainability and
discoverability by having one file per tool with co-located input
schemas.

## Changes

- Create individual tool files in `tools/` directory (11 files)
- Co-locate input schemas with their tool implementations
- Add shared types file for dependencies and context
- Simplify workspace service to aggregate tool factories
- Remove centralized `schemas/` directory

## New Structure

```
workflow-tools/
├── services/
│   └── workflow-tool.workspace-service.ts
├── tools/
│   ├── activate-workflow-version.tool.ts
│   ├── compute-step-output-schema.tool.ts
│   ├── create-complete-workflow.tool.ts
│   ├── create-draft-from-workflow-version.tool.ts
│   ├── create-workflow-version-edge.tool.ts
│   ├── create-workflow-version-step.tool.ts
│   ├── deactivate-workflow-version.tool.ts
│   ├── delete-workflow-version-edge.tool.ts
│   ├── delete-workflow-version-step.tool.ts
│   ├── update-workflow-version-positions.tool.ts
│   └── update-workflow-version-step.tool.ts
├── types/
│   └── workflow-tool-dependencies.type.ts
└── workflow-tools.module.ts
```

## Benefits

- **Co-location**: Schema and tool logic are in the same file
- **Single responsibility**: Each file handles one tool
- **Easier maintenance**: Changes to a tool only touch one file
- **Better discoverability**: File names match tool names
2025-12-04 21:06:49 +01:00
WeikoandGitHub 1991ee850e Fix seeding perf + batch role targets creation (#16337) 2025-12-04 20:29:48 +01:00
e653385485 i18n - docs translations (#16345)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 19:22:39 +01:00
ddece3aac8 i18n - translations (#16342)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 19:01:28 +01:00
nitinandGitHub 8f756937ae [Dashboards] Pie chart custom tooltip (#16318)
closes https://github.com/twentyhq/core-team-issues/issues/1884

and https://discord.com/channels/1130383047699738754/1443679780414427267


before - 


https://github.com/user-attachments/assets/db19657f-d7d3-4965-9b88-83e5829d3942


after -


https://github.com/user-attachments/assets/d88ec686-0298-4b5f-a214-03e8d8169e7d
2025-12-04 17:22:21 +00:00
aede5bab34 i18n - translations (#16338)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 18:21:26 +01:00
Paul RastoinandGitHub 28cdb02fbb Twenty standard application Objects and fields as allFlatEntityMaps ID non-agnostic (#16298)
# Introduction

Related to https://github.com/twentyhq/core-team-issues/issues/1995
This PR introduces the basis of the `twentyStandard` application as code
on demand, it's highly tied to `ids` where it will becomes workspace
agnostic following the builder and runner `universalIdentifier` refactor
later.

The goal here to allow computing the `allFlatEntityMaps` `to` of the
`twentyStandard` application on a empty workspace ( workspace creation
). Allowing installing the twenty standard app through a workspace
migration instead of passing by the sync metadata

Nothing done will be run in production for the moment if it's not the
small validation refactor we've introduced

Please note that everything introduced here will be replaced at some
point by a twenty app instance when the twenty sdk is mature enough to
handle of the edge cases we need here

## How we've proceeded
We've been iterating over every workspace entity both objects and their
fields, and transpiled them to flatEntity.
Being sure we migrate the defaultValue, settings and so on accordingly.
We've also compute all the ids in prior of the whole entities
computation so we don't face any hoisting issue.

## Current state
At the moment only handling all of the 29 standard objects and their
fields
Settings a unique universalIdentifier for all of them

Will come views, agent role targets and so on later

## `workspace:compute-twenty-standard-migration` command
This command allow generating a workspace migration that will result in
installing the twenty standard app in an empty workspace
It's temporary and aims to allow debugging for the moment we might not
keep it in the future as it is right now
It contains debug writeFileSync which is expected no worries greptile

## `LabelFieldMetadataIdentifierId`
Small refactor allowing defining the label identifier field metadata id
of a uuid field metadata type for system object, as some of our standard
object don't have a name field and don't aim to
Also please note that we might remove this build options later in the
sake of the currently installed universal identifier application that we
could compare with the deterministic twenty standard one

## `runFlatFieldMetadataValidators`
Deprecated this pattern which was redundant and not v2 friendly pattern

## Current errors that will address in upcoming PR
Current standard objects and fields metadata does not pass the
validation that we have in place, as historically the sync metadata
would directly consume the repositories and would just ignore the
validation. This is about to change.
Will handle the below errors in dedicated PRs as they will required
upgrade commands in order to migrate the data, or will handle that from
the sync metadata instead still to be determined but nothing critical
here

- camel case field metadata name
- options label invalid format

```json
{
  "status": "fail",
  "report": {
    "fieldMetadata": [
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Name should be in camelCase",
            "userFriendlyMessage": {
              "id": "P+jdmX",
              "message": "Name should be in camelCase"
            },
            "value": "iCalUID"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "68dd83cd-92c8-4233-bb28-47939bab6124",
          "name": "iCalUID",
          "objectMetadataId": "11c16ab6-9176-439e-a2db-a12c5a58a524"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"email\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "email"
              }
            },
            "value": "email"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"sms\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "sms"
              }
            },
            "value": "sms"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "e3caaf2a-e07d-4146-8dfc-9eef904e82c9",
          "name": "type",
          "objectMetadataId": "4b777de5-4c7b-4af4-9b92-655c0f87512b"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"incoming\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "incoming"
              }
            },
            "value": "incoming"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"outgoing\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "outgoing"
              }
            },
            "value": "outgoing"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "d96233a4-93be-45ea-9548-3b50f3c700cf",
          "name": "direction",
          "objectMetadataId": "480a648a-d2e5-482a-992f-ef053e1b4bb0"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"from\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "from"
              }
            },
            "value": "from"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"to\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "to"
              }
            },
            "value": "to"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"cc\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "cc"
              }
            },
            "value": "cc"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"bcc\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "bcc"
              }
            },
            "value": "bcc"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "961c598e-67c3-452d-8bb2-b92c0bc64404",
          "name": "role",
          "objectMetadataId": "8af8a13c-ff97-4cd3-b70d-52a7dc2924b4"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Commas and dot (1,234.56)"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Spaces and comma (1 234,56)"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Dots and comma (1.234,56)"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "7fa20caf-2597-42e3-84e5-15a91b125b9b",
          "name": "numberFormat",
          "objectMetadataId": "a6974302-9e72-461c-aa09-9390f4ff16fc"
        },
        "type": "create_field"
      }
    ],
    "objectMetadata": [],
    "view": [],
    "viewField": [],
    "viewGroup": [],
    "index": [],
    "serverlessFunction": [],
    "cronTrigger": [],
    "databaseEventTrigger": [],
    "routeTrigger": [],
    "viewFilter": [],
    "role": [],
    "roleTarget": [],
    "agent": []
  }
}
```
2025-12-04 17:39:12 +01:00
b2d785de4b Page layout tab v2 (#16319)
# Introduction
Migrating `pageLayoutTab` to the v2 engine
- Types and constants
- Builder and validate
- Runner

Introduced a new `StrictSyncableEntity` that enforces that
`universalIdentifier` and `applicationId` are defined
As these entities are brand new we could enforce this rule already
This still requires a migration command to associate the existing
entities to custom workspace application instance and define
universalIdentifier

Handled retro-comp of the migration through a migration as upgrade
command fallback

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2025-12-04 17:37:16 +01:00
1194 changed files with 41676 additions and 10606 deletions
+1 -1
View File
@@ -67,7 +67,7 @@
"--config",
"./jest-integration.config.ts",
"${relativeFile}",
"--silent=false"
"--silent=false",
],
"cwd": "${workspaceFolder}/packages/twenty-server",
"console": "integratedTerminal",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "0.1.3",
"version": "0.2.0",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
@@ -127,7 +127,7 @@ const createPackageJson = async ({
auth: 'twenty auth login',
},
dependencies: {
'twenty-sdk': '0.1.3',
'twenty-sdk': '0.2.0',
},
devDependencies: {
'@types/node': '^24.7.2',
@@ -17,7 +17,7 @@ FIREFLIES_API_KEY=your_fireflies_api_key_here
# Fireflies plan level - affects which fields are available
# Options: free, pro, business, enterprise
# This controls which GraphQL fields are requested to avoid 403 errors
FIREFLIES_PLAN_LEVEL=pro
FIREFLIES_PLAN=free
# Twenty CRM API key for authentication
# Generate this from your Twenty instance: Settings > Developers > API Keys
@@ -0,0 +1 @@
.yarn
@@ -1,5 +1,76 @@
# Changelog
## [0.3.1] - 2025-12-08
Import all
### Added
- Historical import CLI: `yarn meeting:all` to fetch and insert historical Fireflies meetings with filters (date range, organizers, participants, channel, mine) and dry-run support.
- Fireflies transcripts listing with pagination and date filtering to support bulk imports.
### Changed
- Deduplication now checks `firefliesMeetingId` before creating meetings (webhook + bulk).
- Shared historical importer pipeline reusing existing note/meeting formatting.
## [0.3.0] - 2025-12-08
Subscription-based query / Full transcript and AI notes for Pro+ / More
### Added
- **Full transcript capture**: Meeting object now stores the complete meeting transcript with speaker names and timestamps (`transcript` field)
- **Rich AI meeting notes**: Captures detailed AI-generated meeting notes from Fireflies (`notes` field with 7,000+ char summaries)
- **Expanded summary fields**: Now fetches all available Fireflies summary data:
- `notes` - Detailed AI-generated meeting notes with timestamps and section headers
- `bullet_gist` - Emoji-enhanced bullet point summaries
- `outline` / `shorthand_bullet` - Timestamped meeting outline
- `gist` - One-sentence meeting summary
- `short_summary` - Single paragraph summary
- `short_overview` - Brief overview
- **New Meeting fields**:
- `transcript` - Full meeting transcript with speaker attribution
- `notes` - AI-generated detailed notes
- `audioUrl` - Link to audio recording (Pro+)
- `videoUrl` - Link to video recording (Business+)
- `meetingLink` - Original meeting link
- `neutralPercent` - Neutral sentiment percentage
- **Meeting delete utility**: New `yarn meeting:delete <meetingId>` script for cleanup and re-import
- **Debug meeting utility**: New `scripts/debug-meeting.ts` to inspect raw Fireflies API responses
### Changed
- **Plan-based GraphQL queries**: Completely redesigned query system with three tiers:
- **Free**: Basic fields only (title, date, duration, participants, transcript_url, meeting_link)
- **Pro**: Adds full transcript (`sentences`), summary fields, speakers, audio_url
- **Business+**: Adds analytics, video_url, speaker stats, meeting metrics
- **Action items parsing**: Fixed parsing of `action_items` which Fireflies returns as newline-separated string, not array
- **Note body format**: Enhanced with Meeting Notes, Outline, Key Points sections from rich Fireflies data
- **Import status**: Added `PARTIAL` status for imports missing summary/analytics data
### Fixed
- Missing `notes` and `bullet_gist` fields in data transform (were fetched but not passed through)
- Proper fallback: Uses `shorthand_bullet` when `outline` is empty (Fireflies stores outline content there)
- Summary readiness detection now checks `notes` field in addition to `overview` and `action_items`
### Documentation
- Updated README with complete API access comparison table by subscription plan
- Documented all available Fireflies summary fields and their plan requirements
## [0.2.3] - 2025-12-06
### Added
- **Meeting ingest utility**: New `yarn meeting:ingest <meetingId>` script to manually fetch and import specific Fireflies meetings into Twenty
- **Plan-based field selection**: Added `FIREFLIES_PLAN` configuration to control which GraphQL fields are requested based on subscription level (free, pro, business, enterprise)
- **Main entry point**: New `src/index.ts` centralizing all exports for cleaner imports
### Changed
- **Auth configuration**: Disabled authentication requirement for webhook route (`isAuthRequired: false`) to support serverless deployments
- **Signature verification fallback**: Webhook handler now supports signature in payload body as fallback when HTTP headers aren't forwarded to serverless functions (production doesn't work for Fireflies webhook)
- **Improved type safety**: Replaced `any` types with proper TypeScript types throughout codebase
### Enhanced
- **Webhook debugging**: Added detailed debug output including param keys, header info, and signature comparison details
- **Test webhook script**: Includes signature in both header and payload, with diagnostic output for header forwarding status
- **Documentation**: Added README sections on current twenty headers forward limitations and utility scripts
## [0.2.2] - 2025-11-04
### Added
@@ -2,6 +2,10 @@
Automatically captures meeting notes with AI-generated summaries and insights from Fireflies.ai into your Twenty CRM.
### Current Status
- Doesn't work with Fireflies webhook yet due to missing headers forwarding in twenty serverless func
- Meeting ingestion utility scripts are available for individual meeting insertion and historical meetings with filters with yarn meeting:all
## Integration Overview
**Fireflies webhook → Fireflies API → Twenty CRM with summary-focused insights**
@@ -19,19 +23,54 @@ Automatically captures meeting notes with AI-generated summaries and insights fr
## API Access by Subscription Plan
Fireflies API access varies significantly by subscription tier:
Fireflies API access varies by subscription tier. This integration automatically adapts queries based on your plan and falls back gracefully if restrictions are encountered.
### Plan Comparison
| Feature | Free | Pro | Business | Enterprise |
|---------|------|-----|----------|------------|
| **API Rate Limit** | 50 requests/day | 50 requests/day | 60 requests/minute | 60 requests/minute |
| **Storage** | 800 mins/seat | 8,000 mins/seat | Unlimited | Unlimited |
| **AI Summaries** | Limited (20 credits) | Unlimited | Unlimited | Unlimited |
| **Video Upload** | 100MB max | 1.5GB max | 1.5GB max | 1.5GB max |
| **Advanced Features** | Basic transcription | AI apps, analytics | Team analytics, CI | Full API, SSO, compliance |
|---------|:----:|:---:|:--------:|:----------:|
| **API Rate Limit** | 50/day | 50/day | 60/min | 60/min |
| **Basic Data** (title, date, duration) | ✅ | ✅ | ✅ | ✅ |
| **Participants List** | ✅ | ✅ | ✅ | ✅ |
| **Transcript URL** | ✅ | ✅ | ✅ | ✅ |
| **Speakers** | ❌ | ✅ | ✅ | ✅ |
| **Summary** (overview, keywords) | ❌ | ✅ | ✅ | ✅ |
| **Audio URL** | ❌ | ✅ | ✅ | ✅ |
| **Action Items** | ❌ | ❌ | ✅ | ✅ |
| **Topics Discussed** | ❌ | ❌ | ✅ | ✅ |
| **Video URL** | ❌ | ❌ | ✅ | ✅ |
| **Sentiment Analytics** | ❌ | ❌ | ✅ | ✅ |
| **Meeting Attendees (detailed)** | ❌ | ❌ | ✅ | ✅ |
**Key Design Pattern:** Subscription-based API access uses **tiered rate limiting** rather than feature gating. Lower tiers get severely restricted throughput (50/day vs 60/minute = 1,700x difference), making production integrations effectively require Business+ plans.
### What You'll Get Per Plan
**Pro Plan Limitation:** Despite "unlimited" AI summaries, the 50 requests/day limit severely constrains production usage for meeting-heavy organizations.
**Free Plan:**
- Meeting title, date, duration
- Participant names/emails (basic)
- Link to transcript
**Pro Plan:**
- Everything in Free, plus:
- Speaker identification
- AI summary (overview + keywords)
- Audio recording URL
**Business Plan:**
- Everything in Pro, plus:
- Action items extraction
- Topics discussed
- Sentiment analysis (positive/negative/neutral %)
- Video recording URL
- Detailed meeting attendee info
### Configuration
Set your plan in `.env`:
```bash
FIREFLIES_PLAN=free # Options: free, pro, business, enterprise
```
**Rate Limiting:** Free/Pro plans are limited to 50 API calls/day. The integration uses conservative retry settings by default to stay within limits.
## What Gets Captured
@@ -114,11 +153,6 @@ The `setup:fields` script adds 13 custom fields to store rich Fireflies data:
| `firefliesMeetingId` | TEXT | Fireflies Meeting ID | Unique identifier from Fireflies |
| `organizerEmail` | TEXT | Organizer Email | Email address of the meeting organizer |
Then re-sync:
```bash
npx twenty-cli app sync
```
**Note:** Without custom fields, meetings will be created with just the title. The rich summary data will only be stored in Notes for 1-on-1 meetings.
## Configuration
@@ -160,6 +194,29 @@ The integration uses **HMAC SHA-256 signature verification**:
- Twenty verifies signature using your webhook secret
- Invalid signatures are rejected immediately
### Current Platform Limitation (Headers)
- Twenty serverless route triggers currently do **not forward HTTP headers** to functions. Fireflies signatures sent in headers are stripped, so header-based verification does not work in production.
- Workaround: the provided test script also includes the signature inside the payload; the handler falls back to that payload signature. Use this only for testing until header forwarding is supported.
## Utilities for meeting insertion (workarounds)
- Ingest a specific Fireflies meeting into Twenty:
`yarn meeting:ingest <meetingId>` or `MEETING_ID=... yarn meeting:ingest`
- Fetch all/historical Fireflies meetings into Twenty:
`yarn meeting:all [--from 2024-01-01] [--to 2024-02-01] [--organizer a@x.com] [--participant b@x.com] [--channel <channelId>] [--mine] [--dry-run]`
- Filters (combine as needed):
- `--from` / `--to`: ISO or date string range filter
- `--organizer` / `--participant`: comma-separated emails
- `--channel`: Fireflies channel id
- `--mine`: only meetings for the current Fireflies user
- Controls:
- `--dry-run`: list and transform without writing to Twenty
- `--page-size`: pagination size (default 50)
- `--max-records`: stop after N transcripts (default 500)
## Development
```bash
@@ -247,10 +304,7 @@ Client expressed strong interest in the enterprise plan.
## Future Implementation Opportunities
### Past Meetings Retrieval
- **New trigger to retrieve past meetings from a contact** - Enable users to fetch historical meeting data from Fireflies for specific contacts, allowing retrospective capture and analysis of past interactions.
Next iteration would enhance the **intelligence layer** to:
Next iterations would enhance the **intelligence layer** to:
### AI-Powered Insights
- **Extract pain points, objections & buying signals** automatically from transcripts
@@ -1,4 +1,4 @@
import { type ApplicationConfig } from 'twenty-sdk/application';
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: 'a4df0c0f-c65e-44e5-8436-24814182d4ac',
@@ -9,17 +9,25 @@ const config: ApplicationConfig = {
FIREFLIES_WEBHOOK_SECRET: {
universalIdentifier: 'f51f7646-be9f-4ba9-9b75-160dd288cd0c',
description: 'Secret key for verifying Fireflies webhook signatures',
isSecret: true,
//isSecret: true,
value: '',
},
FIREFLIES_API_KEY: {
universalIdentifier: 'faa41f07-b28e-4500-b1c0-ce4b3d27924c',
description: 'Fireflies GraphQL API key used to fetch meeting summaries',
isSecret: true,
//isSecret: true,
value: '',
},
FIREFLIES_PLAN: {
universalIdentifier: '57dbb73c-aac5-4247-9fcc-a070bb669f16',
description: 'Fireflies plan: free, pro, business, enterprise',
value: 'free',
},
TWENTY_API_KEY: {
universalIdentifier: '02756551-5bf7-4fb2-8e08-1f622008d305',
description: 'Twenty API key used when running scripts locally',
isSecret: true,
//isSecret: true,
value: '',
},
SERVER_URL: {
universalIdentifier: '9b3a5e8e-5973-4e6b-a059-2966075652aa',
@@ -36,6 +44,11 @@ const config: ApplicationConfig = {
description: 'Log level: silent, error, warn, info, debug (default: error)',
value: 'error',
},
CAPTURE_LOGS: {
universalIdentifier: 'adbcc267-309d-49b2-af71-76f1299d863e',
description: 'Capture logs in webhook response for debugging (true/false)',
value: 'true',
},
FIREFLIES_SUMMARY_STRATEGY: {
universalIdentifier: '562b43d9-cd47-4ec1-ae16-5cc7ebc9729b',
description: 'Summary fetch strategy: immediate_only, immediate_with_retry, delayed_polling, or basic_only',
@@ -1,6 +1,6 @@
{
"name": "fireflies",
"version": "0.2.2",
"version": "0.3.0",
"license": "MIT",
"engines": {
"node": "^24.5.0",
@@ -11,12 +11,15 @@
"scripts": {
"test": "jest",
"setup:fields": "tsx scripts/add-meeting-fields.ts",
"test:webhook": "tsx scripts/test-webhook.ts"
"test:webhook": "tsx scripts/test-webhook.ts",
"meeting:ingest": "tsx scripts/ingest-meeting.ts",
"meeting:delete": "tsx scripts/delete-meeting.ts",
"meeting:all": "tsx scripts/fetch-all-meetings.ts"
},
"dependencies": {
"axios": "^1.13.1",
"dotenv": "^16.3.1",
"twenty-sdk": "0.0.3"
"dotenv": "^17.2.3",
"twenty-sdk": "0.1.3"
},
"devDependencies": {
"@types/jest": "^29.5.5",
@@ -1,7 +1,7 @@
{
"name": "fireflies",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/twenty-apps/fireflies/src",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/twenty-apps/community/fireflies/src",
"projectType": "application",
"tags": [
"scope:apps"
@@ -13,7 +13,7 @@
"{workspaceRoot}/coverage/{projectRoot}"
],
"options": {
"jestConfig": "packages/twenty-apps/fireflies/jest.config.mjs",
"jestConfig": "packages/twenty-apps/community/fireflies/jest.config.mjs",
"passWithNoTests": true
},
"configurations": {
@@ -36,7 +36,7 @@
],
"options": {
"lintFilePatterns": [
"packages/twenty-apps/fireflies/**/*.{ts,tsx,js,jsx}"
"packages/twenty-apps/community/fireflies/**/*.{ts,tsx,js,jsx}"
]
},
"configurations": {
@@ -50,7 +50,11 @@ interface FieldDefinition {
options?: FieldOption[];
}
// Meeting fields based on Fireflies GraphQL API transcript schema
// See: https://docs.fireflies.ai/graphql-api/query/transcript
// Note: Some fields require higher plans (Pro, Business, Enterprise)
const MEETING_FIELDS: FieldDefinition[] = [
// === Internal Twenty Relations ===
{
type: 'RELATION',
name: 'note',
@@ -59,11 +63,21 @@ const MEETING_FIELDS: FieldDefinition[] = [
icon: 'IconNotes',
isNullable: true,
},
// === Basic Fields (All Plans) ===
{
type: 'TEXT',
name: 'firefliesMeetingId',
label: 'Fireflies ID',
description: 'Unique transcript ID from Fireflies (maps to: id)',
icon: 'IconKey',
isNullable: true,
},
{
type: 'DATE_TIME',
name: 'meetingDate',
label: 'Meeting Date',
description: 'Date and time when the meeting occurred',
description: 'When the meeting occurred (maps to: date)',
icon: 'IconCalendar',
isNullable: true,
},
@@ -71,39 +85,107 @@ const MEETING_FIELDS: FieldDefinition[] = [
type: 'NUMBER',
name: 'duration',
label: 'Duration (minutes)',
description: 'Meeting duration in minutes',
description: 'Meeting duration in minutes (maps to: duration)',
icon: 'IconClock',
isNullable: true,
},
{
type: 'TEXT',
name: 'meetingType',
label: 'Meeting Type',
description: 'Type of meeting (e.g., Sales Call, Sprint Planning, 1:1)',
icon: 'IconTag',
name: 'organizerEmail',
label: 'Organizer Email',
description: 'Meeting organizer email (maps to: organizer_email)',
icon: 'IconMail',
isNullable: true,
},
{
type: 'LINKS',
name: 'transcriptUrl',
label: 'Transcript URL',
description: 'Link to full transcript (maps to: transcript_url)',
icon: 'IconFileText',
isNullable: true,
},
{
type: 'LINKS',
name: 'meetingLink',
label: 'Meeting Link',
description: 'Original meeting link (maps to: meeting_link)',
icon: 'IconLink',
isNullable: true,
},
// === Pro+ Fields (summary, speakers, audio_url, transcript) ===
{
type: 'TEXT',
name: 'transcript',
label: 'Full Transcript',
description: 'Full meeting transcript with speaker names and timestamps [Pro+]',
icon: 'IconFileText',
isNullable: true,
},
{
type: 'TEXT',
name: 'overview',
label: 'Overview',
description: 'AI-generated meeting summary (maps to: summary.overview) [Pro+]',
icon: 'IconFileDescription',
isNullable: true,
},
{
type: 'TEXT',
name: 'notes',
label: 'AI Notes',
description: 'Detailed AI-generated meeting notes (maps to: summary.notes) [Pro+]',
icon: 'IconNotes',
isNullable: true,
},
{
type: 'TEXT',
name: 'keywords',
label: 'Keywords',
description: 'Key topics and themes discussed (comma-separated)',
description: 'Key topics extracted (maps to: summary.keywords) [Pro+]',
icon: 'IconTags',
isNullable: true,
},
{
type: 'LINKS',
name: 'audioUrl',
label: 'Audio URL',
description: 'Link to audio recording (maps to: audio_url) [Pro+]',
icon: 'IconHeadphones',
isNullable: true,
},
// === Business+ Fields (analytics, video_url, full summary) ===
{
type: 'TEXT',
name: 'meetingType',
label: 'Meeting Type',
description: 'AI-detected meeting type (maps to: summary.meeting_type) [Business+]',
icon: 'IconTag',
isNullable: true,
},
{
type: 'TEXT',
name: 'topics',
label: 'Topics Discussed',
description: 'Topics covered in meeting (maps to: summary.topics_discussed) [Business+]',
icon: 'IconListDetails',
isNullable: true,
},
{
type: 'NUMBER',
name: 'sentimentScore',
label: 'Sentiment Score',
description: 'Overall meeting sentiment (0-1 scale, where 1 is most positive)',
icon: 'IconMoodSmile',
name: 'actionItemsCount',
label: 'Action Items',
description: 'Number of action items (count of: summary.action_items) [Business+]',
icon: 'IconCheckbox',
isNullable: true,
},
{
type: 'NUMBER',
name: 'positivePercent',
label: 'Positive %',
description: 'Percentage of positive sentiment in conversation',
description: 'Positive sentiment % (maps to: analytics.sentiments.positive_pct) [Business+]',
icon: 'IconThumbUp',
isNullable: true,
},
@@ -111,69 +193,48 @@ const MEETING_FIELDS: FieldDefinition[] = [
type: 'NUMBER',
name: 'negativePercent',
label: 'Negative %',
description: 'Percentage of negative sentiment in conversation',
description: 'Negative sentiment % (maps to: analytics.sentiments.negative_pct) [Business+]',
icon: 'IconThumbDown',
isNullable: true,
},
{
type: 'NUMBER',
name: 'actionItemsCount',
label: 'Action Items',
description: 'Number of action items identified',
icon: 'IconCheckbox',
name: 'neutralPercent',
label: 'Neutral %',
description: 'Neutral sentiment % (maps to: analytics.sentiments.neutral_pct) [Business+]',
icon: 'IconMoodNeutral',
isNullable: true,
},
{
type: 'LINKS',
name: 'transcriptUrl',
label: 'Transcript URL',
description: 'Link to full transcript in Fireflies',
icon: 'IconFileText',
isNullable: true,
},
{
type: 'LINKS',
name: 'recordingUrl',
label: 'Recording URL',
description: 'Link to video/audio recording in Fireflies',
name: 'videoUrl',
label: 'Video URL',
description: 'Link to video recording (maps to: video_url) [Business+]',
icon: 'IconVideo',
isNullable: true,
},
{
type: 'TEXT',
name: 'firefliesMeetingId',
label: 'Fireflies Meeting ID',
description: 'Unique identifier from Fireflies',
icon: 'IconKey',
isNullable: true,
},
{
type: 'TEXT',
name: 'organizerEmail',
label: 'Organizer Email',
description: 'Email address of the meeting organizer',
icon: 'IconMail',
isNullable: true,
},
// === Import Tracking Fields (Internal) ===
{
type: 'SELECT',
name: 'importStatus',
label: 'Import Status',
description: 'Status of the meeting import from Fireflies',
description: 'Status of the Fireflies import',
icon: 'IconCheck',
isNullable: true,
options: [
{ value: 'SUCCESS', label: 'Success', position: 0, color: 'green' },
{ value: 'FAILED', label: 'Failed', position: 1, color: 'red' },
{ value: 'PENDING', label: 'Pending', position: 2, color: 'yellow' },
{ value: 'RETRYING', label: 'Retrying', position: 3, color: 'orange' },
{ value: 'PARTIAL', label: 'Partial', position: 1, color: 'blue' },
{ value: 'FAILED', label: 'Failed', position: 2, color: 'red' },
{ value: 'PENDING', label: 'Pending', position: 3, color: 'yellow' },
{ value: 'RETRYING', label: 'Retrying', position: 4, color: 'orange' },
],
},
{
type: 'TEXT',
name: 'importError',
label: 'Import Error',
description: 'Error message if meeting import failed',
description: 'Error message if import failed',
icon: 'IconAlertTriangle',
isNullable: true,
},
@@ -181,7 +242,7 @@ const MEETING_FIELDS: FieldDefinition[] = [
type: 'DATE_TIME',
name: 'lastImportAttempt',
label: 'Last Import Attempt',
description: 'Date and time of the last import attempt',
description: 'When import was last attempted',
icon: 'IconClock',
isNullable: true,
},
@@ -189,7 +250,7 @@ const MEETING_FIELDS: FieldDefinition[] = [
type: 'NUMBER',
name: 'importAttempts',
label: 'Import Attempts',
description: 'Number of times import has been attempted',
description: 'Number of import attempts',
icon: 'IconRepeat',
isNullable: true,
},
@@ -414,13 +475,9 @@ const main = async () => {
}
if (createdCount === 0 && skippedCount === MEETING_FIELDS.length) {
console.log('\n✨ All fields already exist. Nothing to do!');
console.log('\n✨ All fields already exist. Nothing to do!\n');
} else if (createdCount > 0) {
console.log('\n✨ Custom fields added successfully!');
console.log('\n📝 Next steps:');
console.log(' 1. Re-sync your app: npx twenty-cli app sync');
console.log(' 2. Update the createMeetingRecord function to use these fields');
console.log(' 3. Test the integration with a real meeting');
console.log('\n✨ Custom fields added successfully!\n');
}
} catch (error) {
@@ -0,0 +1,81 @@
/* eslint-disable no-console */
import * as dotenv from 'dotenv';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.join(__dirname, '../.env') });
const FIREFLIES_API_KEY = process.env.FIREFLIES_API_KEY;
const meetingId = process.argv[2] || '01KBMR1ZYQ34YP8D2KB4B16QPH';
const main = async (): Promise<void> => {
if (!FIREFLIES_API_KEY) {
console.error('❌ FIREFLIES_API_KEY is required');
process.exit(1);
}
const query = `
query GetTranscript($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
summary {
overview
notes
gist
bullet_gist
short_summary
short_overview
outline
shorthand_bullet
action_items
keywords
topics_discussed
meeting_type
transcript_chapters
}
}
}
`;
const response = await fetch('https://api.fireflies.ai/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${FIREFLIES_API_KEY}`,
},
body: JSON.stringify({ query, variables: { transcriptId: meetingId } }),
});
if (!response.ok) {
const errorText = await response.text();
console.error(`❌ API request failed with status ${response.status}`);
console.error(errorText);
process.exit(1);
}
const json = await response.json();
console.log('=== Fireflies API Response ===\n');
console.log(JSON.stringify(json, null, 2));
if (json.data?.transcript?.summary) {
const s = json.data.transcript.summary;
console.log('\n=== Summary Fields Status ===');
console.log('overview:', s.overview ? `✓ (${s.overview.length} chars)` : '✗ empty');
console.log('notes:', s.notes ? `✓ (${s.notes.length} chars)` : '✗ empty');
console.log('gist:', s.gist ? `✓ (${s.gist.length} chars)` : '✗ empty');
console.log('bullet_gist:', s.bullet_gist ? `✓ (${s.bullet_gist.length} chars)` : '✗ empty');
console.log('outline:', s.outline ? `✓ (${s.outline.length} chars)` : '✗ empty');
console.log('action_items:', s.action_items?.length || 0, 'items');
console.log('topics_discussed:', s.topics_discussed?.length || 0, 'topics');
console.log('keywords:', s.keywords?.length || 0, 'keywords');
}
};
main().catch((error) => {
console.error('❌ Failed to fetch meeting');
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
@@ -0,0 +1,59 @@
/* eslint-disable no-console */
import * as dotenv from 'dotenv';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.join(__dirname, '../.env') });
const SERVER_URL = process.env.SERVER_URL || 'http://localhost:3000';
const API_KEY = process.env.TWENTY_API_KEY;
const meetingId = process.argv[2];
const main = async (): Promise<void> => {
if (!API_KEY) {
console.error('❌ TWENTY_API_KEY is required');
process.exit(1);
}
if (!meetingId) {
console.error('Usage: yarn delete:meeting <meetingId>');
process.exit(1);
}
const response = await fetch(`${SERVER_URL}/graphql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`,
},
body: JSON.stringify({
query: `mutation DeleteMeeting($id: UUID!) { deleteMeeting(id: $id) { id } }`,
variables: { id: meetingId },
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error(`❌ Delete failed (status ${response.status})`);
console.error(errorText);
process.exit(1);
}
const result = await response.json();
const deletedId = result.data?.deleteMeeting?.id;
if (result.errors || !deletedId) {
const message = result.errors?.[0]?.message || 'deleteMeeting returned null';
console.error('❌ Error:', message);
process.exit(1);
}
console.log('✅ Deleted meeting:', deletedId);
};
main().catch((error) => {
console.error('❌ Failed to delete meeting');
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
@@ -0,0 +1,222 @@
/* eslint-disable no-console */
/**
* Fetch historical Fireflies meetings and insert into Twenty.
*
* Usage:
* yarn meeting:all [--from 2024-01-01] [--to 2024-02-01] [--organizer alice@x.com] [--participant bob@x.com] [--channel <channelId>] [--mine] [--dry-run] [--page-size 50] [--max-records 200]
*
* Required env:
* FIREFLIES_API_KEY
* TWENTY_API_KEY
*
* Optional env:
* SERVER_URL (defaults to http://localhost:3000)
* FIREFLIES_PLAN (free|pro|business|enterprise)
* AUTO_CREATE_CONTACTS (true|false)
* FIREFLIES_* retry settings (see README)
*/
import * as dotenv from 'dotenv';
import { existsSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { FirefliesApiClient } from '../src/fireflies-api-client';
import { type HistoricalImportFilters, HistoricalImporter } from '../src/historical-importer';
import { createLogger } from '../src/logger';
import { TwentyCrmService } from '../src/twenty-crm-service';
import {
getApiUrl,
getFirefliesPlan,
getSummaryFetchConfig,
shouldAutoCreateContacts,
} from '../src/utils';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '..', '.env');
if (existsSync(envPath)) {
dotenv.config({ path: envPath });
}
const logger = createLogger('cli:meeting:all');
type CliArgs = {
from?: string;
to?: string;
organizer?: string[];
participant?: string[];
channel?: string;
host?: string;
mine?: boolean;
dryRun?: boolean;
pageSize?: number;
maxRecords?: number;
limit?: number;
};
const parseArgs = (argv: string[]): CliArgs => {
const args: CliArgs = {};
const parseNumberArg = (value?: string): number | undefined => {
if (!value) return undefined;
const parsed = Number.parseInt(value, 10);
return Number.isNaN(parsed) ? undefined : parsed;
};
for (let i = 0; i < argv.length; i += 1) {
const current = argv[i];
const next = argv[i + 1];
switch (current) {
case '--from':
args.from = next;
i += 1;
break;
case '--to':
args.to = next;
i += 1;
break;
case '--organizer':
args.organizer = next ? next.split(',') : [];
i += 1;
break;
case '--participant':
args.participant = next ? next.split(',') : [];
i += 1;
break;
case '--channel':
args.channel = next;
i += 1;
break;
case '--host':
args.host = next;
i += 1;
break;
case '--mine':
args.mine = true;
break;
case '--dry-run':
args.dryRun = true;
break;
case '--page-size':
args.pageSize = parseNumberArg(next);
i += 1;
break;
case '--max-records':
args.maxRecords = parseNumberArg(next);
i += 1;
break;
case '--limit':
args.limit = parseNumberArg(next);
i += 1;
break;
default:
break;
}
}
return args;
};
const parseDate = (value?: string): number | undefined => {
if (!value) {
return undefined;
}
const parsed = Date.parse(value);
return Number.isNaN(parsed) ? undefined : parsed;
};
const main = async (): Promise<void> => {
const args = parseArgs(process.argv.slice(2));
const firefliesApiKey = process.env.FIREFLIES_API_KEY || '';
const twentyApiKey = process.env.TWENTY_API_KEY || '';
if (!firefliesApiKey) {
console.error('❌ FIREFLIES_API_KEY is required');
process.exit(1);
}
if (!twentyApiKey) {
console.error('❌ TWENTY_API_KEY is required');
process.exit(1);
}
const fromDate = parseDate(args.from);
const toDate = parseDate(args.to);
const filters: HistoricalImportFilters = {
fromDate,
toDate,
organizers: args.organizer,
participants: args.participant,
channelId: args.channel,
hostEmail: args.host,
mine: args.mine,
limit: args.limit,
pageSize: args.pageSize,
maxRecords: args.maxRecords,
};
const summaryConfig = getSummaryFetchConfig();
const plan = getFirefliesPlan();
const autoCreateContacts = shouldAutoCreateContacts();
logger.info(
`Starting historical import (dryRun=${Boolean(args.dryRun)}, plan=${plan}, pageSize=${filters.pageSize ?? 50})`,
);
const firefliesClient = new FirefliesApiClient(firefliesApiKey);
const twentyService = new TwentyCrmService(twentyApiKey, getApiUrl());
const importer = new HistoricalImporter(firefliesClient, twentyService);
const result = await importer.run(filters, {
dryRun: args.dryRun,
autoCreateContacts,
summaryConfig,
plan,
});
console.log('✅ Historical import summary:');
const summary = {
dryRun: result.dryRun,
totalListed: result.totalListed,
imported: result.imported,
skippedExisting: result.skippedExisting,
summaryPending: result.summaryPending,
failed: result.failed,
};
console.log(JSON.stringify(summary, null, 2));
if (result.statuses.length > 0) {
console.log('Status by meeting:');
console.table(
result.statuses.map((s) => ({
meetingId: s.meetingId,
title: s.title ?? '',
status: s.status,
reason: s.reason ?? '',
})),
);
}
if (result.failed.length > 0) {
process.exitCode = 1;
}
};
main().catch((error) => {
console.error('❌ Failed to import historical meetings');
if (error instanceof Error) {
console.error(error.message);
if (error.stack) {
console.error(error.stack);
}
} else {
console.error(String(error));
}
process.exit(1);
});
@@ -0,0 +1,93 @@
/* eslint-disable no-console */
/**
* Fetch a Fireflies meeting by ID and insert it into Twenty using the same path
* as the webhook handler.
*
* Usage:
* yarn meeting:ingest <meetingId>
* Or
* MEETING_ID=... yarn meeting:ingest
*
* Required env:
* FIREFLIES_API_KEY
* FIREFLIES_WEBHOOK_SECRET
* TWENTY_API_KEY
*
* Optional env:
* SERVER_URL (defaults to http://localhost:3000)
* FIREFLIES_PLAN (free|pro|business|enterprise)
*/
import { createHmac } from 'crypto';
import * as dotenv from 'dotenv';
import { existsSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { WebhookHandler } from '../src/webhook-handler';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '..', '.env');
if (existsSync(envPath)) {
dotenv.config({ path: envPath });
}
const args = process.argv.slice(2);
const meetingId = args[0] || process.env.MEETING_ID;
if (!meetingId) {
console.error('❌ meetingId is required (arg or MEETING_ID env)');
process.exit(1);
}
const firefliesApiKey = process.env.FIREFLIES_API_KEY || '';
const twentyApiKey = process.env.TWENTY_API_KEY || '';
const webhookSecret = process.env.FIREFLIES_WEBHOOK_SECRET || '';
if (!firefliesApiKey) {
console.error('❌ FIREFLIES_API_KEY is required');
process.exit(1);
}
if (!twentyApiKey) {
console.error('❌ TWENTY_API_KEY is required');
process.exit(1);
}
if (!webhookSecret) {
console.error('❌ FIREFLIES_WEBHOOK_SECRET is required to generate signature');
process.exit(1);
}
const payload = {
meetingId,
eventType: 'Transcription completed',
};
const body = JSON.stringify(payload);
const signature = `sha256=${createHmac('sha256', webhookSecret)
.update(body, 'utf8')
.digest('hex')}`;
const main = async (): Promise<void> => {
console.log(`🚀 Ingesting meeting ${meetingId} via webhook handler`);
const handler = new WebhookHandler();
const result = await handler.handle(payload, {
'x-hub-signature': signature,
body,
});
console.log('✅ Result:');
console.log(JSON.stringify(result, null, 2));
if (result.errors && result.errors.length > 0) {
process.exitCode = 1;
}
};
main().catch((error) => {
console.error('❌ Failed to ingest meeting');
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
@@ -40,11 +40,13 @@ const FIREFLIES_WEBHOOK_SECRET = process.env.FIREFLIES_WEBHOOK_SECRET || 'test_s
const _FIREFLIES_API_KEY = process.env.FIREFLIES_API_KEY || 'test_api_key';
// Test meeting data (simulating Fireflies API response)
const TEST_MEETING_ID = 'test-meeting-local-' + Date.now();
const TEST_MEETING_ID = process.env.MEETING_ID || 'test-meeting-local-' + Date.now();
const CLIENT_REFERENCE_ID = process.env.CLIENT_REFERENCE_ID;
const TEST_WEBHOOK_PAYLOAD = {
meetingId: TEST_MEETING_ID,
eventType: 'Transcription completed',
clientReferenceId: 'test-client-ref',
...(CLIENT_REFERENCE_ID ? { clientReferenceId: CLIENT_REFERENCE_ID } : {}),
};
// Mock Fireflies GraphQL API response
@@ -119,11 +121,17 @@ const main = async () => {
}
// Prepare webhook payload
const body = JSON.stringify(TEST_WEBHOOK_PAYLOAD);
const signature = generateHMACSignature(body, FIREFLIES_WEBHOOK_SECRET);
const unsignedBody = JSON.stringify(TEST_WEBHOOK_PAYLOAD);
const signature = generateHMACSignature(unsignedBody, FIREFLIES_WEBHOOK_SECRET);
const payloadWithSignature = {
...TEST_WEBHOOK_PAYLOAD,
'x-hub-signature': signature,
};
const body = JSON.stringify(payloadWithSignature);
console.log('📤 Sending webhook payload:');
console.log(JSON.stringify(TEST_WEBHOOK_PAYLOAD, null, 2));
console.log(JSON.stringify(payloadWithSignature, null, 2));
console.log('\n️ Signature is sent both as header (preferred) and in payload as fallback (headers are not passed to serverless functions)\n');
console.log(`\n🔐 HMAC Signature: ${signature}\n`);
// Check if server is reachable
@@ -178,6 +186,31 @@ const main = async () => {
console.log('📥 Response Body:');
console.log(JSON.stringify(responseData, null, 2));
// Report whether the server appears to have received the header signature
const debugMessages = Array.isArray((responseData as any)?.debug)
? ((responseData as any).debug as string[])
: [];
const headerMissing =
debugMessages.some((msg) => msg.includes('headerKeys=none')) ||
debugMessages.some((msg) => msg.includes('providedSignature=undefined'));
const signatureErrors =
Array.isArray((responseData as any)?.errors) &&
((responseData as any).errors as unknown[]).some(
(err) => typeof err === 'string' && err.toLowerCase().includes('signature'),
);
if (headerMissing) {
console.log(
'\n⚠️ Server did not report any received headers; it may be using payload fallback for signature verification.',
);
} else {
console.log('\n✅ Server reported headers present (header-based signature should be used).');
}
if (signatureErrors) {
console.log('⚠️ Signature was rejected by the server (check webhook secret / payload).');
}
if (response.ok) {
console.log('\n✅ Webhook test completed successfully!');
console.log('\n📋 Next steps:');
@@ -1,426 +0,0 @@
import { createLogger } from './logger';
import type { FirefliesMeetingData, FirefliesParticipant, SummaryFetchConfig } from './types';
const logger = createLogger('fireflies-api');
export class FirefliesApiClient {
private apiKey: string;
constructor(apiKey: string) {
if (!apiKey) {
logger.critical('FIREFLIES_API_KEY is required but not provided - this is a critical configuration error');
throw new Error('FIREFLIES_API_KEY is required');
}
this.apiKey = apiKey;
}
async fetchMeetingData(
meetingId: string,
options?: { timeout?: number }
): Promise<FirefliesMeetingData> {
const query = `
query GetTranscript($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
date
duration
participants
organizer_email
meeting_attendees {
displayName
email
phoneNumber
name
location
}
meeting_attendance {
name
join_time
leave_time
}
speakers {
name
}
summary {
action_items
overview
}
transcript_url
}
}
`;
const controller = new AbortController();
const timeoutId = options?.timeout
? setTimeout(() => controller.abort(), options.timeout)
: null;
try {
const response = await fetch('https://api.fireflies.ai/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
query,
variables: { transcriptId: meetingId },
}),
signal: controller.signal,
});
if (timeoutId) clearTimeout(timeoutId);
if (!response.ok) {
let errorDetails = `Fireflies API request failed with status ${response.status}`;
try {
const errorBody = await response.text();
if (errorBody) {
errorDetails += `: ${errorBody}`;
}
} catch {
// Ignore if we can't read the response body
}
throw new Error(errorDetails);
}
const json = await response.json() as {
data?: { transcript?: any };
errors?: Array<{ message?: string }>;
};
if (json.errors && json.errors.length > 0) {
throw new Error(`Fireflies API error: ${json.errors[0]?.message || 'Unknown error'}`);
}
const transcript = json.data?.transcript;
if (!transcript) {
throw new Error('Invalid response from Fireflies API: missing transcript data');
}
return this.transformMeetingData(transcript, meetingId);
} catch (error) {
if (timeoutId) clearTimeout(timeoutId);
throw error;
}
}
async fetchMeetingDataWithRetry(
meetingId: string,
config: SummaryFetchConfig
): Promise<{ data: FirefliesMeetingData; summaryReady: boolean }> {
// immediate_only: single attempt, no retries
if (config.strategy === 'immediate_only') {
logger.debug(`fetching meeting ${meetingId} (strategy: immediate_only)`);
const meetingData = await this.fetchMeetingData(meetingId, { timeout: 10000 });
const ready = this.isSummaryReady(meetingData);
logger.debug(`summary ready: ${ready}`);
return { data: meetingData, summaryReady: ready };
}
// immediate_with_retry: retry with exponential backoff
logger.debug(`fetching meeting ${meetingId} (strategy: immediate_with_retry, maxAttempts: ${config.retryAttempts})`);
for (let attempt = 1; attempt <= config.retryAttempts; attempt++) {
try {
const meetingData = await this.fetchMeetingData(meetingId, { timeout: 10000 });
const ready = this.isSummaryReady(meetingData);
logger.debug(`attempt ${attempt}/${config.retryAttempts}: summary ready=${ready}`);
if (ready) {
return { data: meetingData, summaryReady: true };
}
if (attempt < config.retryAttempts) {
const delayMs = config.retryDelay * attempt;
logger.debug(`summary not ready, waiting ${delayMs}ms before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, delayMs));
} else {
logger.debug(`max retries reached, returning partial data`);
return { data: meetingData, summaryReady: false };
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error);
logger.error(`attempt ${attempt}/${config.retryAttempts} failed: ${errorMsg}`);
if (attempt === config.retryAttempts) {
throw error;
}
const delayMs = config.retryDelay * attempt;
logger.debug(`retrying in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
throw new Error('Failed to fetch meeting data after retries');
}
private isSummaryReady(meetingData: FirefliesMeetingData): boolean {
return (
(meetingData.summary?.action_items?.length > 0) ||
(meetingData.summary?.overview?.length > 0) ||
meetingData.summary_status === 'completed'
);
}
private extractAllParticipants(transcript: any): FirefliesParticipant[] {
const participantsWithEmails: FirefliesParticipant[] = [];
const participantsNameOnly: FirefliesParticipant[] = [];
logger.debug('=== PARTICIPANT EXTRACTION DEBUG ===');
logger.debug('participants field:', JSON.stringify(transcript.participants));
logger.debug('meeting_attendees field:', JSON.stringify(transcript.meeting_attendees));
logger.debug('speakers field:', transcript.speakers?.map((s: any) => s.name));
logger.debug('meeting_attendance field:', transcript.meeting_attendance?.map((a: any) => a.name));
logger.debug('organizer_email:', transcript.organizer_email);
// Helper function to check if a string is an email
const isEmail = (str: string): boolean => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str.trim());
};
// Helper function to check if already exists
const isDuplicate = (name: string, email: string): boolean => {
const nameLower = name.toLowerCase().trim();
const emailLower = email.toLowerCase().trim();
return participantsWithEmails.some(p =>
p.name.toLowerCase().trim() === nameLower ||
(email && p.email.toLowerCase() === emailLower)
) || participantsNameOnly.some(p =>
p.name.toLowerCase().trim() === nameLower
);
};
// 1. Extract from legacy participants field (with emails)
if (transcript.participants && Array.isArray(transcript.participants)) {
transcript.participants.forEach((participant: string) => {
// Handle comma-separated emails or names
const parts = participant.split(',').map(p => p.trim());
parts.forEach(part => {
const emailMatch = part.match(/<([^>]+)>/);
const email = emailMatch ? emailMatch[1] : '';
// Extract name properly: if there's an email in angle brackets, get the part before it
const name = emailMatch
? part.substring(0, part.indexOf('<')).trim()
: part.trim();
// Skip if the "name" is actually an email address
if (isEmail(name)) {
logger.debug(`Skipping participant with email as name: "${name}"`);
return;
}
// Skip if empty name
if (!name) {
return;
}
// Skip duplicates
if (isDuplicate(name, email)) {
logger.debug(`Skipping duplicate participant: "${name}" <${email}>`);
return;
}
if (name && email) {
participantsWithEmails.push({ name, email });
} else if (name) {
participantsNameOnly.push({ name, email: '' });
}
});
});
}
// 2. Extract from meeting_attendees field (structured)
if (transcript.meeting_attendees && Array.isArray(transcript.meeting_attendees)) {
transcript.meeting_attendees.forEach((attendee: any) => {
const name = attendee.displayName || attendee.name || '';
const email = attendee.email || '';
// Skip if name is actually an email
if (isEmail(name)) {
logger.debug(`Skipping attendee with email as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, email)) {
if (email) {
participantsWithEmails.push({ name, email });
} else {
participantsNameOnly.push({ name, email: '' });
}
}
});
}
// 3. Extract from speakers field (name only)
if (transcript.speakers && Array.isArray(transcript.speakers)) {
transcript.speakers.forEach((speaker: any) => {
const name = speaker.name || '';
// Skip if name is actually an email
if (isEmail(name)) {
logger.debug(`Skipping speaker with email as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, '')) {
participantsNameOnly.push({ name, email: '' });
}
});
}
// 4. Extract from meeting_attendance field (name only)
if (transcript.meeting_attendance && Array.isArray(transcript.meeting_attendance)) {
transcript.meeting_attendance.forEach((attendance: any) => {
const name = attendance.name || '';
// Skip if name is actually an email or contains comma-separated emails
if (isEmail(name) || name.includes(',')) {
logger.debug(`Skipping attendance with email/list as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, '')) {
participantsNameOnly.push({ name, email: '' });
}
});
}
// 5. Add organizer email if available and not already included
const organizerEmail = transcript.organizer_email;
if (organizerEmail) {
// Check if organizer email is already in the participants
const existsWithEmail = participantsWithEmails.some(p =>
p.email.toLowerCase() === organizerEmail.toLowerCase()
);
if (!existsWithEmail) {
// Try to find organizer name from speakers/attendance and match with email
let organizerName = '';
// Extract username from organizer email for matching
const emailUsername = organizerEmail.split('@')[0].toLowerCase();
const emailNameVariations = [emailUsername];
// Add common name variations based on email username
if (emailUsername === 'alex') {
emailNameVariations.push('alexander', 'alexandre', 'alex');
}
// Look for organizer in speakers by matching email username to speaker names
if (transcript.speakers && Array.isArray(transcript.speakers)) {
const potentialOrganizerSpeaker = transcript.speakers.find((speaker: any) => {
const name = (speaker.name || '').toLowerCase();
return emailNameVariations.some(variation =>
name.includes(variation) || variation.includes(name)
);
});
if (potentialOrganizerSpeaker) {
organizerName = potentialOrganizerSpeaker.name;
}
}
// Look for organizer in attendance
if (!organizerName && transcript.meeting_attendance && Array.isArray(transcript.meeting_attendance)) {
const potentialOrganizerAttendance = transcript.meeting_attendance.find((attendance: any) => {
const name = (attendance.name || '').toLowerCase();
return emailNameVariations.some(variation =>
name.includes(variation) || variation.includes(name)
);
});
if (potentialOrganizerAttendance) {
organizerName = potentialOrganizerAttendance.name;
}
}
// If we found a name match, add as participant with email
if (organizerName) {
participantsWithEmails.push({ name: organizerName, email: organizerEmail });
// Remove from name-only participants to avoid duplicates
const nameIndex = participantsNameOnly.findIndex(p =>
p.name.toLowerCase().includes(organizerName.toLowerCase()) ||
organizerName.toLowerCase().includes(p.name.toLowerCase())
);
if (nameIndex !== -1) {
participantsNameOnly.splice(nameIndex, 1);
}
} else {
// If no name found, add with generic organizer name
participantsWithEmails.push({ name: 'Meeting Organizer', email: organizerEmail });
}
}
}
// Return participants with emails first, then name-only participants
const allParticipants = [...participantsWithEmails, ...participantsNameOnly];
logger.debug('=== EXTRACTED PARTICIPANTS ===');
logger.debug('With emails:', participantsWithEmails.length, JSON.stringify(participantsWithEmails));
logger.debug('Name only:', participantsNameOnly.length, JSON.stringify(participantsNameOnly));
logger.debug('Total:', allParticipants.length);
return allParticipants;
}
private transformMeetingData(transcript: any, meetingId: string): FirefliesMeetingData {
// Convert date to ISO string - handle both timestamp and ISO string formats
let dateString: string;
if (transcript.date) {
if (typeof transcript.date === 'number') {
// Unix timestamp in milliseconds
dateString = new Date(transcript.date).toISOString();
} else if (typeof transcript.date === 'string') {
// Could be ISO string or timestamp string
const parsed = Number(transcript.date);
if (!isNaN(parsed)) {
// It's a numeric string (timestamp)
dateString = new Date(parsed).toISOString();
} else {
// It's already an ISO string
dateString = transcript.date;
}
} else {
dateString = new Date().toISOString();
}
} else {
dateString = new Date().toISOString();
}
return {
id: transcript.id || meetingId,
title: transcript.title || 'Untitled Meeting',
date: dateString,
duration: transcript.duration || 0,
participants: this.extractAllParticipants(transcript),
organizer_email: transcript.organizer_email,
summary: {
action_items: Array.isArray(transcript.summary?.action_items)
? transcript.summary.action_items
: (typeof transcript.summary?.action_items === 'string'
? [transcript.summary.action_items]
: []),
overview: transcript.summary?.overview || '',
keywords: transcript.summary?.keywords,
topics_discussed: transcript.summary?.topics_discussed,
meeting_type: transcript.summary?.meeting_type,
},
analytics: transcript.sentiments ? {
sentiments: {
positive_pct: transcript.sentiments.positive_pct || 0,
negative_pct: transcript.sentiments.negative_pct || 0,
neutral_pct: transcript.sentiments.neutral_pct || 0,
}
} : undefined,
transcript_url: transcript.transcript_url || `https://app.fireflies.ai/view/${meetingId}`,
recording_url: transcript.video_url || undefined,
summary_status: transcript.summary_status,
};
}
}
@@ -1,227 +0,0 @@
import type { FirefliesMeetingData, MeetingCreateInput } from './types';
export class MeetingFormatter {
static formatNoteBody(meetingData: FirefliesMeetingData): string {
const meetingDate = new Date(meetingData.date);
const formattedDate = meetingDate.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
const durationMinutes = Math.round(meetingData.duration);
let noteBody = `**Date:** ${formattedDate}\n`;
noteBody += `**Duration:** ${durationMinutes} minutes\n`;
if (meetingData.participants.length > 0) {
const participantNames = meetingData.participants.map(p => p.name).join(', ');
noteBody += `**Participants:** ${participantNames}\n`;
}
// Overview section
if (meetingData.summary?.overview) {
noteBody += `\n## Overview\n${meetingData.summary.overview}\n`;
}
// Key topics
if (meetingData.summary?.topics_discussed && Array.isArray(meetingData.summary.topics_discussed) && meetingData.summary.topics_discussed.length > 0) {
noteBody += `\n## Key Topics\n`;
meetingData.summary.topics_discussed.forEach(topic => {
noteBody += `- ${topic}\n`;
});
}
// Action items
if (meetingData.summary?.action_items && Array.isArray(meetingData.summary.action_items) && meetingData.summary.action_items.length > 0) {
noteBody += `\n## Action Items\n`;
meetingData.summary.action_items.forEach(item => {
noteBody += `- ${item}\n`;
});
}
// Insights section
noteBody += `\n## Insights\n`;
if (meetingData.summary?.keywords && Array.isArray(meetingData.summary.keywords) && meetingData.summary.keywords.length > 0) {
noteBody += `**Keywords:** ${meetingData.summary.keywords.join(', ')}\n`;
}
if (meetingData.analytics?.sentiments) {
const sentiments = meetingData.analytics.sentiments;
noteBody += `**Sentiment:** ${sentiments.positive_pct}% positive, ${sentiments.negative_pct}% negative, ${sentiments.neutral_pct}% neutral\n`;
}
if (meetingData.summary?.meeting_type) {
noteBody += `**Meeting Type:** ${meetingData.summary.meeting_type}\n`;
}
// Resources section
noteBody += `\n## Resources\n`;
noteBody += `[View Full Transcript](${meetingData.transcript_url})\n`;
if (meetingData.recording_url) {
noteBody += `[Watch Recording](${meetingData.recording_url})\n`;
}
return noteBody;
}
static formatMeetingNotes(meetingData: FirefliesMeetingData): string {
const meetingDate = new Date(meetingData.date);
const formattedDate = meetingDate.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
const durationMinutes = Math.round(meetingData.duration);
let meetingNotes = `**Date:** ${formattedDate}\n`;
meetingNotes += `**Duration:** ${durationMinutes} minutes\n`;
if (meetingData.participants.length > 0) {
const participantNames = meetingData.participants.map(p => p.name).join(', ');
meetingNotes += `**Participants:** ${participantNames}\n`;
}
// Overview section
if (meetingData.summary?.overview) {
meetingNotes += `\n## Overview\n${meetingData.summary.overview}\n`;
}
// Key topics
if (meetingData.summary?.topics_discussed && Array.isArray(meetingData.summary.topics_discussed) && meetingData.summary.topics_discussed.length > 0) {
meetingNotes += `\n## Key Topics\n`;
meetingData.summary.topics_discussed.forEach(topic => {
meetingNotes += `- ${topic}\n`;
});
}
// Action items
if (meetingData.summary?.action_items && Array.isArray(meetingData.summary.action_items) && meetingData.summary.action_items.length > 0) {
meetingNotes += `\n## Action Items\n`;
meetingData.summary.action_items.forEach(item => {
meetingNotes += `- ${item}\n`;
});
}
// Insights section
meetingNotes += `\n## Insights\n`;
if (meetingData.summary?.keywords && Array.isArray(meetingData.summary.keywords) && meetingData.summary.keywords.length > 0) {
meetingNotes += `**Keywords:** ${meetingData.summary.keywords.join(', ')}\n`;
}
if (meetingData.analytics?.sentiments) {
const sentiments = meetingData.analytics.sentiments;
meetingNotes += `**Sentiment:** ${sentiments.positive_pct}% positive, ${sentiments.negative_pct}% negative, ${sentiments.neutral_pct}% neutral\n`;
}
if (meetingData.summary?.meeting_type) {
meetingNotes += `**Meeting Type:** ${meetingData.summary.meeting_type}\n`;
}
// Resources section
meetingNotes += `\n## Resources\n`;
meetingNotes += `[View Full Transcript](${meetingData.transcript_url})\n`;
if (meetingData.recording_url) {
meetingNotes += `[Watch Recording](${meetingData.recording_url})\n`;
}
return meetingNotes;
}
static toMeetingCreateInput(
meetingData: FirefliesMeetingData,
noteId?: string
): MeetingCreateInput {
const durationMinutes = Math.round(meetingData.duration);
// Build input object with only defined values (omit null fields)
const input: MeetingCreateInput = {
name: meetingData.title,
meetingDate: meetingData.date,
duration: durationMinutes,
actionItemsCount: meetingData.summary?.action_items?.length || 0,
firefliesMeetingId: meetingData.id,
};
// Add direct relationship to note if noteId is provided
if (noteId) {
input.noteId = noteId;
}
// Only add optional fields if they have values
if (meetingData.summary?.meeting_type) {
input.meetingType = meetingData.summary.meeting_type;
}
if (meetingData.summary?.keywords && Array.isArray(meetingData.summary.keywords) && meetingData.summary.keywords.length > 0) {
input.keywords = meetingData.summary.keywords.join(', ');
}
if (meetingData.analytics?.sentiments?.positive_pct) {
input.sentimentScore = meetingData.analytics.sentiments.positive_pct / 100;
input.positivePercent = meetingData.analytics.sentiments.positive_pct;
}
if (meetingData.analytics?.sentiments?.negative_pct) {
input.negativePercent = meetingData.analytics.sentiments.negative_pct;
}
// Only add URLs if they are valid (not empty strings)
if (meetingData.transcript_url && meetingData.transcript_url.trim()) {
input.transcriptUrl = {
primaryLinkUrl: meetingData.transcript_url,
primaryLinkLabel: 'View Transcript'
};
}
if (meetingData.recording_url && meetingData.recording_url.trim()) {
input.recordingUrl = {
primaryLinkUrl: meetingData.recording_url,
primaryLinkLabel: 'Watch Recording'
};
}
if (meetingData.organizer_email) {
input.organizerEmail = meetingData.organizer_email;
}
// Set success status and timestamps
input.importStatus = 'SUCCESS';
input.lastImportAttempt = new Date().toISOString();
input.importAttempts = 1;
return input;
}
static toFailedMeetingCreateInput(
meetingId: string,
title: string,
error: string,
attempts: number = 1
): MeetingCreateInput {
const currentDate = new Date().toISOString();
return {
name: title || `Failed Meeting Import - ${meetingId}`,
meetingDate: currentDate,
duration: 0,
actionItemsCount: 0,
firefliesMeetingId: meetingId,
importStatus: 'FAILED',
importError: error,
lastImportAttempt: currentDate,
importAttempts: attempts,
};
}
}
@@ -1,4 +1,5 @@
export { config, main } from './receive-fireflies-notes';
// Serverless function entry point - re-exports from src/lib
export { config, main } from '../../../src';
export type {
FirefliesMeetingData,
FirefliesParticipant,
@@ -6,5 +7,5 @@ export type {
ProcessResult,
SummaryFetchConfig,
SummaryStrategy
} from './types';
} from '../../../src';
@@ -1,95 +0,0 @@
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
export interface LoggerConfig {
logLevel: LogLevel;
isTestEnvironment: boolean;
}
const LOG_LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
silent: 4,
};
/**
* App-level Fireflies application logger with configurable log levels.
*/
export class AppLogger {
private config: LoggerConfig;
private context: string;
constructor(context: string) {
this.context = context;
this.config = {
logLevel: this.parseLogLevel(process.env.LOG_LEVEL || 'error'),
isTestEnvironment: process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined,
};
}
private parseLogLevel(level: string): LogLevel {
const normalizedLevel = level.toLowerCase() as LogLevel;
return Object.keys(LOG_LEVELS).includes(normalizedLevel) ? normalizedLevel : 'error';
}
private shouldLog(level: LogLevel): boolean {
return LOG_LEVELS[level] >= LOG_LEVELS[this.config.logLevel];
}
/**
* Log debug information (LOG_LEVEL=debug)
*/
debug(message: string, ...args: any[]): void {
if (this.shouldLog('debug')) {
// eslint-disable-next-line no-console
console.log(`[${this.context}] ${message}`, ...args);
}
}
/**
* Log informational messages (LOG_LEVEL=info or lower)
*/
info(message: string, ...args: any[]): void {
if (this.shouldLog('info')) {
// eslint-disable-next-line no-console
console.log(`[${this.context}] ${message}`, ...args);
}
}
/**
* Log warnings (LOG_LEVEL=warn or lower)
*/
warn(message: string, ...args: any[]): void {
if (this.shouldLog('warn')) {
// eslint-disable-next-line no-console
console.warn(`[${this.context}] ${message}`, ...args);
}
}
/**
* Log errors (LOG_LEVEL=error or lower)
*/
error(message: string, ...args: any[]): void {
if (this.shouldLog('error')) {
// eslint-disable-next-line no-console
console.error(`[${this.context}] ${message}`, ...args);
}
}
/**
* Log critical errors that should ALWAYS be visible regardless of log level
* Use sparingly - only for fatal errors, security issues, or data corruption
*/
critical(message: string, ...args: any[]): void {
// eslint-disable-next-line no-console
console.error(`[${this.context}] CRITICAL: ${message}`, ...args);
}
}
/**
* Factory function to create loggers with automatic context detection
*/
export const createLogger = (context: string): AppLogger => {
return new AppLogger(context);
};
@@ -1,130 +0,0 @@
// Fireflies API Types
export type FirefliesParticipant = {
email: string;
name: string;
};
export type FirefliesWebhookPayload = {
meetingId: string;
eventType: string;
clientReferenceId?: string;
};
export type FirefliesMeetingData = {
id: string;
title: string;
date: string;
duration: number;
participants: FirefliesParticipant[];
organizer_email?: string;
summary: {
action_items: string[];
keywords?: string[];
overview: string;
gist?: string;
topics_discussed?: string[];
meeting_type?: string;
bullet_gist?: string;
};
analytics?: {
sentiments?: {
positive_pct: number;
negative_pct: number;
neutral_pct: number;
};
};
transcript_url: string;
recording_url?: string;
summary_status?: string;
};
// Configuration Types
export type SummaryStrategy = 'immediate_only' | 'immediate_with_retry' | 'delayed_polling' | 'basic_only';
export type SummaryFetchConfig = {
strategy: SummaryStrategy;
retryAttempts: number;
retryDelay: number;
pollInterval: number;
maxPolls: number;
};
export type WebhookConfig = {
secret: string;
apiUrl: string;
};
// Processing Result Types
export type ProcessResult = {
success: boolean;
meetingId?: string;
noteIds?: string[];
newContacts?: string[];
errors?: string[];
debug?: string[];
summaryReady?: boolean;
summaryPending?: boolean;
enhancementScheduled?: boolean;
actionItemsCount?: number;
sentimentScore?: number;
meetingType?: string;
keyTopics?: string[];
};
// Twenty CRM Types
export type GraphQLResponse<T> = {
data: T;
errors?: Array<{
message?: string;
extensions?: { code?: string }
}>;
};
export type IdNode = { id: string };
export type FindMeetingResponse = {
meetings: { edges: Array<{ node: IdNode }> };
};
export type FindPeopleResponse = {
people: { edges: Array<{ node: { id: string; emails: { primaryEmail: string } } }> };
};
export type CreatePersonResponse = {
createPerson: { id: string }
};
export type CreateNoteResponse = {
createNote: { id: string }
};
export type CreateMeetingResponse = {
createMeeting: { id: string }
};
export type Contact = {
id: string;
email: string;
};
export type MeetingCreateInput = {
name: string;
noteId?: string | null; // This is the relation field
meetingDate: string;
duration: number;
meetingType?: string | null;
keywords?: string | null;
sentimentScore?: number | null;
positivePercent?: number | null;
negativePercent?: number | null;
actionItemsCount: number;
transcriptUrl?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
recordingUrl?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
firefliesMeetingId: string;
organizerEmail?: string | null;
importStatus?: 'SUCCESS' | 'FAILED' | 'PENDING' | 'RETRYING' | null;
importError?: string | null;
lastImportAttempt?: string | null;
importAttempts?: number | null;
};
@@ -4,7 +4,7 @@ import {
main,
type FirefliesMeetingData,
type FirefliesWebhookPayload,
} from '../actions/receive-fireflies-notes';
} from '../';
// Helper to generate HMAC signature
const generateHMACSignature = (body: string, secret: string): string => {
@@ -39,10 +39,12 @@ const mockFirefliesApiResponseWithSummary = {
meeting_type: 'Sales Call',
bullet_gist: '• Demonstrated core product features\n• Discussed enterprise pricing\n• Addressed integration questions',
},
sentiments: { // Note: Raw API has sentiments at top level, not in analytics
positive_pct: 75,
negative_pct: 10,
neutral_pct: 15,
analytics: {
sentiments: {
positive_pct: 75,
negative_pct: 10,
neutral_pct: 15,
},
},
transcript_url: 'https://app.fireflies.ai/transcript/test-001',
video_url: 'https://app.fireflies.ai/recording/test-001',
@@ -81,7 +83,7 @@ const mockMeetingWithFullSummary: FirefliesMeetingData = {
},
},
transcript_url: 'https://app.fireflies.ai/transcript/test-001',
recording_url: 'https://app.fireflies.ai/recording/test-001',
video_url: 'https://app.fireflies.ai/recording/test-001',
summary_status: 'completed',
};
@@ -404,7 +406,11 @@ describe('Fireflies Webhook Integration v2', () => {
expect(result.success).toBe(true);
expect(result.summaryReady).toBe(true);
expect(result.actionItemsCount).toBe(3);
expect(result.sentimentScore).toBeCloseTo(0.75, 2); // Use toBeCloseTo for floating point comparison
expect(result.sentimentAnalysis).toEqual({
positive_pct: 75,
negative_pct: 10,
neutral_pct: 15,
});
expect(result.meetingType).toBe('Sales Call');
expect(result.keyTopics).toEqual(['product features', 'pricing discussion', 'integration capabilities', 'support options']);
});
@@ -565,7 +571,7 @@ describe('Fireflies Webhook Integration v2', () => {
const createNoteMock = jest.fn();
global.fetch = jest.fn().mockImplementation((url: string, options?: any) => {
global.fetch = jest.fn().mockImplementation((url: string, options?: RequestInit) => {
if (url === 'https://api.fireflies.ai/graphql') {
return Promise.resolve({
ok: true,
@@ -576,9 +582,9 @@ describe('Fireflies Webhook Integration v2', () => {
}
// Twenty API
const body = options?.body ? JSON.parse(options.body) : {};
if (body.query?.includes('createNote')) {
createNoteMock(body.variables);
const requestBody = options?.body ? JSON.parse(options.body as string) : {};
if (requestBody.query?.includes('createNote')) {
createNoteMock(requestBody.variables);
return Promise.resolve({
ok: true,
json: () => Promise.resolve({
@@ -610,7 +616,7 @@ describe('Fireflies Webhook Integration v2', () => {
expect(noteData.data.bodyV2.markdown).toContain('## Overview'); // Markdown header, not bold
expect(noteData.data.bodyV2.markdown).toContain('## Action Items'); // Markdown header, not bold
expect(noteData.data.bodyV2.markdown).toContain('**Sentiment:**'); // This is bold
expect(noteData.data.bodyV2.markdown).toContain('[View Full Transcript]');
expect(noteData.data.bodyV2.markdown).toContain('View Full Transcript on Fireflies');
});
it('should create meeting records for multi-party meetings', async () => {
@@ -624,7 +630,7 @@ describe('Fireflies Webhook Integration v2', () => {
const createMeetingMock = jest.fn();
global.fetch = jest.fn().mockImplementation((url: string, options?: any) => {
global.fetch = jest.fn().mockImplementation((url: string, options?: RequestInit) => {
if (url === 'https://api.fireflies.ai/graphql') {
return Promise.resolve({
ok: true,
@@ -635,9 +641,9 @@ describe('Fireflies Webhook Integration v2', () => {
}
// Twenty API
const body = options?.body ? JSON.parse(options.body) : {};
if (body.query?.includes('createMeeting')) {
createMeetingMock(body.variables);
const requestBody = options?.body ? JSON.parse(options.body as string) : {};
if (requestBody.query?.includes('createMeeting')) {
createMeetingMock(requestBody.variables);
return Promise.resolve({
ok: true,
json: () => Promise.resolve({
@@ -645,7 +651,7 @@ describe('Fireflies Webhook Integration v2', () => {
}),
});
}
if (body.query?.includes('createNote')) {
if (requestBody.query?.includes('createNote')) {
return Promise.resolve({
ok: true,
json: () => Promise.resolve({
@@ -694,7 +700,7 @@ describe('Fireflies Webhook Integration v2', () => {
});
it('should handle missing payload gracefully', async () => {
const result = await main(null as any, {});
const result = await main(null as unknown, {});
expect(result.success).toBe(false);
expect(result.errors).toBeDefined();
@@ -703,10 +709,10 @@ describe('Fireflies Webhook Integration v2', () => {
it('should handle invalid payload structure', async () => {
const invalidPayload = { invalid: 'data' };
const result = await main(invalidPayload as any, {});
const result = await main(invalidPayload as unknown, {});
expect(result.success).toBe(false);
expect(result.errors).toBeDefined();
expect(result.success).toBe(false);
expect(result.errors).toBeDefined();
});
});
});
@@ -0,0 +1,84 @@
import { HistoricalImporter } from '../historical-importer';
import type { FirefliesMeetingData, SummaryFetchConfig } from '../types';
const summaryConfig: SummaryFetchConfig = {
strategy: 'immediate_with_retry',
retryAttempts: 1,
retryDelay: 0,
pollInterval: 0,
maxPolls: 0,
};
const sampleMeeting: FirefliesMeetingData = {
id: 'm-1',
title: 'Sample',
date: new Date().toISOString(),
duration: 30,
participants: [],
summary: { action_items: [], overview: '' },
transcript_url: 'https://example.com',
};
describe('HistoricalImporter', () => {
const buildImporter = () => {
const firefliesClient = {
listTranscripts: jest.fn(),
fetchMeetingDataWithRetry: jest.fn(),
} as unknown as jest.Mocked<any>;
const twentyService = {
findMeetingByFirefliesId: jest.fn(),
matchParticipantsToContacts: jest.fn(),
createContactsForUnmatched: jest.fn(),
createNoteOnly: jest.fn(),
createMeeting: jest.fn(),
createNoteTarget: jest.fn(),
} as unknown as jest.Mocked<any>;
return { firefliesClient, twentyService };
};
it('skips meetings that already exist by firefliesMeetingId', async () => {
const { firefliesClient, twentyService } = buildImporter();
firefliesClient.listTranscripts.mockResolvedValue([{ id: 'existing' }]);
twentyService.findMeetingByFirefliesId.mockResolvedValue({ id: 'twenty-id' });
const importer = new HistoricalImporter(firefliesClient, twentyService);
const result = await importer.run(
{},
{ dryRun: false, autoCreateContacts: true, summaryConfig, plan: 'free' },
);
expect(result.skippedExisting).toBe(1);
expect(result.imported).toBe(0);
expect(twentyService.createMeeting).not.toHaveBeenCalled();
expect(result.statuses[0].status).toBe('skipped_existing');
});
it('supports dry-run without writing to Twenty', async () => {
const { firefliesClient, twentyService } = buildImporter();
firefliesClient.listTranscripts.mockResolvedValue([{ id: 'm-2' }]);
firefliesClient.fetchMeetingDataWithRetry.mockResolvedValue({
data: sampleMeeting,
summaryReady: false,
});
twentyService.findMeetingByFirefliesId.mockResolvedValue(undefined);
const importer = new HistoricalImporter(firefliesClient, twentyService);
const result = await importer.run(
{},
{ dryRun: true, autoCreateContacts: false, summaryConfig, plan: 'free' },
);
expect(result.imported).toBe(1);
expect(result.summaryPending).toBe(1);
expect(twentyService.createMeeting).not.toHaveBeenCalled();
expect(twentyService.createNoteOnly).not.toHaveBeenCalled();
expect(result.statuses).toHaveLength(1);
expect(result.statuses[0].status).toBe('pending_summary');
});
});
@@ -9,6 +9,7 @@ process.env.AUTO_CREATE_CONTACTS = 'true';
process.env.SERVER_URL = 'http://localhost:3000';
process.env.TWENTY_API_KEY = 'test-api-key';
process.env.LOG_LEVEL = 'silent';
process.env.CAPTURE_LOGS = 'false';
// Reset mocks before each test
beforeEach(() => {
@@ -0,0 +1,40 @@
import { WebhookHandler } from '../webhook-handler';
describe('WebhookHandler log capture', () => {
const originalEnv = process.env;
beforeEach(() => {
process.env = {
...originalEnv,
FIREFLIES_WEBHOOK_SECRET: 'testsecret',
FIREFLIES_API_KEY: '',
TWENTY_API_KEY: '',
CAPTURE_LOGS: 'false',
LOG_LEVEL: 'silent',
};
});
afterEach(() => {
process.env = originalEnv;
});
it('includes debug logs in response when CAPTURE_LOGS is true', async () => {
process.env.CAPTURE_LOGS = 'true';
const handler = new WebhookHandler();
const result = await handler.handle(null);
expect(result.success).toBe(false);
expect(Array.isArray(result.debug)).toBe(true);
});
it('omits debug logs when CAPTURE_LOGS is false', async () => {
process.env.CAPTURE_LOGS = 'false';
const handler = new WebhookHandler();
const result = await handler.handle(null);
expect(result.debug).toBeUndefined();
});
});
@@ -1,11 +0,0 @@
export {
config,
main,
type FirefliesMeetingData,
type FirefliesParticipant,
type FirefliesWebhookPayload,
type ProcessResult,
type SummaryFetchConfig,
type SummaryStrategy
} from '../../serverlessFunctions/receive-fireflies-notes/src/index';
@@ -0,0 +1,823 @@
import { createLogger } from './logger';
import {
FIREFLIES_PLANS,
type FirefliesMeetingData,
type FirefliesParticipant,
type FirefliesPlan,
type FirefliesTranscriptListItem,
type FirefliesTranscriptListOptions,
type SummaryFetchConfig
} from './types';
const logger = createLogger('fireflies-api');
export class FirefliesApiClient {
private apiKey: string;
constructor(apiKey: string) {
if (!apiKey) {
logger.critical('FIREFLIES_API_KEY is required but not provided - this is a critical configuration error');
throw new Error('FIREFLIES_API_KEY is required');
}
this.apiKey = apiKey;
}
async listTranscripts(options: FirefliesTranscriptListOptions = {}): Promise<FirefliesTranscriptListItem[]> {
const {
organizers,
participants,
hostEmail,
participantEmail,
userId,
channelId,
mine,
fromDate,
toDate,
pageSize = 50,
maxRecords = 500,
} = options;
const sanitizedOrganizers = organizers?.filter(Boolean);
const sanitizedParticipants = participants?.filter(Boolean);
const transcripts: FirefliesTranscriptListItem[] = [];
let skip = options.skip ?? 0;
const limit = options.limit ?? pageSize;
const baseQuery = `
query Transcripts(
$limit: Int
$skip: Int
$hostEmail: String
$participantEmail: String
$organizers: [String!]
$participants: [String!]
$userId: String
$channelId: String
$mine: Boolean
$date: Float
) {
transcripts(
limit: $limit
skip: $skip
host_email: $hostEmail
participant_email: $participantEmail
organizers: $organizers
participants: $participants
user_id: $userId
channel_id: $channelId
mine: $mine
date: $date
) {
id
title
date
duration
organizer_email
participants
transcript_url
meeting_link
meeting_info { summary_status }
}
}
`;
while (transcripts.length < maxRecords) {
const pageVariables = {
limit,
skip,
hostEmail,
participantEmail,
organizers: sanitizedOrganizers,
participants: sanitizedParticipants,
userId,
channelId,
mine,
date: fromDate,
};
const page = await this.executeTranscriptListQuery(baseQuery, pageVariables);
const normalized = page
.map((item) => {
const normalizedDate = this.normalizeDate(item.date);
return {
id: (item.id as string) || '',
title: (item.title as string) || 'Untitled Meeting',
date: normalizedDate,
duration: (item.duration as number) || 0,
organizer_email: item.organizer_email as string | undefined,
participants: Array.isArray(item.participants)
? (item.participants as string[])
: undefined,
transcript_url: item.transcript_url as string | undefined,
meeting_link: item.meeting_link as string | undefined,
summary_status: (item.meeting_info as { summary_status?: string } | undefined)?.summary_status,
};
})
.filter((item) => {
if (toDate && item.date) {
const itemTime = Date.parse(item.date);
if (!Number.isNaN(itemTime) && itemTime > toDate) {
return false;
}
}
return true;
});
transcripts.push(...normalized);
if (page.length < limit) {
break;
}
skip += limit;
}
if (transcripts.length > maxRecords) {
return transcripts.slice(0, maxRecords);
}
return transcripts;
}
async fetchMeetingData(
meetingId: string,
options?: { timeout?: number; plan?: FirefliesPlan }
): Promise<FirefliesMeetingData> {
const plan = options?.plan ?? FIREFLIES_PLANS.FREE;
const isPremiumPlan =
plan === FIREFLIES_PLANS.BUSINESS || plan === FIREFLIES_PLANS.ENTERPRISE;
// Minimal query for free plans - only basic fields available on all plans
// Note: audio_url requires Pro+, video_url requires Business+
const freeQuery = `
query GetTranscriptMinimal($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
date
duration
participants
organizer_email
transcript_url
meeting_link
}
}
`;
// Standard query for pro plans - adds speakers, summary, sentences, and audio_url (Pro+)
// Note: video_url requires Business+
const proQuery = `
query GetTranscriptBasic($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
date
duration
participants
organizer_email
speakers {
name
}
sentences {
index
speaker_name
text
start_time
end_time
}
summary {
overview
keywords
action_items
notes
gist
bullet_gist
short_summary
short_overview
outline
shorthand_bullet
}
meeting_info {
summary_status
}
transcript_url
audio_url
meeting_link
}
}
`;
// Full query for business/enterprise - includes all fields
const businessQuery = `
query GetTranscriptFull($transcriptId: String!) {
transcript(id: $transcriptId) {
id
title
date
duration
participants
organizer_email
analytics {
sentiments {
positive_pct
negative_pct
neutral_pct
}
categories {
questions
tasks
metrics
date_times
}
speakers {
speaker_id
name
duration
word_count
longest_monologue
filler_words
questions
words_per_minute
}
}
meeting_attendees {
displayName
email
phoneNumber
name
location
}
meeting_attendance {
name
join_time
leave_time
}
speakers {
name
}
sentences {
index
speaker_name
text
start_time
end_time
ai_filters {
task
question
sentiment
}
}
summary {
action_items
overview
keywords
notes
gist
bullet_gist
short_summary
short_overview
outline
shorthand_bullet
topics_discussed
meeting_type
transcript_chapters
}
meeting_info {
summary_status
}
transcript_url
audio_url
video_url
meeting_link
}
}
`;
// Select query based on plan
const queryToUse = isPremiumPlan ? businessQuery :
(plan === FIREFLIES_PLANS.PRO ? proQuery : freeQuery);
const planFeatures = {
[FIREFLIES_PLANS.FREE]: 'basic fields only (no summary, no audio/video)',
[FIREFLIES_PLANS.PRO]: 'summary, speakers, audio_url',
[FIREFLIES_PLANS.BUSINESS]: 'full access including analytics, video_url',
[FIREFLIES_PLANS.ENTERPRISE]: 'full access including analytics, video_url',
};
logger.debug(`using ${plan} plan query (${planFeatures[plan]})`);
try {
return await this.executeTranscriptQuery({
meetingId,
query: queryToUse,
timeout: options?.timeout,
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
// Detect plan-specific errors
const requiresBusiness = message.toLowerCase().includes('business or higher');
const requiresPro = message.toLowerCase().includes('pro or higher');
const planError = requiresBusiness || requiresPro ||
message.toLowerCase().includes('higher plan') ||
message.includes('Cannot query field');
// Fallback cascade: business -> pro -> free
if (planError) {
if (isPremiumPlan) {
logger.warn(`Plan limitation detected (configured: ${plan}), falling back to pro query`);
try {
return await this.executeTranscriptQuery({
meetingId,
query: proQuery,
timeout: options?.timeout,
});
} catch (proError) {
const proMessage = proError instanceof Error ? proError.message : String(proError);
if (proMessage.toLowerCase().includes('plan') || proMessage.includes('Cannot query field')) {
logger.warn('Pro query also failed, falling back to minimal free query');
return this.executeTranscriptQuery({
meetingId,
query: freeQuery,
timeout: options?.timeout,
});
}
throw proError;
}
} else if (plan === FIREFLIES_PLANS.PRO) {
logger.warn(`Pro plan query failed (${requiresBusiness ? 'requires Business+' : 'unknown restriction'}), falling back to free query`);
return this.executeTranscriptQuery({
meetingId,
query: freeQuery,
timeout: options?.timeout,
});
} else {
// Already using free query - some field might still be restricted
logger.error(
'Fireflies API rejected the minimal free query. This may indicate: ' +
'1) The transcript ID is invalid, or ' +
'2) Your API key does not have access to this transcript, or ' +
'3) An unexpected API restriction : open an issue'
);
}
}
throw error;
}
}
async fetchMeetingDataWithRetry(
meetingId: string,
config: SummaryFetchConfig,
plan: FirefliesPlan = FIREFLIES_PLANS.FREE
): Promise<{ data: FirefliesMeetingData; summaryReady: boolean }> {
// immediate_only: single attempt, no retries
if (config.strategy === 'immediate_only') {
logger.debug(`fetching meeting ${meetingId} (strategy: immediate_only)`);
const meetingData = await this.fetchMeetingData(meetingId, { timeout: 10000, plan });
const ready = this.isSummaryReady(meetingData);
logger.debug(`summary ready: ${ready}`);
return { data: meetingData, summaryReady: ready };
}
// immediate_with_retry: retry with linear backoff
logger.debug(`fetching meeting ${meetingId} (strategy: immediate_with_retry, maxAttempts: ${config.retryAttempts})`);
for (let attempt = 1; attempt <= config.retryAttempts; attempt++) {
try {
const meetingData = await this.fetchMeetingData(meetingId, { timeout: 10000, plan });
const ready = this.isSummaryReady(meetingData);
logger.debug(`attempt ${attempt}/${config.retryAttempts}: summary ready=${ready}`);
if (ready) {
return { data: meetingData, summaryReady: true };
}
if (attempt < config.retryAttempts) {
const delayMs = config.retryDelay * attempt;
logger.debug(`summary not ready, waiting ${delayMs}ms before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, delayMs));
} else {
logger.debug(`max retries reached, returning partial data`);
return { data: meetingData, summaryReady: false };
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error);
logger.error(`attempt ${attempt}/${config.retryAttempts} failed: ${errorMsg}`);
if (attempt === config.retryAttempts) {
throw error;
}
const delayMs = config.retryDelay * attempt;
logger.debug(`retrying in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
throw new Error('Failed to fetch meeting data after retries');
}
private async executeTranscriptQuery({
meetingId,
query,
timeout,
}: {
meetingId: string;
query: string;
timeout?: number;
}): Promise<FirefliesMeetingData> {
const controller = new AbortController();
const timeoutId = timeout ? setTimeout(() => controller.abort(), timeout) : null;
try {
const response = await fetch('https://api.fireflies.ai/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
query,
variables: { transcriptId: meetingId },
}),
signal: controller.signal,
});
if (timeoutId) clearTimeout(timeoutId);
if (!response.ok) {
let errorDetails = `Fireflies API request failed with status ${response.status}`;
try {
const errorBody = await response.text();
if (errorBody) {
errorDetails += `: ${errorBody}`;
}
} catch {
// Ignore if we can't read the response body
}
throw new Error(errorDetails);
}
const json = await response.json() as {
data?: { transcript?: Record<string, unknown> };
errors?: Array<{ message?: string }>;
};
if (json.errors && json.errors.length > 0) {
throw new Error(`Fireflies API error: ${json.errors[0]?.message || 'Unknown error'}`);
}
const transcript = json.data?.transcript;
if (!transcript) {
throw new Error('Invalid response from Fireflies API: missing transcript data');
}
return this.transformMeetingData(transcript, meetingId);
} finally {
if (timeoutId) clearTimeout(timeoutId);
}
}
private isSummaryReady(meetingData: FirefliesMeetingData): boolean {
return (
(meetingData.summary?.action_items?.length > 0) ||
(meetingData.summary?.overview?.length > 0) ||
meetingData.summary_status === 'completed'
);
}
private extractAllParticipants(transcript: Record<string, unknown>): FirefliesParticipant[] {
const participantsWithEmails: FirefliesParticipant[] = [];
const participantsNameOnly: FirefliesParticipant[] = [];
logger.debug('=== PARTICIPANT EXTRACTION DEBUG ===');
logger.debug('participants field:', JSON.stringify(transcript.participants));
logger.debug('meeting_attendees field:', JSON.stringify(transcript.meeting_attendees));
logger.debug('speakers field:', (transcript.speakers as Array<{ name: string }>)?.map((s) => s.name));
logger.debug('meeting_attendance field:', (transcript.meeting_attendance as Array<{ name: string }>)?.map((a) => a.name));
logger.debug('organizer_email:', transcript.organizer_email);
const isEmail = (str: string): boolean => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str.trim());
};
const isDuplicate = (name: string, email: string): boolean => {
const nameLower = name.toLowerCase().trim();
const emailLower = email.toLowerCase().trim();
return participantsWithEmails.some(p =>
p.name.toLowerCase().trim() === nameLower ||
(email && p.email.toLowerCase() === emailLower)
) || participantsNameOnly.some(p =>
p.name.toLowerCase().trim() === nameLower
);
};
// 1. Extract from legacy participants field (with emails)
if (transcript.participants && Array.isArray(transcript.participants)) {
transcript.participants.forEach((participant: string) => {
const parts = participant.split(',').map(p => p.trim());
parts.forEach(part => {
const emailMatch = part.match(/<([^>]+)>/);
const email = emailMatch ? emailMatch[1] : '';
const name = emailMatch
? part.substring(0, part.indexOf('<')).trim()
: part.trim();
if (isEmail(name)) {
logger.debug(`Skipping participant with email as name: "${name}"`);
return;
}
if (!name) {
return;
}
if (isDuplicate(name, email)) {
logger.debug(`Skipping duplicate participant: "${name}" <${email}>`);
return;
}
if (name && email) {
participantsWithEmails.push({ name, email });
} else if (name) {
participantsNameOnly.push({ name, email: '' });
}
});
});
}
// 2. Extract from meeting_attendees field (structured)
if (transcript.meeting_attendees && Array.isArray(transcript.meeting_attendees)) {
transcript.meeting_attendees.forEach((attendee: Record<string, unknown>) => {
const name = (attendee.displayName || attendee.name || '') as string;
const email = (attendee.email || '') as string;
if (isEmail(name)) {
logger.debug(`Skipping attendee with email as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, email)) {
if (email) {
participantsWithEmails.push({ name, email });
} else {
participantsNameOnly.push({ name, email: '' });
}
}
});
}
// 3. Extract from speakers field (name only)
if (transcript.speakers && Array.isArray(transcript.speakers)) {
transcript.speakers.forEach((speaker: Record<string, unknown>) => {
const name = (speaker.name || '') as string;
if (isEmail(name)) {
logger.debug(`Skipping speaker with email as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, '')) {
participantsNameOnly.push({ name, email: '' });
}
});
}
// 4. Extract from meeting_attendance field (name only)
if (transcript.meeting_attendance && Array.isArray(transcript.meeting_attendance)) {
transcript.meeting_attendance.forEach((attendance: Record<string, unknown>) => {
const name = (attendance.name || '') as string;
if (isEmail(name) || name.includes(',')) {
logger.debug(`Skipping attendance with email/list as name: "${name}"`);
return;
}
if (name && !isDuplicate(name, '')) {
participantsNameOnly.push({ name, email: '' });
}
});
}
// 5. Add organizer email if available and not already included
const organizerEmail = transcript.organizer_email as string | undefined;
if (organizerEmail) {
const existsWithEmail = participantsWithEmails.some(p =>
p.email.toLowerCase() === organizerEmail.toLowerCase()
);
if (!existsWithEmail) {
let organizerName = '';
const emailUsername = organizerEmail.split('@')[0].toLowerCase();
const emailNameVariations = [emailUsername];
if (transcript.speakers && Array.isArray(transcript.speakers)) {
const potentialOrganizerSpeaker = transcript.speakers.find((speaker: Record<string, unknown>) => {
const name = ((speaker.name || '') as string).toLowerCase();
return emailNameVariations.some(variation =>
name.includes(variation) || variation.includes(name)
);
}) as Record<string, unknown> | undefined;
if (potentialOrganizerSpeaker) {
organizerName = potentialOrganizerSpeaker.name as string;
}
}
if (!organizerName && transcript.meeting_attendance && Array.isArray(transcript.meeting_attendance)) {
const potentialOrganizerAttendance = transcript.meeting_attendance.find((attendance: Record<string, unknown>) => {
const name = ((attendance.name || '') as string).toLowerCase();
return emailNameVariations.some(variation =>
name.includes(variation) || variation.includes(name)
);
}) as Record<string, unknown> | undefined;
if (potentialOrganizerAttendance) {
organizerName = potentialOrganizerAttendance.name as string;
}
}
if (organizerName) {
participantsWithEmails.push({ name: organizerName, email: organizerEmail });
const nameIndex = participantsNameOnly.findIndex(p =>
p.name.toLowerCase().includes(organizerName.toLowerCase()) ||
organizerName.toLowerCase().includes(p.name.toLowerCase())
);
if (nameIndex !== -1) {
participantsNameOnly.splice(nameIndex, 1);
}
} else {
participantsWithEmails.push({ name: 'Meeting Organizer', email: organizerEmail });
}
}
}
const allParticipants = [...participantsWithEmails, ...participantsNameOnly];
logger.debug('=== EXTRACTED PARTICIPANTS ===');
logger.debug('With emails:', participantsWithEmails.length, JSON.stringify(participantsWithEmails));
logger.debug('Name only:', participantsNameOnly.length, JSON.stringify(participantsNameOnly));
logger.debug('Total:', allParticipants.length);
return allParticipants;
}
private transformMeetingData(transcript: Record<string, unknown>, meetingId: string): FirefliesMeetingData {
let dateString: string;
if (transcript.date) {
if (typeof transcript.date === 'number') {
dateString = new Date(transcript.date).toISOString();
} else if (typeof transcript.date === 'string') {
const parsed = Number(transcript.date);
if (!isNaN(parsed)) {
dateString = new Date(parsed).toISOString();
} else {
dateString = transcript.date;
}
} else {
dateString = new Date().toISOString();
}
} else {
dateString = new Date().toISOString();
}
const summary = transcript.summary as Record<string, unknown> | undefined;
const analytics = transcript.analytics as Record<string, unknown> | undefined;
const sentiments = analytics?.sentiments as Record<string, number> | undefined;
const categories = analytics?.categories as Record<string, number> | undefined;
const speakersAnalytics = analytics?.speakers as Array<Record<string, unknown>> | undefined;
const meetingInfo = transcript.meeting_info as Record<string, unknown> | undefined;
// Transform sentences array
const rawSentences = transcript.sentences as Array<Record<string, unknown>> | undefined;
const sentences = rawSentences?.map(s => ({
index: (s.index as number) || 0,
speaker_name: (s.speaker_name as string) || 'Unknown',
text: (s.text as string) || '',
start_time: (s.start_time as string) || '0',
end_time: (s.end_time as string) || '0',
ai_filters: s.ai_filters as { task?: boolean; question?: boolean; sentiment?: string } | undefined,
}));
// Transform speaker analytics
const speakers = speakersAnalytics?.map(sp => ({
speaker_id: (sp.speaker_id as string) || '',
name: (sp.name as string) || 'Unknown',
duration: (sp.duration as number) || 0,
word_count: (sp.word_count as number) || 0,
longest_monologue: (sp.longest_monologue as number) || 0,
filler_words: (sp.filler_words as number) || 0,
questions: (sp.questions as number) || 0,
words_per_minute: (sp.words_per_minute as number) || 0,
}));
return {
id: (transcript.id as string) || meetingId,
title: (transcript.title as string) || 'Untitled Meeting',
date: dateString,
duration: (transcript.duration as number) || 0,
participants: this.extractAllParticipants(transcript),
organizer_email: transcript.organizer_email as string | undefined,
sentences,
summary: {
// action_items can be string or array - normalize to array
action_items: Array.isArray(summary?.action_items)
? summary.action_items as string[]
: (typeof summary?.action_items === 'string' && summary.action_items.trim()
? summary.action_items.split('\n').filter((item: string) => item.trim())
: []),
overview: (summary?.overview as string) || '',
notes: summary?.notes as string | undefined,
gist: summary?.gist as string | undefined,
bullet_gist: summary?.bullet_gist as string | undefined,
short_summary: summary?.short_summary as string | undefined,
short_overview: summary?.short_overview as string | undefined,
outline: summary?.outline as string | undefined,
shorthand_bullet: summary?.shorthand_bullet as string | undefined,
keywords: summary?.keywords as string[] | undefined,
topics_discussed: summary?.topics_discussed as string[] | undefined,
meeting_type: summary?.meeting_type as string | undefined,
transcript_chapters: summary?.transcript_chapters as string[] | undefined,
},
analytics: (sentiments || categories || speakers) ? {
sentiments: sentiments ? {
positive_pct: sentiments.positive_pct || 0,
negative_pct: sentiments.negative_pct || 0,
neutral_pct: sentiments.neutral_pct || 0,
} : undefined,
categories: categories ? {
questions: categories.questions || 0,
tasks: categories.tasks || 0,
metrics: categories.metrics || 0,
date_times: categories.date_times || 0,
} : undefined,
speakers,
} : undefined,
meeting_info: meetingInfo ? {
summary_status: meetingInfo.summary_status as string | undefined,
} : undefined,
// URLs by plan availability:
transcript_url: (transcript.transcript_url as string) || `https://app.fireflies.ai/view/${meetingId}`,
audio_url: transcript.audio_url as string | undefined, // Pro+
video_url: transcript.video_url as string | undefined, // Business+
meeting_link: transcript.meeting_link as string | undefined, // All plans
summary_status: (meetingInfo?.summary_status as string) || (transcript.summary_status as string) || undefined,
};
}
private async executeTranscriptListQuery(
query: string,
variables: Record<string, unknown>,
): Promise<Array<Record<string, unknown>>> {
const response = await fetch('https://api.fireflies.ai/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
body: JSON.stringify({ query, variables }),
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(`Fireflies transcripts request failed: ${response.status} ${errorBody}`);
}
const json = await response.json() as {
data?: { transcripts?: Array<Record<string, unknown>> };
errors?: Array<{ message?: string }>;
};
if (json.errors && json.errors.length > 0) {
const message = json.errors[0]?.message || 'Unknown error';
throw new Error(`Fireflies API error: ${message}`);
}
return json.data?.transcripts ?? [];
}
private normalizeDate(dateValue: unknown): string | undefined {
if (!dateValue) {
return undefined;
}
if (typeof dateValue === 'number') {
return new Date(dateValue).toISOString();
}
if (typeof dateValue === 'string') {
const parsed = Number(dateValue);
if (!Number.isNaN(parsed)) {
return new Date(parsed).toISOString();
}
return dateValue;
}
return undefined;
}
}
@@ -0,0 +1,259 @@
import type { FirefliesMeetingData, FirefliesSentence, MeetingCreateInput } from './types';
export class MeetingFormatter {
// Format timestamp from seconds to MM:SS
private static formatTimestamp(timeStr: string): string {
const seconds = parseFloat(timeStr);
if (isNaN(seconds)) return '00:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
// Format full transcript from sentences
private static formatTranscript(sentences: FirefliesSentence[]): string {
if (!sentences || sentences.length === 0) return '';
let transcript = '';
let currentSpeaker = '';
for (const sentence of sentences) {
const timestamp = this.formatTimestamp(sentence.start_time);
const speaker = sentence.speaker_name || 'Unknown';
// Add speaker header when speaker changes
if (speaker !== currentSpeaker) {
currentSpeaker = speaker;
transcript += `\n**${speaker}** [${timestamp}]\n`;
}
transcript += `${sentence.text} `;
}
return transcript.trim();
}
static formatNoteBody(meetingData: FirefliesMeetingData): string {
const meetingDate = meetingData.date ? new Date(meetingData.date) : null;
const hasValidDate = meetingDate instanceof Date && !Number.isNaN(meetingDate.getTime());
const formattedDate = hasValidDate
? meetingDate.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
: 'Unknown date';
const durationMinutes = Math.round(meetingData.duration);
let noteBody = `**Date:** ${formattedDate}\n`;
noteBody += `**Duration:** ${durationMinutes} minutes\n`;
if (meetingData.participants.length > 0) {
const participantNames = meetingData.participants.map(p => p.name).join(', ');
noteBody += `**Participants:** ${participantNames}\n`;
}
// Overview section
if (meetingData.summary?.overview) {
noteBody += `\n## Overview\n${meetingData.summary.overview}\n`;
}
// Detailed AI Notes (the rich content from Fireflies)
if (meetingData.summary?.notes) {
noteBody += `\n## Meeting Notes\n${meetingData.summary.notes}\n`;
}
// Bullet gist with emojis (if available and different from notes)
if (meetingData.summary?.bullet_gist && !meetingData.summary?.notes) {
noteBody += `\n## Key Points\n${meetingData.summary.bullet_gist}\n`;
}
// Meeting outline with timestamps (shorthand_bullet contains the timestamped outline)
const outline = meetingData.summary?.outline || meetingData.summary?.shorthand_bullet;
if (outline) {
noteBody += `\n## Outline\n${outline}\n`;
}
// Key topics
if (meetingData.summary?.topics_discussed?.length) {
noteBody += `\n## Key Topics\n`;
meetingData.summary.topics_discussed.forEach(topic => {
noteBody += `- ${topic}\n`;
});
}
// Action items
if (meetingData.summary?.action_items?.length) {
noteBody += `\n## Action Items\n`;
meetingData.summary.action_items.forEach(item => {
noteBody += `- [ ] ${item}\n`;
});
}
// Insights section
noteBody += `\n## Insights\n`;
if (meetingData.summary?.keywords?.length) {
noteBody += `**Keywords:** ${meetingData.summary.keywords.join(', ')}\n`;
}
if (meetingData.analytics?.sentiments) {
const sentiments = meetingData.analytics.sentiments;
noteBody += `**Sentiment:** ${sentiments.positive_pct}% positive, ${sentiments.negative_pct}% negative, ${sentiments.neutral_pct}% neutral\n`;
}
if (meetingData.summary?.meeting_type) {
noteBody += `**Meeting Type:** ${meetingData.summary.meeting_type}\n`;
}
// Speaker analytics (Business+)
if (meetingData.analytics?.speakers?.length) {
noteBody += `\n### Speaker Stats\n`;
for (const speaker of meetingData.analytics.speakers) {
const talkTime = Math.round(speaker.duration / 60);
noteBody += `- **${speaker.name}**: ${talkTime} min talk time, ${speaker.word_count} words, ${speaker.questions} questions\n`;
}
}
// Meeting metrics (Business+)
if (meetingData.analytics?.categories) {
const cats = meetingData.analytics.categories;
noteBody += `\n### Meeting Metrics\n`;
noteBody += `- Questions asked: ${cats.questions}\n`;
noteBody += `- Tasks identified: ${cats.tasks}\n`;
if (cats.metrics > 0) noteBody += `- Metrics mentioned: ${cats.metrics}\n`;
if (cats.date_times > 0) noteBody += `- Dates/times discussed: ${cats.date_times}\n`;
}
// Resources section
noteBody += `\n## Resources\n`;
noteBody += `[View Full Transcript on Fireflies](${meetingData.transcript_url})\n`;
if (meetingData.video_url) {
noteBody += `[Watch Video Recording](${meetingData.video_url})\n`;
}
if (meetingData.audio_url) {
noteBody += `[Listen to Audio](${meetingData.audio_url})\n`;
}
if (meetingData.meeting_link) {
noteBody += `[Original Meeting Link](${meetingData.meeting_link})\n`;
}
return noteBody;
}
static toMeetingCreateInput(
meetingData: FirefliesMeetingData,
noteId?: string
): MeetingCreateInput {
const durationMinutes = Math.round(meetingData.duration);
const hasSummary = Boolean(meetingData.summary?.overview || meetingData.summary?.action_items?.length);
const hasAnalytics = Boolean(meetingData.analytics?.sentiments);
// Build input object with only defined values (omit null fields)
const input: MeetingCreateInput = {
name: meetingData.title,
meetingDate: meetingData.date,
duration: durationMinutes,
actionItemsCount: meetingData.summary?.action_items?.length || 0,
firefliesMeetingId: meetingData.id,
};
// Add direct relationship to note if noteId is provided
if (noteId) {
input.noteId = noteId;
}
// Basic fields (All plans)
if (meetingData.organizer_email) {
input.organizerEmail = meetingData.organizer_email;
}
if (meetingData.transcript_url?.trim()) {
input.transcriptUrl = {
primaryLinkUrl: meetingData.transcript_url,
primaryLinkLabel: 'View Transcript'
};
}
if (meetingData.meeting_link?.trim()) {
input.meetingLink = {
primaryLinkUrl: meetingData.meeting_link,
primaryLinkLabel: 'Join Meeting'
};
}
// Pro+ fields (transcript, summary, notes, keywords, audio)
if (meetingData.sentences?.length) {
input.transcript = this.formatTranscript(meetingData.sentences);
}
if (meetingData.summary?.overview) {
input.overview = meetingData.summary.overview;
}
if (meetingData.summary?.notes) {
input.notes = meetingData.summary.notes;
}
if (meetingData.summary?.keywords?.length) {
input.keywords = meetingData.summary.keywords.join(', ');
}
if (meetingData.audio_url?.trim()) {
input.audioUrl = {
primaryLinkUrl: meetingData.audio_url,
primaryLinkLabel: 'Listen to Audio'
};
}
// Business+ fields (analytics, video, detailed summary)
if (meetingData.summary?.meeting_type) {
input.meetingType = meetingData.summary.meeting_type;
}
if (meetingData.summary?.topics_discussed?.length) {
input.topics = meetingData.summary.topics_discussed.join(', ');
}
if (meetingData.analytics?.sentiments) {
const sentiments = meetingData.analytics.sentiments;
input.positivePercent = sentiments.positive_pct;
input.negativePercent = sentiments.negative_pct;
input.neutralPercent = sentiments.neutral_pct;
}
if (meetingData.video_url?.trim()) {
input.videoUrl = {
primaryLinkUrl: meetingData.video_url,
primaryLinkLabel: 'Watch Video'
};
}
// Import status based on data completeness
const isPartial = !hasSummary && !hasAnalytics;
input.importStatus = isPartial ? 'PARTIAL' : 'SUCCESS';
input.lastImportAttempt = new Date().toISOString();
input.importAttempts = 1;
return input;
}
static toFailedMeetingCreateInput(
meetingId: string,
title: string,
error: string,
attempts: number = 1
): MeetingCreateInput {
const currentDate = new Date().toISOString();
return {
name: title || `Failed Meeting Import - ${meetingId}`,
meetingDate: currentDate,
duration: 0,
actionItemsCount: 0,
firefliesMeetingId: meetingId,
importStatus: 'FAILED',
importError: error,
lastImportAttempt: currentDate,
importAttempts: attempts,
};
}
}
@@ -0,0 +1,161 @@
import { type FirefliesApiClient } from './fireflies-api-client';
import { MeetingFormatter } from './formatters';
import { createLogger } from './logger';
import { type TwentyCrmService } from './twenty-crm-service';
import type {
FirefliesPlan,
FirefliesTranscriptListOptions,
SummaryFetchConfig,
} from './types';
const logger = createLogger('historical-importer');
export type HistoricalImportFilters = FirefliesTranscriptListOptions;
export type HistoricalImportOptions = {
dryRun?: boolean;
autoCreateContacts: boolean;
summaryConfig: SummaryFetchConfig;
plan: FirefliesPlan;
};
export type HistoricalImportResult = {
dryRun: boolean;
totalListed: number;
imported: number;
skippedExisting: number;
failed: Array<{ meetingId: string; reason: string }>;
summaryPending: number;
statuses: Array<{
meetingId: string;
title?: string;
status: 'imported' | 'skipped_existing' | 'failed' | 'dry_run' | 'pending_summary';
reason?: string;
}>;
};
export class HistoricalImporter {
private firefliesClient: FirefliesApiClient;
private twentyService: TwentyCrmService;
constructor(firefliesClient: FirefliesApiClient, twentyService: TwentyCrmService) {
this.firefliesClient = firefliesClient;
this.twentyService = twentyService;
}
async run(
filters: HistoricalImportFilters,
options: HistoricalImportOptions,
): Promise<HistoricalImportResult> {
const { dryRun = false, autoCreateContacts, summaryConfig, plan } = options;
logger.info('Listing Fireflies transcripts for historical import...');
const transcripts = await this.firefliesClient.listTranscripts(filters);
logger.info(`Found ${transcripts.length} transcript(s) to process`);
let imported = 0;
let skippedExisting = 0;
let summaryPending = 0;
const failed: Array<{ meetingId: string; reason: string }> = [];
const statuses: HistoricalImportResult['statuses'] = [];
for (const transcript of transcripts) {
const meetingId = transcript.id;
try {
const existing = await this.twentyService.findMeetingByFirefliesId(meetingId);
if (existing) {
logger.debug(`Skipping ${meetingId}: already exists in Twenty (${existing.id})`);
skippedExisting += 1;
statuses.push({
meetingId,
title: transcript.title,
status: 'skipped_existing',
});
continue;
}
logger.info(`Fetching meeting ${meetingId} details`);
const { data: meetingData, summaryReady } =
await this.firefliesClient.fetchMeetingDataWithRetry(
meetingId,
summaryConfig,
plan,
);
const isPendingSummary = summaryReady === false;
if (isPendingSummary) {
summaryPending += 1;
}
if (dryRun) {
logger.info(`[dry-run] Would import meeting "${meetingData.title}" (${meetingId})`);
imported += 1;
statuses.push({
meetingId,
title: meetingData.title,
status: isPendingSummary ? 'pending_summary' : 'dry_run',
reason: isPendingSummary ? 'summary not ready' : undefined,
});
continue;
}
const { matchedContacts, unmatchedParticipants } =
await this.twentyService.matchParticipantsToContacts(
meetingData.participants,
);
const newContactIds = autoCreateContacts
? await this.twentyService.createContactsForUnmatched(unmatchedParticipants)
: [];
const allContactIds = [...matchedContacts.map(({ id }) => id), ...newContactIds];
const noteBody = MeetingFormatter.formatNoteBody(meetingData);
const noteId = await this.twentyService.createNoteOnly(
`Meeting: ${meetingData.title}`,
noteBody,
);
const meetingInput = MeetingFormatter.toMeetingCreateInput(meetingData, noteId);
const createdMeetingId = await this.twentyService.createMeeting(meetingInput);
for (const contactId of allContactIds) {
await this.twentyService.createNoteTarget(noteId, contactId);
}
logger.info(
`Imported meeting "${meetingData.title}" (${meetingId}) as ${createdMeetingId}`,
);
imported += 1;
statuses.push({
meetingId,
title: meetingData.title,
status: isPendingSummary ? 'pending_summary' : 'imported',
reason: isPendingSummary ? 'summary not ready' : undefined,
});
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
logger.error(`Failed to import meeting ${meetingId}: ${reason}`);
failed.push({ meetingId, reason });
statuses.push({
meetingId,
title: transcript.title,
status: 'failed',
reason,
});
}
}
return {
dryRun,
totalListed: transcripts.length,
imported,
skippedExisting,
failed,
summaryPending,
statuses,
};
}
}
@@ -0,0 +1,31 @@
// Main exports for the Fireflies integration
export { config, main } from './receive-fireflies-notes';
// Types
export type {
FirefliesMeetingData,
FirefliesParticipant,
FirefliesWebhookPayload,
ProcessResult,
SummaryFetchConfig,
SummaryStrategy
} from './types';
// Services (for advanced usage)
export { FirefliesApiClient } from './fireflies-api-client';
export { MeetingFormatter } from './formatters';
export { TwentyCrmService } from './twenty-crm-service';
export { WebhookHandler } from './webhook-handler';
// Objects
export { Meeting } from './objects';
// Utilities
export { createLogger } from './logger';
export { getApiUrl, getSummaryFetchConfig, shouldAutoCreateContacts, toBoolean } from './utils';
export {
getWebhookSecretFingerprint,
isValidFirefliesPayload,
verifyWebhookSignature
} from './webhook-validator';
@@ -0,0 +1,142 @@
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
export type LoggerConfig = {
logLevel: LogLevel;
isTestEnvironment: boolean;
captureForResponse: boolean;
};
const LOG_LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
silent: 4,
};
const loggerRegistry = new Set<AppLogger>();
export class AppLogger {
private config: LoggerConfig;
private context: string;
private capturedLogs: string[] = [];
constructor(context: string) {
this.context = context;
this.config = {
logLevel: this.parseLogLevel(process.env.LOG_LEVEL || 'error'),
isTestEnvironment: process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined,
captureForResponse: process.env.CAPTURE_LOGS === 'true',
};
// Silence logs in test environment unless explicitly overridden
if (this.config.isTestEnvironment && process.env.LOG_LEVEL === undefined) {
this.config.logLevel = 'silent';
}
}
private parseLogLevel(level: string): LogLevel {
const normalizedLevel = level.toLowerCase() as LogLevel;
return Object.keys(LOG_LEVELS).includes(normalizedLevel) ? normalizedLevel : 'error';
}
private shouldLog(level: LogLevel): boolean {
return LOG_LEVELS[level] >= LOG_LEVELS[this.config.logLevel];
}
private safeStringify(value: unknown): string {
try {
if (typeof value === 'string') return value;
return JSON.stringify(value);
} catch {
return '[unserializable]';
}
}
private captureLog(level: LogLevel, message: string, ...args: unknown[]): void {
if (!this.config.captureForResponse) {
return;
}
const timestamp = new Date().toISOString();
const formattedMessage =
args.length > 0
? `${message} ${args.map((arg) => this.safeStringify(arg)).join(' ')}`
: message;
this.capturedLogs.push(
`[${timestamp}] [${level.toUpperCase()}] [${this.context}] ${formattedMessage}`,
);
}
debug(message: string, ...args: unknown[]): void {
this.captureLog('debug', message, ...args);
if (this.shouldLog('debug')) {
// eslint-disable-next-line no-console
console.log(`[${this.context}] ${message}`, ...args);
}
}
info(message: string, ...args: unknown[]): void {
this.captureLog('info', message, ...args);
if (this.shouldLog('info')) {
// eslint-disable-next-line no-console
console.log(`[${this.context}] ${message}`, ...args);
}
}
warn(message: string, ...args: unknown[]): void {
this.captureLog('warn', message, ...args);
if (this.shouldLog('warn')) {
// eslint-disable-next-line no-console
console.warn(`[${this.context}] ${message}`, ...args);
}
}
error(message: string, ...args: unknown[]): void {
this.captureLog('error', message, ...args);
if (this.shouldLog('error')) {
// eslint-disable-next-line no-console
console.error(`[${this.context}] ${message}`, ...args);
}
}
// For fatal errors, security issues, or data corruption - always visible
critical(message: string, ...args: unknown[]): void {
this.captureLog('error', `CRITICAL: ${message}`, ...args);
// eslint-disable-next-line no-console
console.error(`[${this.context}] CRITICAL: ${message}`, ...args);
}
getCapturedLogs(): string[] {
return [...this.capturedLogs];
}
clearCapturedLogs(): void {
this.capturedLogs = [];
}
}
export const createLogger = (context: string): AppLogger => {
const logger = new AppLogger(context);
loggerRegistry.add(logger);
return logger;
};
export const removeLogger = (logger: AppLogger): void => {
loggerRegistry.delete(logger);
};
export const getAllCapturedLogs = (): string[] => {
const allLogs: string[] = [];
for (const logger of loggerRegistry) {
allLogs.push(...logger.getCapturedLogs());
}
return allLogs.sort();
};
export const clearAllCapturedLogs = (): void => {
for (const logger of loggerRegistry) {
logger.clearCapturedLogs();
}
};
@@ -0,0 +1,3 @@
export { Meeting } from './meeting';
@@ -1,6 +1,6 @@
import { ObjectMetadata } from 'twenty-sdk/application';
import { Object } from 'twenty-sdk';
@ObjectMetadata({
@Object({
universalIdentifier: 'd1831348-b4a4-4426-9c0b-0af19e7a9c27',
nameSingular: 'meeting',
namePlural: 'meetings',
@@ -1,4 +1,4 @@
import { type ServerlessFunctionConfig } from 'twenty-sdk/application';
import { type FunctionConfig } from 'twenty-sdk';
import type { ProcessResult } from './types';
import { WebhookHandler } from './webhook-handler';
@@ -10,7 +10,7 @@ export const main = async (
return handler.handle(params, headers);
};
export const config: ServerlessFunctionConfig = {
export const config: FunctionConfig = {
universalIdentifier: '2d3ea303-667c-4bbe-9e3d-db6ffb9d6c74',
name: 'receive-fireflies-notes',
description:
@@ -22,7 +22,8 @@ export const config: ServerlessFunctionConfig = {
type: 'route',
path: '/webhook/fireflies',
httpMethod: 'POST',
isAuthRequired: true,
isAuthRequired: false,
},
],
};
@@ -43,6 +43,20 @@ export class TwentyCrmService {
return response.data?.meetings?.edges?.[0]?.node;
}
async findMeetingByFirefliesId(meetingId: string): Promise<IdNode | undefined> {
const query = `
query FindMeetingByFirefliesId($meetingId: String!) {
meetings(filter: { firefliesMeetingId: { eq: $meetingId } }) {
edges { node { id } }
}
}
`;
const variables = { meetingId };
const response = await this.gqlRequest<FindMeetingResponse>(query, variables);
return response.data?.meetings?.edges?.[0]?.node;
}
async matchParticipantsToContacts(
participants: FirefliesParticipant[],
): Promise<{
@@ -53,21 +67,18 @@ export class TwentyCrmService {
return { matchedContacts: [], unmatchedParticipants: [] };
}
// Split participants into those with emails and those with names only
const participantsWithEmails = participants.filter(p => p.email && p.email.trim());
const participantsNameOnly = participants.filter(p => !p.email || !p.email.trim());
let matchedContacts: Contact[] = [];
let unmatchedParticipants: FirefliesParticipant[] = [];
// 1. Match by email first
if (participantsWithEmails.length > 0) {
const emailMatches = await this.matchByEmail(participantsWithEmails);
matchedContacts.push(...emailMatches.matchedContacts);
unmatchedParticipants.push(...emailMatches.unmatchedParticipants);
}
// 2. For participants without emails, try name-based matching
if (participantsNameOnly.length > 0) {
const nameMatches = await this.matchByName(participantsNameOnly, matchedContacts);
matchedContacts.push(...nameMatches.matchedContacts);
@@ -126,7 +137,6 @@ export class TwentyCrmService {
const matchedContacts: Contact[] = [];
const unmatchedParticipants: FirefliesParticipant[] = [];
// Get set of already matched contact IDs to avoid duplicates
const alreadyMatchedIds = new Set(alreadyMatchedContacts.map(c => c.id));
for (const participant of participants) {
@@ -152,27 +162,37 @@ export class TwentyCrmService {
const firstName = nameParts[0];
const lastName = nameParts.slice(1).join(' ');
// Try exact name match first
let query = `
query FindPeopleByName($firstName: String!, $lastName: String) {
people(filter: {
and: [
{ name: { firstName: { eq: $firstName } } }
${lastName ? '{ name: { lastName: { eq: $lastName } } }' : ''}
]
}) {
edges { node { id emails { primaryEmail } name { firstName lastName } } }
}
}
`;
const hasLastName = Boolean(lastName);
let variables: any = { firstName };
if (lastName) {
variables.lastName = lastName;
}
const query = hasLastName
? `
query FindPeopleByName($firstName: String!, $lastName: String!) {
people(filter: {
and: [
{ name: { firstName: { eq: $firstName } } }
{ name: { lastName: { eq: $lastName } } }
]
}) {
edges { node { id emails { primaryEmail } name { firstName lastName } } }
}
}
`
: `
query FindPeopleByName($firstName: String!) {
people(filter: {
name: { firstName: { eq: $firstName } }
}) {
edges { node { id emails { primaryEmail } name { firstName lastName } } }
}
}
`;
const variables: Record<string, unknown> = hasLastName
? { firstName, lastName }
: { firstName };
try {
const response = await this.gqlRequest<any>(query, variables);
const response = await this.gqlRequest<{ people: { edges: Array<{ node: { id: string; emails?: { primaryEmail?: string }; name?: { firstName?: string; lastName?: string } } }> } }>(query, variables);
const people = response.data?.people?.edges;
if (people && people.length > 0) {
@@ -183,9 +203,8 @@ export class TwentyCrmService {
};
}
// If no exact match and we have a last name, try fuzzy matching
if (lastName) {
query = `
if (hasLastName) {
const fuzzyQuery = `
query FindPeopleByNameFuzzy($firstName: String!) {
people(filter: { name: { firstName: { ilike: $firstName } } }) {
edges { node { id emails { primaryEmail } name { firstName lastName } } }
@@ -193,12 +212,11 @@ export class TwentyCrmService {
}
`;
const fuzzyResponse = await this.gqlRequest<any>(query, { firstName: `%${firstName}%` });
const fuzzyResponse = await this.gqlRequest<{ people: { edges: Array<{ node: { id: string; emails?: { primaryEmail?: string }; name?: { firstName?: string; lastName?: string } } }> } }>(fuzzyQuery, { firstName: `%${firstName}%` });
const fuzzyPeople = fuzzyResponse.data?.people?.edges;
if (fuzzyPeople && fuzzyPeople.length > 0) {
// Find best match by checking if last name contains our target
const bestMatch = fuzzyPeople.find((edge: any) => {
const bestMatch = fuzzyPeople.find((edge) => {
const personLastName = edge.node.name?.lastName || '';
return personLastName.toLowerCase().includes(lastName.toLowerCase());
});
@@ -215,7 +233,6 @@ export class TwentyCrmService {
return null;
} catch {
// Silently fail - don't break the entire process for a single contact lookup
return null;
}
}
@@ -225,17 +242,14 @@ export class TwentyCrmService {
): Promise<string[]> {
const newContactIds: string[] = [];
// Split participants into those with emails and those with names only
const participantsWithEmails = participants.filter(p => p.email && p.email.trim());
const participantsNameOnly = participants.filter(p => !p.email || !p.email.trim());
// Process participants with emails
if (participantsWithEmails.length > 0) {
const emailContactIds = await this.createContactsWithEmails(participantsWithEmails);
newContactIds.push(...emailContactIds);
}
// Process participants with names only
if (participantsNameOnly.length > 0) {
const nameContactIds = await this.createContactsNameOnly(participantsNameOnly);
newContactIds.push(...nameContactIds);
@@ -247,7 +261,6 @@ export class TwentyCrmService {
private async createContactsWithEmails(participants: FirefliesParticipant[]): Promise<string[]> {
const newContactIds: string[] = [];
// Deduplicate participants by email to prevent duplicate contact creation
const uniqueParticipants = participants.reduce<FirefliesParticipant[]>((unique, participant) => {
const existing = unique.find(p => p.email === participant.email);
if (!existing) {
@@ -276,7 +289,10 @@ export class TwentyCrmService {
};
try {
const response = await this.gqlRequest<CreatePersonResponse>(mutation, variables);
const response = await this.gqlRequest<CreatePersonResponse>(mutation, variables, {
suppressErrorCodes: ['BAD_USER_INPUT'],
suppressErrorMessageIncludes: ['Duplicate Emails', 'duplicate entry'],
});
if (!response.data?.createPerson?.id) {
throw new Error(`Failed to create contact for ${participant.email}`);
}
@@ -297,7 +313,6 @@ export class TwentyCrmService {
private async createContactsNameOnly(participants: FirefliesParticipant[]): Promise<string[]> {
const newContactIds: string[] = [];
// Deduplicate participants by name to prevent duplicate contact creation
const uniqueParticipants = participants.reduce<FirefliesParticipant[]>((unique, participant) => {
const existing = unique.find(p =>
p.name.toLowerCase().trim() === participant.name.toLowerCase().trim()
@@ -311,7 +326,6 @@ export class TwentyCrmService {
}, []);
for (const participant of uniqueParticipants) {
// Check if we already have a contact with this exact name to avoid duplicates
const existingContact = await this.findContactByName(participant.name);
if (existingContact) {
logger.warn(`Contact with name "${participant.name}" already exists. Skipping creation.`);
@@ -330,8 +344,6 @@ export class TwentyCrmService {
const variables = {
data: {
name: { firstName, lastName },
// Note: We don't set emails for name-only participants
// This will create a contact without an email address
},
};
@@ -346,7 +358,6 @@ export class TwentyCrmService {
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
logger.warn(`Failed to create contact for ${participant.name}: ${errorMessage}`);
// Continue processing other participants instead of failing completely
continue;
}
}
@@ -409,28 +420,7 @@ export class TwentyCrmService {
},
};
await this.gqlRequest<any>(mutation, variables);
}
async createMeetingTarget(meetingId: string, contactId: string): Promise<void> {
const mutation = `
mutation CreateMeetingTarget($data: NoteTargetCreateInput!) {
createNoteTarget(data: $data) {
id
meetingId
personId
}
}
`;
const variables = {
data: {
meetingId,
personId: contactId,
},
};
await this.gqlRequest<any>(mutation, variables);
await this.gqlRequest<{ createNoteTarget: { id: string; noteId: string; personId: string } }>(mutation, variables);
}
async createMeeting(meetingData: MeetingCreateInput): Promise<string> {
@@ -442,7 +432,6 @@ export class TwentyCrmService {
const variables = { data: meetingData };
// Debug: log the variables being sent
if (!this.isTestEnvironment) {
logger.debug('createMeeting variables:', JSON.stringify(variables, null, 2));
}
@@ -456,7 +445,8 @@ export class TwentyCrmService {
private async gqlRequest<T>(
query: string,
variables?: Record<string, unknown>
variables?: Record<string, unknown>,
options?: { suppressErrorCodes?: string[]; suppressErrorMessageIncludes?: string[] }
): Promise<GraphQLResponse<T>> {
const url = `${this.apiUrl}/graphql`;
@@ -500,7 +490,16 @@ export class TwentyCrmService {
return json;
} catch (error) {
logger.error('GraphQL request error:', error);
const message = error instanceof Error ? error.message : String(error);
const suppressByCode = options?.suppressErrorCodes?.some((code) =>
message.includes(code),
);
const suppressByMessage = options?.suppressErrorMessageIncludes?.some((substring) =>
message.includes(substring),
);
if (!suppressByCode && !suppressByMessage) {
logger.error('GraphQL request error:', error);
}
throw error;
}
}
@@ -525,7 +524,15 @@ export class TwentyCrmService {
return response.data.createMeeting.id;
}
async findFailedMeetings(): Promise<any[]> {
async findFailedMeetings(): Promise<Array<{
id: string;
name: string;
firefliesMeetingId: string;
importError: string;
lastImportAttempt: string;
importAttempts: number;
createdAt: string;
}>> {
const query = `
query FindFailedMeetings {
meetings(filter: { importStatus: { eq: "FAILED" } }) {
@@ -544,8 +551,8 @@ export class TwentyCrmService {
}
`;
const response = await this.gqlRequest<any>(query);
return response.data?.meetings?.edges?.map((edge: any) => edge.node) || [];
const response = await this.gqlRequest<{ meetings: { edges: Array<{ node: { id: string; name: string; firefliesMeetingId: string; importError: string; lastImportAttempt: string; importAttempts: number; createdAt: string } }> } }>(query);
return response.data?.meetings?.edges?.map((edge) => edge.node) || [];
}
async retryFailedMeeting(meetingId: string, updatedData: Partial<MeetingCreateInput>): Promise<void> {
@@ -564,7 +571,7 @@ export class TwentyCrmService {
}
};
await this.gqlRequest<any>(mutation, variables);
await this.gqlRequest<{ updateMeeting: { id: string } }>(mutation, variables);
}
}
@@ -0,0 +1,235 @@
// Fireflies API Types
export type FirefliesParticipant = {
email: string;
name: string;
};
export type FirefliesWebhookPayload = {
meetingId: string;
eventType: string;
clientReferenceId?: string;
};
// Transcript sentence from Fireflies API
export type FirefliesSentence = {
index: number;
speaker_name: string;
text: string;
start_time: string;
end_time: string;
ai_filters?: {
task?: boolean;
question?: boolean;
sentiment?: string;
};
};
// Speaker analytics from Fireflies API (Business+)
export type FirefliesSpeakerAnalytics = {
speaker_id: string;
name: string;
duration: number;
word_count: number;
longest_monologue: number;
filler_words: number;
questions: number;
words_per_minute: number;
};
// Based on Fireflies GraphQL API transcript schema
// See: https://docs.fireflies.ai/graphql-api/query/transcript
export type FirefliesMeetingData = {
id: string;
title: string;
date: string;
duration: number;
participants: FirefliesParticipant[];
organizer_email?: string;
// Full transcript (Pro+)
sentences?: FirefliesSentence[];
summary: {
// Pro+ fields
action_items: string[];
keywords?: string[];
overview: string;
notes?: string; // Detailed AI-generated meeting notes
gist?: string; // 1-sentence summary
bullet_gist?: string; // Bullet point summary with emojis
short_summary?: string; // Single paragraph summary
short_overview?: string; // Brief overview
outline?: string; // Meeting outline with timestamps
shorthand_bullet?: string;
// Business+ fields
topics_discussed?: string[];
meeting_type?: string;
transcript_chapters?: string[];
};
// Business+ analytics
analytics?: {
sentiments?: {
positive_pct: number;
negative_pct: number;
neutral_pct: number;
};
categories?: {
questions: number;
tasks: number;
metrics: number;
date_times: number;
};
speakers?: FirefliesSpeakerAnalytics[];
};
meeting_info?: {
summary_status?: string;
};
// URLs
transcript_url: string;
audio_url?: string; // Pro+
video_url?: string; // Business+
meeting_link?: string; // All plans
summary_status?: string;
};
export type FirefliesTranscriptListItem = {
id: string;
title: string;
date?: string;
duration?: number;
organizer_email?: string;
participants?: string[];
transcript_url?: string;
meeting_link?: string;
summary_status?: string;
};
export type FirefliesTranscriptListOptions = {
limit?: number;
skip?: number;
organizers?: string[];
participants?: string[];
hostEmail?: string;
participantEmail?: string;
userId?: string;
channelId?: string;
mine?: boolean;
fromDate?: number;
toDate?: number;
pageSize?: number;
maxRecords?: number;
};
// Configuration Types
export type SummaryStrategy = 'immediate_only' | 'immediate_with_retry' | 'delayed_polling' | 'basic_only';
export type SummaryFetchConfig = {
strategy: SummaryStrategy;
retryAttempts: number;
retryDelay: number;
pollInterval: number;
maxPolls: number;
};
export const FIREFLIES_PLANS = {
FREE: 'free',
PRO: 'pro',
BUSINESS: 'business',
ENTERPRISE: 'enterprise',
} as const;
export type FirefliesPlan = typeof FIREFLIES_PLANS[keyof typeof FIREFLIES_PLANS];
export type WebhookConfig = {
secret: string;
apiUrl: string;
};
// Processing Result Types
export type ProcessResult = {
success: boolean;
meetingId?: string;
noteIds?: string[];
newContacts?: string[];
errors?: string[];
debug?: string[];
summaryReady?: boolean;
summaryPending?: boolean;
enhancementScheduled?: boolean;
actionItemsCount?: number;
sentimentAnalysis?: {
positive_pct: number;
negative_pct: number;
neutral_pct: number;
};
meetingType?: string;
keyTopics?: string[];
};
// Twenty CRM Types
export type GraphQLResponse<T> = {
data: T;
errors?: Array<{
message?: string;
extensions?: { code?: string }
}>;
};
export type IdNode = { id: string };
export type FindMeetingResponse = {
meetings: { edges: Array<{ node: IdNode }> };
};
export type FindPeopleResponse = {
people: { edges: Array<{ node: { id: string; emails: { primaryEmail: string } } }> };
};
export type CreatePersonResponse = {
createPerson: { id: string }
};
export type CreateNoteResponse = {
createNote: { id: string }
};
export type CreateMeetingResponse = {
createMeeting: { id: string }
};
export type Contact = {
id: string;
email: string;
};
// Twenty CRM Meeting custom field input
// Maps to fields defined in add-meeting-fields.ts
export type MeetingCreateInput = {
name: string;
noteId?: string | null;
// Basic fields (All plans)
meetingDate: string;
duration: number;
firefliesMeetingId: string;
organizerEmail?: string | null;
transcriptUrl?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
meetingLink?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
// Pro+ fields
transcript?: string | null;
overview?: string | null;
notes?: string | null;
keywords?: string | null;
audioUrl?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
// Business+ fields
meetingType?: string | null;
topics?: string | null;
actionItemsCount: number;
positivePercent?: number | null;
negativePercent?: number | null;
neutralPercent?: number | null;
videoUrl?: { primaryLinkUrl: string; primaryLinkLabel: string } | null;
// Import tracking
importStatus?: 'SUCCESS' | 'PARTIAL' | 'FAILED' | 'PENDING' | 'RETRYING' | null;
importError?: string | null;
lastImportAttempt?: string | null;
importAttempts?: number | null;
};
@@ -1,11 +1,11 @@
import { FIREFLIES_PLANS, type FirefliesPlan, type SummaryFetchConfig, type SummaryStrategy } from './types';
export const toBoolean = (value: string | undefined, defaultValue: boolean): boolean => {
if (value === undefined) return defaultValue;
const normalized = value.trim().toLowerCase();
return normalized === 'true' || normalized === '1' || normalized === 'yes';
};
import type { SummaryFetchConfig, SummaryStrategy } from './types';
export const getApiUrl = (): string => {
return process.env.SERVER_URL || 'http://localhost:3000';
};
@@ -28,3 +28,14 @@ export const shouldAutoCreateContacts = (): boolean => {
return toBoolean(process.env.AUTO_CREATE_CONTACTS, true);
};
export const getFirefliesPlan = (): FirefliesPlan => {
const plan = (process.env.FIREFLIES_PLAN || FIREFLIES_PLANS.FREE).toLowerCase();
if (plan === FIREFLIES_PLANS.BUSINESS || plan === FIREFLIES_PLANS.ENTERPRISE) {
return plan as FirefliesPlan;
}
if (plan === FIREFLIES_PLANS.PRO) {
return FIREFLIES_PLANS.PRO;
}
return FIREFLIES_PLANS.FREE;
};
@@ -1,9 +1,9 @@
import { FirefliesApiClient } from './fireflies-api-client';
import { MeetingFormatter } from './formatters';
import { createLogger } from './logger';
import { clearAllCapturedLogs, createLogger, getAllCapturedLogs } from './logger';
import { TwentyCrmService } from './twenty-crm-service';
import type { FirefliesWebhookPayload, ProcessResult } from './types';
import { getApiUrl, getSummaryFetchConfig, shouldAutoCreateContacts } from './utils';
import { getApiUrl, getFirefliesPlan, getSummaryFetchConfig, shouldAutoCreateContacts } from './utils';
import {
getWebhookSecretFingerprint,
isValidFirefliesPayload,
@@ -12,17 +12,28 @@ import {
declare const process: { env: Record<string, string | undefined> };
const logger = createLogger('fireflies');
export class WebhookHandler {
private debug: string[] = [];
private isTestEnvironment: boolean;
private logger: ReturnType<typeof createLogger>;
constructor() {
this.isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;
this.logger = createLogger('fireflies');
}
private addDebugLogs(result: ProcessResult): ProcessResult {
if (process.env.CAPTURE_LOGS === 'true') {
const captured = getAllCapturedLogs();
result.debug = [...this.debug, ...captured];
} else {
delete result.debug;
}
return result;
}
async handle(params: unknown, headers?: Record<string, string>): Promise<ProcessResult> {
clearAllCapturedLogs();
this.debug = [];
const result: ProcessResult = {
success: false,
noteIds: [],
@@ -31,19 +42,27 @@ export class WebhookHandler {
};
try {
logger.debug('invoked');
logger.debug(`apiUrl=${getApiUrl()}`);
this.logger.debug('invoked');
this.logger.debug(`apiUrl=${getApiUrl()}`);
const paramKeys =
params && typeof params === 'object'
? Object.keys(params as Record<string, unknown>)
: [];
this.debug.push(
`paramsType=${typeof params}`,
`paramKeys=${paramKeys.join(',') || 'none'}`
);
// 0) Validate environment configuration
const firefliesApiKey = process.env.FIREFLIES_API_KEY || '';
const twentyApiKey = process.env.TWENTY_API_KEY || '';
if (!firefliesApiKey) {
logger.critical('FIREFLIES_API_KEY not configured - this is a critical configuration error');
this.logger.critical('FIREFLIES_API_KEY not configured - this is a critical configuration error');
throw new Error('FIREFLIES_API_KEY environment variable is required');
}
if (!twentyApiKey) {
logger.critical('TWENTY_API_KEY not configured - this is a critical configuration error');
this.logger.critical('TWENTY_API_KEY not configured - this is a critical configuration error');
throw new Error('TWENTY_API_KEY environment variable is required');
}
@@ -51,28 +70,30 @@ export class WebhookHandler {
const { payload, extractedHeaders } = this.parsePayload(params);
const finalHeaders = extractedHeaders || headers;
logger.debug(`payload meetingId=${payload.meetingId} eventType="${payload.eventType}"`);
this.logger.debug(`payload meetingId=${payload.meetingId} eventType="${payload.eventType}"`);
// 2) Verify webhook signature
const webhookSecret = process.env.FIREFLIES_WEBHOOK_SECRET || '';
const secretFingerprint = getWebhookSecretFingerprint(webhookSecret);
logger.debug(`webhook secret fingerprint=${secretFingerprint}`);
this.logger.debug(`webhook secret fingerprint=${secretFingerprint}`);
this.verifySignature(payload, finalHeaders, webhookSecret);
logger.debug('signature verification: ok');
this.logger.debug('signature verification: ok');
// 3) Fetch meeting data from Fireflies
const summaryConfig = getSummaryFetchConfig();
logger.debug(`summary strategy: ${summaryConfig.strategy} (retryAttempts=${summaryConfig.retryAttempts}, retryDelay=${summaryConfig.retryDelay}ms)`);
logger.debug(`fetching meeting data from Fireflies API`);
const firefliesPlan = getFirefliesPlan();
this.logger.debug(`summary strategy: ${summaryConfig.strategy} (retryAttempts=${summaryConfig.retryAttempts}, retryDelay=${summaryConfig.retryDelay}ms)`);
this.logger.debug(`fetching meeting data from Fireflies API`);
const firefliesClient = new FirefliesApiClient(firefliesApiKey);
const { data: meetingData, summaryReady } = await firefliesClient.fetchMeetingDataWithRetry(
payload.meetingId,
summaryConfig
summaryConfig,
firefliesPlan
);
logger.debug(`meeting data fetched: title="${meetingData.title}" summaryReady=${summaryReady}`);
this.logger.debug(`meeting data fetched: title="${meetingData.title}" summaryReady=${summaryReady}`);
result.summaryReady = summaryReady;
result.summaryPending = !summaryReady;
@@ -84,8 +105,7 @@ export class WebhookHandler {
result.meetingType = meetingData.summary.meeting_type;
if (meetingData.analytics?.sentiments) {
const sentiments = meetingData.analytics.sentiments;
result.sentimentScore = sentiments.positive_pct / 100;
result.sentimentAnalysis = meetingData.analytics.sentiments;
}
}
@@ -95,29 +115,36 @@ export class WebhookHandler {
getApiUrl()
);
const existingMeeting = await twentyService.findExistingMeeting(meetingData.title);
if (existingMeeting) {
logger.debug(`meeting already exists id=${existingMeeting.id}`);
const existingMeetingById = await twentyService.findMeetingByFirefliesId(meetingData.id);
if (existingMeetingById) {
this.logger.debug(`meeting already exists by firefliesMeetingId id=${existingMeetingById.id}`);
result.success = true;
result.meetingId = existingMeeting.id;
result.debug = this.debug;
return result;
result.meetingId = existingMeetingById.id;
return this.addDebugLogs(result);
}
logger.debug('no existing meeting found, proceeding');
const existingMeetingByTitle = await twentyService.findExistingMeeting(meetingData.title);
if (existingMeetingByTitle) {
this.logger.debug(`meeting already exists by title id=${existingMeetingByTitle.id}`);
result.success = true;
result.meetingId = existingMeetingByTitle.id;
return this.addDebugLogs(result);
}
this.logger.debug('no existing meeting found, proceeding');
// 5) Match participants to existing contacts
logger.debug(`total participants from API: ${meetingData.participants.length}`);
this.logger.debug(`total participants from API: ${meetingData.participants.length}`);
meetingData.participants.forEach((p, idx) => {
logger.debug(`participant ${idx + 1}: name="${p.name}" email="${p.email || 'none'}"`);
this.logger.debug(`participant ${idx + 1}: name="${p.name}" email="${p.email || 'none'}"`);
});
const { matchedContacts, unmatchedParticipants } = await twentyService.matchParticipantsToContacts(
meetingData.participants
);
logger.debug(`matched=${matchedContacts.length} unmatched=${unmatchedParticipants.length}`);
this.logger.debug(`matched=${matchedContacts.length} unmatched=${unmatchedParticipants.length}`);
unmatchedParticipants.forEach((p, idx) => {
logger.debug(`unmatched ${idx + 1}: name="${p.name}" email="${p.email || 'none'}"`);
this.logger.debug(`unmatched ${idx + 1}: name="${p.name}" email="${p.email || 'none'}"`);
});
// 6) Optionally create contacts
@@ -126,7 +153,7 @@ export class WebhookHandler {
? await twentyService.createContactsForUnmatched(unmatchedParticipants)
: [];
result.newContacts = newContactIds;
logger.debug(`autoCreate=${autoCreate} createdContacts=${newContactIds.length}`);
this.logger.debug(`autoCreate=${autoCreate} createdContacts=${newContactIds.length}`);
// 7) Create note first (so we can link to it from the meeting)
const allContactIds = [...matchedContacts.map(({ id }) => id), ...newContactIds];
@@ -136,13 +163,13 @@ export class WebhookHandler {
noteBody
);
result.noteIds = [noteId];
logger.debug(`created note id=${noteId}`);
this.logger.debug(`created note id=${noteId}`);
// 8) Create meeting with direct relationship to the note
const meetingInput = MeetingFormatter.toMeetingCreateInput(meetingData, noteId);
logger.debug(`meeting duration: ${meetingData.duration} min (raw from API) → ${meetingInput.duration} min (rounded)`);
this.logger.debug(`meeting duration: ${meetingData.duration} min (raw from API) → ${meetingInput.duration} min (rounded)`);
result.meetingId = await twentyService.createMeeting(meetingInput);
logger.debug(`created meeting id=${result.meetingId} with noteId=${noteId}`);
this.logger.debug(`created meeting id=${result.meetingId} with noteId=${noteId}`);
// 9) Link note to participants (Meeting link is handled via the relation field)
await this.linkNoteToParticipants(
@@ -150,20 +177,19 @@ export class WebhookHandler {
noteId,
allContactIds
);
logger.debug(`linked note to ${allContactIds.length} participants`);
this.logger.debug(`linked note to ${allContactIds.length} participants`);
result.success = true;
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error(`error: ${message}`);
this.logger.error(`error: ${message}`);
result.errors?.push(message);
// Try to create a failed meeting record for tracking
await this.createFailedMeetingRecord(params, message);
}
result.debug = this.debug;
return result;
return this.addDebugLogs(result);
}
private parsePayload(params: unknown): { payload: FirefliesWebhookPayload; extractedHeaders?: Record<string, string> } {
@@ -172,16 +198,16 @@ export class WebhookHandler {
// Handle string-encoded params
if (typeof normalizedParams === 'string') {
logger.debug(`received params as string length=${normalizedParams.length}`);
this.logger.debug(`received params as string length=${normalizedParams.length}`);
try {
const parsed = JSON.parse(normalizedParams);
normalizedParams = parsed;
if (parsed && typeof parsed === 'object') {
const parsedKeys = Object.keys(parsed as Record<string, unknown>);
logger.debug(`parsed params keys: ${parsedKeys.join(',') || 'none'}`);
this.logger.debug(`parsed params keys: ${parsedKeys.join(',') || 'none'}`);
}
} catch (parseError) {
logger.error(`error parsing string params: ${String(parseError)}`);
this.logger.error(`error parsing string params: ${String(parseError)}`);
throw new Error('Invalid or missing webhook payload');
}
}
@@ -197,14 +223,14 @@ export class WebhookHandler {
if (wrapper.headers && typeof wrapper.headers === 'object' && !Array.isArray(wrapper.headers)) {
extractedHeaders = wrapper.headers as Record<string, string>;
const headerKeys = Object.keys(extractedHeaders);
logger.debug(`extracted headers from wrapper: ${headerKeys.join(',')}`);
this.logger.debug(`extracted headers from wrapper: ${headerKeys.join(',')}`);
}
const wrapperKeys = ['params', 'payload', 'body', 'data', 'event'];
for (const key of wrapperKeys) {
const candidate = wrapper[key];
if (isValidFirefliesPayload(candidate)) {
logger.debug(`detected payload under wrapper key "${key}"`);
this.logger.debug(`detected payload under wrapper key "${key}"`);
payload = candidate as FirefliesWebhookPayload;
break;
}
@@ -212,7 +238,7 @@ export class WebhookHandler {
}
if (!payload) {
logger.error('error: Invalid or missing webhook payload');
this.logger.error('error: Invalid or missing webhook payload');
throw new Error('Invalid or missing webhook payload');
}
@@ -220,7 +246,7 @@ export class WebhookHandler {
const payloadRecord = payload as Record<string, unknown>;
const payloadKeys = Object.keys(payloadRecord);
if (payloadKeys.length > 0) {
logger.debug(`payload keys: ${payloadKeys.join(',')}`);
this.logger.debug(`payload keys: ${payloadKeys.join(',')}`);
}
return { payload, extractedHeaders };
@@ -235,8 +261,9 @@ export class WebhookHandler {
const normalizedHeaders = headers || {};
const headerKeys = Object.keys(normalizedHeaders);
if (headerKeys.length > 0) {
logger.debug(`header keys: ${headerKeys.join(',')}`);
this.logger.debug(`header keys: ${headerKeys.join(',')}`);
}
this.debug.push(`headerKeys=${headerKeys.join(',') || 'none'}`);
const headerSignature = Object.entries(normalizedHeaders).find(
([key]) => key.toLowerCase() === 'x-hub-signature',
@@ -249,32 +276,40 @@ export class WebhookHandler {
: undefined;
if (payloadSignature) {
logger.debug('found signature inside payload');
this.logger.debug('found signature inside payload');
}
const signature =
(typeof headerSignature === 'string' ? headerSignature : undefined) || payloadSignature;
const body = typeof normalizedHeaders['body'] === 'string'
? normalizedHeaders['body']
: JSON.stringify(payloadRecord);
const payloadForSignature =
payloadSignature && 'x-hub-signature' in payloadRecord
? Object.fromEntries(
Object.entries(payloadRecord).filter(
([key]) => key.toLowerCase() !== 'x-hub-signature',
),
)
: payloadRecord;
const body =
typeof normalizedHeaders['body'] === 'string'
? normalizedHeaders['body']
: JSON.stringify(payloadForSignature);
const signatureCheck = verifyWebhookSignature(body, signature, webhookSecret);
if (!signatureCheck.isValid) {
logger.debug(
this.debug.push(
`signatureProvided=${Boolean(signature)}`,
`signatureMatched=${signatureCheck.isValid}`,
`webhookSecretFingerprint=${getWebhookSecretFingerprint(webhookSecret)}`
);
this.logger.debug(
`signature check failed. headerPresent=${Boolean(
headerSignature,
)} payloadSignaturePresent=${Boolean(payloadSignature)}`,
);
if (signature) {
logger.debug(`provided signature=${signature}`);
} else {
logger.debug('provided signature=undefined');
}
logger.debug(
`computed signature=${signatureCheck.computedSignature ?? 'unavailable'}`,
);
logger.critical('Invalid webhook signature - potential security threat detected in production');
this.logger.debug(`provided signature present=${Boolean(signature)}`);
this.logger.critical('Invalid webhook signature - potential security threat detected in production');
throw new Error('Invalid webhook signature');
}
}
@@ -284,15 +319,13 @@ export class WebhookHandler {
noteId: string,
contactIds: string[]
): Promise<void> {
// Create Note-Person links for each participant
for (const contactId of contactIds) {
try {
await twentyService.createNoteTarget(noteId, contactId);
logger.debug(`linked note ${noteId} to person ${contactId}`);
this.logger.debug(`linked note ${noteId} to person ${contactId}`);
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error(`failed to link note to person ${contactId}: ${message}`);
// Continue with other participants
this.logger.error(`failed to link note to person ${contactId}: ${message}`);
}
}
}
@@ -302,11 +335,10 @@ export class WebhookHandler {
try {
const twentyApiKey = process.env.TWENTY_API_KEY || '';
if (!twentyApiKey) {
logger.debug('Cannot create failed meeting record: TWENTY_API_KEY not configured');
this.logger.debug('Cannot create failed meeting record: TWENTY_API_KEY not configured');
return;
}
// Try to extract meeting ID and title from the params
let meetingId = 'unknown';
let meetingTitle = 'Unknown Meeting';
@@ -314,7 +346,6 @@ export class WebhookHandler {
if (payload?.meetingId) {
meetingId = payload.meetingId;
// Try to get meeting title from Fireflies API if possible
const firefliesApiKey = process.env.FIREFLIES_API_KEY || '';
if (firefliesApiKey) {
try {
@@ -322,7 +353,11 @@ export class WebhookHandler {
const meetingData = await firefliesClient.fetchMeetingData(meetingId);
meetingTitle = meetingData.title || meetingTitle;
} catch (fetchError) {
logger.debug(`Could not fetch meeting title: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`);
this.logger.debug(
`Could not fetch meeting title: ${
fetchError instanceof Error ? fetchError.message : 'Unknown error'
}`,
);
}
}
}
@@ -335,10 +370,13 @@ export class WebhookHandler {
);
const failedMeetingId = await twentyService.createFailedMeeting(failedMeetingData);
logger.debug(`Created failed meeting record: ${failedMeetingId}`);
this.logger.debug(`Created failed meeting record: ${failedMeetingId}`);
} catch (recordError) {
// Don't throw here - we don't want to break the original error handling
logger.error(`Failed to create failed meeting record: ${recordError instanceof Error ? recordError.message : 'Unknown error'}`);
this.logger.error(
`Failed to create failed meeting record: ${
recordError instanceof Error ? recordError.message : 'Unknown error'
}`,
);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -32,3 +32,4 @@ export const MyComponent = () => {
</Tab>
</Tabs>
@@ -115,6 +115,14 @@ Figma プラットフォームの学習に関するより包括的な詳細と
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
これは推奨拡張機能の一部です。
## コラボレーション
@@ -158,7 +158,7 @@ SSLHTTPS)は、特定のブラウザ機能が正しく動作するために
2. **.env ファイルを更新**
.env`ファイルを開き、`SERVER_URL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\`を更新します:
.env`ファイルを開き、`SERVER_URL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\`を更新します:
```ini
SERVER_URL=http(s)://your-domain-or-ip:your-port
@@ -30,7 +30,7 @@ Twentyは、日常をサポートする最適なデータモデルを形成す
既存のオブジェクトの特性に過ぎないもの(例:会社の「業種」や機会の「ステータス」)はフィールドにします。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。 カテゴリ、ラベル、属性にはフィールドが最適です。
**3. 単独で存在する場合には新しいオブジェクトを作成してください。**
概念が独自のライフサイクル、プロパティ、または関係を持つ場合、それは通常オブジェクトに値します。 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば:
概念が独自のライフサイクル、プロパティ、または関係を持つ場合、それは通常オブジェクトに値します。 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば: 例えば:
- **プロジェクト**:独自の期限、所有者、タスクを持つもの
- **サブスクリプション**:会社、製品、請求書を結ぶもの
@@ -45,7 +45,7 @@ sectionInfo: 配置您的Twenty工作空间设置和偏好
刪除您的帳號將永久移除您對所有工作空間的訪問。
刪除您的帳號將永久移除您對所有工作空間的訪問。 此操作無法撤銷,您將失去所有工作空間權限,如您只想退出特定的團隊,應考慮退出個別的工作空間。 刪除您的帳號將永久移除您對所有工作空間的訪問。
刪除您的帳號將永久移除您對所有工作空間的訪問。 此操作無法撤銷,您將失去所有工作空間權限,如您只想退出特定的團隊,應考慮退出個別的工作空間。
</Warning> 刪除您的帳號將永久移除您對所有工作空間的訪問。
</Warning> 刪除您的帳號將永久移除您對所有工作空間的訪問。
刪除您的帳號將永久移除您對所有工作空間的訪問。 此操作無法撤銷,您將失去所有工作空間權限,如您只想退出特定的團隊,應考慮退出個別的工作空間。
</Warning>
@@ -152,6 +152,7 @@ export type AggregateChartConfiguration = {
graphType: GraphType;
label?: Maybe<Scalars['String']>;
prefix?: Maybe<Scalars['String']>;
ratioAggregateConfig?: Maybe<RatioAggregateConfig>;
suffix?: Maybe<Scalars['String']>;
timezone?: Maybe<Scalars['String']>;
};
@@ -753,7 +754,6 @@ export type CoreViewGroup = {
__typename?: 'CoreViewGroup';
createdAt: Scalars['DateTime'];
deletedAt?: Maybe<Scalars['DateTime']>;
fieldMetadataId: Scalars['UUID'];
fieldValue: Scalars['String'];
id: Scalars['UUID'];
isVisible: Scalars['Boolean'];
@@ -905,6 +905,7 @@ export type CreateRoleInput = {
canAccessAllTools?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToAgents?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApiKeys?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApplications?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToUsers?: InputMaybe<Scalars['Boolean']>;
canDestroyAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
canReadAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
@@ -963,7 +964,6 @@ export type CreateViewFilterInput = {
};
export type CreateViewGroupInput = {
fieldMetadataId: Scalars['UUID'];
fieldValue: Scalars['String'];
id?: InputMaybe<Scalars['UUID']>;
isVisible?: InputMaybe<Scalars['Boolean']>;
@@ -3696,6 +3696,17 @@ export type QueueRetentionConfig = {
failedMaxCount: Scalars['Float'];
};
export type RatioAggregateConfig = {
__typename?: 'RatioAggregateConfig';
fieldMetadataId: Scalars['UUID'];
optionValue: Scalars['String'];
};
export type RatioAggregateConfigInput = {
fieldMetadataId: Scalars['UUID'];
optionValue: Scalars['String'];
};
export type Relation = {
__typename?: 'Relation';
sourceFieldMetadata: Field;
@@ -3796,6 +3807,7 @@ export type Role = {
objectPermissions?: Maybe<Array<ObjectPermission>>;
permissionFlags?: Maybe<Array<PermissionFlag>>;
standardId?: Maybe<Scalars['UUID']>;
universalIdentifier?: Maybe<Scalars['UUID']>;
workspaceMembers: Array<WorkspaceMember>;
};
@@ -4360,6 +4372,7 @@ export type UpdateRolePayload = {
canAccessAllTools?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToAgents?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApiKeys?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApplications?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToUsers?: InputMaybe<Scalars['Boolean']>;
canDestroyAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
canReadAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
@@ -5573,7 +5586,7 @@ export type GetConnectedImapSmtpCaldavAccountQueryVariables = Exact<{
}>;
export type GetConnectedImapSmtpCaldavAccountQuery = { __typename?: 'Query', getConnectedImapSmtpCaldavAccount: { __typename?: 'ConnectedImapSmtpCaldavAccount', id: string, handle: string, provider: string, accountOwnerId: string, connectionParameters?: { __typename?: 'ImapSmtpCaldavConnectionParameters', IMAP?: { __typename?: 'ConnectionParametersOutput', host: string, port: number, secure?: boolean | null, password: string } | null, SMTP?: { __typename?: 'ConnectionParametersOutput', host: string, username?: string | null, port: number, secure?: boolean | null, password: string } | null, CALDAV?: { __typename?: 'ConnectionParametersOutput', host: string, username?: string | null, password: string } | null } | null } };
export type GetConnectedImapSmtpCaldavAccountQuery = { __typename?: 'Query', getConnectedImapSmtpCaldavAccount: { __typename?: 'ConnectedImapSmtpCaldavAccount', id: string, handle: string, provider: string, accountOwnerId: string, connectionParameters?: { __typename?: 'ImapSmtpCaldavConnectionParameters', IMAP?: { __typename?: 'ConnectionParametersOutput', host: string, port: number, secure?: boolean | null, username?: string | null, password: string } | null, SMTP?: { __typename?: 'ConnectionParametersOutput', host: string, username?: string | null, port: number, secure?: boolean | null, password: string } | null, CALDAV?: { __typename?: 'ConnectionParametersOutput', host: string, username?: string | null, password: string } | null } | null } };
export type CreateDatabaseConfigVariableMutationVariables = Exact<{
key: Scalars['String'];
@@ -6069,9 +6082,9 @@ export type ViewFilterFragmentFragment = { __typename?: 'CoreViewFilter', id: st
export type ViewFilterGroupFragmentFragment = { __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string };
export type ViewFragmentFragment = { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type ViewFragmentFragment = { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null };
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null };
export type ViewSortFragmentFragment = { __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string };
@@ -6080,7 +6093,7 @@ export type CreateCoreViewMutationVariables = Exact<{
}>;
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type CreateCoreViewFieldMutationVariables = Exact<{
input: CreateViewFieldInput;
@@ -6108,7 +6121,7 @@ export type CreateCoreViewGroupMutationVariables = Exact<{
}>;
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type CreateCoreViewSortMutationVariables = Exact<{
input: CreateViewSortInput;
@@ -6129,7 +6142,7 @@ export type CreateManyCoreViewGroupsMutationVariables = Exact<{
}>;
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type DeleteCoreViewMutationVariables = Exact<{
id: Scalars['String'];
@@ -6164,7 +6177,7 @@ export type DeleteCoreViewGroupMutationVariables = Exact<{
}>;
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DeleteCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -6206,7 +6219,7 @@ export type DestroyCoreViewGroupMutationVariables = Exact<{
}>;
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DestroyCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -6221,7 +6234,7 @@ export type UpdateCoreViewMutationVariables = Exact<{
}>;
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type UpdateCoreViewFieldMutationVariables = Exact<{
input: UpdateViewFieldInput;
@@ -6250,7 +6263,7 @@ export type UpdateCoreViewGroupMutationVariables = Exact<{
}>;
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type UpdateCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -6263,7 +6276,7 @@ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreVi
export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>;
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindManyCoreViewFieldsQueryVariables = Exact<{
viewId: Scalars['String'];
@@ -6291,7 +6304,7 @@ export type FindManyCoreViewGroupsQueryVariables = Exact<{
}>;
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type FindManyCoreViewSortsQueryVariables = Exact<{
viewId?: InputMaybe<Scalars['String']>;
@@ -6305,14 +6318,14 @@ export type FindManyCoreViewsQueryVariables = Exact<{
}>;
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindOneCoreViewQueryVariables = Exact<{
id: Scalars['String'];
}>;
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
export type FindOneCoreViewFieldQueryVariables = Exact<{
id: Scalars['String'];
@@ -6340,7 +6353,7 @@ export type FindOneCoreViewGroupQueryVariables = Exact<{
}>;
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
export type FindOneCoreViewSortQueryVariables = Exact<{
id: Scalars['String'];
@@ -7142,7 +7155,6 @@ export const ViewSortFragmentFragmentDoc = gql`
export const ViewGroupFragmentFragmentDoc = gql`
fragment ViewGroupFragment on CoreViewGroup {
id
fieldMetadataId
isVisible
fieldValue
position
@@ -7165,6 +7177,7 @@ export const ViewFragmentFragmentDoc = gql`
openRecordIn
kanbanAggregateOperation
kanbanAggregateOperationFieldMetadataId
mainGroupByFieldMetadataId
anyFieldFilterValue
calendarFieldMetadataId
calendarLayout
@@ -10201,6 +10214,7 @@ export const GetConnectedImapSmtpCaldavAccountDocument = gql`
host
port
secure
username
password
}
SMTP {
+36 -19
View File
@@ -152,6 +152,7 @@ export type AggregateChartConfiguration = {
graphType: GraphType;
label?: Maybe<Scalars['String']>;
prefix?: Maybe<Scalars['String']>;
ratioAggregateConfig?: Maybe<RatioAggregateConfig>;
suffix?: Maybe<Scalars['String']>;
timezone?: Maybe<Scalars['String']>;
};
@@ -753,7 +754,6 @@ export type CoreViewGroup = {
__typename?: 'CoreViewGroup';
createdAt: Scalars['DateTime'];
deletedAt?: Maybe<Scalars['DateTime']>;
fieldMetadataId: Scalars['UUID'];
fieldValue: Scalars['String'];
id: Scalars['UUID'];
isVisible: Scalars['Boolean'];
@@ -888,6 +888,7 @@ export type CreateRoleInput = {
canAccessAllTools?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToAgents?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApiKeys?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApplications?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToUsers?: InputMaybe<Scalars['Boolean']>;
canDestroyAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
canReadAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
@@ -946,7 +947,6 @@ export type CreateViewFilterInput = {
};
export type CreateViewGroupInput = {
fieldMetadataId: Scalars['UUID'];
fieldValue: Scalars['String'];
id?: InputMaybe<Scalars['UUID']>;
isVisible?: InputMaybe<Scalars['Boolean']>;
@@ -3547,6 +3547,17 @@ export type QueueRetentionConfig = {
failedMaxCount: Scalars['Float'];
};
export type RatioAggregateConfig = {
__typename?: 'RatioAggregateConfig';
fieldMetadataId: Scalars['UUID'];
optionValue: Scalars['String'];
};
export type RatioAggregateConfigInput = {
fieldMetadataId: Scalars['UUID'];
optionValue: Scalars['String'];
};
export type Relation = {
__typename?: 'Relation';
sourceFieldMetadata: Field;
@@ -3633,6 +3644,7 @@ export type Role = {
objectPermissions?: Maybe<Array<ObjectPermission>>;
permissionFlags?: Maybe<Array<PermissionFlag>>;
standardId?: Maybe<Scalars['UUID']>;
universalIdentifier?: Maybe<Scalars['UUID']>;
workspaceMembers: Array<WorkspaceMember>;
};
@@ -4189,6 +4201,7 @@ export type UpdateRolePayload = {
canAccessAllTools?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToAgents?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApiKeys?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToApplications?: InputMaybe<Scalars['Boolean']>;
canBeAssignedToUsers?: InputMaybe<Scalars['Boolean']>;
canDestroyAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
canReadAllObjectRecords?: InputMaybe<Scalars['Boolean']>;
@@ -4824,7 +4837,7 @@ export type SearchQueryVariables = Exact<{
export type SearchQuery = { __typename?: 'Query', search: { __typename?: 'SearchResultConnection', edges: Array<{ __typename?: 'SearchResultEdge', cursor: string, node: { __typename?: 'SearchRecord', recordId: any, objectNameSingular: string, label: string, imageUrl?: string | null, tsRankCD: number, tsRank: number } }>, pageInfo: { __typename?: 'SearchResultPageInfo', hasNextPage: boolean, endCursor?: string | null } } };
export type PageLayoutWidgetFragmentFragment = { __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null };
export type PageLayoutWidgetFragmentFragment = { __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null };
export type UpdatePageLayoutWithTabsAndWidgetsMutationVariables = Exact<{
id: Scalars['String'];
@@ -4832,7 +4845,7 @@ export type UpdatePageLayoutWithTabsAndWidgetsMutationVariables = Exact<{
}>;
export type UpdatePageLayoutWithTabsAndWidgetsMutation = { __typename?: 'Mutation', updatePageLayoutWithTabsAndWidgets: { __typename?: 'PageLayout', id: any, name: string, type: PageLayoutType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, tabs?: Array<{ __typename?: 'PageLayoutTab', id: any, title: string, position: number, pageLayoutId: any, createdAt: string, updatedAt: string, widgets?: Array<{ __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null }> | null }> | null } };
export type UpdatePageLayoutWithTabsAndWidgetsMutation = { __typename?: 'Mutation', updatePageLayoutWithTabsAndWidgets: { __typename?: 'PageLayout', id: any, name: string, type: PageLayoutType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, tabs?: Array<{ __typename?: 'PageLayoutTab', id: any, title: string, position: number, pageLayoutId: any, createdAt: string, updatedAt: string, widgets?: Array<{ __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null }> | null }> | null } };
export type OnDbEventSubscriptionVariables = Exact<{
input: OnDbEventInput;
@@ -4847,9 +4860,9 @@ export type ViewFilterFragmentFragment = { __typename?: 'CoreViewFilter', id: an
export type ViewFilterGroupFragmentFragment = { __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any };
export type ViewFragmentFragment = { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type ViewFragmentFragment = { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null };
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null };
export type ViewSortFragmentFragment = { __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any };
@@ -4858,7 +4871,7 @@ export type CreateCoreViewMutationVariables = Exact<{
}>;
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type CreateCoreViewFieldMutationVariables = Exact<{
input: CreateViewFieldInput;
@@ -4886,7 +4899,7 @@ export type CreateCoreViewGroupMutationVariables = Exact<{
}>;
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type CreateCoreViewSortMutationVariables = Exact<{
input: CreateViewSortInput;
@@ -4907,7 +4920,7 @@ export type CreateManyCoreViewGroupsMutationVariables = Exact<{
}>;
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type DeleteCoreViewMutationVariables = Exact<{
id: Scalars['String'];
@@ -4942,7 +4955,7 @@ export type DeleteCoreViewGroupMutationVariables = Exact<{
}>;
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DeleteCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -4984,7 +4997,7 @@ export type DestroyCoreViewGroupMutationVariables = Exact<{
}>;
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type DestroyCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -4999,7 +5012,7 @@ export type UpdateCoreViewMutationVariables = Exact<{
}>;
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
export type UpdateCoreViewFieldMutationVariables = Exact<{
input: UpdateViewFieldInput;
@@ -5028,7 +5041,7 @@ export type UpdateCoreViewGroupMutationVariables = Exact<{
}>;
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
export type UpdateCoreViewSortMutationVariables = Exact<{
id: Scalars['String'];
@@ -5041,7 +5054,7 @@ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreVi
export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>;
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindManyCoreViewFieldsQueryVariables = Exact<{
viewId: Scalars['String'];
@@ -5069,7 +5082,7 @@ export type FindManyCoreViewGroupsQueryVariables = Exact<{
}>;
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
export type FindManyCoreViewSortsQueryVariables = Exact<{
viewId?: InputMaybe<Scalars['String']>;
@@ -5083,14 +5096,14 @@ export type FindManyCoreViewsQueryVariables = Exact<{
}>;
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
export type FindOneCoreViewQueryVariables = Exact<{
id: Scalars['String'];
}>;
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
export type FindOneCoreViewFieldQueryVariables = Exact<{
id: Scalars['String'];
@@ -5118,7 +5131,7 @@ export type FindOneCoreViewGroupQueryVariables = Exact<{
}>;
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
export type FindOneCoreViewSortQueryVariables = Exact<{
id: Scalars['String'];
@@ -5225,6 +5238,10 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql`
suffix
timezone
firstDayOfTheWeek
ratioAggregateConfig {
fieldMetadataId
optionValue
}
}
... on GaugeChartConfiguration {
graphType
@@ -5299,7 +5316,6 @@ export const ViewSortFragmentFragmentDoc = gql`
export const ViewGroupFragmentFragmentDoc = gql`
fragment ViewGroupFragment on CoreViewGroup {
id
fieldMetadataId
isVisible
fieldValue
position
@@ -5322,6 +5338,7 @@ export const ViewFragmentFragmentDoc = gql`
openRecordIn
kanbanAggregateOperation
kanbanAggregateOperationFieldMetadataId
mainGroupByFieldMetadataId
anyFieldFilterValue
calendarFieldMetadataId
calendarLayout
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Laat oplaai van lêers en aanhegsels toe"
msgid "Allow users to sign in with an email and password."
msgstr "Laat toe dat gebruikers aanmeld met 'n e-pos en wagwoord."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "9n Fout het voorgekom terwyl jou boodskap verwerk is"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Voer 'n getal tussen 0 en 23 in"
msgid "Enter number between 0 and 59"
msgstr "Voer 'n getal tussen 0 en 59 in"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Kon nie beeld opgelaai word nie: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Vals"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Minimum reeks"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Geen konfig veranderlikes pas by jou huidige filters nie. Probeer jou fi
msgid "No country"
msgstr "Geen land nie"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Bladsy"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Wagry: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Soek vir 'n objek"
msgid "Search operations"
msgstr "Soek werksaamhede"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Kies die gebeure wat jy wil stuur na hierdie eindpunt"
msgid "Select the sheet to use"
msgstr "Kies die bladsy om te gebruik"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Sneller die funksie met Http-versoek"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Waar"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "السماح برفع الملفات والمرفقات"
msgid "Allow users to sign in with an email and password."
msgstr "السماح للمستخدمين بتسجيل الدخول باستخدام البريد الإلكتروني وكلمة المرور."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "حدث خطأ أثناء معالجة رسالتك"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "أدخل رقمًا بين 0 و 23"
msgid "Enter number between 0 and 59"
msgstr "أدخل رقمًا بين 0 و 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "فشل في تحميل الصورة: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "خاطئ"
@@ -6015,6 +6023,11 @@ msgstr "الأدنى"
msgid "Min range"
msgstr "الحد الأدنى للمدى"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "لا توجد متغيرات تكوين مطابقة للمرشحات ا
msgid "No country"
msgstr "لا دولة"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "الصفحة"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "الطابور: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "ابحث عن الكائنات"
msgid "Search operations"
msgstr "ابحث عن العمليات"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "\\\\"
msgid "Select the sheet to use"
msgstr "اختر الورقة لاستخدامها"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "تشغيل الوظيفة عند طلب http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "صحيح"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Permetre carregar arxius i adjunts"
msgid "Allow users to sign in with an email and password."
msgstr "Permetre als usuaris iniciar sessió amb un correu electrònic i contrasenya."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "S'ha produït un error mentre es processava el teu missatge"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Introdueix un número entre 0 i 23"
msgid "Enter number between 0 and 59"
msgstr "Introdueix un número entre 0 i 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Error en pujar la imatge: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Fals"
@@ -6015,6 +6023,11 @@ msgstr "Mínim"
msgid "Min range"
msgstr "Rang mínim"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "No hi ha variables de configuració que coincideixen amb els teus filtre
msgid "No country"
msgstr "Sense país"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Pàgina"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Cua: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Cerca objectes"
msgid "Search operations"
msgstr "Cerca operacions"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Selecciona els esdeveniments que vols enviar a aquest endpoint"
msgid "Select the sheet to use"
msgstr "Selecciona el full a utilitzar"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Desencadena la funció amb una sol·licitud Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Cert"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Povolit nahrávání souborů a příloh"
msgid "Allow users to sign in with an email and password."
msgstr "Povolit uživatelům přihlášení pomocí e-mailu a hesla."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Při zpracování vaší zprávy došlo k chybě"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Zadejte číslo mezi 0 a 23"
msgid "Enter number between 0 and 59"
msgstr "Zadejte číslo mezi 0 a 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Nelze nahrát obrázek: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Nepravdivé"
@@ -6015,6 +6023,11 @@ msgstr "Minimální"
msgid "Min range"
msgstr "Minimální rozsah"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Žádné konfigurační proměnné neodpovídají vašim současným fil
msgid "No country"
msgstr "Žádná země"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Stránka"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Fronta: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Hledat objekty"
msgid "Search operations"
msgstr "Hledat operace"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Vyberte události, které chcete poslat na tento koncový bod"
msgid "Select the sheet to use"
msgstr "Vyberte list k použití"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Spouští funkci pomocí Http požadavku"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Pravda"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Tillad upload af filer og vedhæftninger"
msgid "Allow users to sign in with an email and password."
msgstr "Tillad brugere at logge ind med en e-mail og password."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Der opstod en fejl under behandlingen af din meddelelse"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Indtast et tal mellem 0 og 23"
msgid "Enter number between 0 and 59"
msgstr "Indtast et tal mellem 0 og 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Kunne ikke uploade billede: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Falsk"
@@ -6015,6 +6023,11 @@ msgstr "Min."
msgid "Min range"
msgstr "Minimumsgrænse"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Ingen konfigurationsvariabler matcher dine nuværende filtre. Prøv at j
msgid "No country"
msgstr "Intet land"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Side"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Kø: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Søg objekter"
msgid "Search operations"
msgstr "Søg operationer"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Vælg de begivenheder du ønsker at sende til dette endepunkt"
msgid "Select the sheet to use"
msgstr "Vælg arket til brug"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9485,6 +9537,8 @@ msgstr "Udløser funktionen med Http-anmodning"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Sand"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Hochladen von Dateien und Anhängen erlauben"
msgid "Allow users to sign in with an email and password."
msgstr "Benutzern erlauben, sich mit E-Mail und Passwort anzumelden."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Ein Fehler ist bei der Verarbeitung Ihrer Nachricht aufgetreten"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Geben Sie eine Zahl zwischen 0 und 23 ein"
msgid "Enter number between 0 and 59"
msgstr "Geben Sie eine Zahl zwischen 0 und 59 ein"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Fehler beim Hochladen des Bildes: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Falsch"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Minimaler Bereich"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Keine Konfigurationsvariablen entsprechen Ihren aktuellen Filtern. Versu
msgid "No country"
msgstr "Kein Land"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Seite"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Warteschlange: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Objekte suchen"
msgid "Search operations"
msgstr "Operationen suchen"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Wählen Sie die Ereignisse aus, die Sie an diesen Endpunkt senden möcht
msgid "Select the sheet to use"
msgstr "Tabelle zur Verwendung auswählen"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Löst die Funktion mit HTTP-Anfrage aus"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Wahr"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Επιτρέπεται η ανάρτηση αρχείων και συν
msgid "Allow users to sign in with an email and password."
msgstr "Επιτρέψτε στους χρήστες να συνδέονται με email και κωδικό πρόσβασης."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Παρουσιάστηκε σφάλμα κατά την επεξεργα
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Εισάγετε αριθμό μεταξύ 0 και 23"
msgid "Enter number between 0 and 59"
msgstr "Εισάγετε αριθμό μεταξύ 0 και 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Αποτυχία μεταφόρτωσης εικόνας: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Ψευδής"
@@ -6015,6 +6023,11 @@ msgstr "Ελάχιστο"
msgid "Min range"
msgstr "Ελάχιστο εύρος"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Καμία μεταβλητή διαμόρφωσης δεν ταιριά
msgid "No country"
msgstr "Χωρίς χώρα"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Σελίδα"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Ουρά: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Αναζήτηση αντικειμένων"
msgid "Search operations"
msgstr "Αναζήτηση ενεργειών"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Επιλέξτε τα γεγονότα που επιθυμείτε να
msgid "Select the sheet to use"
msgstr "Επιλέξτε το φύλλο που θα χρησιμοποιηθεί"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9487,6 +9539,8 @@ msgstr "Ενεργοποιεί τη λειτουργία με αίτημα Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Αληθής"
+58 -4
View File
@@ -1058,6 +1058,11 @@ msgstr "Allow uploading files and attachments"
msgid "Allow users to sign in with an email and password."
msgstr "Allow users to sign in with an email and password."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr "An error occurred"
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1071,9 +1076,6 @@ msgstr "An error occurred while processing your message"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3806,8 +3808,12 @@ msgstr "Enter number between 0 and 23"
msgid "Enter number between 0 and 59"
msgstr "Enter number between 0 and 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr "Enter number between 1 and 60"
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4379,6 +4385,8 @@ msgstr "Failed to upload image: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "False"
@@ -6010,6 +6018,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Min range"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6423,6 +6436,12 @@ msgstr "No config variables match your current filters. Try adjusting your filte
msgid "No country"
msgstr "No country"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr "No data"
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7066,6 +7085,21 @@ msgstr "p"
msgid "Page"
msgstr "Page"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr "page layout"
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr "page layout tab"
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr "page layout widget"
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7502,6 +7536,14 @@ msgstr "Queue: {queueName}"
msgid "Quick model for routing decisions"
msgstr "Quick model for routing decisions"
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr "Ratio"
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8088,6 +8130,11 @@ msgstr "Search objects"
msgid "Search operations"
msgstr "Search operations"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr "Search options"
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8317,6 +8364,11 @@ msgstr "Select the events you wish to send to this endpoint"
msgid "Select the sheet to use"
msgstr "Select the sheet to use"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr "Select value"
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9480,6 +9532,8 @@ msgstr "Triggers the function with Http request"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "True"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Permitir la carga de archivos y archivos adjuntos"
msgid "Allow users to sign in with an email and password."
msgstr "Permitir que los usuarios inicien sesión con un correo electrónico y una contraseña."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Ocurrió un error al procesar su mensaje"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Introduce un número entre 0 y 23"
msgid "Enter number between 0 and 59"
msgstr "Introduce un número entre 0 y 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Error al subir la imagen: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Falso"
@@ -6015,6 +6023,11 @@ msgstr "Mín"
msgid "Min range"
msgstr "Rango mínimo"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Ninguna variable de configuración coincide con sus filtros actuales. In
msgid "No country"
msgstr "Sin país"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr "p"
msgid "Page"
msgstr "Página"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Cola: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Buscar objetos"
msgid "Search operations"
msgstr "Buscar operaciones"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Selecciona los eventos que deseas enviar a este endpoint"
msgid "Select the sheet to use"
msgstr "Selecciona la hoja a utilizar"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9485,6 +9537,8 @@ msgstr "Activa la función con solicitud Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Verdadero"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Salli tiedostojen ja liitteiden lataaminen"
msgid "Allow users to sign in with an email and password."
msgstr "Salli käyttäjien kirjautua sisään sähköpostilla ja salasanalla."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Virhe viestin käsittelyssä"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Syötä luku 0:n ja 23:n väliltä"
msgid "Enter number between 0 and 59"
msgstr "Syötä luku 0:n ja 59:n väliltä"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Kuvan lataus epäonnistui: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Väärä"
@@ -6015,6 +6023,11 @@ msgstr "Pienin"
msgid "Min range"
msgstr "Minimirajoitus"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Mikään määritysmuuttuja ei vastaa nykyisiä suodattimiasi. Yritä mu
msgid "No country"
msgstr "Ei maata"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Sivu"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Jono: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Etsi objekteja"
msgid "Search operations"
msgstr "Etsi toimintoja"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Valitse tapahtumat, jotka haluat lähettää tähän päätepisteeseen"
msgid "Select the sheet to use"
msgstr "Valitse käytettävä arkki"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Aktivoi toiminnon Http-pyynnöllä"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Tosi"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Permettre le téléversement des fichiers et des pièces jointes"
msgid "Allow users to sign in with an email and password."
msgstr "Permettre aux utilisateurs de se connecter avec un courriel et un mot de passe."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Une erreur s'est produite lors du traitement de votre message"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Entrez un nombre entre 0 et 23"
msgid "Enter number between 0 and 59"
msgstr "Entrez un nombre entre 0 et 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Échec du téléchargement de l'image : "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Faux"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Portée min"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Aucune variable de configuration ne correspond à vos filtres actuels. E
msgid "No country"
msgstr "Aucun pays"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Page"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "File d'attente : {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Rechercher des objets"
msgid "Search operations"
msgstr "Rechercher des opérations"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Sélectionnez les événements à envoyer à ce point de terminaison"
msgid "Select the sheet to use"
msgstr "Sélectionnez la feuille à utiliser"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9485,6 +9537,8 @@ msgstr "Déclenche la fonction avec une requête HTTP"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Vrai"
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
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "אפשר העלאה של קבצים וצרופות"
msgid "Allow users to sign in with an email and password."
msgstr "אפשר למשתמשים להיכנס עם דוא\"ל וסיסמה."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "אירעה שגיאה בעת עיבוד ההודעה שלך"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "הזן מספר בין 0 ל-23"
msgid "Enter number between 0 and 59"
msgstr "הזן מספר בין 0 ל-59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "העלאת תמונה נכשלה: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "שקר"
@@ -6015,6 +6023,11 @@ msgstr "מינ'"
msgid "Min range"
msgstr "טווח מינימלי"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "אין משתני הגדרות שתואמים לסינון הנוכחי
msgid "No country"
msgstr "אין מדינה"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "דף"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "תור: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "חפש אובייקטים"
msgid "Search operations"
msgstr "חפש פעולות"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "\\"
msgid "Select the sheet to use"
msgstr "בחר את הגיליון לשימוש"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "מפעיל את הפונקציה עם בקשת Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "נכון"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Fájlok és mellékletek feltöltésének engedélyezése"
msgid "Allow users to sign in with an email and password."
msgstr "Engedélyezze a felhasználóknak a bejelentkezést e-mail és jelszó segítségével."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Hiba történt az üzenet feldolgozása során"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Adjon meg egy számot 0 és 23 között"
msgid "Enter number between 0 and 59"
msgstr "Adjon meg egy számot 0 és 59 között"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Nem sikerült feltölteni a képet: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Hamis"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Minimális tartomány"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Nincsenek konfigurációs változók, amelyek megfelelnének a jelenlegi
msgid "No country"
msgstr "Nincs ország"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Oldal"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Sor: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Objektumok keresése"
msgid "Search operations"
msgstr "Műveletek keresése"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Válassza ki, mely események szeretné elküldeni ebbe az végpontba"
msgid "Select the sheet to use"
msgstr "Válassza ki a használni kívánt lapot"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "A funkciót Http kéréssel indítja el"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Igaz"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Consenti il caricamento di file e allegati"
msgid "Allow users to sign in with an email and password."
msgstr "Consenti agli utenti di accedere con e-mail e password."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Si è verificato un errore durante l'elaborazione del messaggio"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Immettere un numero tra 0 e 23"
msgid "Enter number between 0 and 59"
msgstr "Immettere un numero tra 0 e 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Caricamento dell'immagine fallito: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Falso"
@@ -6015,6 +6023,11 @@ msgstr "Minimo"
msgid "Min range"
msgstr "Intervallo minimo"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Nessuna variabile di configurazione corrisponde ai filtri attuali. Prova
msgid "No country"
msgstr "Nessun paese"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Pagina"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Coda: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Cerca oggetti"
msgid "Search operations"
msgstr "Cerca operazioni"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Seleziona gli eventi da inviare a questo endpoint"
msgid "Select the sheet to use"
msgstr "Seleziona il foglio da utilizzare"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9485,6 +9537,8 @@ msgstr "Attiva la funzione con richiesta Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Vero"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "ファイルと添付ファイルのアップロードを許可する"
msgid "Allow users to sign in with an email and password."
msgstr "ユーザーがメールとパスワードでサインインできるようにする。"
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "メッセージの処理中にエラーが発生しました"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "0 から 23 の間の数字を入力してください"
msgid "Enter number between 0 and 59"
msgstr "0 から 59 の間の数字を入力してください"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "画像のアップロードに失敗しました: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "偽"
@@ -6015,6 +6023,11 @@ msgstr "最小"
msgid "Min range"
msgstr "最小範囲"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "現在のフィルターでは設定変数と一致しません。フィ
msgid "No country"
msgstr "国なし"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "ページ"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "キュー: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "オブジェクトを検索"
msgid "Search operations"
msgstr "操作を検索"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "このエンドポイントに送信したいイベントを選択して
msgid "Select the sheet to use"
msgstr "使用するシートを選択"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Httpリクエストで関数をトリガーします"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "真"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "파일 및 첨부파일 업로드 허용"
msgid "Allow users to sign in with an email and password."
msgstr "이메일과 비밀번호로 로그인 허용."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "메시지를 처리하는 중 오류가 발생했습니다"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "0에서 23 사이의 숫자를 입력하세요"
msgid "Enter number between 0 and 59"
msgstr "0에서 59 사이의 숫자를 입력하세요"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "이미지 업로드 실패: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "거짓"
@@ -6015,6 +6023,11 @@ msgstr "최소"
msgid "Min range"
msgstr "최소 범위"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "현재 필터와 일치하는 설정 변수가 없습니다. 필터 또
msgid "No country"
msgstr "국가 없음"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "페이지"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "큐: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "개체 검색"
msgid "Search operations"
msgstr "작업 검색"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "이 엔드포인트로 보내려는 이벤트를 선택하세요"
msgid "Select the sheet to use"
msgstr "사용할 시트 선택"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Http 요청으로 함수를 실행"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "참"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Toestaan dat bestanden en bijlagen worden geüpload"
msgid "Allow users to sign in with an email and password."
msgstr "Sta gebruikers toe om in te loggen met een e-mailadres en wachtwoord."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Er is een fout opgetreden tijdens het verwerken van uw bericht"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Voer een getal in tussen 0 en 23"
msgid "Enter number between 0 and 59"
msgstr "Voer een getal in tussen 0 en 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Afbeelding uploaden mislukt: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Vals"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Min bereik"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Geen configuratievariabelen die overeenkomen met je huidige filters. Pro
msgid "No country"
msgstr "Geen land"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Pagina"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Wachtrij: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Zoek objecten"
msgid "Search operations"
msgstr "Zoek bewerkingen"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Selecteer de gebeurtenissen die je naar dit eindpunt wilt sturen"
msgid "Select the sheet to use"
msgstr "Selecteer het te gebruiken blad"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9485,6 +9537,8 @@ msgstr "Activeert de functie met een Http-verzoek"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Waar"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Tillat opplasting av filer og vedlegg"
msgid "Allow users to sign in with an email and password."
msgstr "Tillat brukere å logge inn med en e-post og passord."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "En feil oppstod ved behandling av meldingen din"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Skriv inn et tall mellom 0 og 23"
msgid "Enter number between 0 and 59"
msgstr "Skriv inn et tall mellom 0 og 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Feil ved opplasting av bilde: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Usann"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Min rekkevidde"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Ingen konfigurasjonsvariabler samsvarer med dine gjeldende filtre. Prøv
msgid "No country"
msgstr "Intet land"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Side"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Kø: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Søk objekter"
msgid "Search operations"
msgstr "Søk operasjoner"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Velg hendelsene du ønsker å sende til dette sluttpunktet"
msgid "Select the sheet to use"
msgstr "Velg arket som skal brukes"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Utløser funksjonen med Http-forespørsel"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Sant"
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Zezwól na przesyłanie plików i załączników"
msgid "Allow users to sign in with an email and password."
msgstr "Zezwalaj użytkownikom na logowanie się za pomocą adresu e-mail i hasła."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Wystąpił błąd podczas przetwarzania twojej wiadomości"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Wprowadź liczbę między 0 a 23"
msgid "Enter number between 0 and 59"
msgstr "Wprowadź liczbę między 0 a 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Nie udało się przesłać obrazu: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Fałsz"
@@ -6015,6 +6023,11 @@ msgstr "Min"
msgid "Min range"
msgstr "Minimalny zakres"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Żadne zmienne konfiguracyjne nie odpowiadają bieżącym filtrom. Spró
msgid "No country"
msgstr "Brak kraju"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Strona"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Kolejka: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Szukaj obiektów"
msgid "Search operations"
msgstr "Szukaj operacji"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Wybierz zdarzenia, które chcesz wysłać do tego punktu końcowego"
msgid "Select the sheet to use"
msgstr "Wybierz arkusz do użycia"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Wywołuje funkcję żądaniem Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Prawda"
+58 -4
View File
@@ -1058,6 +1058,11 @@ msgstr ""
msgid "Allow users to sign in with an email and password."
msgstr ""
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1071,9 +1076,6 @@ msgstr ""
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3806,8 +3808,12 @@ msgstr ""
msgid "Enter number between 0 and 59"
msgstr ""
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4379,6 +4385,8 @@ msgstr ""
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr ""
@@ -6010,6 +6018,11 @@ msgstr ""
msgid "Min range"
msgstr ""
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6423,6 +6436,12 @@ msgstr ""
msgid "No country"
msgstr ""
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7066,6 +7085,21 @@ msgstr ""
msgid "Page"
msgstr ""
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7502,6 +7536,14 @@ msgstr ""
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8088,6 +8130,11 @@ msgstr ""
msgid "Search operations"
msgstr ""
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8317,6 +8364,11 @@ msgstr ""
msgid "Select the sheet to use"
msgstr ""
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9478,6 +9530,8 @@ msgstr ""
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr ""
+58 -4
View File
@@ -1063,6 +1063,11 @@ msgstr "Permitir upload de arquivos e anexos"
msgid "Allow users to sign in with an email and password."
msgstr "Permitir que os usuários façam login com e-mail e senha."
#. js-lingui-id: Vw8l6h
#: src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/HttpRequestExecutionResult.tsx
msgid "An error occurred"
msgstr ""
#. js-lingui-id: VD5bNu
#: src/modules/auth/sign-in-up/hooks/useSignInUp.ts
msgid "An error occurred while checking user existence"
@@ -1076,9 +1081,6 @@ msgstr "Ocorreu um erro ao processar sua mensagem"
#. js-lingui-id: XyOToQ
#: src/utils/get-error-message-from-apollo-error.util.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewGroup.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
#: src/modules/views/hooks/internal/usePersistViewFilter.ts
@@ -3811,8 +3813,12 @@ msgstr "Insira um número entre 0 e 23"
msgid "Enter number between 0 and 59"
msgstr "Insira um número entre 0 e 59"
#. js-lingui-id: ahwn7G
#. js-lingui-id: uhDeRP
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number between 1 and 60"
msgstr ""
#. js-lingui-id: ahwn7G
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Enter number greater than 1"
@@ -4384,6 +4390,8 @@ msgstr "Falha ao enviar imagem: "
#. js-lingui-id: ocUvR+
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "False"
msgstr "Falso"
@@ -6015,6 +6023,11 @@ msgstr "Mínimo"
msgid "Min range"
msgstr "Intervalo mínimo"
#. js-lingui-id: G6wJK8
#: src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
msgid "Minute value cannot exceed 60. For intervals greater than 60 minutes, use the \"Hours\" trigger type or a custom cron expression"
msgstr ""
#. js-lingui-id: agRWc1
#: src/modules/workflow/workflow-steps/workflow-actions/delay-actions/components/WorkflowEditActionDelay.tsx
msgid "Minutes"
@@ -6428,6 +6441,12 @@ msgstr "Nenhuma variável de configuração corresponde aos seus filtros atuais.
msgid "No country"
msgstr "Sem país"
#. js-lingui-id: plhHQt
#: src/modules/page-layout/widgets/graph/graphWidgetPieChart/components/PieChartCenterMetricLayer.tsx
#: src/modules/page-layout/widgets/graph/components/NoDataLayer.tsx
msgid "No data"
msgstr ""
#. js-lingui-id: B6C0XJ
#: src/modules/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay.tsx
msgid "No Data"
@@ -7071,6 +7090,21 @@ msgstr ""
msgid "Page"
msgstr "Página"
#. js-lingui-id: SZgFyi
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout"
msgstr ""
#. js-lingui-id: HFlGEK
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout tab"
msgstr ""
#. js-lingui-id: sVcqjZ
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
msgid "page layout widget"
msgstr ""
#. js-lingui-id: boJlGf
#: src/pages/not-found/NotFound.tsx
msgid "Page Not Found"
@@ -7507,6 +7541,14 @@ msgstr "Fila: {queueName}"
msgid "Quick model for routing decisions"
msgstr ""
#. js-lingui-id: iaocTt
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts
#: src/modules/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
msgid "Ratio"
msgstr ""
#. js-lingui-id: BT7pY+
#: src/modules/settings/profile/components/SetOrChangePassword.tsx
msgid "Receive an email containing password set link"
@@ -8093,6 +8135,11 @@ msgstr "Pesquisar objetos"
msgid "Search operations"
msgstr "Pesquisar operações"
#. js-lingui-id: 996xRf
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Search options"
msgstr ""
#. js-lingui-id: IMeaSJ
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
#: src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx
@@ -8322,6 +8369,11 @@ msgstr "Selecione os eventos que deseja enviar para este endpoint"
msgid "Select the sheet to use"
msgstr "Selecione a planilha a ser usada"
#. js-lingui-id: iWf0Zj
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "Select value"
msgstr ""
#. js-lingui-id: AXTJAW
#: src/pages/settings/profile/appearance/components/SettingsExperience.tsx
msgid "Select your preferred language"
@@ -9483,6 +9535,8 @@ msgstr "Aciona a função com solicitação Http"
#. js-lingui-id: c+xCSz
#: src/modules/settings/data-model/fields/forms/boolean/constants/BooleanDataModelSelectOptions.ts
#: src/modules/object-record/record-board/record-board-column/utils/getAggregateOperationShortLabel.ts
#: src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts
#: src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx
msgid "True"
msgstr "Verdadeiro"

Some files were not shown because too many files have changed in this diff Show More