mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-16 12:14:34 -06:00
feat: refactor translation key management (#6717)
Co-authored-by: Piyush Gupta <piyushguptaa2z123@gmail.com> Co-authored-by: Piyush Gupta <56182734+gupta-piyush19@users.noreply.github.com> Co-authored-by: Victor Hugo dos Santos <115753265+victorvhs017@users.noreply.github.com> Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com> Co-authored-by: Matti Nannt <matti@formbricks.com> Co-authored-by: Matti Nannt <mail@matthiasnannt.com> Co-authored-by: Johannes <johannes@formbricks.com> Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c9a50a6ff2
commit
a5fa876aa3
457
.cursor/rules/i18n-management.mdc
Normal file
457
.cursor/rules/i18n-management.mdc
Normal file
@@ -0,0 +1,457 @@
|
||||
---
|
||||
title: i18n Management with Lingo.dev
|
||||
description: Guidelines for managing internationalization (i18n) with Lingo.dev, including translation workflow, key validation, and best practices
|
||||
---
|
||||
|
||||
# i18n Management with Lingo.dev
|
||||
|
||||
This rule defines the workflow and best practices for managing internationalization (i18n) in the Formbricks project using Lingo.dev.
|
||||
|
||||
## Overview
|
||||
|
||||
Formbricks uses [Lingo.dev](https://lingo.dev) for managing translations across multiple languages. The translation workflow includes:
|
||||
|
||||
1. **Translation Keys**: Defined in code using the `t()` function from `react-i18next`
|
||||
2. **Translation Files**: JSON files stored in `apps/web/locales/` for each supported language
|
||||
3. **Validation**: Automated scanning to detect missing and unused translation keys
|
||||
4. **CI/CD**: Pre-commit hooks and GitHub Actions to enforce translation quality
|
||||
|
||||
## Translation Workflow
|
||||
|
||||
### 1. Using Translations in Code
|
||||
|
||||
When adding translatable text in the web app, use the `t()` function or `<Trans>` component:
|
||||
|
||||
**Using the `t()` function:**
|
||||
```tsx
|
||||
import { useTranslate } from "@/lib/i18n/translate";
|
||||
|
||||
const MyComponent = () => {
|
||||
const { t } = useTranslate();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t("common.welcome")}</h1>
|
||||
<p>{t("pages.dashboard.description")}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Using the `<Trans>` component (for text with HTML elements):**
|
||||
```tsx
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
const MyComponent = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="auth.terms_agreement"
|
||||
components={{
|
||||
link: <a href="/terms" />,
|
||||
b: <b />
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**Key Naming Conventions:**
|
||||
- Use dot notation for nested keys: `section.subsection.key`
|
||||
- Use descriptive names: `auth.login.success_message` not `auth.msg1`
|
||||
- Group related keys together: `auth.*`, `errors.*`, `common.*`
|
||||
- Use lowercase with underscores: `user_profile_settings` not `UserProfileSettings`
|
||||
|
||||
### 2. Translation File Structure
|
||||
|
||||
Translation files are located in `apps/web/locales/` and use the following naming convention:
|
||||
- `en-US.json` (English - United States, default)
|
||||
- `de-DE.json` (German)
|
||||
- `fr-FR.json` (French)
|
||||
- `pt-BR.json` (Portuguese - Brazil)
|
||||
- etc.
|
||||
|
||||
**File Structure:**
|
||||
```json
|
||||
{
|
||||
"common": {
|
||||
"welcome": "Welcome",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Login",
|
||||
"email_placeholder": "Enter your email",
|
||||
"password_placeholder": "Enter your password"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Adding New Translation Keys
|
||||
|
||||
When adding new translation keys:
|
||||
|
||||
1. **Add the key in your code** using `t("your.new.key")`
|
||||
2. **Add translation for that key in en-US.json file**
|
||||
3. **Run the translation workflow:**
|
||||
```bash
|
||||
pnpm i18n
|
||||
```
|
||||
This will:
|
||||
- Generate translations for all languages using Lingo.dev
|
||||
- Validate that all keys are present and used
|
||||
|
||||
4. **Review and commit** the generated translation files
|
||||
|
||||
### 4. Available Scripts
|
||||
|
||||
```bash
|
||||
# Generate translations using Lingo.dev
|
||||
pnpm generate-translations
|
||||
|
||||
# Scan and validate translation keys
|
||||
pnpm scan-translations
|
||||
|
||||
# Full workflow: generate + validate
|
||||
pnpm i18n
|
||||
|
||||
# Validate only (without generation)
|
||||
pnpm i18n:validate
|
||||
```
|
||||
|
||||
## Translation Key Validation
|
||||
|
||||
### Automated Validation
|
||||
|
||||
The project includes automated validation that runs:
|
||||
- **Pre-commit hook**: Validates translations before allowing commits (when `LINGODOTDEV_API_KEY` is set)
|
||||
- **GitHub Actions**: Validates translations on every PR and push to main
|
||||
|
||||
### Validation Rules
|
||||
|
||||
The validation script (`scan-translations.ts`) checks for:
|
||||
|
||||
1. **Missing Keys**: Translation keys used in code but not present in translation files
|
||||
2. **Unused Keys**: Translation keys present in translation files but not used in code
|
||||
3. **Incomplete Translations**: Keys that exist in the default language (`en-US`) but are missing in target languages
|
||||
|
||||
**What gets scanned:**
|
||||
- All `.ts` and `.tsx` files in `apps/web/`
|
||||
- Both `t()` function calls and `<Trans i18nKey="">` components
|
||||
- All locale files (`de-DE.json`, `fr-FR.json`, `ja-JP.json`, etc.)
|
||||
|
||||
**What gets excluded:**
|
||||
- Test files (`*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`)
|
||||
- Build directories (`node_modules`, `dist`, `build`, `.next`, `coverage`)
|
||||
- Locale files themselves (from code scanning)
|
||||
|
||||
**Note:** Test files are excluded because they often use mock or example translation keys for testing purposes that don't need to exist in production translation files.
|
||||
|
||||
### Fixing Validation Errors
|
||||
|
||||
#### Missing Keys
|
||||
|
||||
If you encounter missing key errors:
|
||||
|
||||
```
|
||||
❌ MISSING KEYS (2):
|
||||
|
||||
These keys are used in code but not found in translation files:
|
||||
|
||||
• auth.signup.email_required
|
||||
• settings.profile.update_success
|
||||
```
|
||||
|
||||
**Resolution:**
|
||||
1. Ensure that translations for those keys are present in en-US.json .
|
||||
2. Run `pnpm generate-translations` to have Lingo.dev generate the missing translations
|
||||
3. OR manually add the keys to `apps/web/locales/en-US.json`:
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"signup": {
|
||||
"email_required": "Email is required"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"profile": {
|
||||
"update_success": "Profile updated successfully"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Run `pnpm scan-translations` to verify
|
||||
4. Commit the changes
|
||||
|
||||
#### Unused Keys
|
||||
|
||||
If you encounter unused key errors:
|
||||
|
||||
```
|
||||
⚠️ UNUSED KEYS (1):
|
||||
|
||||
These keys exist in translation files but are not used in code:
|
||||
|
||||
• old.deprecated.key
|
||||
```
|
||||
|
||||
**Resolution:**
|
||||
1. If the key is truly unused, remove it from all translation files
|
||||
2. If the key should be used, add it to your code using `t("old.deprecated.key")`
|
||||
3. Run `pnpm scan-translations` to verify
|
||||
4. Commit the changes
|
||||
|
||||
#### Incomplete Translations
|
||||
|
||||
If you encounter incomplete translation errors:
|
||||
|
||||
```
|
||||
⚠️ INCOMPLETE TRANSLATIONS:
|
||||
|
||||
Some keys from en-US are missing in target languages:
|
||||
|
||||
📝 de-DE (5 missing keys):
|
||||
• auth.new_feature.title
|
||||
• auth.new_feature.description
|
||||
• settings.advanced.option
|
||||
... and 2 more
|
||||
```
|
||||
|
||||
**Resolution:**
|
||||
1. **Recommended:** Run `pnpm generate-translations` to have Lingo.dev automatically translate the missing keys
|
||||
2. **Manual:** Add the missing keys to the target language files:
|
||||
```bash
|
||||
# Copy the structure from en-US.json and translate the values
|
||||
# For example, in de-DE.json:
|
||||
{
|
||||
"auth": {
|
||||
"new_feature": {
|
||||
"title": "Neues Feature",
|
||||
"description": "Beschreibung des neuen Features"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Run `pnpm scan-translations` to verify all translations are complete
|
||||
4. Commit the changes
|
||||
|
||||
## Pre-commit Hook Behavior
|
||||
|
||||
The pre-commit hook will:
|
||||
|
||||
1. Run `lint-staged` for code formatting
|
||||
2. If `LINGODOTDEV_API_KEY` is set:
|
||||
- Generate translations using Lingo.dev
|
||||
- Validate translation keys
|
||||
- Auto-add updated locale files to the commit
|
||||
- **Block the commit** if validation fails
|
||||
3. If `LINGODOTDEV_API_KEY` is not set:
|
||||
- Skip translation validation (for community contributors)
|
||||
- Show a warning message
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### LINGODOTDEV_API_KEY
|
||||
|
||||
This is the API key for Lingo.dev integration.
|
||||
|
||||
**For Core Team:**
|
||||
- Add to your local `.env` file
|
||||
- Required for running translation generation
|
||||
|
||||
**For Community Contributors:**
|
||||
- Not required for local development
|
||||
- Translation validation will be skipped
|
||||
- The CI will still validate translations
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Keep Keys Organized
|
||||
|
||||
Group related keys together:
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"login": { ... },
|
||||
"signup": { ... },
|
||||
"forgot_password": { ... }
|
||||
},
|
||||
"dashboard": {
|
||||
"header": { ... },
|
||||
"sidebar": { ... }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Avoid Hardcoded Strings
|
||||
|
||||
**❌ Bad:**
|
||||
```tsx
|
||||
<button>Click here</button>
|
||||
```
|
||||
|
||||
**✅ Good:**
|
||||
```tsx
|
||||
<button>{t("common.click_here")}</button>
|
||||
```
|
||||
|
||||
### 3. Use Interpolation for Dynamic Content
|
||||
|
||||
**❌ Bad:**
|
||||
```tsx
|
||||
{t("welcome")} {userName}!
|
||||
```
|
||||
|
||||
**✅ Good:**
|
||||
```tsx
|
||||
{t("auth.welcome_message", { userName })}
|
||||
```
|
||||
|
||||
With translation:
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"welcome_message": "Welcome, {userName}!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Avoid Dynamic Key Construction
|
||||
|
||||
**❌ Bad:**
|
||||
```tsx
|
||||
const key = `errors.${errorCode}`;
|
||||
t(key);
|
||||
```
|
||||
|
||||
**✅ Good:**
|
||||
```tsx
|
||||
switch (errorCode) {
|
||||
case "401":
|
||||
return t("errors.unauthorized");
|
||||
case "404":
|
||||
return t("errors.not_found");
|
||||
default:
|
||||
return t("errors.unknown");
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Test Translation Keys
|
||||
|
||||
When adding new features:
|
||||
1. Add translation keys
|
||||
2. Test in multiple languages using the language switcher
|
||||
3. Ensure text doesn't overflow in longer translations (German, French)
|
||||
4. Run `pnpm scan-translations` before committing
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Pre-commit hook fails with validation errors
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Run the full i18n workflow
|
||||
pnpm i18n
|
||||
|
||||
# Fix any missing or unused keys
|
||||
# Then commit again
|
||||
git add .
|
||||
git commit -m "your message"
|
||||
```
|
||||
|
||||
### Issue: Translation validation passes locally but fails in CI
|
||||
|
||||
**Solution:**
|
||||
- Ensure all translation files are committed
|
||||
- Check that `scan-translations.ts` hasn't been modified
|
||||
- Verify that locale files are properly formatted JSON
|
||||
|
||||
### Issue: Cannot commit because of missing translations
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# If you have LINGODOTDEV_API_KEY:
|
||||
pnpm generate-translations
|
||||
|
||||
# If you don't have the API key (community contributor):
|
||||
# Manually add the missing keys to en-US.json
|
||||
# Then run validation:
|
||||
pnpm scan-translations
|
||||
```
|
||||
|
||||
### Issue: Getting "unused keys" for keys that are used
|
||||
|
||||
**Solution:**
|
||||
- The script scans `.ts` and `.tsx` files only
|
||||
- If keys are used in other file types, they may be flagged
|
||||
- Verify the key is actually used with `grep -r "your.key" apps/web/`
|
||||
- If it's a false positive, consider updating the scanning patterns in `scan-translations.ts`
|
||||
|
||||
## AI Assistant Guidelines
|
||||
|
||||
When assisting with i18n-related tasks, always:
|
||||
|
||||
1. **Use the `t()` function** for all user-facing text
|
||||
2. **Follow key naming conventions** (lowercase, dots for nesting)
|
||||
3. **Run validation** after making changes: `pnpm scan-translations`
|
||||
4. **Fix missing keys** by adding them to `en-US.json`
|
||||
5. **Remove unused keys** from all translation files
|
||||
6. **Test the pre-commit hook** if making changes to translation workflow
|
||||
7. **Update this rule file** if translation workflow changes
|
||||
|
||||
### Fixing Missing Translation Keys
|
||||
|
||||
When the AI encounters missing translation key errors:
|
||||
|
||||
1. Identify the missing keys from the error output
|
||||
2. Determine the appropriate section and naming for each key
|
||||
3. Add the keys to `apps/web/locales/en-US.json` with meaningful English text
|
||||
4. Ensure proper JSON structure and nesting
|
||||
5. Run `pnpm scan-translations` to verify
|
||||
6. Inform the user that other language files will be updated via Lingo.dev
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
// Error: Missing key "settings.api.rate_limit_exceeded"
|
||||
|
||||
// Add to en-US.json:
|
||||
{
|
||||
"settings": {
|
||||
"api": {
|
||||
"rate_limit_exceeded": "API rate limit exceeded. Please try again later."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Removing Unused Translation Keys
|
||||
|
||||
When the AI encounters unused translation key errors:
|
||||
|
||||
1. Verify the keys are truly unused by searching the codebase
|
||||
2. Remove the keys from `apps/web/locales/en-US.json`
|
||||
3. Note that removal from other language files can be handled via Lingo.dev
|
||||
4. Run `pnpm scan-translations` to verify
|
||||
|
||||
## Migration Notes
|
||||
|
||||
This project previously used Tolgee for translations. As of this migration:
|
||||
|
||||
- **Old scripts**: `tolgee-pull` is deprecated (kept for reference)
|
||||
- **New scripts**: Use `pnpm i18n` or `pnpm generate-translations`
|
||||
- **Old workflows**: `tolgee.yml` and `tolgee-missing-key-check.yml` removed
|
||||
- **New workflow**: `translation-check.yml` handles all validation
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** October 14, 2025
|
||||
**Related Files:**
|
||||
- `scan-translations.ts` - Translation validation script
|
||||
- `.husky/pre-commit` - Pre-commit hook with i18n validation
|
||||
- `.github/workflows/translation-check.yml` - CI workflow for translation validation
|
||||
- `apps/web/locales/*.json` - Translation files
|
||||
@@ -214,3 +214,7 @@ REDIS_URL=redis://localhost:6379
|
||||
# AUDIT_LOG_ENABLED=0
|
||||
# If the ip should be added in the log or not. Default 0
|
||||
# AUDIT_LOG_GET_USER_IP=0
|
||||
|
||||
|
||||
# Lingo.dev API key for translation generation
|
||||
LINGODOTDEV_API_KEY=your_api_key_here
|
||||
51
.github/workflows/tolgee-missing-key-check.yml
vendored
51
.github/workflows/tolgee-missing-key-check.yml
vendored
@@ -1,51 +0,0 @@
|
||||
name: Check Missing Translations
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check-missing-translations:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Tolgee CLI
|
||||
run: npm install -g @tolgee/cli
|
||||
|
||||
- name: Compare Tolgee Keys
|
||||
id: compare
|
||||
run: |
|
||||
tolgee compare --api-key ${{ secrets.TOLGEE_API_KEY }} > compare_output.txt
|
||||
cat compare_output.txt
|
||||
|
||||
- name: Check for Missing Translations
|
||||
run: |
|
||||
if grep -q "new key found" compare_output.txt; then
|
||||
echo "New keys found that may require translations:"
|
||||
exit 1
|
||||
else
|
||||
echo "No new keys found."
|
||||
fi
|
||||
95
.github/workflows/tolgee.yml
vendored
95
.github/workflows/tolgee.yml
vendored
@@ -1,95 +0,0 @@
|
||||
name: Tolgee Tagging on PR Merge
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
tag-production-keys:
|
||||
name: Tag Production Keys
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0 # This ensures we get the full git history
|
||||
|
||||
- name: Get source branch name
|
||||
id: branch-name
|
||||
env:
|
||||
RAW_BRANCH: ${{ github.head_ref }}
|
||||
run: |
|
||||
# Validate and sanitize branch name - only allow alphanumeric, dots, underscores, hyphens, and forward slashes
|
||||
SOURCE_BRANCH=$(echo "$RAW_BRANCH" | sed 's/[^a-zA-Z0-9._\/-]//g')
|
||||
|
||||
# Additional validation - ensure branch name is not empty after sanitization
|
||||
if [[ -z "$SOURCE_BRANCH" ]]; then
|
||||
echo "❌ Error: Branch name is empty after sanitization"
|
||||
echo "Original branch: $RAW_BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Safely add to environment variables using GitHub's recommended method
|
||||
# This prevents environment variable injection attacks
|
||||
echo "SOURCE_BRANCH<<EOF" >> $GITHUB_ENV
|
||||
echo "$SOURCE_BRANCH" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
echo "Detected source branch: $SOURCE_BRANCH"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 18 # Ensure compatibility with your project
|
||||
|
||||
- name: Install Tolgee CLI
|
||||
run: npm install -g @tolgee/cli
|
||||
|
||||
- name: Tag Production Keys
|
||||
run: |
|
||||
npx tolgee tag \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-extracted \
|
||||
--filter-tag "draft:${SOURCE_BRANCH}" \
|
||||
--tag production \
|
||||
--untag "draft:${SOURCE_BRANCH}"
|
||||
|
||||
- name: Tag unused production keys as Deprecated
|
||||
run: |
|
||||
npx tolgee tag \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-not-extracted --filter-tag production \
|
||||
--tag deprecated --untag production
|
||||
|
||||
- name: Tag unused draft:current-branch keys as Deprecated
|
||||
run: |
|
||||
npx tolgee tag \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-not-extracted --filter-tag "draft:${SOURCE_BRANCH}" \
|
||||
--tag deprecated --untag "draft:${SOURCE_BRANCH}"
|
||||
|
||||
- name: Sync with backup
|
||||
run: |
|
||||
npx tolgee sync \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--backup ./tolgee-backup \
|
||||
--continue-on-warning \
|
||||
--yes
|
||||
|
||||
- name: Upload backup as artifact
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
with:
|
||||
name: tolgee-backup-${{ github.sha }}
|
||||
path: ./tolgee-backup
|
||||
retention-days: 90
|
||||
63
.github/workflows/translation-check.yml
vendored
Normal file
63
.github/workflows/translation-check.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Translation Validation
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "apps/web/**/*.ts"
|
||||
- "apps/web/**/*.tsx"
|
||||
- "apps/web/locales/**/*.json"
|
||||
- "scan-translations.ts"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "apps/web/**/*.ts"
|
||||
- "apps/web/**/*.tsx"
|
||||
- "apps/web/locales/**/*.json"
|
||||
- "scan-translations.ts"
|
||||
|
||||
jobs:
|
||||
validate-translations:
|
||||
name: Validate Translation Keys
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0
|
||||
with:
|
||||
version: 9.15.9
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Validate translation keys
|
||||
run: |
|
||||
echo ""
|
||||
echo "🔍 Validating translation keys..."
|
||||
echo ""
|
||||
pnpm run scan-translations
|
||||
|
||||
- name: Summary
|
||||
if: success()
|
||||
run: |
|
||||
echo ""
|
||||
echo "✅ Translation validation completed successfully!"
|
||||
echo ""
|
||||
@@ -10,12 +10,34 @@ fi
|
||||
|
||||
pnpm lint-staged
|
||||
|
||||
# Run tolgee-pull if branch.json exists and NEXT_PUBLIC_TOLGEE_API_KEY is not set
|
||||
if [ -f branch.json ]; then
|
||||
if [ -z "$NEXT_PUBLIC_TOLGEE_API_KEY" ]; then
|
||||
echo "Skipping tolgee-pull: NEXT_PUBLIC_TOLGEE_API_KEY is not set"
|
||||
# Run Lingo.dev i18n workflow if LINGODOTDEV_API_KEY is set
|
||||
if [ -n "$LINGODOTDEV_API_KEY" ]; then
|
||||
echo ""
|
||||
echo "🌍 Running Lingo.dev translation workflow..."
|
||||
echo ""
|
||||
|
||||
# Run translation generation and validation
|
||||
if pnpm run i18n; then
|
||||
echo ""
|
||||
echo "✅ Translation validation passed"
|
||||
echo ""
|
||||
# Add updated locale files to git
|
||||
git add apps/web/locales/*.json
|
||||
else
|
||||
pnpm run tolgee-pull
|
||||
git add apps/web/locales
|
||||
echo ""
|
||||
echo "❌ Translation validation failed!"
|
||||
echo ""
|
||||
echo "Please fix the translation issues above before committing:"
|
||||
echo " • Add missing translation keys to your locale files"
|
||||
echo " • Remove unused translation keys"
|
||||
echo ""
|
||||
echo "Or run 'pnpm i18n' to see the detailed report"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ Skipping translation validation: LINGODOTDEV_API_KEY is not set"
|
||||
echo " (This is expected for community contributors)"
|
||||
echo ""
|
||||
fi
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.tolgee.io/cli-schema.json",
|
||||
"format": "JSON_TOLGEE",
|
||||
"patterns": ["./apps/web/**/*.ts?(x)"],
|
||||
"projectId": 10304,
|
||||
"pull": {
|
||||
"path": "./apps/web/locales"
|
||||
},
|
||||
"push": {
|
||||
"files": [
|
||||
{
|
||||
"language": "en-US",
|
||||
"path": "./apps/web/locales/en-US.json"
|
||||
},
|
||||
{
|
||||
"language": "de-DE",
|
||||
"path": "./apps/web/locales/de-DE.json"
|
||||
},
|
||||
{
|
||||
"language": "fr-FR",
|
||||
"path": "./apps/web/locales/fr-FR.json"
|
||||
},
|
||||
{
|
||||
"language": "pt-BR",
|
||||
"path": "./apps/web/locales/pt-BR.json"
|
||||
},
|
||||
{
|
||||
"language": "zh-Hant-TW",
|
||||
"path": "./apps/web/locales/zh-Hant-TW.json"
|
||||
},
|
||||
{
|
||||
"language": "pt-PT",
|
||||
"path": "./apps/web/locales/pt-PT.json"
|
||||
},
|
||||
{
|
||||
"language": "ro-RO",
|
||||
"path": "./apps/web/locales/ro-RO.json"
|
||||
},
|
||||
{
|
||||
"language": "ja-JP",
|
||||
"path": "./apps/web/locales/ja-JP.json"
|
||||
},
|
||||
{
|
||||
"language": "zh-Hans-CN",
|
||||
"path": "./apps/web/locales/zh-Hans-CN.json"
|
||||
}
|
||||
],
|
||||
"forceMode": "OVERRIDE"
|
||||
},
|
||||
"strictNamespace": false
|
||||
}
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||
"eslint.workingDirectories": [{ "mode": "auto" }],
|
||||
"eslint.workingDirectories": [
|
||||
{
|
||||
"mode": "auto"
|
||||
}
|
||||
],
|
||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||
"sonarlint.connectedMode.project": {
|
||||
"connectionId": "formbricks",
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
import type { Preview } from "@storybook/react-vite";
|
||||
import { TolgeeProvider } from "@tolgee/react";
|
||||
import React from "react";
|
||||
// Import translation data for Storybook
|
||||
import enUSTranslations from "../../web/locales/en-US.json";
|
||||
import { I18nProvider } from "../../web/lingodotdev/client";
|
||||
import "../../web/modules/ui/globals.css";
|
||||
import { TolgeeBase } from "../../web/tolgee/shared";
|
||||
|
||||
// Create a Storybook-specific Tolgee decorator
|
||||
const withTolgee = (Story: any) => {
|
||||
const tolgee = TolgeeBase().init({
|
||||
tagNewKeys: [], // No branch tagging in Storybook
|
||||
});
|
||||
|
||||
// Create a Storybook-specific Lingodot Dev decorator
|
||||
const withLingodotDev = (Story: any) => {
|
||||
return React.createElement(
|
||||
TolgeeProvider,
|
||||
I18nProvider,
|
||||
{
|
||||
tolgee,
|
||||
fallback: "Loading",
|
||||
ssr: {
|
||||
language: "en-US",
|
||||
staticData: {
|
||||
"en-US": enUSTranslations,
|
||||
},
|
||||
},
|
||||
},
|
||||
language: "en-US",
|
||||
defaultLanguage: "en-US",
|
||||
} as any,
|
||||
React.createElement(Story)
|
||||
);
|
||||
};
|
||||
@@ -37,7 +24,7 @@ const preview: Preview = {
|
||||
},
|
||||
},
|
||||
},
|
||||
decorators: [withTolgee],
|
||||
decorators: [withLingodotDev],
|
||||
};
|
||||
|
||||
export default preview;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TProjectConfigChannel } from "@formbricks/types/project";
|
||||
import { cn } from "@/lib/cn";
|
||||
@@ -23,7 +23,7 @@ export const ConnectWithFormbricks = ({
|
||||
appSetupCompleted,
|
||||
channel,
|
||||
}: ConnectWithFormbricksProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const handleFinishOnboarding = async () => {
|
||||
router.push(`/environments/${environment.id}/surveys`);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import "prismjs/themes/prism.css";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TProjectConfigChannel } from "@formbricks/types/project";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { CodeBlock } from "@/modules/ui/components/code-block";
|
||||
@@ -29,7 +29,7 @@ export const OnboardingSetupInstructions = ({
|
||||
channel,
|
||||
appSetupCompleted,
|
||||
}: OnboardingSetupInstructionsProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
||||
const htmlSnippetForAppSurveys = `<!-- START Formbricks Surveys -->
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -4,9 +4,9 @@ import { ConnectWithFormbricks } from "@/app/(app)/(onboarding)/environments/[en
|
||||
import { getEnvironment } from "@/lib/environment/service";
|
||||
import { getPublicDomain } from "@/lib/getPublicUrl";
|
||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface ConnectPageProps {
|
||||
params: Promise<{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ActivityIcon, ShoppingCartIcon, SmileIcon, StarIcon, ThumbsUpIcon, UsersIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import { TSurveyCreateInput } from "@formbricks/types/surveys/types";
|
||||
import { TXMTemplate } from "@formbricks/types/templates";
|
||||
@@ -23,7 +23,7 @@ interface XMTemplateListProps {
|
||||
|
||||
export const XMTemplateList = ({ project, user, environmentId }: XMTemplateListProps) => {
|
||||
const [activeTemplateId, setActiveTemplateId] = useState<number | null>(null);
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
const createSurvey = async (activeTemplate: TXMTemplate) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { cleanup } from "@testing-library/preact";
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { getXMSurveyDefault, getXMTemplates } from "./xm-templates";
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("xm-templates", () => {
|
||||
});
|
||||
|
||||
test("getXMSurveyDefault returns default survey template", () => {
|
||||
const tMock = vi.fn((key) => key) as TFnType;
|
||||
const tMock = vi.fn((key) => key) as TFunction;
|
||||
const result = getXMSurveyDefault(tMock);
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -29,7 +29,7 @@ describe("xm-templates", () => {
|
||||
});
|
||||
|
||||
test("getXMTemplates returns all templates", () => {
|
||||
const tMock = vi.fn((key) => key) as TFnType;
|
||||
const tMock = vi.fn((key) => key) as TFunction;
|
||||
const result = getXMTemplates(tMock);
|
||||
|
||||
expect(result).toHaveLength(6);
|
||||
@@ -44,7 +44,7 @@ describe("xm-templates", () => {
|
||||
test("getXMTemplates handles errors gracefully", async () => {
|
||||
const tMock = vi.fn(() => {
|
||||
throw new Error("Test error");
|
||||
}) as TFnType;
|
||||
}) as TFunction;
|
||||
|
||||
const result = getXMTemplates(tMock);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { TXMTemplate } from "@formbricks/types/templates";
|
||||
import {
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
getDefaultEndingCard,
|
||||
} from "@/app/lib/survey-builder";
|
||||
|
||||
export const getXMSurveyDefault = (t: TFnType): TXMTemplate => {
|
||||
export const getXMSurveyDefault = (t: TFunction): TXMTemplate => {
|
||||
try {
|
||||
return {
|
||||
name: "",
|
||||
@@ -26,7 +26,7 @@ export const getXMSurveyDefault = (t: TFnType): TXMTemplate => {
|
||||
}
|
||||
};
|
||||
|
||||
const npsSurvey = (t: TFnType): TXMTemplate => {
|
||||
const npsSurvey = (t: TFunction): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.nps_survey_name"),
|
||||
@@ -55,7 +55,7 @@ const npsSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const starRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
const starRatingSurvey = (t: TFunction): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
@@ -153,7 +153,7 @@ const starRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const csatSurvey = (t: TFnType): TXMTemplate => {
|
||||
const csatSurvey = (t: TFunction): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
@@ -247,7 +247,7 @@ const csatSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const cessSurvey = (t: TFnType): TXMTemplate => {
|
||||
const cessSurvey = (t: TFunction): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.cess_survey_name"),
|
||||
@@ -272,7 +272,7 @@ const cessSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const smileysRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
const smileysRatingSurvey = (t: TFunction): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
@@ -370,7 +370,7 @@ const smileysRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const enpsSurvey = (t: TFnType): TXMTemplate => {
|
||||
const enpsSurvey = (t: TFunction): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.enps_survey_name"),
|
||||
@@ -399,7 +399,7 @@ const enpsSurvey = (t: TFnType): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
export const getXMTemplates = (t: TFnType): TXMTemplate[] => {
|
||||
export const getXMTemplates = (t: TFunction): TXMTemplate[] => {
|
||||
try {
|
||||
return [
|
||||
npsSurvey(t),
|
||||
|
||||
@@ -6,10 +6,10 @@ import { getEnvironment } from "@/lib/environment/service";
|
||||
import { getProjectByEnvironmentId, getUserProjects } from "@/lib/project/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getOrganizationIdFromEnvironmentId } from "@/lib/utils/helper";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface XMTemplatePageProps {
|
||||
params: Promise<{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowUpRightIcon, ChevronRightIcon, LogOutIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOrganization } from "@formbricks/types/organizations";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import FBLogo from "@/images/formbricks-wordmark.svg";
|
||||
@@ -27,7 +27,7 @@ interface LandingSidebarProps {
|
||||
export const LandingSidebar = ({ user, organization }: LandingSidebarProps) => {
|
||||
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState<boolean>(false);
|
||||
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const { signOut: signOutWithAudit } = useSignOut({ id: user.id, email: user.email });
|
||||
|
||||
const dropdownNavigation = [
|
||||
|
||||
@@ -6,10 +6,10 @@ import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
||||
import { getAccessFlags } from "@/lib/membership/utils";
|
||||
import { getOrganizationsByUserId } from "@/lib/organization/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -6,9 +6,9 @@ import { IS_POSTHOG_CONFIGURED } from "@/lib/constants";
|
||||
import { canUserAccessOrganization } from "@/lib/organization/auth";
|
||||
import { getOrganization } from "@/lib/organization/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { ToasterClient } from "@/modules/ui/components/toaster-client";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const ProjectOnboardingLayout = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -3,10 +3,10 @@ import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
||||
import { getUserProjects } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface ChannelPageProps {
|
||||
params: Promise<{
|
||||
|
||||
@@ -4,9 +4,9 @@ import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
||||
import { getAccessFlags } from "@/lib/membership/utils";
|
||||
import { getOrganization } from "@/lib/organization/service";
|
||||
import { getOrganizationProjectsCount } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getOrganizationProjectsLimit } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const OnboardingLayout = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -3,10 +3,10 @@ import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
||||
import { getUserProjects } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface ModePageProps {
|
||||
params: Promise<{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TProjectConfigChannel,
|
||||
TProjectConfigIndustry,
|
||||
@@ -59,7 +59,7 @@ export const ProjectSettings = ({
|
||||
const [createTeamModalOpen, setCreateTeamModalOpen] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const addProject = async (data: TProjectUpdateInput) => {
|
||||
try {
|
||||
const createProjectResponse = await createProjectAction({
|
||||
|
||||
@@ -6,11 +6,11 @@ import { getTeamsByOrganizationId } from "@/app/(app)/(onboarding)/lib/onboardin
|
||||
import { ProjectSettings } from "@/app/(app)/(onboarding)/organizations/[organizationId]/projects/new/settings/components/ProjectSettings";
|
||||
import { DEFAULT_BRAND_COLOR } from "@/lib/constants";
|
||||
import { getUserProjects } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getAccessControlPermission } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface ProjectSettingsPageProps {
|
||||
params: Promise<{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Confetti } from "@/modules/ui/components/confetti";
|
||||
|
||||
@@ -11,7 +11,7 @@ interface ConfirmationPageProps {
|
||||
}
|
||||
|
||||
export const ConfirmationPage = ({ environmentId }: ConfirmationPageProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [showConfetti, setShowConfetti] = useState(false);
|
||||
useEffect(() => {
|
||||
setShowConfetti(true);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getOrganizationByEnvironmentId,
|
||||
} from "@/lib/organization/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
||||
import {
|
||||
getAccessControlPermission,
|
||||
@@ -21,7 +22,6 @@ import {
|
||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||
import { LimitsReachedBanner } from "@/modules/ui/components/limits-reached-banner";
|
||||
import { PendingDowngradeBanner } from "@/modules/ui/components/pending-downgrade-banner";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
interface EnvironmentLayoutProps {
|
||||
environmentId: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
@@ -14,7 +14,7 @@ interface EnvironmentSwitchProps {
|
||||
}
|
||||
|
||||
export const EnvironmentSwitch = ({ environment, environments }: EnvironmentSwitchProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const [isEnvSwitchChecked, setIsEnvSwitchChecked] = useState(environment?.type === "development");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import {
|
||||
ArrowUpRightIcon,
|
||||
ChevronRightIcon,
|
||||
@@ -17,6 +16,7 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||
import { TOrganization } from "@formbricks/types/organizations";
|
||||
@@ -59,7 +59,7 @@ export const MainNavigation = ({
|
||||
}: NavigationProps) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
const [isTextVisible, setIsTextVisible] = useState(true);
|
||||
const [latestVersion, setLatestVersion] = useState("");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { AlertTriangleIcon, CheckIcon, RotateCcwIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
@@ -12,7 +12,7 @@ interface WidgetStatusIndicatorProps {
|
||||
}
|
||||
|
||||
export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const stati = {
|
||||
notImplemented: {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ChevronDownIcon, CircleHelpIcon, Code2Icon, Loader2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -21,7 +21,7 @@ export const EnvironmentBreadcrumb = ({
|
||||
environments: { id: string; type: string }[];
|
||||
currentEnvironment: { id: string; type: string };
|
||||
}) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isEnvironmentDropdownOpen, setIsEnvironmentDropdownOpen] = useState(false);
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import {
|
||||
BuildingIcon,
|
||||
ChevronDownIcon,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { CreateOrganizationModal } from "@/modules/organization/components/CreateOrganizationModal";
|
||||
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
||||
@@ -43,7 +43,7 @@ export const OrganizationBreadcrumb = ({
|
||||
isMember,
|
||||
isOwnerOrManager,
|
||||
}: OrganizationBreadcrumbProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isOrganizationDropdownOpen, setIsOrganizationDropdownOpen] = useState(false);
|
||||
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ChevronDownIcon, ChevronRightIcon, CogIcon, FolderOpenIcon, Loader2, PlusIcon } from "lucide-react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { CreateProjectModal } from "@/modules/projects/components/create-project-modal";
|
||||
import { ProjectLimitModal } from "@/modules/projects/components/project-limit-modal";
|
||||
@@ -44,7 +44,7 @@ export const ProjectBreadcrumb = ({
|
||||
isAccessControlAllowed,
|
||||
isEnvironmentBreadcrumbVisible,
|
||||
}: ProjectBreadcrumbProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isProjectDropdownOpen, setIsProjectDropdownOpen] = useState(false);
|
||||
const [openCreateProjectModal, setOpenCreateProjectModal] = useState(false);
|
||||
const [openLimitModal, setOpenLimitModal] = useState(false);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { TFnType, useTranslate } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Control, Controller, useForm } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import {
|
||||
TIntegrationAirtable,
|
||||
@@ -58,7 +59,7 @@ type AddIntegrationModalProps = {
|
||||
} & EditModeProps;
|
||||
|
||||
const NoBaseFoundError = () => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Alert>
|
||||
<AlertTitle>{t("environments.integrations.airtable.no_bases_found")}</AlertTitle>
|
||||
@@ -80,7 +81,7 @@ const renderQuestionSelection = ({
|
||||
includeCreatedAt,
|
||||
setIncludeCreatedAt,
|
||||
}: {
|
||||
t: TFnType;
|
||||
t: TFunction;
|
||||
selectedSurvey: TSurvey;
|
||||
control: Control<IntegrationModalInputs>;
|
||||
includeVariables: boolean;
|
||||
@@ -153,7 +154,7 @@ export const AddIntegrationModal = ({
|
||||
isEditMode,
|
||||
defaultData,
|
||||
}: AddIntegrationModalProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const [tables, setTables] = useState<TIntegrationAirtableTables["tables"]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Control, Controller, UseFormSetValue } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import {
|
||||
@@ -30,7 +30,7 @@ export const BaseSelectDropdown = ({
|
||||
setValue,
|
||||
defaultValue,
|
||||
}: BaseSelectProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex w-full flex-col">
|
||||
<Label htmlFor="base">{t("environments.integrations.airtable.airtable_base")}</Label>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Trash2Icon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
|
||||
@@ -30,7 +30,7 @@ interface ManageIntegrationProps {
|
||||
|
||||
export const ManageIntegration = (props: ManageIntegrationProps) => {
|
||||
const { airtableIntegration, environment, environmentId, setIsConnected, surveys, airtableArray } = props;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tableHeaders = [
|
||||
t("common.survey"),
|
||||
|
||||
@@ -7,11 +7,11 @@ import { getAirtableTables } from "@/lib/airtable/service";
|
||||
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
|
||||
import { getIntegrations } from "@/lib/integration/service";
|
||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TIntegrationGoogleSheets,
|
||||
TIntegrationGoogleSheetsConfigData,
|
||||
@@ -56,7 +56,7 @@ export const AddIntegrationModal = ({
|
||||
googleSheetIntegration,
|
||||
selectedIntegration,
|
||||
}: AddIntegrationModalProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const integrationData: TIntegrationGoogleSheetsConfigData = {
|
||||
spreadsheetId: "",
|
||||
spreadsheetName: "",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Trash2Icon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import {
|
||||
TIntegrationGoogleSheets,
|
||||
@@ -34,7 +34,7 @@ export const ManageIntegration = ({
|
||||
setSelectedIntegration,
|
||||
locale,
|
||||
}: ManageIntegrationProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||
let integrationArray: TIntegrationGoogleSheetsConfigData[] = [];
|
||||
if (googleSheetIntegration?.config.data) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
|
||||
const Loading = () => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-6 p-6">
|
||||
<GoBackButton />
|
||||
|
||||
@@ -10,11 +10,11 @@ import {
|
||||
} from "@/lib/constants";
|
||||
import { getIntegrations } from "@/lib/integration/service";
|
||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TIntegrationInput } from "@formbricks/types/integration";
|
||||
import {
|
||||
TIntegrationNotion,
|
||||
@@ -56,7 +56,7 @@ export const AddIntegrationModal = ({
|
||||
databases,
|
||||
selectedIntegration,
|
||||
}: AddIntegrationModalProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const { handleSubmit } = useForm();
|
||||
const [selectedDatabase, setSelectedDatabase] = useState<TIntegrationNotionDatabase | null>();
|
||||
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { RefreshCcwIcon, Trash2Icon } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationNotion, TIntegrationNotionConfigData } from "@formbricks/types/integration/notion";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -36,7 +36,7 @@ export const ManageIntegration = ({
|
||||
locale,
|
||||
handleNotionAuthorization,
|
||||
}: ManageIntegrationProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||
const [isDeleting, setisDeleting] = useState(false);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
|
||||
const Loading = () => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-6 p-6">
|
||||
<GoBackButton />
|
||||
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
import { getIntegrationByType } from "@/lib/integration/service";
|
||||
import { getNotionDatabases } from "@/lib/notion/service";
|
||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import Image from "next/image";
|
||||
import { redirect } from "next/navigation";
|
||||
import { TIntegrationType } from "@formbricks/types/integration";
|
||||
@@ -14,14 +14,14 @@ import SlackLogo from "@/images/slacklogo.png";
|
||||
import WebhookLogo from "@/images/webhook.png";
|
||||
import ZapierLogo from "@/images/zapier-small.png";
|
||||
import { getIntegrations } from "@/lib/integration/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||
import { Card } from "@/modules/ui/components/integration-card";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const getStatusText = (count: number, t: TFnType, type: string) => {
|
||||
const getStatusText = (count: number, t: TFunction, type: string) => {
|
||||
if (count === 1) return `1 ${type}`;
|
||||
if (count === 0) return t("common.not_connected");
|
||||
return `${count} ${type}s`;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CircleHelpIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||
import {
|
||||
TIntegrationSlack,
|
||||
@@ -54,7 +54,7 @@ export const AddChannelMappingModal = ({
|
||||
selectedIntegration,
|
||||
}: AddChannelMappingModalProps) => {
|
||||
const { handleSubmit } = useForm();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [selectedQuestions, setSelectedQuestions] = useState<string[]>([]);
|
||||
const [isLinkingChannel, setIsLinkingChannel] = useState(false);
|
||||
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { T, useTranslate } from "@tolgee/react";
|
||||
import { Trash2Icon } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TIntegrationSlack, TIntegrationSlackConfigData } from "@formbricks/types/integration/slack";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -39,7 +39,7 @@ export const ManageIntegration = ({
|
||||
handleSlackAuthorization,
|
||||
locale,
|
||||
}: ManageIntegrationProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||
const [isDeleting, setisDeleting] = useState(false);
|
||||
let integrationArray: TIntegrationSlackConfigData[] = [];
|
||||
@@ -76,9 +76,9 @@ export const ManageIntegration = ({
|
||||
{showReconnectButton && (
|
||||
<div className="mb-4 flex w-full items-center justify-between space-x-4">
|
||||
<p className="text-amber-700">
|
||||
<T
|
||||
keyName="environments.integrations.slack.slack_reconnect_button_description"
|
||||
params={{ b: <b /> }}
|
||||
<Trans
|
||||
i18nKey="environments.integrations.slack.slack_reconnect_button_description"
|
||||
components={{ b: <b /> }}
|
||||
/>
|
||||
</p>
|
||||
<Button onClick={handleSlackAuthorization} variant="secondary">
|
||||
|
||||
@@ -5,11 +5,11 @@ import { SlackWrapper } from "@/app/(app)/environments/[environmentId]/project/i
|
||||
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
||||
import { getIntegrationByType } from "@/lib/integration/service";
|
||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||
|
||||
interface AccountSettingsNavbarProps {
|
||||
@@ -12,7 +12,7 @@ interface AccountSettingsNavbarProps {
|
||||
|
||||
export const AccountSettingsNavbar = ({ environmentId, activeId, loading }: AccountSettingsNavbarProps) => {
|
||||
const pathname = usePathname();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const navigation = [
|
||||
{
|
||||
id: "profile",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const AccountSettingsLayout = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { HelpCircleIcon, UsersIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
||||
import { Membership } from "../types";
|
||||
@@ -23,7 +23,7 @@ export const EditAlerts = ({
|
||||
autoDisableNotificationType,
|
||||
autoDisableNotificationElementId,
|
||||
}: EditAlertsProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{memberships.map((membership) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SlackIcon } from "@/modules/ui/components/icons";
|
||||
|
||||
interface IntegrationsTipProps {
|
||||
@@ -8,7 +8,7 @@ interface IntegrationsTipProps {
|
||||
}
|
||||
|
||||
export const IntegrationsTip = ({ environmentId }: IntegrationsTipProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<div className="flex max-w-4xl items-center space-y-3 rounded-lg border border-blue-100 bg-blue-50 p-4 text-sm text-blue-900 shadow-sm md:space-y-0 md:text-base">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TUserNotificationSettings } from "@formbricks/types/user";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
@@ -25,7 +25,7 @@ export const NotificationSwitch = ({
|
||||
autoDisableNotificationElementId,
|
||||
}: NotificationSwitchProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const isChecked =
|
||||
notificationType === "unsubscribedOrganizationIds"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
|
||||
const Loading = () => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const cards = [
|
||||
{
|
||||
title: t("environments.settings.notifications.email_alerts_surveys"),
|
||||
|
||||
@@ -4,10 +4,10 @@ import { TUserNotificationSettings } from "@formbricks/types/user";
|
||||
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { EditAlerts } from "./components/EditAlerts";
|
||||
import { IntegrationsTip } from "./components/IntegrationsTip";
|
||||
import type { Membership } from "./types";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { DisableTwoFactorModal } from "@/modules/ee/two-factor-auth/components/disable-two-factor-modal";
|
||||
import { EnableTwoFactorModal } from "@/modules/ee/two-factor-auth/components/enable-two-factor-modal";
|
||||
@@ -12,7 +12,7 @@ interface AccountSecurityProps {
|
||||
}
|
||||
|
||||
export const AccountSecurity = ({ user }: AccountSecurityProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [twoFactorModalOpen, setTwoFactorModalOpen] = useState(false);
|
||||
const [disableTwoFactorModalOpen, setDisableTwoFactorModalOpen] = useState(false);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import type { Session } from "next-auth";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOrganization } from "@formbricks/types/organizations";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { DeleteAccountModal } from "@/modules/account/components/DeleteAccountModal";
|
||||
@@ -24,7 +24,7 @@ export const DeleteAccount = ({
|
||||
}) => {
|
||||
const [isModalOpen, setModalOpen] = useState(false);
|
||||
const isDeleteDisabled = !isMultiOrgEnabled && organizationsWithSingleOwner.length > 0;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { TUser, TUserUpdateInput, ZUser, ZUserEmail } from "@formbricks/types/user";
|
||||
import { PasswordConfirmationModal } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/password-confirmation-modal";
|
||||
@@ -42,7 +42,7 @@ export const EditProfileDetailsForm = ({
|
||||
isPasswordResetEnabled,
|
||||
emailVerificationDisabled,
|
||||
}: IEditProfileDetailsFormProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm<TEditProfileNameForm>({
|
||||
defaultValues: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { ZUserPassword } from "@formbricks/types/user";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
@@ -39,7 +39,7 @@ export const PasswordConfirmationModal = ({
|
||||
newEmail,
|
||||
onConfirm,
|
||||
}: PasswordConfirmationModalProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(PasswordConfirmationSchema),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
|
||||
const Loading = () => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const cards = [
|
||||
{
|
||||
title: t("environments.settings.profile.personal_information"),
|
||||
|
||||
@@ -3,13 +3,13 @@ import { AccountSecurity } from "@/app/(app)/environments/[environmentId]/settin
|
||||
import { EMAIL_VERIFICATION_DISABLED, IS_FORMBRICKS_CLOUD, PASSWORD_RESET_DISABLED } from "@/lib/constants";
|
||||
import { getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getIsMultiOrgEnabled, getIsTwoFactorAuthEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { SettingsCard } from "../../components/SettingsCard";
|
||||
import { DeleteAccount } from "./components/DeleteAccount";
|
||||
import { EditProfileDetailsForm } from "./components/EditProfileDetailsForm";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Loading = async () => {
|
||||
const t = await getTranslate();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||
import { getAccessFlags } from "@/lib/membership/utils";
|
||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||
@@ -24,7 +24,7 @@ export const OrganizationSettingsNavbar = ({
|
||||
const pathname = usePathname();
|
||||
const { isMember, isOwner } = getAccessFlags(membershipRole);
|
||||
const isPricingDisabled = isMember;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigation = [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Loading = async () => {
|
||||
const t = await getTranslate();
|
||||
|
||||
@@ -3,12 +3,12 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOrganization } from "@formbricks/types/organizations";
|
||||
import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
||||
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
|
||||
@@ -25,7 +25,7 @@ export const DeleteOrganization = ({
|
||||
}: DeleteOrganizationProps) => {
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
const handleDeleteOrganization = async () => {
|
||||
@@ -100,7 +100,7 @@ const DeleteOrganizationModal = ({
|
||||
isDeleting,
|
||||
}: DeleteOrganizationModalProps) => {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const handleInputChange = (e) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||
import { TOrganization, ZOrganization } from "@formbricks/types/organizations";
|
||||
@@ -32,7 +32,7 @@ const ZEditOrganizationNameFormSchema = ZOrganization.pick({ name: true });
|
||||
type EditOrganizationNameForm = z.infer<typeof ZEditOrganizationNameFormSchema>;
|
||||
|
||||
export const EditOrganizationNameForm = ({ organization, membershipRole }: EditOrganizationNameProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const form = useForm<EditOrganizationNameForm>({
|
||||
defaultValues: {
|
||||
name: organization.name,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Loading = async () => {
|
||||
const t = await getTranslate();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||
import { FB_LOGO_URL, IS_FORMBRICKS_CLOUD, IS_STORAGE_CONFIGURED } from "@/lib/constants";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getIsMultiOrgEnabled, getWhiteLabelPermission } from "@/modules/ee/license-check/lib/utils";
|
||||
import { EmailCustomizationSettings } from "@/modules/ee/whitelabel/email-customization/components/email-customization-settings";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
@@ -8,7 +9,6 @@ import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { SettingsCard } from "../../components/SettingsCard";
|
||||
import { DeleteOrganization } from "./components/DeleteOrganization";
|
||||
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Layout = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
@@ -31,7 +31,7 @@ export const SettingsCard = ({
|
||||
className?: string;
|
||||
buttonInfo?: ButtonInfo;
|
||||
}) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Unplug } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
|
||||
@@ -11,7 +11,7 @@ interface TEmptyAppSurveysProps {
|
||||
}
|
||||
|
||||
export const EmptyAppSurveys = ({ environment }: TEmptyAppSurveysProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex w-full items-center justify-center gap-8 bg-slate-100 py-12">
|
||||
<div className="flex h-20 w-20 items-center justify-center rounded-full border border-slate-200 bg-white">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon, PresentationIcon } from "lucide-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { revalidateSurveyIdPath } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
|
||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||
@@ -19,7 +19,7 @@ export const SurveyAnalysisNavigation = ({
|
||||
activeId,
|
||||
}: SurveyAnalysisNavigationProps) => {
|
||||
const pathname = usePathname();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const url = `/environments/${environmentId}/surveys/${survey.id}`;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { TFnType, useTranslate } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveyQuota } from "@formbricks/types/quota";
|
||||
import { TResponseDataValue, TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
||||
@@ -83,7 +84,7 @@ export const extractResponseData = (response: TResponseWithQuotas, survey: TSurv
|
||||
export const mapResponsesToTableData = (
|
||||
responses: TResponseWithQuotas[],
|
||||
survey: TSurvey,
|
||||
t: TFnType
|
||||
t: TFunction
|
||||
): TResponseTableData[] => {
|
||||
return responses.map((response) => ({
|
||||
responseData: extractResponseData(response, survey),
|
||||
@@ -124,7 +125,7 @@ export const ResponseDataView: React.FC<ResponseDataViewProps> = ({
|
||||
isQuotasAllowed,
|
||||
quotas,
|
||||
}) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const data = mapResponsesToTableData(responses, survey, t);
|
||||
|
||||
return (
|
||||
|
||||
@@ -15,9 +15,9 @@ import { SortableContext, arrayMove, horizontalListSortingStrategy } from "@dnd-
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { VisibilityState, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveyQuota } from "@formbricks/types/quota";
|
||||
import { TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
||||
@@ -74,7 +74,7 @@ export const ResponseTable = ({
|
||||
isQuotasAllowed,
|
||||
quotas,
|
||||
}: ResponseTableProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = useState({});
|
||||
const [isTableSettingsModalOpen, setIsTableSettingsModalOpen] = useState(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import { CircleHelpIcon, EyeOffIcon, MailIcon, TagIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { TResponseTableData } from "@formbricks/types/responses";
|
||||
@@ -32,7 +32,7 @@ const getQuestionColumnsData = (
|
||||
question: TSurveyQuestion,
|
||||
survey: TSurvey,
|
||||
isExpanded: boolean,
|
||||
t: TFnType
|
||||
t: TFunction
|
||||
): ColumnDef<TResponseTableData>[] => {
|
||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||
const addressFields = ["addressLine1", "addressLine2", "city", "state", "zip", "country"];
|
||||
@@ -231,7 +231,7 @@ const getQuestionColumnsData = (
|
||||
}
|
||||
};
|
||||
|
||||
const getMetadataColumnsData = (t: TFnType): ColumnDef<TResponseTableData>[] => {
|
||||
const getMetadataColumnsData = (t: TFunction): ColumnDef<TResponseTableData>[] => {
|
||||
const metadataColumns: ColumnDef<TResponseTableData>[] = [];
|
||||
|
||||
METADATA_FIELDS.forEach((label) => {
|
||||
@@ -262,7 +262,7 @@ export const generateResponseTableColumns = (
|
||||
survey: TSurvey,
|
||||
isExpanded: boolean,
|
||||
isReadOnly: boolean,
|
||||
t: TFnType,
|
||||
t: TFunction,
|
||||
showQuotasColumn: boolean
|
||||
): ColumnDef<TResponseTableData>[] => {
|
||||
const questionColumns = survey.questions.flatMap((question) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TFunction } from "i18next";
|
||||
import { capitalize } from "lodash";
|
||||
import {
|
||||
AirplayIcon,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { TResponseMeta } from "@formbricks/types/responses";
|
||||
|
||||
export const getAddressFieldLabel = (field: string, t: TFnType) => {
|
||||
export const getAddressFieldLabel = (field: string, t: TFunction) => {
|
||||
switch (field) {
|
||||
case "addressLine1":
|
||||
return t("environments.surveys.responses.address_line_1");
|
||||
@@ -29,7 +29,7 @@ export const getAddressFieldLabel = (field: string, t: TFnType) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getContactInfoFieldLabel = (field: string, t: TFnType) => {
|
||||
export const getContactInfoFieldLabel = (field: string, t: TFunction) => {
|
||||
switch (field) {
|
||||
case "firstName":
|
||||
return t("environments.surveys.responses.first_name");
|
||||
@@ -46,7 +46,7 @@ export const getContactInfoFieldLabel = (field: string, t: TFnType) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getMetadataFieldLabel = (label: string, t: TFnType) => {
|
||||
export const getMetadataFieldLabel = (label: string, t: TFunction) => {
|
||||
switch (label) {
|
||||
case "action":
|
||||
return t("common.action");
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getSurvey } from "@/lib/survey/service";
|
||||
import { getTagsByEnvironmentId } from "@/lib/tag/service";
|
||||
import { getUser } from "@/lib/user/service";
|
||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { getSegments } from "@/modules/ee/contacts/segments/lib/segments";
|
||||
import { getIsContactsEnabled, getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getQuotas } from "@/modules/ee/quotas/lib/quotas";
|
||||
@@ -17,7 +18,6 @@ import { getOrganizationIdFromEnvironmentId } from "@/modules/survey/lib/organiz
|
||||
import { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -18,7 +18,7 @@ interface AddressSummaryProps {
|
||||
}
|
||||
|
||||
export const AddressSummary = ({ questionSummary, environmentId, survey, locale }: AddressSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys/types";
|
||||
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
@@ -13,7 +13,7 @@ interface CTASummaryProps {
|
||||
}
|
||||
|
||||
export const CTASummary = ({ questionSummary, survey }: CTASummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys/types";
|
||||
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
||||
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
||||
@@ -13,7 +13,7 @@ interface CalSummaryProps {
|
||||
}
|
||||
|
||||
export const CalSummary = ({ questionSummary, survey }: CalSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -25,7 +25,7 @@ interface ConsentSummaryProps {
|
||||
}
|
||||
|
||||
export const ConsentSummary = ({ questionSummary, survey, setFilter }: ConsentSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const summaryItems = [
|
||||
{
|
||||
title: t("common.accepted"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryContactInfo } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -23,7 +23,7 @@ export const ContactInfoSummary = ({
|
||||
survey,
|
||||
locale,
|
||||
}: ContactInfoSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -25,7 +25,7 @@ export const DateQuestionSummary = ({
|
||||
survey,
|
||||
locale,
|
||||
}: DateQuestionSummary) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { DownloadIcon, FileIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -27,7 +27,7 @@ export const FileUploadSummary = ({
|
||||
locale,
|
||||
}: FileUploadSummaryProps) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const handleLoadMore = () => {
|
||||
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
||||
setVisibleResponses((prevVisibleResponses) =>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon, Link, MessageSquareTextIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveyQuestionSummaryHiddenFields } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -19,7 +19,7 @@ interface HiddenFieldsSummaryProps {
|
||||
|
||||
export const HiddenFieldsSummary = ({ environment, questionSummary, locale }: HiddenFieldsSummaryProps) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const handleLoadMore = () => {
|
||||
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
||||
setVisibleResponses((prevVisibleResponses) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -24,7 +24,7 @@ interface MatrixQuestionSummaryProps {
|
||||
}
|
||||
|
||||
export const MatrixQuestionSummary = ({ questionSummary, survey, setFilter }: MatrixQuestionSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const getOpacityLevel = (percentage: number): string => {
|
||||
const parsedPercentage = percentage;
|
||||
const opacity = parsedPercentage * 0.75 + 15;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Fragment, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -42,7 +42,7 @@ export const MultipleChoiceSummary = ({
|
||||
survey,
|
||||
setFilter,
|
||||
}: MultipleChoiceSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [visibleOtherResponses, setVisibleOtherResponses] = useState(10);
|
||||
const otherValue = questionSummary.question.choices.find((choice) => choice.id === "other")?.label.default;
|
||||
// sort by count and transform to array
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -25,7 +25,7 @@ interface NPSSummaryProps {
|
||||
}
|
||||
|
||||
export const NPSSummary = ({ questionSummary, survey, setFilter }: NPSSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const applyFilter = (group: string) => {
|
||||
const filters = {
|
||||
promoters: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -21,7 +21,7 @@ interface OpenTextSummaryProps {
|
||||
}
|
||||
|
||||
export const OpenTextSummary = ({ questionSummary, environmentId, survey, locale }: OpenTextSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -30,7 +30,7 @@ interface PictureChoiceSummaryProps {
|
||||
|
||||
export const PictureChoiceSummary = ({ questionSummary, survey, setFilter }: PictureChoiceSummaryProps) => {
|
||||
const results = questionSummary.choices;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
import type { JSX } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys/types";
|
||||
import { getTextContent } from "@formbricks/types/surveys/validation";
|
||||
import { recallToHeadline } from "@/lib/utils/recall";
|
||||
@@ -23,7 +23,7 @@ export const QuestionSummaryHeader = ({
|
||||
showResponses = true,
|
||||
survey,
|
||||
}: HeadProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const questionType = getQuestionTypes(t).find((type) => type.id === questionSummary.question.type);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionSummaryRanking } from "@formbricks/types/surveys/types";
|
||||
import { getChoiceIdByValue } from "@/lib/response/utils";
|
||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||
@@ -12,7 +12,7 @@ interface RankingSummaryProps {
|
||||
|
||||
export const RankingSummary = ({ questionSummary, survey }: RankingSummaryProps) => {
|
||||
// sort by count and transform to array
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const results = Object.values(questionSummary.choices).sort((a, b) => {
|
||||
return a.avgRanking - b.avgRanking; // Sort by count
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CircleSlash2, SmileIcon, StarIcon } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
TI18nString,
|
||||
TSurvey,
|
||||
@@ -28,7 +28,7 @@ interface RatingSummaryProps {
|
||||
}
|
||||
|
||||
export const RatingSummary = ({ questionSummary, survey, setFilter }: RatingSummaryProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const getIconBasedOnScale = useMemo(() => {
|
||||
const scale = questionSummary.question.scale;
|
||||
if (scale === "number") return <CircleSlash2 className="h-4 w-4" />;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Confetti } from "@/modules/ui/components/confetti";
|
||||
@@ -14,7 +14,7 @@ interface SummaryMetadataProps {
|
||||
}
|
||||
|
||||
export const SuccessMessage = ({ environment, survey }: SummaryMetadataProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const searchParams = useSearchParams();
|
||||
const [confetti, setConfetti] = useState(false);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { TimerIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey, TSurveyQuestionType, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { recallToHeadline } from "@/lib/utils/recall";
|
||||
import { formatTextWithSlashes } from "@/modules/survey/editor/lib/utils";
|
||||
@@ -14,7 +14,7 @@ interface SummaryDropOffsProps {
|
||||
}
|
||||
|
||||
export const SummaryDropOffs = ({ dropOff, survey }: SummaryDropOffsProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const getIcon = (questionType: TSurveyQuestionType) => {
|
||||
const Icon = getQuestionIcon(questionType, t);
|
||||
return <Icon className="mt-[3px] h-5 w-5 shrink-0 text-slate-600" />;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TI18nString, TSurveyQuestionId, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
@@ -43,7 +43,7 @@ interface SummaryListProps {
|
||||
|
||||
export const SummaryList = ({ summary, environment, responseCount, survey, locale }: SummaryListProps) => {
|
||||
const { setSelectedFilter, selectedFilter } = useResponseFilter();
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const setFilter = (
|
||||
questionId: TSurveyQuestionId,
|
||||
label: TI18nString,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||
import { InteractiveCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/interactive-card";
|
||||
import { StatCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/stat-card";
|
||||
@@ -48,7 +48,7 @@ export const SummaryMetadata = ({
|
||||
quotasCompleted,
|
||||
quotasCompletedPercentage,
|
||||
} = surveySummary;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
|
||||
|
||||
const handleTabChange = (val: "dropOffs" | "quotas") => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { BellRing, Eye, ListRestart, SquarePenIcon } from "lucide-react";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
@@ -54,7 +54,7 @@ export const SurveyAnalysisCTA = ({
|
||||
isFormbricksCloud,
|
||||
isStorageConfigured,
|
||||
}: SurveyAnalysisCTAProps) => {
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import {
|
||||
Code2Icon,
|
||||
LinkIcon,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
UserIcon,
|
||||
} from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
@@ -69,7 +69,7 @@ export const ShareSurveyModal = ({
|
||||
const [surveyUrl, setSurveyUrl] = useState<string>(getSurveyUrl(survey, publicDomain, "default"));
|
||||
const [showView, setShowView] = useState<ModalView>(modalView);
|
||||
const { email } = user;
|
||||
const { t } = useTranslate();
|
||||
const { t } = useTranslation();
|
||||
const linkTabs: {
|
||||
id: ShareViaType | ShareSettingsType;
|
||||
type: LinkTabsType;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user