mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-20 05:00:29 -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
|
# AUDIT_LOG_ENABLED=0
|
||||||
# If the ip should be added in the log or not. Default 0
|
# If the ip should be added in the log or not. Default 0
|
||||||
# AUDIT_LOG_GET_USER_IP=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
|
pnpm lint-staged
|
||||||
|
|
||||||
# Run tolgee-pull if branch.json exists and NEXT_PUBLIC_TOLGEE_API_KEY is not set
|
# Run Lingo.dev i18n workflow if LINGODOTDEV_API_KEY is set
|
||||||
if [ -f branch.json ]; then
|
if [ -n "$LINGODOTDEV_API_KEY" ]; then
|
||||||
if [ -z "$NEXT_PUBLIC_TOLGEE_API_KEY" ]; then
|
echo ""
|
||||||
echo "Skipping tolgee-pull: NEXT_PUBLIC_TOLGEE_API_KEY is not set"
|
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
|
else
|
||||||
pnpm run tolgee-pull
|
echo ""
|
||||||
git add apps/web/locales
|
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
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Skipping translation validation: LINGODOTDEV_API_KEY is not set"
|
||||||
|
echo " (This is expected for community contributors)"
|
||||||
|
echo ""
|
||||||
fi
|
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.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||||
"eslint.workingDirectories": [{ "mode": "auto" }],
|
"eslint.workingDirectories": [
|
||||||
|
{
|
||||||
|
"mode": "auto"
|
||||||
|
}
|
||||||
|
],
|
||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
"sonarlint.connectedMode.project": {
|
"sonarlint.connectedMode.project": {
|
||||||
"connectionId": "formbricks",
|
"connectionId": "formbricks",
|
||||||
|
|||||||
@@ -1,29 +1,16 @@
|
|||||||
import type { Preview } from "@storybook/react-vite";
|
import type { Preview } from "@storybook/react-vite";
|
||||||
import { TolgeeProvider } from "@tolgee/react";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// Import translation data for Storybook
|
import { I18nProvider } from "../../web/lingodotdev/client";
|
||||||
import enUSTranslations from "../../web/locales/en-US.json";
|
|
||||||
import "../../web/modules/ui/globals.css";
|
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(
|
return React.createElement(
|
||||||
TolgeeProvider,
|
I18nProvider,
|
||||||
{
|
{
|
||||||
tolgee,
|
|
||||||
fallback: "Loading",
|
|
||||||
ssr: {
|
|
||||||
language: "en-US",
|
language: "en-US",
|
||||||
staticData: {
|
defaultLanguage: "en-US",
|
||||||
"en-US": enUSTranslations,
|
} as any,
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
React.createElement(Story)
|
React.createElement(Story)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -37,7 +24,7 @@ const preview: Preview = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
decorators: [withTolgee],
|
decorators: [withLingodotDev],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default preview;
|
export default preview;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TProjectConfigChannel } from "@formbricks/types/project";
|
import { TProjectConfigChannel } from "@formbricks/types/project";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
@@ -23,7 +23,7 @@ export const ConnectWithFormbricks = ({
|
|||||||
appSetupCompleted,
|
appSetupCompleted,
|
||||||
channel,
|
channel,
|
||||||
}: ConnectWithFormbricksProps) => {
|
}: ConnectWithFormbricksProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleFinishOnboarding = async () => {
|
const handleFinishOnboarding = async () => {
|
||||||
router.push(`/environments/${environment.id}/surveys`);
|
router.push(`/environments/${environment.id}/surveys`);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import "prismjs/themes/prism.css";
|
import "prismjs/themes/prism.css";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TProjectConfigChannel } from "@formbricks/types/project";
|
import { TProjectConfigChannel } from "@formbricks/types/project";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { CodeBlock } from "@/modules/ui/components/code-block";
|
import { CodeBlock } from "@/modules/ui/components/code-block";
|
||||||
@@ -29,7 +29,7 @@ export const OnboardingSetupInstructions = ({
|
|||||||
channel,
|
channel,
|
||||||
appSetupCompleted,
|
appSetupCompleted,
|
||||||
}: OnboardingSetupInstructionsProps) => {
|
}: OnboardingSetupInstructionsProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
||||||
const htmlSnippetForAppSurveys = `<!-- START Formbricks Surveys -->
|
const htmlSnippetForAppSurveys = `<!-- START Formbricks Surveys -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { ConnectWithFormbricks } from "@/app/(app)/(onboarding)/environments/[en
|
|||||||
import { getEnvironment } from "@/lib/environment/service";
|
import { getEnvironment } from "@/lib/environment/service";
|
||||||
import { getPublicDomain } from "@/lib/getPublicUrl";
|
import { getPublicDomain } from "@/lib/getPublicUrl";
|
||||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface ConnectPageProps {
|
interface ConnectPageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ActivityIcon, ShoppingCartIcon, SmileIcon, StarIcon, ThumbsUpIcon, UsersIcon } from "lucide-react";
|
import { ActivityIcon, ShoppingCartIcon, SmileIcon, StarIcon, ThumbsUpIcon, UsersIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TProject } from "@formbricks/types/project";
|
import { TProject } from "@formbricks/types/project";
|
||||||
import { TSurveyCreateInput } from "@formbricks/types/surveys/types";
|
import { TSurveyCreateInput } from "@formbricks/types/surveys/types";
|
||||||
import { TXMTemplate } from "@formbricks/types/templates";
|
import { TXMTemplate } from "@formbricks/types/templates";
|
||||||
@@ -23,7 +23,7 @@ interface XMTemplateListProps {
|
|||||||
|
|
||||||
export const XMTemplateList = ({ project, user, environmentId }: XMTemplateListProps) => {
|
export const XMTemplateList = ({ project, user, environmentId }: XMTemplateListProps) => {
|
||||||
const [activeTemplateId, setActiveTemplateId] = useState<number | null>(null);
|
const [activeTemplateId, setActiveTemplateId] = useState<number | null>(null);
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const createSurvey = async (activeTemplate: TXMTemplate) => {
|
const createSurvey = async (activeTemplate: TXMTemplate) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import "@testing-library/jest-dom/vitest";
|
import "@testing-library/jest-dom/vitest";
|
||||||
import { cleanup } from "@testing-library/preact";
|
import { cleanup } from "@testing-library/preact";
|
||||||
import { TFnType } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||||
import { getXMSurveyDefault, getXMTemplates } from "./xm-templates";
|
import { getXMSurveyDefault, getXMTemplates } from "./xm-templates";
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ describe("xm-templates", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("getXMSurveyDefault returns default survey template", () => {
|
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);
|
const result = getXMSurveyDefault(tMock);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@@ -29,7 +29,7 @@ describe("xm-templates", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("getXMTemplates returns all 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);
|
const result = getXMTemplates(tMock);
|
||||||
|
|
||||||
expect(result).toHaveLength(6);
|
expect(result).toHaveLength(6);
|
||||||
@@ -44,7 +44,7 @@ describe("xm-templates", () => {
|
|||||||
test("getXMTemplates handles errors gracefully", async () => {
|
test("getXMTemplates handles errors gracefully", async () => {
|
||||||
const tMock = vi.fn(() => {
|
const tMock = vi.fn(() => {
|
||||||
throw new Error("Test error");
|
throw new Error("Test error");
|
||||||
}) as TFnType;
|
}) as TFunction;
|
||||||
|
|
||||||
const result = getXMTemplates(tMock);
|
const result = getXMTemplates(tMock);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createId } from "@paralleldrive/cuid2";
|
import { createId } from "@paralleldrive/cuid2";
|
||||||
import { TFnType } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
import { TXMTemplate } from "@formbricks/types/templates";
|
import { TXMTemplate } from "@formbricks/types/templates";
|
||||||
import {
|
import {
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
getDefaultEndingCard,
|
getDefaultEndingCard,
|
||||||
} from "@/app/lib/survey-builder";
|
} from "@/app/lib/survey-builder";
|
||||||
|
|
||||||
export const getXMSurveyDefault = (t: TFnType): TXMTemplate => {
|
export const getXMSurveyDefault = (t: TFunction): TXMTemplate => {
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
name: "",
|
name: "",
|
||||||
@@ -26,7 +26,7 @@ export const getXMSurveyDefault = (t: TFnType): TXMTemplate => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const npsSurvey = (t: TFnType): TXMTemplate => {
|
const npsSurvey = (t: TFunction): TXMTemplate => {
|
||||||
return {
|
return {
|
||||||
...getXMSurveyDefault(t),
|
...getXMSurveyDefault(t),
|
||||||
name: t("templates.nps_survey_name"),
|
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 reusableQuestionIds = [createId(), createId(), createId()];
|
||||||
const defaultSurvey = getXMSurveyDefault(t);
|
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 reusableQuestionIds = [createId(), createId(), createId()];
|
||||||
const defaultSurvey = getXMSurveyDefault(t);
|
const defaultSurvey = getXMSurveyDefault(t);
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ const csatSurvey = (t: TFnType): TXMTemplate => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const cessSurvey = (t: TFnType): TXMTemplate => {
|
const cessSurvey = (t: TFunction): TXMTemplate => {
|
||||||
return {
|
return {
|
||||||
...getXMSurveyDefault(t),
|
...getXMSurveyDefault(t),
|
||||||
name: t("templates.cess_survey_name"),
|
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 reusableQuestionIds = [createId(), createId(), createId()];
|
||||||
const defaultSurvey = getXMSurveyDefault(t);
|
const defaultSurvey = getXMSurveyDefault(t);
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ const smileysRatingSurvey = (t: TFnType): TXMTemplate => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const enpsSurvey = (t: TFnType): TXMTemplate => {
|
const enpsSurvey = (t: TFunction): TXMTemplate => {
|
||||||
return {
|
return {
|
||||||
...getXMSurveyDefault(t),
|
...getXMSurveyDefault(t),
|
||||||
name: t("templates.enps_survey_name"),
|
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 {
|
try {
|
||||||
return [
|
return [
|
||||||
npsSurvey(t),
|
npsSurvey(t),
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import { getEnvironment } from "@/lib/environment/service";
|
|||||||
import { getProjectByEnvironmentId, getUserProjects } from "@/lib/project/service";
|
import { getProjectByEnvironmentId, getUserProjects } from "@/lib/project/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
import { getOrganizationIdFromEnvironmentId } from "@/lib/utils/helper";
|
import { getOrganizationIdFromEnvironmentId } from "@/lib/utils/helper";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface XMTemplatePageProps {
|
interface XMTemplatePageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ArrowUpRightIcon, ChevronRightIcon, LogOutIcon } from "lucide-react";
|
import { ArrowUpRightIcon, ChevronRightIcon, LogOutIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TOrganization } from "@formbricks/types/organizations";
|
import { TOrganization } from "@formbricks/types/organizations";
|
||||||
import { TUser } from "@formbricks/types/user";
|
import { TUser } from "@formbricks/types/user";
|
||||||
import FBLogo from "@/images/formbricks-wordmark.svg";
|
import FBLogo from "@/images/formbricks-wordmark.svg";
|
||||||
@@ -27,7 +27,7 @@ interface LandingSidebarProps {
|
|||||||
export const LandingSidebar = ({ user, organization }: LandingSidebarProps) => {
|
export const LandingSidebar = ({ user, organization }: LandingSidebarProps) => {
|
||||||
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState<boolean>(false);
|
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState<boolean>(false);
|
||||||
|
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const { signOut: signOutWithAudit } = useSignOut({ id: user.id, email: user.email });
|
const { signOut: signOutWithAudit } = useSignOut({ id: user.id, email: user.email });
|
||||||
|
|
||||||
const dropdownNavigation = [
|
const dropdownNavigation = [
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
|||||||
import { getAccessFlags } from "@/lib/membership/utils";
|
import { getAccessFlags } from "@/lib/membership/utils";
|
||||||
import { getOrganizationsByUserId } from "@/lib/organization/service";
|
import { getOrganizationsByUserId } from "@/lib/organization/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { IS_POSTHOG_CONFIGURED } from "@/lib/constants";
|
|||||||
import { canUserAccessOrganization } from "@/lib/organization/auth";
|
import { canUserAccessOrganization } from "@/lib/organization/auth";
|
||||||
import { getOrganization } from "@/lib/organization/service";
|
import { getOrganization } from "@/lib/organization/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { ToasterClient } from "@/modules/ui/components/toaster-client";
|
import { ToasterClient } from "@/modules/ui/components/toaster-client";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const ProjectOnboardingLayout = async (props) => {
|
const ProjectOnboardingLayout = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import Link from "next/link";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
||||||
import { getUserProjects } from "@/lib/project/service";
|
import { getUserProjects } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface ChannelPageProps {
|
interface ChannelPageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
|||||||
import { getAccessFlags } from "@/lib/membership/utils";
|
import { getAccessFlags } from "@/lib/membership/utils";
|
||||||
import { getOrganization } from "@/lib/organization/service";
|
import { getOrganization } from "@/lib/organization/service";
|
||||||
import { getOrganizationProjectsCount } from "@/lib/project/service";
|
import { getOrganizationProjectsCount } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { getOrganizationProjectsLimit } from "@/modules/ee/license-check/lib/utils";
|
import { getOrganizationProjectsLimit } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const OnboardingLayout = async (props) => {
|
const OnboardingLayout = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import Link from "next/link";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
||||||
import { getUserProjects } from "@/lib/project/service";
|
import { getUserProjects } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface ModePageProps {
|
interface ModePageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TProjectConfigChannel,
|
TProjectConfigChannel,
|
||||||
TProjectConfigIndustry,
|
TProjectConfigIndustry,
|
||||||
@@ -59,7 +59,7 @@ export const ProjectSettings = ({
|
|||||||
const [createTeamModalOpen, setCreateTeamModalOpen] = useState(false);
|
const [createTeamModalOpen, setCreateTeamModalOpen] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const addProject = async (data: TProjectUpdateInput) => {
|
const addProject = async (data: TProjectUpdateInput) => {
|
||||||
try {
|
try {
|
||||||
const createProjectResponse = await createProjectAction({
|
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 { ProjectSettings } from "@/app/(app)/(onboarding)/organizations/[organizationId]/projects/new/settings/components/ProjectSettings";
|
||||||
import { DEFAULT_BRAND_COLOR } from "@/lib/constants";
|
import { DEFAULT_BRAND_COLOR } from "@/lib/constants";
|
||||||
import { getUserProjects } from "@/lib/project/service";
|
import { getUserProjects } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getAccessControlPermission } from "@/modules/ee/license-check/lib/utils";
|
import { getAccessControlPermission } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
import { getOrganizationAuth } from "@/modules/organization/lib/utils";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Header } from "@/modules/ui/components/header";
|
import { Header } from "@/modules/ui/components/header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface ProjectSettingsPageProps {
|
interface ProjectSettingsPageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { Confetti } from "@/modules/ui/components/confetti";
|
import { Confetti } from "@/modules/ui/components/confetti";
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ interface ConfirmationPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ConfirmationPage = ({ environmentId }: ConfirmationPageProps) => {
|
export const ConfirmationPage = ({ environmentId }: ConfirmationPageProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [showConfetti, setShowConfetti] = useState(false);
|
const [showConfetti, setShowConfetti] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowConfetti(true);
|
setShowConfetti(true);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
getOrganizationByEnvironmentId,
|
getOrganizationByEnvironmentId,
|
||||||
} from "@/lib/organization/service";
|
} from "@/lib/organization/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
||||||
import {
|
import {
|
||||||
getAccessControlPermission,
|
getAccessControlPermission,
|
||||||
@@ -21,7 +22,6 @@ import {
|
|||||||
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
|
||||||
import { LimitsReachedBanner } from "@/modules/ui/components/limits-reached-banner";
|
import { LimitsReachedBanner } from "@/modules/ui/components/limits-reached-banner";
|
||||||
import { PendingDowngradeBanner } from "@/modules/ui/components/pending-downgrade-banner";
|
import { PendingDowngradeBanner } from "@/modules/ui/components/pending-downgrade-banner";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
interface EnvironmentLayoutProps {
|
interface EnvironmentLayoutProps {
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
import { Label } from "@/modules/ui/components/label";
|
import { Label } from "@/modules/ui/components/label";
|
||||||
@@ -14,7 +14,7 @@ interface EnvironmentSwitchProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EnvironmentSwitch = ({ environment, environments }: EnvironmentSwitchProps) => {
|
export const EnvironmentSwitch = ({ environment, environments }: EnvironmentSwitchProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isEnvSwitchChecked, setIsEnvSwitchChecked] = useState(environment?.type === "development");
|
const [isEnvSwitchChecked, setIsEnvSwitchChecked] = useState(environment?.type === "development");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import {
|
import {
|
||||||
ArrowUpRightIcon,
|
ArrowUpRightIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
@@ -17,6 +16,7 @@ import Image from "next/image";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||||
import { TOrganization } from "@formbricks/types/organizations";
|
import { TOrganization } from "@formbricks/types/organizations";
|
||||||
@@ -59,7 +59,7 @@ export const MainNavigation = ({
|
|||||||
}: NavigationProps) => {
|
}: NavigationProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||||
const [isTextVisible, setIsTextVisible] = useState(true);
|
const [isTextVisible, setIsTextVisible] = useState(true);
|
||||||
const [latestVersion, setLatestVersion] = useState("");
|
const [latestVersion, setLatestVersion] = useState("");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { AlertTriangleIcon, CheckIcon, RotateCcwIcon } from "lucide-react";
|
import { AlertTriangleIcon, CheckIcon, RotateCcwIcon } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
@@ -12,7 +12,7 @@ interface WidgetStatusIndicatorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProps) => {
|
export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const stati = {
|
const stati = {
|
||||||
notImplemented: {
|
notImplemented: {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ChevronDownIcon, CircleHelpIcon, Code2Icon, Loader2 } from "lucide-react";
|
import { ChevronDownIcon, CircleHelpIcon, Code2Icon, Loader2 } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -21,7 +21,7 @@ export const EnvironmentBreadcrumb = ({
|
|||||||
environments: { id: string; type: string }[];
|
environments: { id: string; type: string }[];
|
||||||
currentEnvironment: { id: string; type: string };
|
currentEnvironment: { id: string; type: string };
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isEnvironmentDropdownOpen, setIsEnvironmentDropdownOpen] = useState(false);
|
const [isEnvironmentDropdownOpen, setIsEnvironmentDropdownOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import {
|
import {
|
||||||
BuildingIcon,
|
BuildingIcon,
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
import { CreateOrganizationModal } from "@/modules/organization/components/CreateOrganizationModal";
|
import { CreateOrganizationModal } from "@/modules/organization/components/CreateOrganizationModal";
|
||||||
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
import { BreadcrumbItem } from "@/modules/ui/components/breadcrumb";
|
||||||
@@ -43,7 +43,7 @@ export const OrganizationBreadcrumb = ({
|
|||||||
isMember,
|
isMember,
|
||||||
isOwnerOrManager,
|
isOwnerOrManager,
|
||||||
}: OrganizationBreadcrumbProps) => {
|
}: OrganizationBreadcrumbProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isOrganizationDropdownOpen, setIsOrganizationDropdownOpen] = useState(false);
|
const [isOrganizationDropdownOpen, setIsOrganizationDropdownOpen] = useState(false);
|
||||||
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState(false);
|
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState(false);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ChevronDownIcon, ChevronRightIcon, CogIcon, FolderOpenIcon, Loader2, PlusIcon } from "lucide-react";
|
import { ChevronDownIcon, ChevronRightIcon, CogIcon, FolderOpenIcon, Loader2, PlusIcon } from "lucide-react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
import { CreateProjectModal } from "@/modules/projects/components/create-project-modal";
|
import { CreateProjectModal } from "@/modules/projects/components/create-project-modal";
|
||||||
import { ProjectLimitModal } from "@/modules/projects/components/project-limit-modal";
|
import { ProjectLimitModal } from "@/modules/projects/components/project-limit-modal";
|
||||||
@@ -44,7 +44,7 @@ export const ProjectBreadcrumb = ({
|
|||||||
isAccessControlAllowed,
|
isAccessControlAllowed,
|
||||||
isEnvironmentBreadcrumbVisible,
|
isEnvironmentBreadcrumbVisible,
|
||||||
}: ProjectBreadcrumbProps) => {
|
}: ProjectBreadcrumbProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isProjectDropdownOpen, setIsProjectDropdownOpen] = useState(false);
|
const [isProjectDropdownOpen, setIsProjectDropdownOpen] = useState(false);
|
||||||
const [openCreateProjectModal, setOpenCreateProjectModal] = useState(false);
|
const [openCreateProjectModal, setOpenCreateProjectModal] = useState(false);
|
||||||
const [openLimitModal, setOpenLimitModal] = useState(false);
|
const [openLimitModal, setOpenLimitModal] = useState(false);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { TFnType, useTranslate } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Control, Controller, useForm } from "react-hook-form";
|
import { Control, Controller, useForm } from "react-hook-form";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||||
import {
|
import {
|
||||||
TIntegrationAirtable,
|
TIntegrationAirtable,
|
||||||
@@ -58,7 +59,7 @@ type AddIntegrationModalProps = {
|
|||||||
} & EditModeProps;
|
} & EditModeProps;
|
||||||
|
|
||||||
const NoBaseFoundError = () => {
|
const NoBaseFoundError = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertTitle>{t("environments.integrations.airtable.no_bases_found")}</AlertTitle>
|
<AlertTitle>{t("environments.integrations.airtable.no_bases_found")}</AlertTitle>
|
||||||
@@ -80,7 +81,7 @@ const renderQuestionSelection = ({
|
|||||||
includeCreatedAt,
|
includeCreatedAt,
|
||||||
setIncludeCreatedAt,
|
setIncludeCreatedAt,
|
||||||
}: {
|
}: {
|
||||||
t: TFnType;
|
t: TFunction;
|
||||||
selectedSurvey: TSurvey;
|
selectedSurvey: TSurvey;
|
||||||
control: Control<IntegrationModalInputs>;
|
control: Control<IntegrationModalInputs>;
|
||||||
includeVariables: boolean;
|
includeVariables: boolean;
|
||||||
@@ -153,7 +154,7 @@ export const AddIntegrationModal = ({
|
|||||||
isEditMode,
|
isEditMode,
|
||||||
defaultData,
|
defaultData,
|
||||||
}: AddIntegrationModalProps) => {
|
}: AddIntegrationModalProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [tables, setTables] = useState<TIntegrationAirtableTables["tables"]>([]);
|
const [tables, setTables] = useState<TIntegrationAirtableTables["tables"]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { Control, Controller, UseFormSetValue } from "react-hook-form";
|
import { Control, Controller, UseFormSetValue } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||||
import { Label } from "@/modules/ui/components/label";
|
import { Label } from "@/modules/ui/components/label";
|
||||||
import {
|
import {
|
||||||
@@ -30,7 +30,7 @@ export const BaseSelectDropdown = ({
|
|||||||
setValue,
|
setValue,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
}: BaseSelectProps) => {
|
}: BaseSelectProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
<Label htmlFor="base">{t("environments.integrations.airtable.airtable_base")}</Label>
|
<Label htmlFor="base">{t("environments.integrations.airtable.airtable_base")}</Label>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { Trash2Icon } from "lucide-react";
|
import { Trash2Icon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||||
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
|
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
|
||||||
@@ -30,7 +30,7 @@ interface ManageIntegrationProps {
|
|||||||
|
|
||||||
export const ManageIntegration = (props: ManageIntegrationProps) => {
|
export const ManageIntegration = (props: ManageIntegrationProps) => {
|
||||||
const { airtableIntegration, environment, environmentId, setIsConnected, surveys, airtableArray } = props;
|
const { airtableIntegration, environment, environmentId, setIsConnected, surveys, airtableArray } = props;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const tableHeaders = [
|
const tableHeaders = [
|
||||||
t("common.survey"),
|
t("common.survey"),
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { getAirtableTables } from "@/lib/airtable/service";
|
|||||||
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
|
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
|
||||||
import { getIntegrations } from "@/lib/integration/service";
|
import { getIntegrations } from "@/lib/integration/service";
|
||||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TIntegrationGoogleSheets,
|
TIntegrationGoogleSheets,
|
||||||
TIntegrationGoogleSheetsConfigData,
|
TIntegrationGoogleSheetsConfigData,
|
||||||
@@ -56,7 +56,7 @@ export const AddIntegrationModal = ({
|
|||||||
googleSheetIntegration,
|
googleSheetIntegration,
|
||||||
selectedIntegration,
|
selectedIntegration,
|
||||||
}: AddIntegrationModalProps) => {
|
}: AddIntegrationModalProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const integrationData: TIntegrationGoogleSheetsConfigData = {
|
const integrationData: TIntegrationGoogleSheetsConfigData = {
|
||||||
spreadsheetId: "",
|
spreadsheetId: "",
|
||||||
spreadsheetName: "",
|
spreadsheetName: "",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { Trash2Icon } from "lucide-react";
|
import { Trash2Icon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import {
|
import {
|
||||||
TIntegrationGoogleSheets,
|
TIntegrationGoogleSheets,
|
||||||
@@ -34,7 +34,7 @@ export const ManageIntegration = ({
|
|||||||
setSelectedIntegration,
|
setSelectedIntegration,
|
||||||
locale,
|
locale,
|
||||||
}: ManageIntegrationProps) => {
|
}: ManageIntegrationProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||||
let integrationArray: TIntegrationGoogleSheetsConfigData[] = [];
|
let integrationArray: TIntegrationGoogleSheetsConfigData[] = [];
|
||||||
if (googleSheetIntegration?.config.data) {
|
if (googleSheetIntegration?.config.data) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
|
|
||||||
const Loading = () => {
|
const Loading = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="mt-6 p-6">
|
<div className="mt-6 p-6">
|
||||||
<GoBackButton />
|
<GoBackButton />
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import {
|
|||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
import { getIntegrations } from "@/lib/integration/service";
|
import { getIntegrations } from "@/lib/integration/service";
|
||||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TIntegrationInput } from "@formbricks/types/integration";
|
import { TIntegrationInput } from "@formbricks/types/integration";
|
||||||
import {
|
import {
|
||||||
TIntegrationNotion,
|
TIntegrationNotion,
|
||||||
@@ -56,7 +56,7 @@ export const AddIntegrationModal = ({
|
|||||||
databases,
|
databases,
|
||||||
selectedIntegration,
|
selectedIntegration,
|
||||||
}: AddIntegrationModalProps) => {
|
}: AddIntegrationModalProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const { handleSubmit } = useForm();
|
const { handleSubmit } = useForm();
|
||||||
const [selectedDatabase, setSelectedDatabase] = useState<TIntegrationNotionDatabase | null>();
|
const [selectedDatabase, setSelectedDatabase] = useState<TIntegrationNotionDatabase | null>();
|
||||||
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { RefreshCcwIcon, Trash2Icon } from "lucide-react";
|
import { RefreshCcwIcon, Trash2Icon } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TIntegrationNotion, TIntegrationNotionConfigData } from "@formbricks/types/integration/notion";
|
import { TIntegrationNotion, TIntegrationNotionConfigData } from "@formbricks/types/integration/notion";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
@@ -36,7 +36,7 @@ export const ManageIntegration = ({
|
|||||||
locale,
|
locale,
|
||||||
handleNotionAuthorization,
|
handleNotionAuthorization,
|
||||||
}: ManageIntegrationProps) => {
|
}: ManageIntegrationProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||||
const [isDeleting, setisDeleting] = useState(false);
|
const [isDeleting, setisDeleting] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
|
|
||||||
const Loading = () => {
|
const Loading = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="mt-6 p-6">
|
<div className="mt-6 p-6">
|
||||||
<GoBackButton />
|
<GoBackButton />
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import {
|
|||||||
import { getIntegrationByType } from "@/lib/integration/service";
|
import { getIntegrationByType } from "@/lib/integration/service";
|
||||||
import { getNotionDatabases } from "@/lib/notion/service";
|
import { getNotionDatabases } from "@/lib/notion/service";
|
||||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TFnType } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { TIntegrationType } from "@formbricks/types/integration";
|
import { TIntegrationType } from "@formbricks/types/integration";
|
||||||
@@ -14,14 +14,14 @@ import SlackLogo from "@/images/slacklogo.png";
|
|||||||
import WebhookLogo from "@/images/webhook.png";
|
import WebhookLogo from "@/images/webhook.png";
|
||||||
import ZapierLogo from "@/images/zapier-small.png";
|
import ZapierLogo from "@/images/zapier-small.png";
|
||||||
import { getIntegrations } from "@/lib/integration/service";
|
import { getIntegrations } from "@/lib/integration/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
|
||||||
import { Card } from "@/modules/ui/components/integration-card";
|
import { Card } from "@/modules/ui/components/integration-card";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
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 === 1) return `1 ${type}`;
|
||||||
if (count === 0) return t("common.not_connected");
|
if (count === 0) return t("common.not_connected");
|
||||||
return `${count} ${type}s`;
|
return `${count} ${type}s`;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { CircleHelpIcon } from "lucide-react";
|
import { CircleHelpIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TIntegrationItem } from "@formbricks/types/integration";
|
import { TIntegrationItem } from "@formbricks/types/integration";
|
||||||
import {
|
import {
|
||||||
TIntegrationSlack,
|
TIntegrationSlack,
|
||||||
@@ -54,7 +54,7 @@ export const AddChannelMappingModal = ({
|
|||||||
selectedIntegration,
|
selectedIntegration,
|
||||||
}: AddChannelMappingModalProps) => {
|
}: AddChannelMappingModalProps) => {
|
||||||
const { handleSubmit } = useForm();
|
const { handleSubmit } = useForm();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [selectedQuestions, setSelectedQuestions] = useState<string[]>([]);
|
const [selectedQuestions, setSelectedQuestions] = useState<string[]>([]);
|
||||||
const [isLinkingChannel, setIsLinkingChannel] = useState(false);
|
const [isLinkingChannel, setIsLinkingChannel] = useState(false);
|
||||||
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
const [selectedSurvey, setSelectedSurvey] = useState<TSurvey | null>(null);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { T, useTranslate } from "@tolgee/react";
|
|
||||||
import { Trash2Icon } from "lucide-react";
|
import { Trash2Icon } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TIntegrationSlack, TIntegrationSlackConfigData } from "@formbricks/types/integration/slack";
|
import { TIntegrationSlack, TIntegrationSlackConfigData } from "@formbricks/types/integration/slack";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
@@ -39,7 +39,7 @@ export const ManageIntegration = ({
|
|||||||
handleSlackAuthorization,
|
handleSlackAuthorization,
|
||||||
locale,
|
locale,
|
||||||
}: ManageIntegrationProps) => {
|
}: ManageIntegrationProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
|
||||||
const [isDeleting, setisDeleting] = useState(false);
|
const [isDeleting, setisDeleting] = useState(false);
|
||||||
let integrationArray: TIntegrationSlackConfigData[] = [];
|
let integrationArray: TIntegrationSlackConfigData[] = [];
|
||||||
@@ -76,9 +76,9 @@ export const ManageIntegration = ({
|
|||||||
{showReconnectButton && (
|
{showReconnectButton && (
|
||||||
<div className="mb-4 flex w-full items-center justify-between space-x-4">
|
<div className="mb-4 flex w-full items-center justify-between space-x-4">
|
||||||
<p className="text-amber-700">
|
<p className="text-amber-700">
|
||||||
<T
|
<Trans
|
||||||
keyName="environments.integrations.slack.slack_reconnect_button_description"
|
i18nKey="environments.integrations.slack.slack_reconnect_button_description"
|
||||||
params={{ b: <b /> }}
|
components={{ b: <b /> }}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
<Button onClick={handleSlackAuthorization} variant="secondary">
|
<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 { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
||||||
import { getIntegrationByType } from "@/lib/integration/service";
|
import { getIntegrationByType } from "@/lib/integration/service";
|
||||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
import { GoBackButton } from "@/modules/ui/components/go-back-button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||||
|
|
||||||
interface AccountSettingsNavbarProps {
|
interface AccountSettingsNavbarProps {
|
||||||
@@ -12,7 +12,7 @@ interface AccountSettingsNavbarProps {
|
|||||||
|
|
||||||
export const AccountSettingsNavbar = ({ environmentId, activeId, loading }: AccountSettingsNavbarProps) => {
|
export const AccountSettingsNavbar = ({ environmentId, activeId, loading }: AccountSettingsNavbarProps) => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{
|
{
|
||||||
id: "profile",
|
id: "profile",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const AccountSettingsLayout = async (props) => {
|
const AccountSettingsLayout = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { HelpCircleIcon, UsersIcon } from "lucide-react";
|
import { HelpCircleIcon, UsersIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TUser } from "@formbricks/types/user";
|
import { TUser } from "@formbricks/types/user";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
||||||
import { Membership } from "../types";
|
import { Membership } from "../types";
|
||||||
@@ -23,7 +23,7 @@ export const EditAlerts = ({
|
|||||||
autoDisableNotificationType,
|
autoDisableNotificationType,
|
||||||
autoDisableNotificationElementId,
|
autoDisableNotificationElementId,
|
||||||
}: EditAlertsProps) => {
|
}: EditAlertsProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{memberships.map((membership) => (
|
{memberships.map((membership) => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { SlackIcon } from "@/modules/ui/components/icons";
|
import { SlackIcon } from "@/modules/ui/components/icons";
|
||||||
|
|
||||||
interface IntegrationsTipProps {
|
interface IntegrationsTipProps {
|
||||||
@@ -8,7 +8,7 @@ interface IntegrationsTipProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const IntegrationsTip = ({ environmentId }: IntegrationsTipProps) => {
|
export const IntegrationsTip = ({ environmentId }: IntegrationsTipProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div>
|
<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">
|
<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";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TUserNotificationSettings } from "@formbricks/types/user";
|
import { TUserNotificationSettings } from "@formbricks/types/user";
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||||
import { Switch } from "@/modules/ui/components/switch";
|
import { Switch } from "@/modules/ui/components/switch";
|
||||||
@@ -25,7 +25,7 @@ export const NotificationSwitch = ({
|
|||||||
autoDisableNotificationElementId,
|
autoDisableNotificationElementId,
|
||||||
}: NotificationSwitchProps) => {
|
}: NotificationSwitchProps) => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isChecked =
|
const isChecked =
|
||||||
notificationType === "unsubscribedOrganizationIds"
|
notificationType === "unsubscribedOrganizationIds"
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||||
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
|
|
||||||
const Loading = () => {
|
const Loading = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
title: t("environments.settings.notifications.email_alerts_surveys"),
|
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 { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||||
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
import { EditAlerts } from "./components/EditAlerts";
|
import { EditAlerts } from "./components/EditAlerts";
|
||||||
import { IntegrationsTip } from "./components/IntegrationsTip";
|
import { IntegrationsTip } from "./components/IntegrationsTip";
|
||||||
import type { Membership } from "./types";
|
import type { Membership } from "./types";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TUser } from "@formbricks/types/user";
|
import { TUser } from "@formbricks/types/user";
|
||||||
import { DisableTwoFactorModal } from "@/modules/ee/two-factor-auth/components/disable-two-factor-modal";
|
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";
|
import { EnableTwoFactorModal } from "@/modules/ee/two-factor-auth/components/enable-two-factor-modal";
|
||||||
@@ -12,7 +12,7 @@ interface AccountSecurityProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AccountSecurity = ({ user }: AccountSecurityProps) => {
|
export const AccountSecurity = ({ user }: AccountSecurityProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [twoFactorModalOpen, setTwoFactorModalOpen] = useState(false);
|
const [twoFactorModalOpen, setTwoFactorModalOpen] = useState(false);
|
||||||
const [disableTwoFactorModalOpen, setDisableTwoFactorModalOpen] = useState(false);
|
const [disableTwoFactorModalOpen, setDisableTwoFactorModalOpen] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import type { Session } from "next-auth";
|
import type { Session } from "next-auth";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TOrganization } from "@formbricks/types/organizations";
|
import { TOrganization } from "@formbricks/types/organizations";
|
||||||
import { TUser } from "@formbricks/types/user";
|
import { TUser } from "@formbricks/types/user";
|
||||||
import { DeleteAccountModal } from "@/modules/account/components/DeleteAccountModal";
|
import { DeleteAccountModal } from "@/modules/account/components/DeleteAccountModal";
|
||||||
@@ -24,7 +24,7 @@ export const DeleteAccount = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [isModalOpen, setModalOpen] = useState(false);
|
const [isModalOpen, setModalOpen] = useState(false);
|
||||||
const isDeleteDisabled = !isMultiOrgEnabled && organizationsWithSingleOwner.length > 0;
|
const isDeleteDisabled = !isMultiOrgEnabled && organizationsWithSingleOwner.length > 0;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { ChevronDownIcon } from "lucide-react";
|
import { ChevronDownIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { TUser, TUserUpdateInput, ZUser, ZUserEmail } from "@formbricks/types/user";
|
import { TUser, TUserUpdateInput, ZUser, ZUserEmail } from "@formbricks/types/user";
|
||||||
import { PasswordConfirmationModal } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/password-confirmation-modal";
|
import { PasswordConfirmationModal } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/password-confirmation-modal";
|
||||||
@@ -42,7 +42,7 @@ export const EditProfileDetailsForm = ({
|
|||||||
isPasswordResetEnabled,
|
isPasswordResetEnabled,
|
||||||
emailVerificationDisabled,
|
emailVerificationDisabled,
|
||||||
}: IEditProfileDetailsFormProps) => {
|
}: IEditProfileDetailsFormProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const form = useForm<TEditProfileNameForm>({
|
const form = useForm<TEditProfileNameForm>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ZUserPassword } from "@formbricks/types/user";
|
import { ZUserPassword } from "@formbricks/types/user";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
@@ -39,7 +39,7 @@ export const PasswordConfirmationModal = ({
|
|||||||
newEmail,
|
newEmail,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
}: PasswordConfirmationModalProps) => {
|
}: PasswordConfirmationModalProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const form = useForm<FormValues>({
|
const form = useForm<FormValues>({
|
||||||
resolver: zodResolver(PasswordConfirmationSchema),
|
resolver: zodResolver(PasswordConfirmationSchema),
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||||
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
|
|
||||||
const Loading = () => {
|
const Loading = () => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
title: t("environments.settings.profile.personal_information"),
|
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 { EMAIL_VERIFICATION_DISABLED, IS_FORMBRICKS_CLOUD, PASSWORD_RESET_DISABLED } from "@/lib/constants";
|
||||||
import { getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
|
import { getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getIsMultiOrgEnabled, getIsTwoFactorAuthEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsMultiOrgEnabled, getIsTwoFactorAuthEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
import { SettingsCard } from "../../components/SettingsCard";
|
import { SettingsCard } from "../../components/SettingsCard";
|
||||||
import { DeleteAccount } from "./components/DeleteAccount";
|
import { DeleteAccount } from "./components/DeleteAccount";
|
||||||
import { EditProfileDetailsForm } from "./components/EditProfileDetailsForm";
|
import { EditProfileDetailsForm } from "./components/EditProfileDetailsForm";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Loading = async () => {
|
const Loading = async () => {
|
||||||
const t = await getTranslate();
|
const t = await getTranslate();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||||
import { getAccessFlags } from "@/lib/membership/utils";
|
import { getAccessFlags } from "@/lib/membership/utils";
|
||||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||||
@@ -24,7 +24,7 @@ export const OrganizationSettingsNavbar = ({
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { isMember, isOwner } = getAccessFlags(membershipRole);
|
const { isMember, isOwner } = getAccessFlags(membershipRole);
|
||||||
const isPricingDisabled = isMember;
|
const isPricingDisabled = isMember;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Loading = async () => {
|
const Loading = async () => {
|
||||||
const t = await getTranslate();
|
const t = await getTranslate();
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import Link from "next/link";
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Dispatch, SetStateAction, useState } from "react";
|
import { Dispatch, SetStateAction, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TOrganization } from "@formbricks/types/organizations";
|
import { TOrganization } from "@formbricks/types/organizations";
|
||||||
import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
||||||
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
|
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
|
||||||
@@ -25,7 +25,7 @@ export const DeleteOrganization = ({
|
|||||||
}: DeleteOrganizationProps) => {
|
}: DeleteOrganizationProps) => {
|
||||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const handleDeleteOrganization = async () => {
|
const handleDeleteOrganization = async () => {
|
||||||
@@ -100,7 +100,7 @@ const DeleteOrganizationModal = ({
|
|||||||
isDeleting,
|
isDeleting,
|
||||||
}: DeleteOrganizationModalProps) => {
|
}: DeleteOrganizationModalProps) => {
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
setInputValue(e.target.value);
|
setInputValue(e.target.value);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { SubmitHandler, useForm } from "react-hook-form";
|
import { SubmitHandler, useForm } from "react-hook-form";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||||
import { TOrganization, ZOrganization } from "@formbricks/types/organizations";
|
import { TOrganization, ZOrganization } from "@formbricks/types/organizations";
|
||||||
@@ -32,7 +32,7 @@ const ZEditOrganizationNameFormSchema = ZOrganization.pick({ name: true });
|
|||||||
type EditOrganizationNameForm = z.infer<typeof ZEditOrganizationNameFormSchema>;
|
type EditOrganizationNameForm = z.infer<typeof ZEditOrganizationNameFormSchema>;
|
||||||
|
|
||||||
export const EditOrganizationNameForm = ({ organization, membershipRole }: EditOrganizationNameProps) => {
|
export const EditOrganizationNameForm = ({ organization, membershipRole }: EditOrganizationNameProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const form = useForm<EditOrganizationNameForm>({
|
const form = useForm<EditOrganizationNameForm>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: organization.name,
|
name: organization.name,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
import { LoadingCard } from "@/app/(app)/components/LoadingCard";
|
||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Loading = async () => {
|
const Loading = async () => {
|
||||||
const t = await getTranslate();
|
const t = await getTranslate();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
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 { FB_LOGO_URL, IS_FORMBRICKS_CLOUD, IS_STORAGE_CONFIGURED } from "@/lib/constants";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getIsMultiOrgEnabled, getWhiteLabelPermission } from "@/modules/ee/license-check/lib/utils";
|
import { getIsMultiOrgEnabled, getWhiteLabelPermission } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { EmailCustomizationSettings } from "@/modules/ee/whitelabel/email-customization/components/email-customization-settings";
|
import { EmailCustomizationSettings } from "@/modules/ee/whitelabel/email-customization/components/email-customization-settings";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
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 { IdBadge } from "@/modules/ui/components/id-badge";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
import { SettingsCard } from "../../components/SettingsCard";
|
import { SettingsCard } from "../../components/SettingsCard";
|
||||||
import { DeleteOrganization } from "./components/DeleteOrganization";
|
import { DeleteOrganization } from "./components/DeleteOrganization";
|
||||||
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Layout = async (props) => {
|
const Layout = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
import { Badge } from "@/modules/ui/components/badge";
|
import { Badge } from "@/modules/ui/components/badge";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
@@ -31,7 +31,7 @@ export const SettingsCard = ({
|
|||||||
className?: string;
|
className?: string;
|
||||||
buttonInfo?: ButtonInfo;
|
buttonInfo?: ButtonInfo;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { Unplug } from "lucide-react";
|
import { Unplug } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ interface TEmptyAppSurveysProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EmptyAppSurveys = ({ environment }: TEmptyAppSurveysProps) => {
|
export const EmptyAppSurveys = ({ environment }: TEmptyAppSurveysProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full items-center justify-center gap-8 bg-slate-100 py-12">
|
<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">
|
<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";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon, PresentationIcon } from "lucide-react";
|
import { InboxIcon, PresentationIcon } from "lucide-react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
import { revalidateSurveyIdPath } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
|
import { revalidateSurveyIdPath } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
|
||||||
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
import { SecondaryNavigation } from "@/modules/ui/components/secondary-navigation";
|
||||||
@@ -19,7 +19,7 @@ export const SurveyAnalysisNavigation = ({
|
|||||||
activeId,
|
activeId,
|
||||||
}: SurveyAnalysisNavigationProps) => {
|
}: SurveyAnalysisNavigationProps) => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const url = `/environments/${environmentId}/surveys/${survey.id}`;
|
const url = `/environments/${environmentId}/surveys/${survey.id}`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { TFnType, useTranslate } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSurveyQuota } from "@formbricks/types/quota";
|
import { TSurveyQuota } from "@formbricks/types/quota";
|
||||||
import { TResponseDataValue, TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
import { TResponseDataValue, TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
||||||
@@ -83,7 +84,7 @@ export const extractResponseData = (response: TResponseWithQuotas, survey: TSurv
|
|||||||
export const mapResponsesToTableData = (
|
export const mapResponsesToTableData = (
|
||||||
responses: TResponseWithQuotas[],
|
responses: TResponseWithQuotas[],
|
||||||
survey: TSurvey,
|
survey: TSurvey,
|
||||||
t: TFnType
|
t: TFunction
|
||||||
): TResponseTableData[] => {
|
): TResponseTableData[] => {
|
||||||
return responses.map((response) => ({
|
return responses.map((response) => ({
|
||||||
responseData: extractResponseData(response, survey),
|
responseData: extractResponseData(response, survey),
|
||||||
@@ -124,7 +125,7 @@ export const ResponseDataView: React.FC<ResponseDataViewProps> = ({
|
|||||||
isQuotasAllowed,
|
isQuotasAllowed,
|
||||||
quotas,
|
quotas,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const data = mapResponsesToTableData(responses, survey, t);
|
const data = mapResponsesToTableData(responses, survey, t);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import { SortableContext, arrayMove, horizontalListSortingStrategy } from "@dnd-
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { VisibilityState, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
import { VisibilityState, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSurveyQuota } from "@formbricks/types/quota";
|
import { TSurveyQuota } from "@formbricks/types/quota";
|
||||||
import { TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
import { TResponseTableData, TResponseWithQuotas } from "@formbricks/types/responses";
|
||||||
@@ -74,7 +74,7 @@ export const ResponseTable = ({
|
|||||||
isQuotasAllowed,
|
isQuotasAllowed,
|
||||||
quotas,
|
quotas,
|
||||||
}: ResponseTableProps) => {
|
}: ResponseTableProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = useState({});
|
const [rowSelection, setRowSelection] = useState({});
|
||||||
const [isTableSettingsModalOpen, setIsTableSettingsModalOpen] = useState(false);
|
const [isTableSettingsModalOpen, setIsTableSettingsModalOpen] = useState(false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
import { TFnType } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import { CircleHelpIcon, EyeOffIcon, MailIcon, TagIcon } from "lucide-react";
|
import { CircleHelpIcon, EyeOffIcon, MailIcon, TagIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { TResponseTableData } from "@formbricks/types/responses";
|
import { TResponseTableData } from "@formbricks/types/responses";
|
||||||
@@ -32,7 +32,7 @@ const getQuestionColumnsData = (
|
|||||||
question: TSurveyQuestion,
|
question: TSurveyQuestion,
|
||||||
survey: TSurvey,
|
survey: TSurvey,
|
||||||
isExpanded: boolean,
|
isExpanded: boolean,
|
||||||
t: TFnType
|
t: TFunction
|
||||||
): ColumnDef<TResponseTableData>[] => {
|
): ColumnDef<TResponseTableData>[] => {
|
||||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||||
const addressFields = ["addressLine1", "addressLine2", "city", "state", "zip", "country"];
|
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>[] = [];
|
const metadataColumns: ColumnDef<TResponseTableData>[] = [];
|
||||||
|
|
||||||
METADATA_FIELDS.forEach((label) => {
|
METADATA_FIELDS.forEach((label) => {
|
||||||
@@ -262,7 +262,7 @@ export const generateResponseTableColumns = (
|
|||||||
survey: TSurvey,
|
survey: TSurvey,
|
||||||
isExpanded: boolean,
|
isExpanded: boolean,
|
||||||
isReadOnly: boolean,
|
isReadOnly: boolean,
|
||||||
t: TFnType,
|
t: TFunction,
|
||||||
showQuotasColumn: boolean
|
showQuotasColumn: boolean
|
||||||
): ColumnDef<TResponseTableData>[] => {
|
): ColumnDef<TResponseTableData>[] => {
|
||||||
const questionColumns = survey.questions.flatMap((question) =>
|
const questionColumns = survey.questions.flatMap((question) =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TFnType } from "@tolgee/react";
|
import { TFunction } from "i18next";
|
||||||
import { capitalize } from "lodash";
|
import { capitalize } from "lodash";
|
||||||
import {
|
import {
|
||||||
AirplayIcon,
|
AirplayIcon,
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { TResponseMeta } from "@formbricks/types/responses";
|
import { TResponseMeta } from "@formbricks/types/responses";
|
||||||
|
|
||||||
export const getAddressFieldLabel = (field: string, t: TFnType) => {
|
export const getAddressFieldLabel = (field: string, t: TFunction) => {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case "addressLine1":
|
case "addressLine1":
|
||||||
return t("environments.surveys.responses.address_line_1");
|
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) {
|
switch (field) {
|
||||||
case "firstName":
|
case "firstName":
|
||||||
return t("environments.surveys.responses.first_name");
|
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) {
|
switch (label) {
|
||||||
case "action":
|
case "action":
|
||||||
return t("common.action");
|
return t("common.action");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { getSurvey } from "@/lib/survey/service";
|
|||||||
import { getTagsByEnvironmentId } from "@/lib/tag/service";
|
import { getTagsByEnvironmentId } from "@/lib/tag/service";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
import { findMatchingLocale } from "@/lib/utils/locale";
|
import { findMatchingLocale } from "@/lib/utils/locale";
|
||||||
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { getSegments } from "@/modules/ee/contacts/segments/lib/segments";
|
import { getSegments } from "@/modules/ee/contacts/segments/lib/segments";
|
||||||
import { getIsContactsEnabled, getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsContactsEnabled, getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { getQuotas } from "@/modules/ee/quotas/lib/quotas";
|
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 { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import { getTranslate } from "@/tolgee/server";
|
|
||||||
|
|
||||||
const Page = async (props) => {
|
const Page = async (props) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
@@ -18,7 +18,7 @@ interface AddressSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AddressSummary = ({ questionSummary, environmentId, survey, locale }: AddressSummaryProps) => {
|
export const AddressSummary = ({ questionSummary, environmentId, survey, locale }: AddressSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||||
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys/types";
|
||||||
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
||||||
import { convertFloatToNDecimal } from "../lib/utils";
|
import { convertFloatToNDecimal } from "../lib/utils";
|
||||||
@@ -13,7 +13,7 @@ interface CTASummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CTASummary = ({ questionSummary, survey }: CTASummaryProps) => {
|
export const CTASummary = ({ questionSummary, survey }: CTASummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys/types";
|
||||||
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
||||||
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
import { ProgressBar } from "@/modules/ui/components/progress-bar";
|
||||||
@@ -13,7 +13,7 @@ interface CalSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CalSummary = ({ questionSummary, survey }: CalSummaryProps) => {
|
export const CalSummary = ({ questionSummary, survey }: CalSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -25,7 +25,7 @@ interface ConsentSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ConsentSummary = ({ questionSummary, survey, setFilter }: ConsentSummaryProps) => {
|
export const ConsentSummary = ({ questionSummary, survey, setFilter }: ConsentSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const summaryItems = [
|
const summaryItems = [
|
||||||
{
|
{
|
||||||
title: t("common.accepted"),
|
title: t("common.accepted"),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryContactInfo } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryContactInfo } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
@@ -23,7 +23,7 @@ export const ContactInfoSummary = ({
|
|||||||
survey,
|
survey,
|
||||||
locale,
|
locale,
|
||||||
}: ContactInfoSummaryProps) => {
|
}: ContactInfoSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||||
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
<QuestionSummaryHeader questionSummary={questionSummary} survey={survey} />
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
@@ -25,7 +25,7 @@ export const DateQuestionSummary = ({
|
|||||||
survey,
|
survey,
|
||||||
locale,
|
locale,
|
||||||
}: DateQuestionSummary) => {
|
}: DateQuestionSummary) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { DownloadIcon, FileIcon } from "lucide-react";
|
import { DownloadIcon, FileIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
@@ -27,7 +27,7 @@ export const FileUploadSummary = ({
|
|||||||
locale,
|
locale,
|
||||||
}: FileUploadSummaryProps) => {
|
}: FileUploadSummaryProps) => {
|
||||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
||||||
setVisibleResponses((prevVisibleResponses) =>
|
setVisibleResponses((prevVisibleResponses) =>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon, Link, MessageSquareTextIcon } from "lucide-react";
|
import { InboxIcon, Link, MessageSquareTextIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSurveyQuestionSummaryHiddenFields } from "@formbricks/types/surveys/types";
|
import { TSurveyQuestionSummaryHiddenFields } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
@@ -19,7 +19,7 @@ interface HiddenFieldsSummaryProps {
|
|||||||
|
|
||||||
export const HiddenFieldsSummary = ({ environment, questionSummary, locale }: HiddenFieldsSummaryProps) => {
|
export const HiddenFieldsSummary = ({ environment, questionSummary, locale }: HiddenFieldsSummaryProps) => {
|
||||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
// Increase the number of visible responses by 10, not exceeding the total number of responses
|
||||||
setVisibleResponses((prevVisibleResponses) =>
|
setVisibleResponses((prevVisibleResponses) =>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -24,7 +24,7 @@ interface MatrixQuestionSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MatrixQuestionSummary = ({ questionSummary, survey, setFilter }: MatrixQuestionSummaryProps) => {
|
export const MatrixQuestionSummary = ({ questionSummary, survey, setFilter }: MatrixQuestionSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const getOpacityLevel = (percentage: number): string => {
|
const getOpacityLevel = (percentage: number): string => {
|
||||||
const parsedPercentage = percentage;
|
const parsedPercentage = percentage;
|
||||||
const opacity = parsedPercentage * 0.75 + 15;
|
const opacity = parsedPercentage * 0.75 + 15;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Fragment, useState } from "react";
|
import { Fragment, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -42,7 +42,7 @@ export const MultipleChoiceSummary = ({
|
|||||||
survey,
|
survey,
|
||||||
setFilter,
|
setFilter,
|
||||||
}: MultipleChoiceSummaryProps) => {
|
}: MultipleChoiceSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [visibleOtherResponses, setVisibleOtherResponses] = useState(10);
|
const [visibleOtherResponses, setVisibleOtherResponses] = useState(10);
|
||||||
const otherValue = questionSummary.question.choices.find((choice) => choice.id === "other")?.label.default;
|
const otherValue = questionSummary.question.choices.find((choice) => choice.id === "other")?.label.default;
|
||||||
// sort by count and transform to array
|
// sort by count and transform to array
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -25,7 +25,7 @@ interface NPSSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const NPSSummary = ({ questionSummary, survey, setFilter }: NPSSummaryProps) => {
|
export const NPSSummary = ({ questionSummary, survey, setFilter }: NPSSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const applyFilter = (group: string) => {
|
const applyFilter = (group: string) => {
|
||||||
const filters = {
|
const filters = {
|
||||||
promoters: {
|
promoters: {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
@@ -21,7 +21,7 @@ interface OpenTextSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const OpenTextSummary = ({ questionSummary, environmentId, survey, locale }: OpenTextSummaryProps) => {
|
export const OpenTextSummary = ({ questionSummary, environmentId, survey, locale }: OpenTextSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -30,7 +30,7 @@ interface PictureChoiceSummaryProps {
|
|||||||
|
|
||||||
export const PictureChoiceSummary = ({ questionSummary, survey, setFilter }: PictureChoiceSummaryProps) => {
|
export const PictureChoiceSummary = ({ questionSummary, survey, setFilter }: PictureChoiceSummaryProps) => {
|
||||||
const results = questionSummary.choices;
|
const results = questionSummary.choices;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
import type { JSX } from "react";
|
import type { JSX } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys/types";
|
||||||
import { getTextContent } from "@formbricks/types/surveys/validation";
|
import { getTextContent } from "@formbricks/types/surveys/validation";
|
||||||
import { recallToHeadline } from "@/lib/utils/recall";
|
import { recallToHeadline } from "@/lib/utils/recall";
|
||||||
@@ -23,7 +23,7 @@ export const QuestionSummaryHeader = ({
|
|||||||
showResponses = true,
|
showResponses = true,
|
||||||
survey,
|
survey,
|
||||||
}: HeadProps) => {
|
}: HeadProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const questionType = getQuestionTypes(t).find((type) => type.id === questionSummary.question.type);
|
const questionType = getQuestionTypes(t).find((type) => type.id === questionSummary.question.type);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionSummaryRanking } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionSummaryRanking } from "@formbricks/types/surveys/types";
|
||||||
import { getChoiceIdByValue } from "@/lib/response/utils";
|
import { getChoiceIdByValue } from "@/lib/response/utils";
|
||||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||||
@@ -12,7 +12,7 @@ interface RankingSummaryProps {
|
|||||||
|
|
||||||
export const RankingSummary = ({ questionSummary, survey }: RankingSummaryProps) => {
|
export const RankingSummary = ({ questionSummary, survey }: RankingSummaryProps) => {
|
||||||
// sort by count and transform to array
|
// sort by count and transform to array
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const results = Object.values(questionSummary.choices).sort((a, b) => {
|
const results = Object.values(questionSummary.choices).sort((a, b) => {
|
||||||
return a.avgRanking - b.avgRanking; // Sort by count
|
return a.avgRanking - b.avgRanking; // Sort by count
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { CircleSlash2, SmileIcon, StarIcon } from "lucide-react";
|
import { CircleSlash2, SmileIcon, StarIcon } from "lucide-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
TI18nString,
|
TI18nString,
|
||||||
TSurvey,
|
TSurvey,
|
||||||
@@ -28,7 +28,7 @@ interface RatingSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const RatingSummary = ({ questionSummary, survey, setFilter }: RatingSummaryProps) => {
|
export const RatingSummary = ({ questionSummary, survey, setFilter }: RatingSummaryProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const getIconBasedOnScale = useMemo(() => {
|
const getIconBasedOnScale = useMemo(() => {
|
||||||
const scale = questionSummary.question.scale;
|
const scale = questionSummary.question.scale;
|
||||||
if (scale === "number") return <CircleSlash2 className="h-4 w-4" />;
|
if (scale === "number") return <CircleSlash2 className="h-4 w-4" />;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
import { Confetti } from "@/modules/ui/components/confetti";
|
import { Confetti } from "@/modules/ui/components/confetti";
|
||||||
@@ -14,7 +14,7 @@ interface SummaryMetadataProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SuccessMessage = ({ environment, survey }: SummaryMetadataProps) => {
|
export const SuccessMessage = ({ environment, survey }: SummaryMetadataProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [confetti, setConfetti] = useState(false);
|
const [confetti, setConfetti] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { TimerIcon } from "lucide-react";
|
import { TimerIcon } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurvey, TSurveyQuestionType, TSurveySummary } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveyQuestionType, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||||
import { recallToHeadline } from "@/lib/utils/recall";
|
import { recallToHeadline } from "@/lib/utils/recall";
|
||||||
import { formatTextWithSlashes } from "@/modules/survey/editor/lib/utils";
|
import { formatTextWithSlashes } from "@/modules/survey/editor/lib/utils";
|
||||||
@@ -14,7 +14,7 @@ interface SummaryDropOffsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SummaryDropOffs = ({ dropOff, survey }: SummaryDropOffsProps) => {
|
export const SummaryDropOffs = ({ dropOff, survey }: SummaryDropOffsProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const getIcon = (questionType: TSurveyQuestionType) => {
|
const getIcon = (questionType: TSurveyQuestionType) => {
|
||||||
const Icon = getQuestionIcon(questionType, t);
|
const Icon = getQuestionIcon(questionType, t);
|
||||||
return <Icon className="mt-[3px] h-5 w-5 shrink-0 text-slate-600" />;
|
return <Icon className="mt-[3px] h-5 w-5 shrink-0 text-slate-600" />;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TI18nString, TSurveyQuestionId, TSurveySummary } from "@formbricks/types/surveys/types";
|
import { TI18nString, TSurveyQuestionId, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||||
import { TSurveyQuestionTypeEnum } 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) => {
|
export const SummaryList = ({ summary, environment, responseCount, survey, locale }: SummaryListProps) => {
|
||||||
const { setSelectedFilter, selectedFilter } = useResponseFilter();
|
const { setSelectedFilter, selectedFilter } = useResponseFilter();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const setFilter = (
|
const setFilter = (
|
||||||
questionId: TSurveyQuestionId,
|
questionId: TSurveyQuestionId,
|
||||||
label: TI18nString,
|
label: TI18nString,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
import { TSurveySummary } from "@formbricks/types/surveys/types";
|
||||||
import { InteractiveCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/interactive-card";
|
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";
|
import { StatCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/stat-card";
|
||||||
@@ -48,7 +48,7 @@ export const SummaryMetadata = ({
|
|||||||
quotasCompleted,
|
quotasCompleted,
|
||||||
quotasCompletedPercentage,
|
quotasCompletedPercentage,
|
||||||
} = surveySummary;
|
} = surveySummary;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
|
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
|
||||||
|
|
||||||
const handleTabChange = (val: "dropOffs" | "quotas") => {
|
const handleTabChange = (val: "dropOffs" | "quotas") => {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import { BellRing, Eye, ListRestart, SquarePenIcon } from "lucide-react";
|
import { BellRing, Eye, ListRestart, SquarePenIcon } from "lucide-react";
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSegment } from "@formbricks/types/segment";
|
import { TSegment } from "@formbricks/types/segment";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
@@ -54,7 +54,7 @@ export const SurveyAnalysisCTA = ({
|
|||||||
isFormbricksCloud,
|
isFormbricksCloud,
|
||||||
isStorageConfigured,
|
isStorageConfigured,
|
||||||
}: SurveyAnalysisCTAProps) => {
|
}: SurveyAnalysisCTAProps) => {
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
||||||
import { useTranslate } from "@tolgee/react";
|
|
||||||
import {
|
import {
|
||||||
Code2Icon,
|
Code2Icon,
|
||||||
LinkIcon,
|
LinkIcon,
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
UserIcon,
|
UserIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { TSegment } from "@formbricks/types/segment";
|
import { TSegment } from "@formbricks/types/segment";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
import { TUser } from "@formbricks/types/user";
|
import { TUser } from "@formbricks/types/user";
|
||||||
@@ -69,7 +69,7 @@ export const ShareSurveyModal = ({
|
|||||||
const [surveyUrl, setSurveyUrl] = useState<string>(getSurveyUrl(survey, publicDomain, "default"));
|
const [surveyUrl, setSurveyUrl] = useState<string>(getSurveyUrl(survey, publicDomain, "default"));
|
||||||
const [showView, setShowView] = useState<ModalView>(modalView);
|
const [showView, setShowView] = useState<ModalView>(modalView);
|
||||||
const { email } = user;
|
const { email } = user;
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslation();
|
||||||
const linkTabs: {
|
const linkTabs: {
|
||||||
id: ShareViaType | ShareSettingsType;
|
id: ShareViaType | ShareSettingsType;
|
||||||
type: LinkTabsType;
|
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