feat: feedback record directories#7592
Conversation
🚨 PR Size WarningThis PR has approximately 1389 lines of changes (1346 additions, 43 deletions across 22 files). Large PRs (>800 lines) are significantly harder to review and increase the chance of merge conflicts. Consider splitting this into smaller, self-contained PRs. 💡 Suggestions:
📊 What was counted:
📚 Guidelines:
If this large PR is unavoidable (e.g., migration, dependency update, major refactor), please explain in the PR description why it couldn't be split. |
WalkthroughThis pull request introduces a new Feedback Record Directory management feature. Changes include database schema and migration for 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/modules/ee/feedback-record-directory/actions.ts`:
- Around line 16-19: The schema ZCreateFeedbackRecordDirectoryAction uses
z.cuid() for organizationId which is inconsistent with other ID fields that use
the shared ZId schema (see directoryId usage); change organizationId to use ZId
instead of z.cuid() and ensure ZId is imported/available in this file so all ID
validations reuse the same shared logic (update the
ZCreateFeedbackRecordDirectoryAction definition to reference ZId for
organizationId).
In
`@apps/web/modules/ee/feedback-record-directory/components/feedback-record-directory-table.tsx`:
- Around line 60-69: The unarchive flow in handleUnarchiveDirectory lacks a
loading state, so wrap the async call to updateFeedbackRecordDirectoryAction in
a boolean state (e.g., isUnarchiving) that you set to true before the await and
false in a finally block; use that state to disable the unarchive button(s) and
optionally show a spinner while the request runs. Update the component state and
the JSX for the unarchive button(s) (also apply same pattern to the manage
action buttons referenced around lines 129-136) so they read
disabled={isUnarchiving} (or similar) and prevent duplicate submissions, keeping
existing toast.success/toast.error and router.refresh behavior. Ensure the state
variable name and the function handleUnarchiveDirectory and
updateFeedbackRecordDirectoryAction are referenced so it's easy to locate and
change.
- Around line 48-58: The Manage handler (handleManageDirectory) issues async
requests without a loading flag, allowing duplicate clicks; add a piece of
component state e.g. loadingDirectoryId: string | null with setter
setLoadingDirectoryId, set it to directoryId before calling
getFeedbackRecordDirectoryDetailsAction and clear it in a finally block so it is
always reset, and use loadingDirectoryId === directory.id to disable the Manage
button and optionally show a spinner while fetching; apply the same pattern to
the other directory-details fetch handler in this file that follows the same
logic (the second occurrence of the fetch/response/setSelectedDirectory flow).
In
`@apps/web/modules/ee/feedback-record-directory/lib/feedback-record-directory.ts`:
- Around line 196-199: The code redundantly fetches the same directory in
buildProjectAssignmentPayload and updateFeedbackRecordDirectory; refactor by
changing buildProjectAssignmentPayload to accept the already-fetched
directory/details (from getFeedbackRecordDirectoryDetails) instead of calling
getFeedbackRecordDirectoryDetails itself, then update
updateFeedbackRecordDirectory to pass the fetched directory (and its projects if
needed) into buildProjectAssignmentPayload and remove the second
fetch/ResourceNotFoundError there; alternatively, if you prefer minimal change,
remove the existence check and getFeedbackRecordDirectoryDetails call inside
buildProjectAssignmentPayload and rely on updateFeedbackRecordDirectory's
validation before calling it.
In
`@apps/web/modules/ee/feedback-record-directory/types/feedback-record-directory.ts`:
- Around line 28-38: The zod schemas ZFeedbackRecordDirectoryCreateInput and
ZFeedbackRecordDirectoryUpdateInput currently use hardcoded user-facing messages
("Directory name is required" and "Directory name must be at least 1 character
long"); replace those literal messages with translatable error keys or codes
(e.g., "error.directory.name_required" / "error.directory.name_too_short") or
remove the custom message and supply a code via the message object so the UI can
look up translations in FormError; update the z.string().min(...) calls in
ZFeedbackRecordDirectoryCreateInput and ZFeedbackRecordDirectoryUpdateInput to
emit those keys instead of English text so the FormError/translation layer can
render localized strings.
In `@apps/web/modules/ui/components/multi-select/index.tsx`:
- Line 33: The default placeholder in the MultiSelect component is a hardcoded
English string; remove the raw string and use react-i18next instead: import
useTranslation from 'react-i18next', call const { t } = useTranslation() inside
the MultiSelect component, and replace the default placeholder = "Select
options..." with placeholder = t('multiSelect.placeholder') (use that
translation key or the project’s agreed key) so all user-facing text goes
through t().
In `@packages/database/schema.prisma`:
- Around line 1037-1048: The FeedbackRecordDirectory model lacks a standalone
index on organizationId which can improve queries like
getFeedbackRecordDirectories that filter solely by organizationId; add a
dedicated index to the model (e.g., using @@index([organizationId])) so database
query planners can efficiently use that column even though
@@unique([organizationId, name]) creates a composite index.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c029900a-73b9-4e00-b502-24f78855c54a
⛔ Files ignored due to path filters (16)
apps/web/i18n.lockis excluded by!**/*.lockapps/web/locales/de-DE.jsonis excluded by!apps/web/locales/**apps/web/locales/en-US.jsonis excluded by!apps/web/locales/**apps/web/locales/es-ES.jsonis excluded by!apps/web/locales/**apps/web/locales/fr-FR.jsonis excluded by!apps/web/locales/**apps/web/locales/hu-HU.jsonis excluded by!apps/web/locales/**apps/web/locales/ja-JP.jsonis excluded by!apps/web/locales/**apps/web/locales/nl-NL.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-BR.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-PT.jsonis excluded by!apps/web/locales/**apps/web/locales/ro-RO.jsonis excluded by!apps/web/locales/**apps/web/locales/ru-RU.jsonis excluded by!apps/web/locales/**apps/web/locales/sv-SE.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hans-CN.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hant-TW.jsonis excluded by!apps/web/locales/**packages/surveys/i18n.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
apps/web/app/(app)/environments/[environmentId]/components/organization-breadcrumb.tsxapps/web/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar.tsxapps/web/app/(app)/environments/[environmentId]/settings/(organization)/feedback-record-directories/page.tsxapps/web/lib/utils/action-client/types/context.tsapps/web/modules/ee/audit-logs/lib/handler.tsapps/web/modules/ee/audit-logs/types/audit-log.tsapps/web/modules/ee/feedback-record-directory/actions.tsapps/web/modules/ee/feedback-record-directory/components/create-feedback-record-directory-modal.tsxapps/web/modules/ee/feedback-record-directory/components/feedback-record-directory-settings/archive-feedback-record-directory.tsxapps/web/modules/ee/feedback-record-directory/components/feedback-record-directory-settings/feedback-record-directory-settings-modal.tsxapps/web/modules/ee/feedback-record-directory/components/feedback-record-directory-table.tsxapps/web/modules/ee/feedback-record-directory/components/feedback-record-directory-view.tsxapps/web/modules/ee/feedback-record-directory/lib/feedback-record-directory.tsapps/web/modules/ee/feedback-record-directory/page.tsxapps/web/modules/ee/feedback-record-directory/types/feedback-record-directory.tsapps/web/modules/ui/components/button/index.tsxapps/web/modules/ui/components/delete-dialog/index.tsxapps/web/modules/ui/components/multi-select/index.tsxpackages/database/migration/20260325000000_add_feedback_record_directory/migration.sqlpackages/database/schema.prisma
Dhruwang
left a comment
There was a problem hiding this comment.
Looks good, left some comments to improve code 🙌
|



What does this PR do?
Adds the FeedbackRecordDirectory feature for Formbricks 5.0. Organizations can now create and manage multiple feedback record directories (representing Hub tenants) at the org level, with explicit workspace access assignments.
Changes:
FeedbackRecordDirectoryandFeedbackRecordDirectoryProject(join table) Prisma models with migrationtitleandbuttonLabelprops to support non-delete confirmation dialogs (e.g. archive)How should this be tested?
Checklist
Required
pnpm buildconsole.logsgit pull origin mainAppreciated