Compare commits

..
Author SHA1 Message Date
Shivam Mishra 1e1f79025e chore: add scripts to test 2025-11-14 15:18:30 +05:30
Tanmay Deep Sharma e84efdccbd Merge remote-tracking branch 'origin/assignment_v2/assignment_service' into assignment_v2/assignment_service 2025-11-12 18:00:34 +05:30
Tanmay Deep Sharma 44da47c845 review changes 2025-11-12 17:59:52 +05:30
Tanmay Deep SharmaandGitHub 0ebe187c8a Merge branch 'develop' into assignment_v2/assignment_service 2025-11-06 01:48:23 +05:30
PranavandGitHub 5491ca2470 feat: Differentiate bot and user in the summary (#12801)
While generating the summary, use the appropriate sender type for the
message.
2025-11-05 11:42:21 -08:00
72391f9c36 fix: Video bubble click and play issue (#12764)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-11-05 15:40:11 +05:30
Sojan JoseandGitHub f89d9a4401 feat: Bulk delete for contacts (#12778)
Introduces a new bulk action `delete` for contacts

ref: https://github.com/chatwoot/chatwoot/pull/12763

## Screens

<img width="1492" height="973" alt="Screenshot 2025-10-31 at 6 27 21 PM"
src="https://github.com/user-attachments/assets/30dab1bb-2c2c-4168-9800-44e0eb5f8e3a"
/>
<img width="1492" height="985" alt="Screenshot 2025-10-31 at 6 27 32 PM"
src="https://github.com/user-attachments/assets/5be610c4-b19e-4614-a164-103b22337382"
/>
2025-11-04 17:47:53 -08:00
Sojan JoseandGitHub e8ae73230d fix: Gate Sidekiq dequeue logger behind env (#12790)
## Summary
- wrap the dequeue middleware registration in a boolean env flag
- document the ENABLE_SIDEKIQ_DEQUEUE_LOGGER option in .env.example
2025-11-04 16:01:47 -08:00
PranavandGitHub 40c75941f5 fix: Avoid introducing new attributes in search (#12791)
Fix `Limit of total fields [1000] has been exceeded`


https://linear.app/chatwoot/issue/CW-5861/searchkickimporterror-type-=-illegal-argument-exception-reason-=-limit#comment-6b6e41bd
2025-11-04 13:28:51 -08:00
d9b840f161 fix: Optimize Message search_data to prevent OpenSearch field explosion (#12786)
## Description

Refactored the `Message#search_data` method to prevent exceeding
OpenSearch's 1000 field limit during reindex operations.

**Problem:** The previous implementation serialized entire ActiveRecord
objects (Inbox, Sender, Conversation) with all their JSONB fields,
causing dynamic field explosion in OpenSearch. This resulted in
`Searchkick::ImportError` with "Limit of total fields [1000] has been
exceeded".

**Solution:** Whitelisted only necessary fields for search and
filtering, and flattened JSONB `custom_attributes` into key-value pair
arrays to prevent unbounded field creation.

Linked to: CW-5861

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [x] This change requires a documentation update

## How Has This Been Tested?

- Verified rubocop passes with no offenses
- Code review of search field usage from
`enterprise/app/services/enterprise/search_service.rb`
- Analyzed actual search queries to determine required indexed fields

**Still needed:**
- Full reindex test on staging/production environment
- Verify search functionality still works after reindex
- Confirm field count is under 1000 limit

## Changes Made

### Before
- Indexed 1000+ fields (entire AR objects with JSONB)
- `inbox` = full Inbox object (23+ fields + JSONB)
- `sender` = full Contact/User/AgentBot object (10+ fields + JSONB)
- `conversation` = full push_event_data
- Dynamic JSONB keys creating unlimited fields

### After
- ~35-40 controlled fields
- Whitelisted search fields: `content`, `attachment_transcribed_text`,
`email_subject`
- Filter fields: `account_id`, `inbox_id`, `conversation_id`,
`sender_id`, `sender_type`, etc.
- Flattened `custom_attributes`: `[{key, value, value_type}]` format
- Helper methods: `search_conversation_data`, `search_inbox_data`,
`search_sender_data`, `search_additional_data`

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

## Post-merge Steps

After merging, the following steps are required:

1. **Reindex all messages:**
   ```bash
   bundle exec rails runner "Message.reindex"
   ```

2. **Verify field count:**
   ```bash
   bundle exec rails runner "
     client = Searchkick.client
     index_name = Message.searchkick_index.name
     mapping = client.indices.get_mapping(index: index_name)
     fields = mapping.dig(index_name, 'mappings', 'properties')
     puts 'Total fields: ' + fields.keys.count.to_s
   "
   ```

3. **Test search functionality** to ensure queries still work as
expected

---------

Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
2025-11-03 17:37:51 -08:00
1fbdd68222 feat: Add company auto-association for contacts (CW-5726 Part 2) (#12711)
## Description

Implements real-time company auto-association for contacts based on
email domains. This is **Part 2** of the company model production
rollout (CW-5726).

**Task:**
- When a contact is created with a business email, automatically create
and associate a company from the email domain
- When a contact is updated with an email for the first time (email was
previously nil), associate with a company
- Preserve existing company associations when email changes to avoid
user confusion
- Skip free email providers and disposable domains

**Dependencies:**
⚠️ Requires PR #12657 (Part 1: Backfill migration) to be merged first

**Linear ticket:**
[CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production)

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Service specs: Tests business email detection, company creation,
association logic, edge cases (existing companies, free emails, nil
emails)
- Integration specs: Tests full callback flow for contact create/update
scenarios
- All tests passing: 10 examples, 0 failures
- RuboCop: 0 offenses

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules (PR #12657 pending)

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2025-11-03 20:36:13 +05:30
ef54f07d5b feat: Add company backfill migration for existing contacts (Part 1) (#12657)
## Description

Implements company backfill migration infrastructure for existing
contacts. This is **Part 1 of 2** for the company model production
rollout as described in
[CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production).

Creates jobs and services to associate existing contacts with companies
based on their email domains, filtering out free email providers (gmail,
yahoo, etc.) and disposable addresses.
 

**What's included:**
- Business email detector service with ValidEmail2 (uses
`disposable_domain?` to avoid DNS lookups)
- Per-account batch job to process contacts for one account
- Orchestrator job to iterate all accounts
- Rake task: `bundle exec rake companies:backfill`

~~*NOTE*: I'm using a hard-coded approach to determine if something is a
"business" email by filtering out emails that are usually personal. I've
also added domains that are common to some of our customers' regions.
This should be simpler. I looked into `Valid_Email2` and I couldn't find
anything to dictate whether an email is a personal email or a business
one. I don't think the approach used in the frontend is valid here.~~
UPDATE: Using `email_provider_info` gem instead.


**Pending - Part 2 (separate PR):** Real-time company creation for new
contacts

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

```bash
# Run all new tests
bundle exec rspec spec/enterprise/services/companies/business_email_detector_service_spec.rb \\
                   spec/enterprise/jobs/migration/company_account_batch_job_spec.rb \\
                   spec/enterprise/jobs/migration/company_backfill_job_spec.rb

# Run RuboCop
bundle exec rubocop enterprise/app/services/companies/business_email_detector_service.rb \\
                     enterprise/app/jobs/migration/company_account_batch_job.rb \\
                     enterprise/app/jobs/migration/company_backfill_job.rb \\
                     lib/tasks/companies.rake
```

**Performance optimization:**
- Uses `disposable_domain?` instead of `disposable?` to avoid DNS MX
lookups (discovered via tcpdump analysis - `disposable?` was making
network calls for every email, causing 100x slowdown)

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2025-11-03 20:03:47 +05:30
Chatwoot BotandGitHub e771d99552 chore: Update translations (#12748) 2025-11-03 15:45:32 +05:30
Muhsin KelothandGitHub 358a3924b8 chore: Add dependant destroy_async for sla events (#12774)
Added the destroy_async to prevent timeout during SLA policy deletion by
processing SLA events asynchronously.
2025-10-31 09:19:56 +05:30
Sivin VargheseandGitHub 6b87d6784e chore: Make contacts bulk action bar sticky (#12773)
# Pull Request Template

## Description

This PR makes the contacts bulk action bar sticky while scrolling.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Screenshots
<img width="1080" height="300" alt="image"
src="https://github.com/user-attachments/assets/21f8f3c6-813e-4ef6-b40a-8dd14e6ffb26"
/>
<img width="1080" height="300" alt="image"
src="https://github.com/user-attachments/assets/bb939f1d-9a13-4f9f-953d-b9872c984b74"
/>



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
2025-10-30 11:57:46 -07:00
Vishnu NarayananandGitHub faaf67129e feat: Enable opensearch on paid plans automatically (#12770)
- enable `advanced_search feature` on all paid plans automatically

ref: https://github.com/chatwoot/chatwoot/pull/12503
2025-10-30 08:47:37 -07:00
159c810117 feat: Bulk actions for contacts (#12763)
Introduces APIs and UI for bulk actions in contacts table. The initial
action available will be assign labels

Fixes: #8536 #12253 

## Screens

<img width="1350" height="747" alt="Screenshot 2025-10-29 at 4 05 08 PM"
src="https://github.com/user-attachments/assets/0792dff5-0371-4b2e-bdfb-cd32db773402"
/>
<img width="1345" height="717" alt="Screenshot 2025-10-29 at 4 05 19 PM"
src="https://github.com/user-attachments/assets/ae510404-c6de-4c15-a720-f6d10cdac25b"
/>

---------

Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2025-10-30 15:28:28 +05:30
ce400a36d7 feat: Always process email content (#12734)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-10-30 13:36:39 +05:30
Shivam MishraandGitHub d20de25da1 fix: run captain v2 outside the transaction (#12756) 2025-10-30 13:35:20 +05:30
Sivin VargheseandGitHub c31d693add chore: Improvements in pending FAQs (#12755)
# Pull Request Template

## Description

**This PR includes:**

1. Added URL-based filter persistence for the responses pages, including
page and search parameters.
2. Introduced a new empty state variant for pending FAQs — without a
backdrop and with a “Clear Filters” option.
3. Made the actions, filter, and search row remain fixed at the top
while scrolling.

Fixes
https://linear.app/chatwoot/issue/CW-5852/improvements-in-pending-faqs

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video
https://www.loom.com/share/1d9eee68c0684f0ab05e08b4ca1e0ce9


## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
2025-10-29 14:34:28 -07:00
Vishnu NarayananandGitHub 31497d9c63 fix: update omniauth to latest to resolve heroku deployment issues (#12749)
# Pull Request Template

## Description

Fixes https://github.com/chatwoot/chatwoot/issues/12553

Heroku build was failing due to `omniauth` version mismatch. Also, added
`NODE_OPTIONS=--max-old-space-size=4096` to handle OOM during Vite
build.

## Type of change

Please delete options that are not relevant.

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

- Tested on heroku

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules
2025-10-29 08:54:29 -07:00
a35c3e4c06 feat: Template types components (#12714)
# Pull Request Template

## Description

Fixes
https://linear.app/chatwoot/issue/CW-5806/create-the-story-book-components-for-template-typestext-media-list

**Pending**
Need to standardize the structure to match the template/campaigns.


## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Screenshots

<img width="669" height="179" alt="image"
src="https://github.com/user-attachments/assets/42efd292-8520-4b05-81ec-8bc526fc12db"
/>
<img width="646" height="304" alt="image"
src="https://github.com/user-attachments/assets/431dd964-006c-4877-a693-dae39b90df4c"
/>
<img width="646" height="380" alt="image"
src="https://github.com/user-attachments/assets/9052e31f-9292-4afb-8897-13931655fa00"
/>
<img width="646" height="272" alt="image"
src="https://github.com/user-attachments/assets/873d2488-e856-4a0d-8579-cc1bcc61cc8e"
/>
<img width="646" height="490" alt="image"
src="https://github.com/user-attachments/assets/14c2aa42-bf27-475f-aa70-fe59c1d00e9b"
/>
<img width="646" height="281" alt="image"
src="https://github.com/user-attachments/assets/1f42408e-03e8-4863-b4c7-715d13d67686"
/>



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-10-29 17:06:32 +05:30
Muhsin KelothandGitHub 344e8d5016 fix: Exclude authentication templates from WhatsApp template selection (#12753)
This PR add the changes for excluding the authentication templates from
the WhatsApp template selection in the frontend, as these templates are
not supported at the moment. Reference:
https://www.chatwoot.com/hc/user-guide/articles/1754940076-whatsapp-templates#what-is-not-supported
2025-10-29 14:03:43 +05:30
3e27e28848 chore: Update captain pending FAQ interface (#12752)
# Pull Request Template

## Description

**This PR includes,**
- Added new pending FAQs view with approve/edit/delete actions for each
response.
- Implemented banner notification showing pending FAQ count on main
approved responses page.
- Created dedicated route for pending FAQs review at
/captain/responses/pending.
- Added automatic pending count updates when switching assistants or
routes.
- Modified ResponseCard component to show action buttons instead of
dropdown in pending view.

Fixes
https://linear.app/chatwoot/issue/CW-5833/pending-faqs-in-a-different-ux

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video
https://www.loom.com/share/5fe8f79b04cd4681b9360c48710b9373


## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2025-10-28 20:47:42 -07:00
38af08534c fix: Captain response builder not getting triggered (#12729)
## Summary
- Fix captain response builder not getting triggered for cases where
responses are created as completed.

## Testing Instructions 
- Test articles with firecrawl
- Test articles without firecrawl
- Test PDF documents

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2025-10-28 18:31:08 -07:00
Tanmay Deep SharmaandGitHub 20d6a11dd9 Merge branch 'develop' into assignment_v2/assignment_service 2025-10-05 15:02:08 +02:00
Tanmay Deep SharmaandGitHub 5dc5037903 Merge branch 'develop' into assignment_v2/assignment_service 2025-09-24 15:50:27 +02:00
Tanmay Sharma 3c7b4f6011 Merge remote-tracking branch 'origin/assignment_v2/assignment_service' into assignment_v2/assignment_service 2025-09-11 13:38:56 +05:30
Tanmay Sharma 73f2aea514 fix user duplication issue for assignment user list 2025-09-11 13:38:22 +05:30
Tanmay Deep Sharma 02424a5a09 fix the exclusion rules 2025-09-10 20:36:08 +05:30
Tanmay Deep Sharma b015c61d71 fix specs 2025-09-04 19:11:18 +07:00
Tanmay Deep Sharma d483974ef1 fix specs 2025-09-04 18:45:22 +07:00
Tanmay Deep Sharma b16b1b6a49 fix balanced assignment spec 2025-09-04 16:53:17 +07:00
Tanmay Deep Sharma b28d00a117 fix rubocop 2025-09-04 16:21:29 +07:00
Tanmay Deep Sharma 2b3ad3dc74 Merge remote-tracking branch 'origin/assignment_v2/assignment_service' into assignment_v2/assignment_service 2025-09-04 16:12:08 +07:00
Tanmay Deep Sharma b95944dd4e update the spec file paths 2025-09-04 16:11:06 +07:00
Tanmay Deep SharmaandGitHub 75ba8e9186 Merge branch 'develop' into assignment_v2/assignment_service 2025-09-03 09:25:54 +07:00
Tanmay Deep Sharma 31969428ae Merge branch 'develop' into assignment_v2/assignment_service 2025-09-02 15:59:11 +07:00
Tanmay Sharma a4fab22e28 fix review changes 2025-08-29 16:26:19 +07:00
Tanmay Sharma 5d27fd798f fix feature listing 2025-08-29 15:49:05 +07:00
Tanmay Sharma ca14eba448 fix review changes 2025-08-29 14:49:13 +07:00
Tanmay Sharma 199a55a8e3 fix codex review 2025-08-28 21:06:06 +07:00
Tanmay Sharma 873094175b add exclusion_rules support 2025-08-28 20:39:30 +07:00
Muhsin KelothandGitHub 096bffa893 Merge branch 'develop' into assignment_v2/assignment_service 2025-08-28 18:33:02 +05:30
Tanmay Sharma ccbc2c5d56 Merge remote-tracking branch 'origin/develop' into assignment_v2/assignment_service 2025-08-28 18:44:11 +07:00
Tanmay Sharma 2c0f235d1d remove capacity status 2025-08-28 18:26:20 +07:00
Tanmay Sharma fc067b7f66 fix capacity test 2025-08-28 17:39:01 +07:00
Tanmay Sharma eacfa2e19a feat: add assignment service 2025-08-28 17:03:11 +07:00
326 changed files with 10406 additions and 3193 deletions
+2
View File
@@ -256,6 +256,8 @@ AZURE_APP_SECRET=
## Change these values to fine tune performance ## Change these values to fine tune performance
# control the concurrency setting of sidekiq # control the concurrency setting of sidekiq
# SIDEKIQ_CONCURRENCY=10 # SIDEKIQ_CONCURRENCY=10
# Enable verbose logging each time a job is dequeued in Sidekiq
# ENABLE_SIDEKIQ_DEQUEUE_LOGGER=false
# AI powered features # AI powered features
+1 -1
View File
@@ -21,7 +21,7 @@ gem 'telephone_number'
gem 'time_diff' gem 'time_diff'
gem 'tzinfo-data' gem 'tzinfo-data'
gem 'valid_email2' gem 'valid_email2'
gem 'octokit' gem 'email-provider-info'
# compress javascript config.assets.js_compressor # compress javascript config.assets.js_compressor
gem 'uglifier' gem 'uglifier'
##-- used for single column multiple binary flags in notification settings/feature flagging --## ##-- used for single column multiple binary flags in notification settings/feature flagging --##
+4 -9
View File
@@ -270,6 +270,7 @@ GEM
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
http (>= 3.0) http (>= 3.0)
ruby2_keywords ruby2_keywords
email-provider-info (0.0.1)
email_reply_trimmer (0.1.13) email_reply_trimmer (0.1.13)
erubi (1.13.0) erubi (1.13.0)
et-orbi (1.2.11) et-orbi (1.2.11)
@@ -591,13 +592,10 @@ GEM
rack (>= 1.2, < 4) rack (>= 1.2, < 4)
snaky_hash (~> 2.0) snaky_hash (~> 2.0)
version_gem (~> 1.1) version_gem (~> 1.1)
octokit (10.0.0)
faraday (>= 1, < 3)
sawyer (~> 0.9)
oj (3.16.10) oj (3.16.10)
bigdecimal (>= 3.0) bigdecimal (>= 3.0)
ostruct (>= 0.2) ostruct (>= 0.2)
omniauth (2.1.3) omniauth (2.1.4)
hashie (>= 3.4.6) hashie (>= 3.4.6)
logger logger
rack (>= 2.2.3) rack (>= 2.2.3)
@@ -656,7 +654,7 @@ GEM
rack (>= 2.0.0) rack (>= 2.0.0)
rack-mini-profiler (3.2.0) rack-mini-profiler (3.2.0)
rack (>= 1.2.0) rack (>= 1.2.0)
rack-protection (4.1.1) rack-protection (4.2.1)
base64 (>= 0.1.0) base64 (>= 0.1.0)
logger (>= 1.6.0) logger (>= 1.6.0)
rack (>= 3.0.0, < 4) rack (>= 3.0.0, < 4)
@@ -820,9 +818,6 @@ GEM
sprockets (> 3.0) sprockets (> 3.0)
sprockets-rails sprockets-rails
tilt tilt
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
scout_apm (5.3.3) scout_apm (5.3.3)
parser parser
scss_lint (0.60.0) scss_lint (0.60.0)
@@ -1022,6 +1017,7 @@ DEPENDENCIES
dotenv-rails (>= 3.0.0) dotenv-rails (>= 3.0.0)
down down
elastic-apm elastic-apm
email-provider-info
email_reply_trimmer email_reply_trimmer
facebook-messenger facebook-messenger
factory_bot_rails (>= 6.4.3) factory_bot_rails (>= 6.4.3)
@@ -1063,7 +1059,6 @@ DEPENDENCIES
net-smtp (~> 0.3.4) net-smtp (~> 0.3.4)
newrelic-sidekiq-metrics (>= 1.6.2) newrelic-sidekiq-metrics (>= 1.6.2)
newrelic_rpm newrelic_rpm
octokit
omniauth (>= 2.1.2) omniauth (>= 2.1.2)
omniauth-google-oauth2 (>= 1.1.3) omniauth-google-oauth2 (>= 1.1.3)
omniauth-oauth2 omniauth-oauth2
+4
View File
@@ -36,6 +36,10 @@
"REDIS_OPENSSL_VERIFY_MODE":{ "REDIS_OPENSSL_VERIFY_MODE":{
"description": "OpenSSL verification mode for Redis connections. ref https://help.heroku.com/HC0F8CUS/redis-connection-issues", "description": "OpenSSL verification mode for Redis connections. ref https://help.heroku.com/HC0F8CUS/redis-connection-issues",
"value": "none" "value": "none"
},
"NODE_OPTIONS": {
"description": "Increase V8 heap for Vite build to avoid OOM",
"value": "--max-old-space-size=4096"
} }
}, },
"formation": { "formation": {
+1 -1
View File
@@ -23,7 +23,7 @@ class Messages::MessageBuilder
process_emails process_emails
# When the message has no quoted content, it will just be rendered as a regular message # When the message has no quoted content, it will just be rendered as a regular message
# The frontend is equipped to handle this case # The frontend is equipped to handle this case
process_email_content if @account.feature_enabled?(:quoted_email_reply) process_email_content
@message.save! @message.save!
@message @message
end end
@@ -1,13 +1,12 @@
class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseController class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseController
before_action :type_matches?
def create def create
if type_matches? case normalized_type
::BulkActionsJob.perform_later( when 'Conversation'
account: @current_account, enqueue_conversation_job
user: current_user, head :ok
params: permitted_params when 'Contact'
) check_authorization_for_contact_action
enqueue_contact_job
head :ok head :ok
else else
render json: { success: false }, status: :unprocessable_entity render json: { success: false }, status: :unprocessable_entity
@@ -16,11 +15,54 @@ class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseControll
private private
def type_matches? def normalized_type
['Conversation'].include?(params[:type]) params[:type].to_s.camelize
end end
def permitted_params def enqueue_conversation_job
params.permit(:type, :snoozed_until, ids: [], fields: [:status, :assignee_id, :team_id], labels: [add: [], remove: []]) ::BulkActionsJob.perform_later(
account: @current_account,
user: current_user,
params: conversation_params
)
end
def enqueue_contact_job
Contacts::BulkActionJob.perform_later(
@current_account.id,
current_user.id,
contact_params
)
end
def delete_contact_action?
params[:action_name] == 'delete'
end
def check_authorization_for_contact_action
authorize(Contact, :destroy?) if delete_contact_action?
end
def conversation_params
# TODO: Align conversation payloads with the `{ action_name, action_attributes }`
# and then remove this method in favor of a common params method.
base = params.permit(
:snoozed_until,
fields: [:status, :assignee_id, :team_id]
)
append_common_bulk_attributes(base)
end
def contact_params
# TODO: remove this method in favor of a common params method.
# once legacy conversation payloads are migrated.
append_common_bulk_attributes({})
end
def append_common_bulk_attributes(base_params)
# NOTE: Conversation payloads historically diverged per action. Going forward we
# want all objects to share a common contract: `{ action_name, action_attributes }`
common = params.permit(:type, :action_name, ids: [], labels: [add: [], remove: []])
base_params.merge(common)
end end
end end
@@ -1,155 +0,0 @@
class Api::V1::Accounts::Integrations::GithubController < Api::V1::Accounts::BaseController
before_action :fetch_hook
before_action :ensure_hook_exists, except: [:destroy]
def destroy
@hook.destroy!
head :ok
end
def repositories
repositories = github_service.repositories
filtered_repos = repositories.map do |repo|
{
full_name: repo[:full_name] || repo.full_name,
private: repo[:private] || repo.private
}
end
render json: filtered_repos
end
def search_repositories
repositories = github_service.search_repositories(params[:q])
filtered_repos = repositories.map do |repo|
{
full_name: repo[:full_name] || repo.full_name,
private: repo[:private] || repo.private
}
end
render json: filtered_repos
end
def assignees
assignees = github_service.assignees(repo_full_name)
filtered_assignees = assignees.map do |assignee|
{
login: assignee.login,
avatar_url: assignee.avatar_url
}
end
render json: filtered_assignees
end
def labels
labels = github_service.labels(repo_full_name)
filtered_labels = labels.map do |label|
{
name: label.name,
color: label.color
}
end
render json: filtered_labels
end
def search_issues
issues = github_service.search_issues(repo_full_name, params[:q])
filtered_issues = issues.map do |issue|
{
number: issue.number,
title: issue.title,
html_url: issue.html_url
}
end
render json: filtered_issues
end
def create_issue
issue_data = github_service.create_issue(
repo_full_name,
params[:title],
params[:body],
issue_options
)
github_issue = create_linked_issue(issue_data)
render json: issue_response(github_issue), status: :created
end
def link_issue
issue_data = github_service.issue(repo_full_name, params[:issue_number])
github_issue = create_linked_issue(issue_data)
render json: issue_response(github_issue), status: :created
end
def linked_issues
issues = GithubIssue.where(conversation_id: params[:conversation_id])
render json: issues.map do |issue|
{
id: issue.id,
issue_number: issue.issue_number,
title: issue.issue_title,
html_url: issue.html_url,
repo_full_name: issue.repo_full_name,
linked_by: {
id: issue.linked_by.id,
name: issue.linked_by.name
},
created_at: issue.created_at
}
end
end
def unlink_issue
github_issue = GithubIssue.find(params[:id])
github_issue.destroy!
head :ok
end
private
def fetch_hook
@hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'github')
end
def ensure_hook_exists
render json: { error: 'GitHub integration not configured' }, status: :unauthorized unless @hook
end
def github_service
@github_service ||= Github::GithubService.new(hook: @hook)
end
def repo_full_name
params[:repo_full_name] || "#{params[:owner]}/#{params[:repo]}"
end
def issue_options
options = {}
options[:assignees] = params[:assignees] if params[:assignees].present?
options[:labels] = params[:labels] if params[:labels].present?
options
end
def create_linked_issue(issue_data)
GithubIssue.create!(
conversation_id: params[:conversation_id],
account: Current.account,
repo_full_name: repo_full_name,
issue_number: issue_data.number,
issue_title: issue_data.title,
html_url: issue_data.html_url,
linked_by: Current.user
)
end
def issue_response(github_issue)
{
id: github_issue.id,
issue_number: github_issue.issue_number,
title: github_issue.issue_title,
html_url: github_issue.html_url,
repo_full_name: github_issue.repo_full_name
}
end
end
@@ -1,160 +0,0 @@
class Github::CallbacksController < ApplicationController
include Github::IntegrationHelper
def show
# Validate account context early for all flows that require it
account if params[:code].present?
if params[:installation_id].present? && params[:code].present?
# Both installation and OAuth code present - handle both
handle_installation_with_oauth
elsif params[:installation_id].present?
# Only installation_id present - redirect to OAuth
handle_installation
else
# Only OAuth code present - handle authorization
handle_authorization
end
rescue StandardError => e
Rails.logger.error("Github callback error: #{e.message}")
redirect_to fallback_redirect_uri
end
private
def handle_installation_with_oauth
# Handle both installation and OAuth in one go
installation_id = params[:installation_id]
@response = oauth_client.auth_code.get_token(
params[:code],
redirect_uri: "#{base_url}/github/callback"
)
handle_response(installation_id)
end
def handle_installation
if params[:setup_action] == 'install'
installation_id = params[:installation_id]
redirect_to build_oauth_url(installation_id)
else
Rails.logger.error("Unknown setup_action: #{params[:setup_action]}")
redirect_to github_integration_settings_url
end
end
def handle_authorization
@response = oauth_client.auth_code.get_token(
params[:code],
redirect_uri: "#{base_url}/github/callback"
)
handle_response
end
def build_oauth_url(installation_id)
GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
# Store installation_id in session for later use
session[:github_installation_id] = installation_id
# For now, redirect to a page that will initiate OAuth with proper account context
# This is a temporary solution until we have a proper account-agnostic setup
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/1/settings/integrations/github?setup_action=install&installation_id=#{installation_id}"
end
def oauth_client
app_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
app_secret = GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil)
OAuth2::Client.new(
app_id,
app_secret,
{
site: 'https://github.com',
token_url: '/login/oauth/access_token',
authorize_url: '/login/oauth/authorize'
}
)
end
def handle_response(installation_id = nil)
settings = build_hook_settings(installation_id)
hook = create_integration_hook(settings)
hook.save!
cleanup_session_data
redirect_to github_redirect_uri
rescue StandardError => e
Rails.logger.error("Github callback error: #{e.message}")
redirect_to fallback_redirect_uri
end
def build_hook_settings(installation_id)
settings = {
token_type: parsed_body['token_type'],
scope: parsed_body['scope']
}
settings[:installation_id] = installation_id || session[:github_installation_id]
settings.compact
end
def create_integration_hook(settings)
account.hooks.new(
access_token: parsed_body['access_token'],
status: 'enabled',
app_id: 'github',
settings: settings
)
end
def cleanup_session_data
session.delete(:github_installation_id)
end
def account
@account ||= account_from_state
end
def account_from_state
raise ActionController::BadRequest, 'Missing state variable' if params[:state].blank?
# Try signed GlobalID first (installation flow)
account = GlobalID::Locator.locate_signed(params[:state])
return account if account
# Fallback to JWT token (direct OAuth flow)
account_id = verify_github_token(params[:state])
return Account.find(account_id) if account_id
raise 'Invalid or expired state'
rescue StandardError
raise ActionController::BadRequest, 'Invalid account context'
end
def github_redirect_uri
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/settings/integrations/github"
end
def github_integration_settings_url
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/1/settings/integrations/github"
end
def fallback_redirect_uri
github_redirect_uri
rescue StandardError
# Fallback if no account context available
"#{ENV.fetch('FRONTEND_URL', nil)}/app/settings/integrations"
end
def parsed_body
@parsed_body ||= @response.response.parsed
end
def base_url
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
end
end
@@ -42,8 +42,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT], 'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT],
'whatsapp_embedded' => %w[WHATSAPP_APP_ID WHATSAPP_APP_SECRET WHATSAPP_CONFIGURATION_ID WHATSAPP_API_VERSION], 'whatsapp_embedded' => %w[WHATSAPP_APP_ID WHATSAPP_APP_SECRET WHATSAPP_CONFIGURATION_ID WHATSAPP_API_VERSION],
'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET], 'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET],
'google' => %w[GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET GOOGLE_OAUTH_REDIRECT_URI], 'google' => %w[GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET GOOGLE_OAUTH_REDIRECT_URI]
'github' => %w[GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET]
} }
@allowed_configs = mapping.fetch(@config, %w[ENABLE_ACCOUNT_SIGNUP FIREBASE_PROJECT_ID FIREBASE_CREDENTIALS]) @allowed_configs = mapping.fetch(@config, %w[ENABLE_ACCOUNT_SIGNUP FIREBASE_PROJECT_ID FIREBASE_CREDENTIALS])
-47
View File
@@ -1,47 +0,0 @@
module Github::IntegrationHelper
# Generates a signed JWT token for Github integration
#
# @param account_id [Integer] The account ID to encode in the token
# @return [String, nil] The encoded JWT token or nil if client secret is missing
def generate_github_token(account_id)
return if github_client_secret.blank?
JWT.encode(github_token_payload(account_id), github_client_secret, 'HS256')
rescue StandardError => e
Rails.logger.error("Failed to generate Github token: #{e.message}")
nil
end
def github_token_payload(account_id)
{
sub: account_id,
iat: Time.current.to_i
}
end
# Verifies and decodes a Github JWT token
#
# @param token [String] The JWT token to verify
# @return [Integer, nil] The account ID from the token or nil if invalid
def verify_github_token(token)
return if token.blank? || github_client_secret.blank?
github_decode_token(token, github_client_secret)
end
private
def github_client_secret
@github_client_secret ||= GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil)
end
def github_decode_token(token, secret)
JWT.decode(token, secret, true, {
algorithm: 'HS256',
verify_expiration: true
}).first['sub']
rescue StandardError => e
Rails.logger.error("Unexpected error verifying Github token: #{e.message}")
nil
end
end
@@ -5,6 +5,7 @@ import { useToggle } from '@vueuse/core';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue'; import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue';
import Policy from 'dashboard/components/policy.vue';
defineProps({ defineProps({
selectedContact: { selectedContact: {
@@ -24,6 +25,7 @@ const openConfirmDeleteContactDialog = () => {
</script> </script>
<template> <template>
<Policy :permissions="['administrator']">
<div class="flex flex-col items-start border-t border-n-strong px-6 py-5"> <div class="flex flex-col items-start border-t border-n-strong px-6 py-5">
<Button <Button
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')" :label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
@@ -62,4 +64,5 @@ const openConfirmDeleteContactDialog = () => {
ref="confirmDeleteContactDialogRef" ref="confirmDeleteContactDialogRef"
:selected-contact="selectedContact" :selected-contact="selectedContact"
/> />
</Policy>
</template> </template>
@@ -8,6 +8,7 @@ import Button from 'dashboard/components-next/button/Button.vue';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue'; import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import Flag from 'dashboard/components-next/flag/Flag.vue'; import Flag from 'dashboard/components-next/flag/Flag.vue';
import ContactDeleteSection from 'dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue'; import ContactDeleteSection from 'dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue';
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
import countries from 'shared/constants/countries'; import countries from 'shared/constants/countries';
const props = defineProps({ const props = defineProps({
@@ -20,9 +21,17 @@ const props = defineProps({
availabilityStatus: { type: String, default: null }, availabilityStatus: { type: String, default: null },
isExpanded: { type: Boolean, default: false }, isExpanded: { type: Boolean, default: false },
isUpdating: { type: Boolean, default: false }, isUpdating: { type: Boolean, default: false },
selectable: { type: Boolean, default: false },
isSelected: { type: Boolean, default: false },
}); });
const emit = defineEmits(['toggle', 'updateContact', 'showContact']); const emit = defineEmits([
'toggle',
'updateContact',
'showContact',
'select',
'avatarHover',
]);
const { t } = useI18n(); const { t } = useI18n();
@@ -88,11 +97,31 @@ const onClickExpand = () => {
}; };
const onClickViewDetails = () => emit('showContact', props.id); const onClickViewDetails = () => emit('showContact', props.id);
const toggleSelect = checked => {
emit('select', checked);
};
const handleAvatarHover = isHovered => {
emit('avatarHover', isHovered);
};
</script> </script>
<template> <template>
<CardLayout :key="id" layout="row"> <div class="relative">
<CardLayout
:key="id"
layout="row"
:class="{
'outline-n-weak !bg-n-slate-3 dark:!bg-n-solid-3': isSelected,
}"
>
<div class="flex items-center justify-start flex-1 gap-4"> <div class="flex items-center justify-start flex-1 gap-4">
<div
class="relative"
@mouseenter="handleAvatarHover(true)"
@mouseleave="handleAvatarHover(false)"
>
<Avatar <Avatar
:name="name" :name="name"
:src="thumbnail" :src="thumbnail"
@@ -100,7 +129,21 @@ const onClickViewDetails = () => emit('showContact', props.id);
:status="availabilityStatus" :status="availabilityStatus"
hide-offline-status hide-offline-status
rounded-full rounded-full
>
<template v-if="selectable" #overlay="{ size }">
<label
class="flex items-center justify-center rounded-full cursor-pointer absolute inset-0 z-10 backdrop-blur-[2px] border border-n-weak"
:style="{ width: `${size}px`, height: `${size}px` }"
@click.stop
>
<Checkbox
:model-value="isSelected"
@change="event => toggleSelect(event.target.checked)"
/> />
</label>
</template>
</Avatar>
</div>
<div class="flex flex-col gap-0.5 flex-1"> <div class="flex flex-col gap-0.5 flex-1">
<div class="flex flex-wrap items-center gap-x-4 gap-y-1"> <div class="flex flex-wrap items-center gap-x-4 gap-y-1">
<span class="text-base font-medium truncate text-n-slate-12"> <span class="text-base font-medium truncate text-n-slate-12">
@@ -119,7 +162,9 @@ const onClickViewDetails = () => emit('showContact', props.id);
</span> </span>
</span> </span>
</div> </div>
<div class="flex flex-wrap items-center justify-start gap-x-3 gap-y-1"> <div
class="flex flex-wrap items-center justify-start gap-x-3 gap-y-1"
>
<div v-if="email" class="truncate max-w-72" :title="email"> <div v-if="email" class="truncate max-w-72" :title="email">
<span class="text-sm text-n-slate-11"> <span class="text-sm text-n-slate-11">
{{ email }} {{ email }}
@@ -195,4 +240,5 @@ const onClickViewDetails = () => emit('showContact', props.id);
</div> </div>
</template> </template>
</CardLayout> </CardLayout>
</div>
</template> </template>
@@ -10,6 +10,7 @@ import Button from 'dashboard/components-next/button/Button.vue';
import ContactLabels from 'dashboard/components-next/Contacts/ContactLabels/ContactLabels.vue'; import ContactLabels from 'dashboard/components-next/Contacts/ContactLabels/ContactLabels.vue';
import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue'; import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue';
import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue'; import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue';
import Policy from 'dashboard/components/policy.vue';
const props = defineProps({ const props = defineProps({
selectedContact: { selectedContact: {
@@ -174,6 +175,7 @@ const handleAvatarDelete = async () => {
@click="updateContact" @click="updateContact"
/> />
</div> </div>
<Policy :permissions="['administrator']">
<div <div
class="flex flex-col items-start w-full gap-4 pt-6 border-t border-n-strong" class="flex flex-col items-start w-full gap-4 pt-6 border-t border-n-strong"
> >
@@ -196,5 +198,6 @@ const handleAvatarDelete = async () => {
:selected-contact="selectedContact" :selected-contact="selectedContact"
@go-to-contacts-list="emit('goToContactsList')" @go-to-contacts-list="emit('goToContactsList')"
/> />
</Policy>
</div> </div>
</template> </template>
@@ -10,7 +10,15 @@ import {
} from 'shared/helpers/CustomErrors'; } from 'shared/helpers/CustomErrors';
import ContactsCard from 'dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue'; import ContactsCard from 'dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue';
defineProps({ contacts: { type: Array, required: true } }); const props = defineProps({
contacts: { type: Array, required: true },
selectedContactIds: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['toggleContact']);
const { t } = useI18n(); const { t } = useI18n();
const store = useStore(); const store = useStore();
@@ -20,6 +28,9 @@ const route = useRoute();
const uiFlags = useMapGetter('contacts/getUIFlags'); const uiFlags = useMapGetter('contacts/getUIFlags');
const isUpdating = computed(() => uiFlags.value.isUpdating); const isUpdating = computed(() => uiFlags.value.isUpdating);
const expandedCardId = ref(null); const expandedCardId = ref(null);
const hoveredAvatarId = ref(null);
const selectedIdsSet = computed(() => new Set(props.selectedContactIds || []));
const updateContact = async updatedData => { const updateContact = async updatedData => {
try { try {
@@ -58,14 +69,27 @@ const onClickViewDetails = async id => {
const toggleExpanded = id => { const toggleExpanded = id => {
expandedCardId.value = expandedCardId.value === id ? null : id; expandedCardId.value = expandedCardId.value === id ? null : id;
}; };
const isSelected = id => selectedIdsSet.value.has(id);
const shouldShowSelection = id => {
return hoveredAvatarId.value === id || isSelected(id);
};
const handleSelect = (id, value) => {
emit('toggleContact', { id, value });
};
const handleAvatarHover = (id, isHovered) => {
hoveredAvatarId.value = isHovered ? id : null;
};
</script> </script>
<template> <template>
<div class="flex flex-col gap-4 px-6 pt-4 pb-6"> <div class="flex flex-col gap-4">
<div v-for="contact in contacts" :key="contact.id" class="relative">
<ContactsCard <ContactsCard
v-for="contact in contacts"
:id="contact.id" :id="contact.id"
:key="contact.id"
:name="contact.name" :name="contact.name"
:email="contact.email" :email="contact.email"
:thumbnail="contact.thumbnail" :thumbnail="contact.thumbnail"
@@ -74,9 +98,14 @@ const toggleExpanded = id => {
:availability-status="contact.availabilityStatus" :availability-status="contact.availabilityStatus"
:is-expanded="expandedCardId === contact.id" :is-expanded="expandedCardId === contact.id"
:is-updating="isUpdating" :is-updating="isUpdating"
:selectable="shouldShowSelection(contact.id)"
:is-selected="isSelected(contact.id)"
@toggle="toggleExpanded(contact.id)" @toggle="toggleExpanded(contact.id)"
@update-contact="updateContact" @update-contact="updateContact"
@show-contact="onClickViewDetails" @show-contact="onClickViewDetails"
@select="value => handleSelect(contact.id, value)"
@avatar-hover="value => handleAvatarHover(contact.id, value)"
/> />
</div> </div>
</div>
</template> </template>
@@ -14,6 +14,10 @@ defineProps({
type: Array, type: Array,
default: () => [], default: () => [],
}, },
showBackdrop: {
type: Boolean,
default: true,
},
}); });
</script> </script>
@@ -25,14 +29,24 @@ defineProps({
class="relative w-full max-w-[60rem] mx-auto overflow-hidden h-full max-h-[28rem]" class="relative w-full max-w-[60rem] mx-auto overflow-hidden h-full max-h-[28rem]"
> >
<div <div
v-if="showBackdrop"
class="w-full h-full space-y-4 overflow-y-hidden opacity-50 pointer-events-none" class="w-full h-full space-y-4 overflow-y-hidden opacity-50 pointer-events-none"
> >
<slot name="empty-state-item" /> <slot name="empty-state-item" />
</div> </div>
<div <div
class="absolute inset-x-0 bottom-0 flex flex-col items-center justify-end w-full h-full pb-20 bg-gradient-to-t from-n-background from-25% dark:from-n-background to-transparent" class="flex flex-col items-center justify-end w-full h-full pb-20"
:class="{
'absolute inset-x-0 bottom-0 bg-gradient-to-t from-n-background from-25% dark:from-n-background to-transparent':
showBackdrop,
}"
>
<div
class="flex flex-col items-center justify-center gap-6"
:class="{
'mt-48': !showBackdrop,
}"
> >
<div class="flex flex-col items-center justify-center gap-6">
<div class="flex flex-col items-center justify-center gap-3"> <div class="flex flex-col items-center justify-center gap-3">
<h2 <h2
class="text-3xl font-medium text-center text-n-slate-12 font-interDisplay" class="text-3xl font-medium text-center text-n-slate-12 font-interDisplay"
@@ -40,6 +54,7 @@ defineProps({
{{ title }} {{ title }}
</h2> </h2>
<p <p
v-if="subtitle"
class="max-w-xl text-base text-center text-n-slate-11 font-interDisplay tracking-[0.3px]" class="max-w-xl text-base text-center text-n-slate-11 font-interDisplay tracking-[0.3px]"
> >
{{ subtitle }} {{ subtitle }}
@@ -114,6 +114,7 @@ const handlePageChange = event => {
<slot name="action" /> <slot name="action" />
</div> </div>
</div> </div>
<slot name="subHeader" />
</div> </div>
</header> </header>
<main class="flex-1 px-6 overflow-y-auto"> <main class="flex-1 px-6 overflow-y-auto">
@@ -64,20 +64,23 @@ const bulkCheckboxState = computed({
class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow" class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow"
> >
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="flex items-center gap-1.5"> <div class="flex items-center gap-1.5 min-w-0">
<Checkbox <Checkbox
v-model="bulkCheckboxState" v-model="bulkCheckboxState"
:indeterminate="isIndeterminate" :indeterminate="isIndeterminate"
/> />
<span class="text-sm font-medium text-n-slate-12 tabular-nums"> <span
class="text-sm font-medium truncate text-n-slate-12 tabular-nums"
>
{{ selectAllLabel }} {{ selectAllLabel }}
</span> </span>
</div> </div>
<span class="text-sm text-n-slate-10 tabular-nums"> <span class="text-sm text-n-slate-10 truncate tabular-nums">
{{ selectedCountLabel }} {{ selectedCountLabel }}
</span> </span>
</div>
<div class="h-4 w-px bg-n-strong" /> <div class="h-4 w-px bg-n-strong" />
<slot name="secondary-actions" />
</div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<slot name="actions" :selected-count="selectedCount"> <slot name="actions" :selected-count="selectedCount">
<Button <Button
@@ -9,6 +9,7 @@ import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.v
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue'; import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
import Policy from 'dashboard/components/policy.vue'; import Policy from 'dashboard/components/policy.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
const props = defineProps({ const props = defineProps({
id: { id: {
@@ -59,6 +60,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
showActions: {
type: Boolean,
default: false,
},
}); });
const emit = defineEmits(['action', 'navigate', 'select', 'hover']); const emit = defineEmits(['action', 'navigate', 'select', 'hover']);
@@ -159,73 +164,116 @@ const handleDocumentableClick = () => {
<span class="text-n-slate-11 text-sm line-clamp-5"> <span class="text-n-slate-11 text-sm line-clamp-5">
{{ answer }} {{ answer }}
</span> </span>
<div v-if="!compact" class="items-center justify-between hidden lg:flex"> <div
<div class="inline-flex items-center"> v-if="!compact"
class="flex items-start justify-between flex-col-reverse md:flex-row gap-3"
>
<Policy v-if="showActions" :permissions="['administrator']">
<div class="flex items-center gap-2 sm:gap-5 w-full">
<Button
v-if="status === 'pending'"
:label="$t('CAPTAIN.RESPONSES.OPTIONS.APPROVE')"
icon="i-lucide-circle-check-big"
sm
link
class="hover:!no-underline"
@click="
handleAssistantAction({ action: 'approve', value: 'approve' })
"
/>
<Button
:label="$t('CAPTAIN.RESPONSES.OPTIONS.EDIT_RESPONSE')"
icon="i-lucide-pencil-line"
sm
slate
link
class="hover:!no-underline"
@click="
handleAssistantAction({
action: 'edit',
value: 'edit',
})
"
/>
<Button
:label="$t('CAPTAIN.RESPONSES.OPTIONS.DELETE_RESPONSE')"
icon="i-lucide-trash"
sm
ruby
link
class="hover:!no-underline"
@click="
handleAssistantAction({ action: 'delete', value: 'delete' })
"
/>
</div>
</Policy>
<div
class="flex items-center gap-3"
:class="{ 'justify-between w-full': !showActions }"
>
<div class="inline-flex items-center gap-3 min-w-0">
<span <span
v-if="status === 'approved'"
class="text-sm shrink-0 truncate text-n-slate-11 inline-flex items-center gap-1" class="text-sm shrink-0 truncate text-n-slate-11 inline-flex items-center gap-1"
> >
<i class="i-woot-captain" /> <Icon icon="i-woot-captain" class="size-3.5" />
{{ assistant?.name || '' }} {{ assistant?.name || '' }}
</span> </span>
<div <div
v-if="documentable" v-if="documentable"
class="shrink-0 text-sm text-n-slate-11 inline-flex line-clamp-1 gap-1 ml-3" class="text-sm text-n-slate-11 grid grid-cols-[auto_1fr] items-center gap-1 min-w-0"
> >
<Icon
v-if="documentable.type === 'Captain::Document'"
icon="i-ph-files-light"
class="size-3.5"
/>
<Icon
v-else-if="documentable.type === 'User'"
icon="i-ph-user-circle-plus"
class="size-3.5"
/>
<Icon
v-else-if="documentable.type === 'Conversation'"
icon="i-ph-chat-circle-dots"
class="size-3.5"
/>
<span <span
v-if="documentable.type === 'Captain::Document'" v-if="documentable.type === 'Captain::Document'"
class="inline-flex items-center gap-1 truncate over" class="truncate"
:title="documentable.name"
> >
<i class="i-ph-files-light text-base" />
<span class="max-w-96 truncate" :title="documentable.name">
{{ documentable.name }} {{ documentable.name }}
</span> </span>
</span>
<span <span
v-if="documentable.type === 'User'" v-else-if="documentable.type === 'User'"
class="inline-flex items-center gap-1" class="truncate"
>
<i class="i-ph-user-circle-plus text-base" />
<span
class="max-w-96 truncate"
:title="documentable.available_name" :title="documentable.available_name"
> >
{{ documentable.available_name }} {{ documentable.available_name }}
</span> </span>
</span>
<span <span
v-else-if="documentable.type === 'Conversation'" v-else-if="documentable.type === 'Conversation'"
class="inline-flex items-center gap-1 group cursor-pointer" class="hover:underline truncate cursor-pointer"
role="button" role="button"
@click="handleDocumentableClick" @click="handleDocumentableClick"
> >
<i class="i-ph-chat-circle-dots text-base" />
<span class="group-hover:underline">
{{ {{
t(`CAPTAIN.RESPONSES.DOCUMENTABLE.CONVERSATION`, { t(`CAPTAIN.RESPONSES.DOCUMENTABLE.CONVERSATION`, {
id: documentable.display_id, id: documentable.display_id,
}) })
}} }}
</span> </span>
</span>
<span v-else />
</div>
<div
v-if="status !== 'approved'"
class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1 ml-3"
>
<i
class="i-ph-stack text-base"
:title="t('CAPTAIN.RESPONSES.STATUS.TITLE')"
/>
{{ t(`CAPTAIN.RESPONSES.STATUS.${status.toUpperCase()}`) }}
</div> </div>
</div> </div>
<div <div
class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1 ml-3" class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1"
> >
<i class="i-ph-calendar-dot" /> <Icon icon="i-ph-calendar-dot" class="size-3.5" />
{{ timestamp }} {{ timestamp }}
</div> </div>
</div> </div>
</div>
</CardLayout> </CardLayout>
</template> </template>
@@ -6,16 +6,39 @@ import ResponseCard from 'dashboard/components-next/captain/assistant/ResponseCa
import FeatureSpotlight from 'dashboard/components-next/feature-spotlight/FeatureSpotlight.vue'; import FeatureSpotlight from 'dashboard/components-next/feature-spotlight/FeatureSpotlight.vue';
import { responsesList } from 'dashboard/components-next/captain/pageComponents/emptyStates/captainEmptyStateContent.js'; import { responsesList } from 'dashboard/components-next/captain/pageComponents/emptyStates/captainEmptyStateContent.js';
const emit = defineEmits(['click']); import { computed } from 'vue';
const props = defineProps({
variant: {
type: String,
default: 'approved',
validator: value => ['approved', 'pending'].includes(value),
},
hasActiveFilters: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['click', 'clearFilters']);
const isApproved = computed(() => props.variant === 'approved');
const isPending = computed(() => props.variant === 'pending');
const { isOnChatwootCloud } = useAccount(); const { isOnChatwootCloud } = useAccount();
const onClick = () => { const onClick = () => {
emit('click'); emit('click');
}; };
const onClearFilters = () => {
emit('clearFilters');
};
</script> </script>
<template> <template>
<FeatureSpotlight <FeatureSpotlight
v-if="isApproved"
:title="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.TITLE')" :title="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.TITLE')"
:note="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.NOTE')" :note="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.NOTE')"
fallback-thumbnail="/assets/images/dashboard/captain/faqs-light.svg" fallback-thumbnail="/assets/images/dashboard/captain/faqs-light.svg"
@@ -25,11 +48,16 @@ const onClick = () => {
class="mb-8" class="mb-8"
/> />
<EmptyStateLayout <EmptyStateLayout
:title="$t('CAPTAIN.RESPONSES.EMPTY_STATE.TITLE')" :title="
:subtitle="$t('CAPTAIN.RESPONSES.EMPTY_STATE.SUBTITLE')" isPending
? $t('CAPTAIN.RESPONSES.EMPTY_STATE.NO_PENDING_TITLE')
: $t('CAPTAIN.RESPONSES.EMPTY_STATE.TITLE')
"
:subtitle="isApproved ? $t('CAPTAIN.RESPONSES.EMPTY_STATE.SUBTITLE') : ''"
:action-perms="['administrator']" :action-perms="['administrator']"
:show-backdrop="isApproved"
> >
<template #empty-state-item> <template v-if="isApproved" #empty-state-item>
<div class="grid grid-cols-1 gap-4 p-px overflow-hidden"> <div class="grid grid-cols-1 gap-4 p-px overflow-hidden">
<ResponseCard <ResponseCard
v-for="(response, index) in responsesList.slice(0, 5)" v-for="(response, index) in responsesList.slice(0, 5)"
@@ -45,11 +73,21 @@ const onClick = () => {
</div> </div>
</template> </template>
<template #actions> <template #actions>
<div class="flex flex-col items-center gap-3">
<Button <Button
v-if="isApproved"
:label="$t('CAPTAIN.RESPONSES.ADD_NEW')" :label="$t('CAPTAIN.RESPONSES.ADD_NEW')"
icon="i-lucide-plus" icon="i-lucide-plus"
@click="onClick" @click="onClick"
/> />
<Button
v-else-if="isPending && hasActiveFilters"
:label="$t('CAPTAIN.RESPONSES.EMPTY_STATE.CLEAR_SEARCH')"
variant="link"
size="sm"
@click="onClearFilters"
/>
</div>
</template> </template>
</EmptyStateLayout> </EmptyStateLayout>
</template> </template>
@@ -0,0 +1,29 @@
<script setup>
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div class="text-n-slate-12 max-w-80 flex flex-col gap-2.5">
<div class="p-3 bg-n-alpha-2 rounded-xl">
<span
v-dompurify-html="message.content"
class="prose prose-bubble font-medium text-sm"
/>
</div>
<div class="flex gap-2">
<Button label="Call us" slate class="!text-n-blue-text w-full" />
<Button
label="Visit our website"
slate
class="!text-n-blue-text w-full"
/>
</div>
</div>
</template>
@@ -0,0 +1,32 @@
<script setup>
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
>
<div class="px-3 py-2.5">
<img :src="message.image_url" class="max-h-44 rounded-lg w-full" />
<div class="pt-2.5 flex flex-col gap-2">
<h6 class="font-semibold">{{ message.title }}</h6>
<span
v-dompurify-html="message.content"
class="prose prose-bubble text-sm"
/>
</div>
</div>
<div class="p-3 flex items-center justify-center">
<Button label="Call us to order" link class="hover:!no-underline" />
</div>
<div class="p-3 flex items-center justify-center">
<Button label="Visit our store" link class="hover:!no-underline" />
</div>
</div>
</template>
@@ -0,0 +1,25 @@
<script setup>
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
>
<div class="p-3">
<span
v-dompurify-html="message.content"
class="prose prose-bubble font-medium text-sm"
/>
</div>
<div class="p-3 flex items-center justify-center">
<Button label="See options" link class="hover:!no-underline" />
</div>
</div>
</template>
@@ -0,0 +1,20 @@
<script setup>
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div
class="bg-n-alpha-2 text-n-slate-12 rounded-xl flex flex-col gap-2.5 p-3 max-w-80"
>
<img :src="message.image_url" class="max-h-44 rounded-lg w-full" />
<span
v-dompurify-html="message.content"
class="prose prose-bubble font-medium text-sm"
/>
</div>
</template>
@@ -0,0 +1,68 @@
<script setup>
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
>
<div class="p-3">
<span
v-dompurify-html="message.content"
class="prose prose-bubble font-medium text-sm"
/>
</div>
<div class="p-3 flex items-center justify-center">
<Button label="No, that will be all" link class="hover:!no-underline">
<template #icon>
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
class="stroke-n-blue-text"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M.667 6.654 5.315.667v3.326c7.968 0 8.878 6.46 8.656 10.007l-.005-.027c-.334-1.79-.474-4.658-8.65-4.658v3.327z"
stroke-width="1.333"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
</Button>
</div>
<div class="p-3 flex items-center justify-center">
<Button
label="I want to talk to an agents"
link
class="hover:!no-underline"
>
<template #icon>
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
class="stroke-n-blue-text"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M.667 6.654 5.315.667v3.326c7.968 0 8.878 6.46 8.656 10.007l-.005-.027c-.334-1.79-.474-4.658-8.65-4.658v3.327z"
stroke-width="1.333"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
</Button>
</div>
</div>
</template>
@@ -0,0 +1,14 @@
<script setup>
defineProps({
message: {
type: Object,
required: true,
},
});
</script>
<template>
<div class="bg-n-alpha-2 text-n-slate-12 rounded-xl p-3 max-w-80">
<span v-dompurify-html="message.content" class="prose prose-bubble" />
</div>
</template>
@@ -47,6 +47,7 @@ const isReel = computed(() => {
'max-w-48': isReel, 'max-w-48': isReel,
'max-w-full': !isReel, 'max-w-full': !isReel,
}" }"
@click.stop
@error="handleError" @error="handleError"
/> />
</div> </div>
@@ -0,0 +1,21 @@
<script setup>
import CallToAction from '../../bubbles/Template/CallToAction.vue';
const message = {
content:
'We have super cool products going live! Pre-order and customize products. Contact us for more details',
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/CallToAction"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="Call To Action">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<CallToAction :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,23 @@
<script setup>
import Card from '../../bubbles/Template/Card.vue';
const message = {
title: 'Two in one cake (1 pound)',
content: 'Customize your order for special occasions',
image_url:
'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=500&h=300&fit=crop',
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/Card"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="Card">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<Card :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,21 @@
<script setup>
import ListPicker from '../../bubbles/Template/ListPicker.vue';
const message = {
content: `Hey there! Thanks for reaching out to us. Could you let us know
what you need to help us better assist you? `,
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/ListPicker"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="ListPicker">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<ListPicker :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,23 @@
<script setup>
import Media from '../../bubbles/Template/Media.vue';
const message = {
content:
'Welcome to our Diwali sale! Get flat 50% off on select items. Hurry now!',
image_url:
'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=500&h=300&fit=crop',
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/Media"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="Image Media">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<Media :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,21 @@
<script setup>
import QuickReply from '../../bubbles/Template/QuickReply.vue';
const message = {
content: `Hey there! Thanks for reaching out to us. Could you let us know
what you need to help us better assist you?`,
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/QuickReply"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="Quick Replies">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<QuickReply :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,20 @@
<script setup>
import Text from '../../bubbles/Template/Text.vue';
const message = {
content: 'Hello John, how may we assist you?',
};
</script>
<template>
<Story
title="Components/Message Bubbles/Template/Text"
:layout="{ type: 'grid', width: '600px' }"
>
<Variant title="Default Text">
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
<Text :message="message" />
</div>
</Variant>
</Story>
</template>
@@ -1,100 +1,129 @@
<script> <script setup>
import { mapGetters } from 'vuex'; import { ref, computed } from 'vue';
import NextButton from 'dashboard/components-next/button/Button.vue'; import { useI18n } from 'vue-i18n';
import { useMapGetter } from 'dashboard/composables/store';
import { vOnClickOutside } from '@vueuse/components';
export default { import NextButton from 'dashboard/components-next/button/Button.vue';
components: { import Input from 'dashboard/components-next/input/Input.vue';
NextButton,
}, const emit = defineEmits(['close', 'assign']);
emits: ['update', 'close', 'assign'],
data() { const { t } = useI18n();
return {
query: '', const labels = useMapGetter('labels/getLabels');
selectedLabels: [],
}; const query = ref('');
}, const selectedLabels = ref([]);
computed: {
...mapGetters({ labels: 'labels/getLabels' }), const filteredLabels = computed(() => {
filteredLabels() { if (!query.value) return labels.value;
return this.labels.filter(label => return labels.value.filter(label =>
label.title.toLowerCase().includes(this.query.toLowerCase()) label.title.toLowerCase().includes(query.value.toLowerCase())
); );
}, });
},
methods: { const hasLabels = computed(() => labels.value.length > 0);
isLabelSelected(label) { const hasFilteredLabels = computed(() => filteredLabels.value.length > 0);
return this.selectedLabels.includes(label);
}, const isLabelSelected = label => {
assignLabels(key) { return selectedLabels.value.includes(label);
this.$emit('update', key); };
},
onClose() { const onClose = () => {
this.$emit('close'); emit('close');
}, };
},
const handleAssign = () => {
if (selectedLabels.value.length > 0) {
emit('assign', selectedLabels.value);
}
}; };
</script> </script>
<template> <template>
<div v-on-clickaway="onClose" class="labels-container"> <div
v-on-click-outside="onClose"
class="absolute ltr:right-2 rtl:left-2 top-12 origin-top-right z-20 w-60 bg-n-alpha-3 backdrop-blur-[100px] border-n-weak rounded-lg border border-solid shadow-md"
role="dialog"
aria-labelledby="label-dialog-title"
>
<div class="triangle"> <div class="triangle">
<svg height="12" viewBox="0 0 24 12" width="24"> <svg height="12" viewBox="0 0 24 12" width="24">
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" /> <path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
</svg> </svg>
</div> </div>
<div class="flex items-center justify-between header"> <div class="flex items-center justify-between p-2.5">
<span>{{ $t('BULK_ACTION.LABELS.ASSIGN_LABELS') }}</span> <span class="text-sm font-medium">{{
t('BULK_ACTION.LABELS.ASSIGN_LABELS')
}}</span>
<NextButton ghost xs slate icon="i-lucide-x" @click="onClose" /> <NextButton ghost xs slate icon="i-lucide-x" @click="onClose" />
</div> </div>
<div class="labels-list"> <div class="flex flex-col max-h-60 min-h-0">
<header class="labels-list__header"> <header class="py-2 px-2.5">
<div <Input
class="flex items-center justify-between h-8 gap-2 label-list-search"
>
<fluent-icon icon="search" class="search-icon" size="16" />
<input
v-model="query" v-model="query"
type="search" type="search"
:placeholder="$t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')" :placeholder="t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
class="reset-base !outline-0 !text-sm label--search_input" icon-left="i-lucide-search"
size="sm"
class="w-full"
:aria-label="t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
/> />
</div>
</header> </header>
<ul class="labels-list__body"> <ul
v-if="hasLabels"
class="flex-1 overflow-y-auto m-0 list-none"
role="listbox"
:aria-label="t('BULK_ACTION.LABELS.ASSIGN_LABELS')"
>
<li v-if="!hasFilteredLabels" class="p-2 text-center">
<span class="text-sm text-n-slate-11">{{
t('BULK_ACTION.LABELS.NO_LABELS_FOUND')
}}</span>
</li>
<li <li
v-for="label in filteredLabels" v-for="label in filteredLabels"
:key="label.id" :key="label.id"
class="label__list-item" class="my-1 mx-0 py-0 px-2.5"
role="option"
:aria-selected="isLabelSelected(label.title)"
> >
<label <label
class="item" class="items-center rounded-md cursor-pointer flex py-1 px-2.5 hover:bg-n-slate-3 dark:hover:bg-n-solid-3 has-[:checked]:bg-n-slate-2"
:class="{ 'label-selected': isLabelSelected(label.title) }"
> >
<input <input
v-model="selectedLabels" v-model="selectedLabels"
type="checkbox" type="checkbox"
:value="label.title" :value="label.title"
class="label-checkbox" class="my-0 ltr:mr-2.5 rtl:ml-2.5"
:aria-label="label.title"
/> />
<span <span
class="overflow-hidden label-title whitespace-nowrap text-ellipsis" class="overflow-hidden flex-grow w-full text-sm whitespace-nowrap text-ellipsis"
> >
{{ label.title }} {{ label.title }}
</span> </span>
<span <span
class="label-pill" class="rounded-md h-3 w-3 flex-shrink-0 border border-solid border-n-weak"
:style="{ backgroundColor: label.color }" :style="{ backgroundColor: label.color }"
/> />
</label> </label>
</li> </li>
</ul> </ul>
<footer class="labels-list__footer"> <div v-else class="p-2 text-center">
<span class="text-sm text-n-slate-11">{{
t('CONTACTS_BULK_ACTIONS.NO_LABELS_FOUND')
}}</span>
</div>
<footer class="p-2">
<NextButton <NextButton
sm sm
type="submit" type="submit"
:label="$t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS')" class="w-full"
:label="t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS')"
:disabled="!selectedLabels.length" :disabled="!selectedLabels.length"
@click="$emit('assign', selectedLabels)" @click="handleAssign"
/> />
</footer> </footer>
</div> </div>
@@ -102,61 +131,6 @@ export default {
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.labels-list {
@apply flex flex-col max-h-[15rem] min-h-[auto];
.labels-list__header {
@apply bg-n-alpha-3 backdrop-blur-[100px] py-0 px-2.5;
}
.labels-list__body {
@apply flex-1 overflow-y-auto py-2.5 mx-0;
}
.labels-list__footer {
@apply p-2;
button {
@apply w-full;
.button__content {
@apply text-center;
}
}
}
}
.label-list-search {
@apply bg-n-alpha-black2 py-0 px-2.5 border border-solid border-n-strong rounded-md;
.search-icon {
@apply text-n-slate-10;
}
.label--search_input {
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset] w-full;
}
}
.labels-container {
@apply absolute ltr:right-2 rtl:left-2 top-12 origin-top-right w-auto z-20 max-w-[15rem] min-w-[15rem] bg-n-alpha-3 backdrop-blur-[100px] border-n-weak rounded-lg border border-solid shadow-md;
.header {
@apply p-2.5;
span {
@apply text-sm font-medium;
}
}
.container {
@apply max-h-[15rem] overflow-y-auto;
.label__list-container {
@apply h-full;
}
}
.triangle { .triangle {
@apply block z-10 absolute text-left -top-3 ltr:right-[--triangle-position] rtl:left-[--triangle-position]; @apply block z-10 absolute text-left -top-3 ltr:right-[--triangle-position] rtl:left-[--triangle-position];
@@ -164,45 +138,4 @@ export default {
@apply fill-n-alpha-3 backdrop-blur-[100px] stroke-n-weak; @apply fill-n-alpha-3 backdrop-blur-[100px] stroke-n-weak;
} }
} }
}
ul {
@apply m-0 list-none;
}
.labels-placeholder {
@apply p-2;
}
.label__list-item {
@apply my-1 mx-0 py-0 px-2.5;
.item {
@apply items-center rounded-md cursor-pointer flex py-1 px-2.5 hover:bg-n-slate-3 dark:hover:bg-n-solid-3;
&.label-selected {
@apply bg-n-slate-2;
}
span {
@apply text-sm;
}
.label-checkbox {
@apply my-0 ltr:mr-2.5 rtl:ml-2.5;
}
.label-title {
@apply flex-grow w-full;
}
.label-pill {
@apply rounded-md h-3 w-3 flex-shrink-0 border border-solid border-n-weak;
}
}
}
.search-container {
@apply bg-n-alpha-3 backdrop-blur-[100px] py-0 px-2.5 sticky top-0 z-20;
}
</style> </style>
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "በአሁኑ ጊዜ ንቁ እውቂያዎች የሉም 🌙" "ACTIVE_EMPTY_STATE_TITLE": "በአሁኑ ጊዜ ንቁ እውቂያዎች የሉም 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "إضافة وسم", "ASSIGN_LABELS": "إضافة وسم",
"NO_LABELS_FOUND": "لم يتم العثور على تسميات لـ", "NO_LABELS_FOUND": "لم يتم العثور على تصنيفات",
"ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة", "ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة",
"ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح.", "ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح.",
"ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى." "ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "تعيين التسميات",
"ASSIGN_LABELS_SUCCESS": "تم تعيين التسميات بنجاح.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "الكل" "ALL": "الكل"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "تعديل",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "حذف"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "Няма намерени етикети",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Всички" "ALL": "Всички"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Редактирай",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Изтрий"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assignar etiquetes", "ASSIGN_LABELS": "Assignar etiquetes",
"NO_LABELS_FOUND": "No s'han trobat etiquetes per a", "NO_LABELS_FOUND": "No s'han trobat etiquetes",
"ASSIGN_SELECTED_LABELS": "Assigna les etiquetes seleccionades", "ASSIGN_SELECTED_LABELS": "Assigna les etiquetes seleccionades",
"ASSIGN_SUCCESFUL": "L'etiqueta s'ha assignat correctament.", "ASSIGN_SUCCESFUL": "L'etiqueta s'ha assignat correctament.",
"ASSIGN_FAILED": "No s'han pogut assignar les etiquetes. Torna-ho a provar." "ASSIGN_FAILED": "No s'han pogut assignar les etiquetes. Torna-ho a provar."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "L'etiqueta s'ha assignat correctament.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Totes" "ALL": "Totes"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edita",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Esborrar"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Vše" "ALL": "Vše"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Upravit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Vymazat"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "Ingen etiketter fundet for", "NO_LABELS_FOUND": "Ingen labels fundet",
"ASSIGN_SELECTED_LABELS": "Tildel valgte etiketter", "ASSIGN_SELECTED_LABELS": "Tildel valgte etiketter",
"ASSIGN_SUCCESFUL": "Etiketter tildelt med succes.", "ASSIGN_SUCCESFUL": "Etiketter tildelt med succes.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Tildel Etiketter",
"ASSIGN_LABELS_SUCCESS": "Etiketter tildelt med succes.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Alle" "ALL": "Alle"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Rediger",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Slet"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Labels zuweisen", "ASSIGN_LABELS": "Labels zuweisen",
"NO_LABELS_FOUND": "Keine Labels gefunden für", "NO_LABELS_FOUND": "Keine Labels gefunden",
"ASSIGN_SELECTED_LABELS": "Ausgewählte Labels zuweisen", "ASSIGN_SELECTED_LABELS": "Ausgewählte Labels zuweisen",
"ASSIGN_SUCCESFUL": "Labels erfolgreich zugewiesen.", "ASSIGN_SUCCESFUL": "Labels erfolgreich zugewiesen.",
"ASSIGN_FAILED": "Labels konnten nicht zugewiesen werden. Bitte versuchen Sie es erneut." "ASSIGN_FAILED": "Labels konnten nicht zugewiesen werden. Bitte versuchen Sie es erneut."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "Im Moment sind keine Kontakte aktiv 🌙" "ACTIVE_EMPTY_STATE_TITLE": "Im Moment sind keine Kontakte aktiv 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Labels zuweisen",
"ASSIGN_LABELS_SUCCESS": "Labels erfolgreich zugewiesen.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "Wir konnten die Suche nicht abschließen. Bitte versuch es erneut." "ERROR_MESSAGE": "Wir konnten die Suche nicht abschließen. Bitte versuch es erneut."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Alle" "ALL": "Alle"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Bearbeiten",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Löschen"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "Δεν βρέθηκαν πρότυπα για", "NO_LABELS_FOUND": "Δεν βρέθηκαν ετικέτες",
"ASSIGN_SELECTED_LABELS": "Ανάθεση επιλεγμένων ετικετών", "ASSIGN_SELECTED_LABELS": "Ανάθεση επιλεγμένων ετικετών",
"ASSIGN_SUCCESFUL": "Επιτυχής ανάθεση ετικετών.", "ASSIGN_SUCCESFUL": "Επιτυχής ανάθεση ετικετών.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Ανάθεση Ετικετών",
"ASSIGN_LABELS_SUCCESS": "Επιτυχής ανάθεση ετικετών.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Όλες" "ALL": "Όλες"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Επεξεργασία",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Διαγραφή"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -572,6 +572,27 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})",
"DELETE_CONTACTS": "Delete",
"DELETE_SUCCESS": "Contacts deleted successfully.",
"DELETE_FAILED": "Failed to delete contacts.",
"DELETE_DIALOG": {
"TITLE": "Delete selected contacts",
"SINGULAR_TITLE": "Delete selected contact",
"DESCRIPTION": "This will permanently delete {count} selected contacts. This action cannot be undone.",
"SINGULAR_DESCRIPTION": "This will permanently delete the selected contact. This action cannot be undone.",
"CONFIRM_MULTIPLE": "Delete contacts",
"CONFIRM_SINGLE": "Delete contact"
}
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
@@ -15,20 +15,6 @@
}, },
"ERROR": "There was an error connecting to Shopify. Please try again or contact support if the issue persists." "ERROR": "There was an error connecting to Shopify. Please try again or contact support if the issue persists."
}, },
"GITHUB": {
"TITLE": "Connect Github",
"LABEL": "Github Client ID",
"PLACEHOLDER": "Enter your Github Client ID",
"HELP": "Enter your Github Client ID",
"CANCEL": "Cancel",
"SUBMIT": "Connect",
"DELETE": {
"TITLE": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"CONFIRM": "Yes, delete",
"CANCEL": "Cancel"
}
},
"HEADER": "Integrations", "HEADER": "Integrations",
"DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.", "DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.",
"LEARN_MORE": "Learn more about integrations", "LEARN_MORE": "Learn more about integrations",
@@ -352,7 +338,6 @@
} }
} }
}, },
"CAPTAIN": { "CAPTAIN": {
"NAME": "Captain", "NAME": "Captain",
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
@@ -876,6 +861,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -915,6 +901,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -946,13 +936,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Asignar etiqueta", "ASSIGN_LABELS": "Asignar etiqueta",
"NO_LABELS_FOUND": "No se encontraron etiquetas para", "NO_LABELS_FOUND": "No se encontraron etiquetas",
"ASSIGN_SELECTED_LABELS": "Asignar etiquetas seleccionadas", "ASSIGN_SELECTED_LABELS": "Asignar etiquetas seleccionadas",
"ASSIGN_SUCCESFUL": "Etiquetas asignadas correctamente.", "ASSIGN_SUCCESFUL": "Etiquetas asignadas correctamente.",
"ASSIGN_FAILED": "Error al asignar etiquetas, inténtalo de nuevo." "ASSIGN_FAILED": "Error al asignar etiquetas, inténtalo de nuevo."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No hay contactos activos por el momento 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No hay contactos activos por el momento 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Asignar etiquetas",
"ASSIGN_LABELS_SUCCESS": "Etiquetas asignadas correctamente.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "No pudimos completar la búsqueda. Por favor, inténtalo de nuevo." "ERROR_MESSAGE": "No pudimos completar la búsqueda. Por favor, inténtalo de nuevo."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "Preguntas frecuentes", "HEADER": "Preguntas frecuentes",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Todos" "ALL": "Todos"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Editar",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Eliminar"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "برچسب اختصاص دهید", "ASSIGN_LABELS": "برچسب اختصاص دهید",
"NO_LABELS_FOUND": "هیچ برچسبی یافت نشد برای", "NO_LABELS_FOUND": "هیچ برچسبی یافت نشد",
"ASSIGN_SELECTED_LABELS": "اختصاص برچسب‌های انتخاب شده", "ASSIGN_SELECTED_LABELS": "اختصاص برچسب‌های انتخاب شده",
"ASSIGN_SUCCESFUL": "برچسب‌ها با موفقیت اختصاص یافتند.", "ASSIGN_SUCCESFUL": "برچسب‌ها با موفقیت اختصاص یافتند.",
"ASSIGN_FAILED": "اختصاص برچسب‌ به صورت موفق انجام نشد، لطفا دوباره امتحان کنید." "ASSIGN_FAILED": "اختصاص برچسب‌ به صورت موفق انجام نشد، لطفا دوباره امتحان کنید."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "اختصاص برچسب‌ها",
"ASSIGN_LABELS_SUCCESS": "برچسب‌ها با موفقیت اختصاص یافتند.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "همه" "ALL": "همه"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "ویرایش",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "حذف"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Kaikki" "ALL": "Kaikki"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Muokkaa",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Poista"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assigner une étiquette", "ASSIGN_LABELS": "Assigner une étiquette",
"NO_LABELS_FOUND": "Aucune étiquette trouvée pour", "NO_LABELS_FOUND": "Aucune étiquette trouvée",
"ASSIGN_SELECTED_LABELS": "Assigner les étiquettes sélectionnées", "ASSIGN_SELECTED_LABELS": "Assigner les étiquettes sélectionnées",
"ASSIGN_SUCCESFUL": "Étiquettes attribuées avec succès.", "ASSIGN_SUCCESFUL": "Étiquettes attribuées avec succès.",
"ASSIGN_FAILED": "Impossible d'attribuer les étiquettes, veuillez réessayer." "ASSIGN_FAILED": "Impossible d'attribuer les étiquettes, veuillez réessayer."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "Aucun contact n'est actif pour le moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "Aucun contact n'est actif pour le moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assigner des étiquettes",
"ASSIGN_LABELS_SUCCESS": "Étiquettes attribuées avec succès.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Tous" "ALL": "Tous"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Modifier",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Supprimer"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "לא נצאו תוויות עבור", "NO_LABELS_FOUND": "לא נמצאו תוויות",
"ASSIGN_SELECTED_LABELS": "שייך תוויות נבחרות", "ASSIGN_SELECTED_LABELS": "שייך תוויות נבחרות",
"ASSIGN_SUCCESFUL": "תוויות שוייכו בהצלחה.", "ASSIGN_SUCCESFUL": "תוויות שוייכו בהצלחה.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "הקצה תוויות",
"ASSIGN_LABELS_SUCCESS": "תוויות שוייכו בהצלחה.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "הכל" "ALL": "הכל"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "ערוך",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "מחק"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Sve" "ALL": "Sve"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Uredi",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Izbriši"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Címkék hozzárendelése", "ASSIGN_LABELS": "Címkék hozzárendelése",
"NO_LABELS_FOUND": "Nem található cimke erre:", "NO_LABELS_FOUND": "Nem találtunk címkét",
"ASSIGN_SELECTED_LABELS": "Válogatott címkék hozzárendelése", "ASSIGN_SELECTED_LABELS": "Válogatott címkék hozzárendelése",
"ASSIGN_SUCCESFUL": "Címkék hozzárendelése sikeres.", "ASSIGN_SUCCESFUL": "Címkék hozzárendelése sikeres.",
"ASSIGN_FAILED": "Nem sikerült címkéket hozzárendelni. Kérjük, próbálja újra." "ASSIGN_FAILED": "Nem sikerült címkéket hozzárendelni. Kérjük, próbálja újra."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Cimke hozzáadása",
"ASSIGN_LABELS_SUCCESS": "Címkék hozzárendelése sikeres.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Mind" "ALL": "Mind"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Szerkesztés",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Törlés"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "No labels found",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Assign Labels",
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "All" "ALL": "All"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Delete"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "Tidak ada label ditemukan untuk", "NO_LABELS_FOUND": "Tidak ada label",
"ASSIGN_SELECTED_LABELS": "Tugaskan label terpilih", "ASSIGN_SELECTED_LABELS": "Tugaskan label terpilih",
"ASSIGN_SUCCESFUL": "Label berhasil ditugaskan.", "ASSIGN_SUCCESFUL": "Label berhasil ditugaskan.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."
@@ -571,6 +571,16 @@
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙" "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
} }
}, },
"CONTACTS_BULK_ACTIONS": {
"ASSIGN_LABELS": "Tugaskan Label",
"ASSIGN_LABELS_SUCCESS": "Label berhasil ditugaskan.",
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
"NO_LABELS_FOUND": "No labels available yet.",
"SELECTED_COUNT": "{count} selected",
"CLEAR_SELECTION": "Clear selection",
"SELECT_ALL": "Select all ({count})"
},
"COMPOSE_NEW_CONVERSATION": { "COMPOSE_NEW_CONVERSATION": {
"CONTACT_SEARCH": { "CONTACT_SEARCH": {
"ERROR_MESSAGE": "We couldnt complete the search. Please try again." "ERROR_MESSAGE": "We couldnt complete the search. Please try again."
@@ -863,6 +863,7 @@
}, },
"RESPONSES": { "RESPONSES": {
"HEADER": "FAQs", "HEADER": "FAQs",
"PENDING_FAQS": "Pending FAQs",
"ADD_NEW": "Create new FAQ", "ADD_NEW": "Create new FAQ",
"DOCUMENTABLE": { "DOCUMENTABLE": {
"CONVERSATION": "Conversation #{id}" "CONVERSATION": "Conversation #{id}"
@@ -902,6 +903,10 @@
"APPROVED": "Approved", "APPROVED": "Approved",
"ALL": "Semua" "ALL": "Semua"
}, },
"PENDING_BANNER": {
"TITLE": "Captain has found some FAQs your customers were looking for.",
"ACTION": "Click here to review"
},
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.", "FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
"CREATE": { "CREATE": {
"TITLE": "Add an FAQ", "TITLE": "Add an FAQ",
@@ -932,13 +937,15 @@
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved" "APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
}, },
"OPTIONS": { "OPTIONS": {
"APPROVE": "Mark as approved", "APPROVE": "Approve",
"EDIT_RESPONSE": "Edit FAQ", "EDIT_RESPONSE": "Edit",
"DELETE_RESPONSE": "Delete FAQ" "DELETE_RESPONSE": "Hapus"
}, },
"EMPTY_STATE": { "EMPTY_STATE": {
"TITLE": "No FAQs Found", "TITLE": "No FAQs Found",
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.", "SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
"CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": { "FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ", "TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it." "NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
@@ -24,7 +24,7 @@
}, },
"LABELS": { "LABELS": {
"ASSIGN_LABELS": "Assign labels", "ASSIGN_LABELS": "Assign labels",
"NO_LABELS_FOUND": "No labels found for", "NO_LABELS_FOUND": "Engar merkingar fundust",
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully.", "ASSIGN_SUCCESFUL": "Labels assigned successfully.",
"ASSIGN_FAILED": "Failed to assign labels. Please try again." "ASSIGN_FAILED": "Failed to assign labels. Please try again."

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