-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: empty state refactor #6404
base: preview
Are you sure you want to change the base?
Conversation
WalkthroughThis pull request introduces comprehensive localization and internationalization updates across the Plane web application. The changes primarily focus on enhancing empty state components, translation management, and user experience by integrating dynamic translations and resolved asset paths. Multiple files across the frontend codebase have been modified to support more robust and flexible localization, with a particular emphasis on replacing static empty states with dynamic, translatable components. Changes
Poem
Suggested labels
Suggested reviewers
Possibly related PRs
Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
…ne into chore-empty-state-refactor
Pull Request Linked with Plane Issues Comment Automatically Generated by Plane |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🔭 Outside diff range comments (3)
web/core/constants/profile.ts (1)
Line range hint
66-71
: Add missing 'key' property for consistency.In the
PROFILE_ADMINS_TAB
array, the "Created" tab object is missing thekey
property, while all other items have it. This breaks the pattern and could cause issues with React key props.{ + key: "created", route: "created", label: "Created", selected: "/created/", },
web/app/[workspaceSlug]/(projects)/settings/webhooks/page.tsx (1)
Line range hint
65-92
: Consider reducing code duplication in the conditional rendering.The UI structure is duplicated between the two conditions. Consider extracting common elements to reduce duplication.
return ( <> <PageHead title={pageTitle} /> <div className="w-full overflow-y-auto"> <CreateWebhookModal createWebhook={createWebhook} clearSecretKey={clearSecretKey} currentWorkspace={currentWorkspace} isOpen={showCreateWebhookModal} onClose={() => { setShowCreateWebhookModal(false); }} /> + <div className="flex h-full w-full flex-col"> + <div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5"> + <div className="text-xl font-medium">Webhooks</div> + <Button variant="primary" size="sm" onClick={() => setShowCreateWebhookModal(true)}> + Add webhook + </Button> + </div> {Object.keys(webhooks).length > 0 ? ( - <div className="flex h-full w-full flex-col"> - <div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5"> - <div className="text-xl font-medium">Webhooks</div> - <Button variant="primary" size="sm" onClick={() => setShowCreateWebhookModal(true)}> - Add webhook - </Button> - </div> <WebhooksList /> - </div> ) : ( - <div className="flex h-full w-full flex-col"> - <div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5"> - <div className="text-xl font-medium">Webhooks</div> - <Button variant="primary" size="sm" onClick={() => setShowCreateWebhookModal(true)}> - Add webhook - </Button> - </div> <div className="h-full w-full flex items-center justify-center"> <DetailedEmptyState title={t("workspace_settings.empty_state.webhooks.title")} description={t("workspace_settings.empty_state.webhooks.description")} assetPath={resolvedPath} /> </div> - </div> )} + </div> </div> </> );web/core/components/integration/guide.tsx (1)
Translation key mismatch detected in locale files
Inconsistent translation keys found for empty state messages:
- English:
issue_relation.empty_state.no_issues
- French/Japanese:
issue_relation.empty_state.general
These keys should be synchronized across all locale files to prevent missing translations.
🔗 Analysis chain
Line range hint
317-780
: Verify translation key consistency across locale filesLet's ensure all locale files have the same structure and no missing translations.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for consistency of translation keys across locale files # Function to extract keys from a JSON file extract_keys() { jq -r 'path(..)|map(tostring)|join(".")' "$1" | sort } # Compare keys between locale files echo "Comparing translation keys..." for locale in en fr ja; do if [ -f "packages/i18n/src/locales/${locale}/translations.json" ]; then extract_keys "packages/i18n/src/locales/${locale}/translations.json" > "/tmp/${locale}_keys.txt" fi done # Find differences echo "Checking for missing keys..." for locale1 in en fr ja; do for locale2 in en fr ja; do if [ "$locale1" != "$locale2" ]; then diff "/tmp/${locale1}_keys.txt" "/tmp/${locale2}_keys.txt" || true fi done doneLength of output: 2378
🧹 Nitpick comments (29)
web/core/components/cycles/active-cycle/cycle-stats.tsx (2)
241-243
: Consider extracting empty state configurations for better maintainability.While the empty state implementations are consistent, consider extracting the configurations into a constant to reduce repetition and improve maintainability.
Example implementation:
const EMPTY_STATES = { priority: { translationKey: "active_cycle.empty_state.priority_issue.title", assetBasePath: "/empty-state/active-cycle/priority" }, assignee: { translationKey: "active_cycle.empty_state.assignee.title", assetBasePath: "/empty-state/active-cycle/assignee" }, label: { translationKey: "active_cycle.empty_state.label.title", assetBasePath: "/empty-state/active-cycle/label" } } as const;Also applies to: 302-304, 344-344
44-46
: LGTM! Consider enhancing the local storage documentation.The local storage implementation is clear, but consider adding a brief JSDoc comment explaining the purpose and possible values of the "activeCycleTab" key.
Example:
/** * Persists the active tab selection. * Possible values: "Priority-Issues" | "Assignees" | "Labels" */ const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees");web/core/components/home/home-dashboard-widgets.tsx (1)
86-92
: Consider improving the empty state wrapper structure.While the empty state implementation is functional, the wrapper structure could be improved for better semantics and accessibility.
Consider this improvement:
- <div className="h-full w-full grid place-items-center"> + <section + className="h-full w-full grid place-items-center" + aria-label={t("home_widgets.empty_state.general.title")} + > <SimpleEmptyState title={t("home_widgets.empty_state.general.title")} description={t("home_widgets.empty_state.general.description")} assetPath={noWidgetsResolvedPath} /> - </div> + </section>This change:
- Uses semantic HTML with
section
- Adds ARIA label for better accessibility
- Maintains the same visual layout
web/core/components/issues/issue-layouts/empty-states/cycle.tsx (2)
36-40
: Consider simplifying the filter count logic.The current implementation could be more concise using
Object.values()
.- const issueFilterCount = size( - Object.fromEntries( - Object.entries(userFilters ?? {}).filter(([, value]) => value && Array.isArray(value) && value.length > 0) - ) - ); + const issueFilterCount = Object.values(userFilters ?? {}) + .filter((value) => value && Array.isArray(value) && value.length > 0) + .length;
49-59
: Consider extracting asset paths to constants.The base paths for assets could be moved to a constants file to improve maintainability and reusability.
// suggested constants file export const EMPTY_STATE_ASSET_PATHS = { EMPTY_FILTERS: '/empty-state/empty-filters/', CYCLE_ISSUES: '/empty-state/cycle-issues/', COMPLETED_NO_ISSUES: '/empty-state/cycle/completed-no-issues', } as const;web/core/components/inbox/sidebar/root.tsx (1)
141-155
: Consider centralizing translation keysThe translation keys are currently hardcoded strings. Consider creating a constant object to centralize these keys, making them easier to maintain and reuse.
Example:
// constants/translations/inbox.ts export const INBOX_TRANSLATIONS = { EMPTY_STATE: { SIDEBAR_FILTER: { TITLE: "inbox.empty_state.sidebar_filter.title", DESCRIPTION: "inbox.empty_state.sidebar_filter.description" }, SIDEBAR_OPEN_TAB: { TITLE: "inbox.empty_state.sidebar_open_tab.title", DESCRIPTION: "inbox.empty_state.sidebar_open_tab.description" }, SIDEBAR_CLOSED_TAB: { TITLE: "inbox.empty_state.sidebar_closed_tab.title", DESCRIPTION: "inbox.empty_state.sidebar_closed_tab.description" } } };This would make the code more maintainable and less prone to typos:
<SimpleEmptyState title={t(INBOX_TRANSLATIONS.EMPTY_STATE.SIDEBAR_FILTER.TITLE)} description={t(INBOX_TRANSLATIONS.EMPTY_STATE.SIDEBAR_FILTER.DESCRIPTION)} assetPath={sidebarFilterAssetPath} />web/core/components/stickies/layout/stickies-list.tsx (1)
119-122
: Add error handling for sticky creation.The sticky creation operation should include error handling to gracefully handle failures and provide user feedback.
onClick: () => { + try { toggleShowNewSticky(true); - stickyOperations.create(); + await stickyOperations.create(); + } catch (error) { + // Handle error appropriately (e.g., show toast notification) + console.error("Failed to create sticky:", error); + toggleShowNewSticky(false); + } },web/core/components/core/modals/issue-search-modal-empty-state.tsx (1)
28-30
: Consider extracting EmptyStateContainer to a shared component.The
EmptyStateContainer
component could be reused across other empty states. Consider moving it to a shared location.web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/page.tsx (1)
38-48
: Consider adding secondary action.While the implementation is correct, consider adding a secondary button for non-admin users to contact admins.
<DetailedEmptyState title={t("disabled_project.empty_state.inbox.title")} description={t("disabled_project.empty_state.inbox.description")} assetPath={resolvedPath} primaryButton={{ text: t("disabled_project.empty_state.inbox.primary_button.text"), onClick: () => { router.push(`/${workspaceSlug}/projects/${projectId}/settings/features`); }, disabled: !canPerformEmptyStateActions, }} + secondaryButton={{ + text: t("disabled_project.empty_state.inbox.secondary_button.text"), + onClick: () => { + // Add contact admin action + } + }} />web/core/components/cycles/archived-cycles/root.tsx (1)
70-74
: Consider adding refresh action.While the empty state implementation is correct, consider adding a refresh action for users to check for new archived cycles.
<DetailedEmptyState title={t("project_cycles.empty_state.archived.title")} description={t("project_cycles.empty_state.archived.description")} assetPath={resolvedPath} + primaryButton={{ + text: t("project_cycles.empty_state.archived.refresh"), + onClick: () => { + fetchArchivedCycles(workspaceSlug.toString(), projectId.toString()); + } + }} />web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx (1)
37-38
: Consider adding a comment explaining the permission check.The permission check is correct, but adding a brief comment would help explain why these specific roles are required for empty state actions.
+ // Only project admins can access feature settings const canPerformEmptyStateActions = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx (1)
35-36
: Consider adding a comment explaining the permission check.Similar to the views page, adding a brief comment would help explain the permission requirements.
+ // Only project admins can access feature settings const canPerformEmptyStateActions = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
web/app/[workspaceSlug]/(projects)/settings/api-tokens/page.tsx (1)
84-88
: Consider adding a primary button to the empty state.The empty state could benefit from a primary button that opens the create token modal, similar to how the header button works. This would provide a more consistent user experience.
<DetailedEmptyState title={t("workspace_settings.empty_state.api_tokens.title")} description={t("workspace_settings.empty_state.api_tokens.description")} assetPath={resolvedPath} + primaryButton={{ + text: t("workspace_settings.empty_state.api_tokens.primary_button.text"), + onClick: () => setIsCreateTokenModalOpen(true) + }} />web/core/components/inbox/root.tsx (2)
30-31
: Consider adding error boundaries for better error handling.While the component handles loading and error states, wrapping it with an error boundary would provide a more robust error handling mechanism.
Also applies to: 34-35
104-104
: Consider enhancing the empty state with more context.The empty state could be more informative by providing guidance on how to create new inbox items or explaining when items appear here.
- <SimpleEmptyState title={t("inbox.empty_state.detail.title")} assetPath={resolvedPath} /> + <SimpleEmptyState + title={t("inbox.empty_state.detail.title")} + description={t("inbox.empty_state.detail.description")} + assetPath={resolvedPath} + />web/ce/components/cycles/active-cycle/root.tsx (1)
39-39
: Consider memoizing the resolved path.The
activeCycleResolvedPath
could be memoized to prevent unnecessary recalculations.- const activeCycleResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/cycle/active" }); + const activeCycleResolvedPath = useMemo( + () => useResolvedAssetPath({ basePath: "/empty-state/cycle/active" }), + [] + );Also applies to: 86-86
web/core/components/issues/workspace-draft/root.tsx (1)
66-81
: Consider adding a loading state for the asset.The empty state implementation looks good, but consider adding a loading state for the asset to handle cases where the asset might take time to load.
<DetailedEmptyState size="sm" title={t("workspace_projects.empty_state.no_projects.title")} description={t("workspace_projects.empty_state.no_projects.description")} assetPath={noProjectResolvedPath} + showLoader={!noProjectResolvedPath} customPrimaryButton={ <ComicBoxButton label={t("workspace_projects.empty_state.no_projects.primary_button.text")}
web/core/components/page-views/workspace-dashboard.tsx (1)
102-117
: Consider adding error handling for asset loading.While the empty state implementation is good, it would be beneficial to handle potential asset loading failures.
<DetailedEmptyState title={t("workspace_dashboard.empty_state.general.title")} description={t("workspace_dashboard.empty_state.general.description")} assetPath={resolvedPath} + onAssetLoadError={() => { + // Handle asset loading error + console.error("Failed to load empty state asset"); + }} customPrimaryButton={web/app/[workspaceSlug]/(projects)/notifications/page.tsx (1)
89-89
: Consider adding a fallback for missing translations.While the empty state implementation is clean, it would be beneficial to add a fallback text in case the translation key is missing.
-<SimpleEmptyState title={t("notification.empty_state.detail.title")} assetPath={resolvedPath} /> +<SimpleEmptyState + title={t("notification.empty_state.detail.title", "No notification selected")} + assetPath={resolvedPath} +/>web/app/[workspaceSlug]/(projects)/analytics/page.tsx (1)
83-98
: Consider adding aria-labels for better accessibility.The empty state implementation looks good, but could benefit from additional accessibility attributes.
<DetailedEmptyState title={t("workspace_analytics.empty_state.general.title")} description={t("workspace_analytics.empty_state.general.description")} assetPath={resolvedPath} + aria-label={t("workspace_analytics.empty_state.general.aria_label", "Analytics empty state")} customPrimaryButton={ <ComicBoxButton label={t("workspace_analytics.empty_state.general.primary_button.text")} title={t("workspace_analytics.empty_state.general.primary_button.comic.title")} description={t("workspace_analytics.empty_state.general.primary_button.comic.description")} + aria-label={t("workspace_analytics.empty_state.general.primary_button.aria_label", "Create new project")} onClick={() => { setTrackElement("Analytics empty state"); toggleCreateProjectModal(true); }} disabled={!canPerformEmptyStateActions} /> } />web/core/components/pages/pages-list-main-content.tsx (1)
39-50
: Consider consolidating asset path resolution.Instead of resolving multiple asset paths individually, consider using a single hook call with a base path and combining it with specific paths.
-const generalPageResolvedPath = useResolvedAssetPath({ - basePath: "/empty-state/onboarding/pages", -}); -const publicPageResolvedPath = useResolvedAssetPath({ - basePath: "/empty-state/pages/public", -}); -const privatePageResolvedPath = useResolvedAssetPath({ - basePath: "/empty-state/pages/private", -}); -const archivedPageResolvedPath = useResolvedAssetPath({ - basePath: "/empty-state/pages/archived", -}); +const resolvedBasePath = useResolvedAssetPath({ + basePath: "/empty-state", +}); +const paths = { + general: `${resolvedBasePath}/onboarding/pages`, + public: `${resolvedBasePath}/pages/public`, + private: `${resolvedBasePath}/pages/private`, + archived: `${resolvedBasePath}/pages/archived`, +};web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx (1)
104-105
: Consider adding more context to event tracking.The event tracking could benefit from additional context about the user's journey.
-setTrackElement("Cycle empty state"); +setTrackElement("Cycle empty state", { + source: "Empty state", + action: "Create cycle", + hasPermission: hasMemberLevelPermission +});web/core/components/modules/modules-list-view.tsx (1)
34-37
: Consider expanding the permission check for empty state actions.The current permission check only allows ADMIN and MEMBER roles. Consider including other roles that might need to perform these actions, or make it configurable based on project settings.
- const canPerformEmptyStateActions = allowPermissions( - [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER], - EUserPermissionsLevel.PROJECT - ); + const canPerformEmptyStateActions = allowPermissions( + [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER, EUserProjectRoles.VIEWER], + EUserPermissionsLevel.PROJECT, + projectPermissions?.canCreateModule + );web/core/components/issues/issue-layouts/empty-states/module.tsx (1)
34-38
: Simplify the filter count calculation for better readability.The current implementation using Object.fromEntries and Object.entries is verbose. Consider using a more straightforward approach.
- const issueFilterCount = size( - Object.fromEntries( - Object.entries(userFilters ?? {}).filter(([, value]) => value && Array.isArray(value) && value.length > 0) - ) - ); + const issueFilterCount = Object.values(userFilters ?? {}) + .filter((value): value is unknown[] => Array.isArray(value) && value.length > 0) + .length;web/core/components/inbox/modals/select-duplicate.tsx (1)
Line range hint
65-67
: Add translation for error toast message.The error toast message is hardcoded in English. Consider using translation keys for consistency.
return setToast({ - title: "Error", + title: t("common.error"), type: TOAST_TYPE.ERROR, });web/core/components/project/multi-select-modal.tsx (2)
Line range hint
166-175
: Add translations for button text.The "Cancel" and "Confirm"/"Confirming" button texts are hardcoded. Consider using translation keys for consistency.
<Button variant="neutral-primary" size="sm" onClick={handleClose}> - Cancel + {t("common.cancel")} </Button> <Button ref={moveButtonRef} variant="primary" size="sm" onClick={handleSubmit} loading={isSubmitting} disabled={!areSelectedProjectsChanged} > - {isSubmitting ? "Confirming" : "Confirm"} + {isSubmitting ? t("common.confirming") : t("common.confirm")} </Button>
Line range hint
85-85
: Add translation for search placeholder.The search input placeholder is hardcoded. Consider using a translation key.
- placeholder="Search for projects" + placeholder={t("workspace_projects.search.placeholder")}web/core/components/integration/guide.tsx (1)
29-29
: Consider creating a migration plan for this deprecated componentThe FIXME comment indicates this component is deprecated, but it's still in use. To prevent technical debt, consider:
- Creating a new component to replace this one
- Planning a gradual migration
- Setting a timeline for removal
packages/i18n/src/locales/es/translations.json (1)
459-614
: Consider making some descriptions more concise.While the empty states for project sections are well-structured and informative, some descriptions could be shortened for better readability. For example, the project issues description (lines 510-511) is quite lengthy and could be more focused.
Example improvement for project issues:
-"Piensa en los problemas como trabajos, tareas, acciones, o JTBD (trabajos por hacer). Nos gusta eso. Un problema y sus subproblemas suelen ser acciones basadas en el tiempo asignadas a los miembros de tu equipo. Tu equipo crea, asigna y completa problemas para avanzar el proyecto hacia su objetivo." +"Los problemas son tareas asignables que ayudan a tu equipo a avanzar el proyecto hacia su objetivo. Pueden contener subproblemas y tienen plazos definidos."
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (60)
packages/i18n/src/locales/en/translations.json
(1 hunks)packages/i18n/src/locales/es/translations.json
(1 hunks)packages/i18n/src/locales/fr/translations.json
(1 hunks)packages/i18n/src/locales/ja/translations.json
(1 hunks)web/app/[workspaceSlug]/(projects)/analytics/page.tsx
(2 hunks)web/app/[workspaceSlug]/(projects)/notifications/page.tsx
(3 hunks)web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx
(4 hunks)web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/page.tsx
(1 hunks)web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx
(2 hunks)web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx
(2 hunks)web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx
(2 hunks)web/app/[workspaceSlug]/(projects)/settings/api-tokens/page.tsx
(3 hunks)web/app/[workspaceSlug]/(projects)/settings/webhooks/page.tsx
(2 hunks)web/app/profile/activity/page.tsx
(2 hunks)web/app/profile/notifications/page.tsx
(1 hunks)web/app/profile/page.tsx
(1 hunks)web/ce/components/cycles/active-cycle/root.tsx
(4 hunks)web/core/components/command-palette/command-modal.tsx
(5 hunks)web/core/components/core/modals/bulk-delete-issues-modal.tsx
(3 hunks)web/core/components/core/modals/issue-search-modal-empty-state.tsx
(2 hunks)web/core/components/cycles/active-cycle/cycle-stats.tsx
(6 hunks)web/core/components/cycles/active-cycle/productivity.tsx
(3 hunks)web/core/components/cycles/active-cycle/progress.tsx
(4 hunks)web/core/components/cycles/archived-cycles/root.tsx
(2 hunks)web/core/components/empty-state/empty-state.tsx
(0 hunks)web/core/components/empty-state/index.ts
(0 hunks)web/core/components/empty-state/simple-empty-state-root.tsx
(2 hunks)web/core/components/exporter/guide.tsx
(3 hunks)web/core/components/home/home-dashboard-widgets.tsx
(3 hunks)web/core/components/inbox/modals/select-duplicate.tsx
(3 hunks)web/core/components/inbox/root.tsx
(3 hunks)web/core/components/inbox/sidebar/root.tsx
(4 hunks)web/core/components/integration/guide.tsx
(2 hunks)web/core/components/issues/issue-detail/issue-activity/comments/root.tsx
(3 hunks)web/core/components/issues/issue-layouts/empty-states/archived-issues.tsx
(2 hunks)web/core/components/issues/issue-layouts/empty-states/cycle.tsx
(2 hunks)web/core/components/issues/issue-layouts/empty-states/draft-issues.tsx
(1 hunks)web/core/components/issues/issue-layouts/empty-states/global-view.tsx
(1 hunks)web/core/components/issues/issue-layouts/empty-states/module.tsx
(2 hunks)web/core/components/issues/issue-layouts/empty-states/profile-view.tsx
(1 hunks)web/core/components/issues/issue-layouts/empty-states/project-epic.tsx
(1 hunks)web/core/components/issues/issue-layouts/empty-states/project-issues.tsx
(2 hunks)web/core/components/issues/workspace-draft/empty-state.tsx
(2 hunks)web/core/components/issues/workspace-draft/root.tsx
(3 hunks)web/core/components/labels/project-setting-label-list.tsx
(2 hunks)web/core/components/modules/archived-modules/root.tsx
(2 hunks)web/core/components/modules/modules-list-view.tsx
(3 hunks)web/core/components/page-views/workspace-dashboard.tsx
(4 hunks)web/core/components/pages/pages-list-main-content.tsx
(2 hunks)web/core/components/project/card-list.tsx
(3 hunks)web/core/components/project/multi-select-modal.tsx
(4 hunks)web/core/components/stickies/layout/stickies-list.tsx
(3 hunks)web/core/components/views/views-list.tsx
(2 hunks)web/core/components/workspace-notifications/sidebar/empty-state.tsx
(1 hunks)web/core/components/workspace/sidebar/dropdown.tsx
(1 hunks)web/core/components/workspace/sidebar/user-menu.tsx
(1 hunks)web/core/constants/empty-state.tsx
(0 hunks)web/core/constants/profile.ts
(1 hunks)web/core/layouts/auth-layout/project-wrapper.tsx
(5 hunks)web/core/store/user/permissions.store.ts
(5 hunks)
💤 Files with no reviewable changes (3)
- web/core/components/empty-state/empty-state.tsx
- web/core/components/empty-state/index.ts
- web/core/constants/empty-state.tsx
✅ Files skipped from review due to trivial changes (1)
- web/core/components/issues/issue-layouts/empty-states/draft-issues.tsx
🔇 Additional comments (82)
web/core/components/cycles/active-cycle/cycle-stats.tsx (1)
11-11
: LGTM! Well-structured internationalization setup.The addition of translation and asset path resolution hooks is well-organized and follows React best practices.
Also applies to: 18-18, 28-28, 51-55
web/core/components/home/home-dashboard-widgets.tsx (3)
3-4
: Well-organized imports with clear grouping!The imports are logically grouped with clear comments, and the new additions for translations and asset path resolution are properly placed in their respective sections.
Also applies to: 11-11
55-56
: Good implementation of i18n translations!The translation integration follows best practices with hierarchical keys and proper hook usage. The empty state messages are now properly internationalized.
Also applies to: 88-89
59-60
: Good practice using asset path resolution!The asset path is properly resolved using a custom hook, and the computation is correctly placed outside the render for better performance.
web/core/components/issues/issue-layouts/empty-states/cycle.tsx (3)
8-17
: Well-organized imports with clear separation of concerns!The imports are properly categorized with comments and include all necessary dependencies for internationalization, permissions, and empty state handling.
24-32
: Excellent hooks organization and initialization!The component properly initializes all necessary hooks for translations, permissions, and store access. The setup follows React best practices and enables internationalization support.
112-147
: Well-structured empty state handling with proper internationalization!The implementation demonstrates:
- Clear conditional rendering based on cycle state
- Proper use of translation keys for all text content
- Appropriate permission checks for button actions
- Integration with event tracking
web/core/components/inbox/sidebar/root.tsx (3)
5-21
: Well-organized imports with clear grouping!The imports are logically grouped with clear comments, making the code more maintainable. The new imports for translations and empty states align well with the refactoring objectives.
Line range hint
42-64
: Good component organization with clear sections!The component's internal organization has been improved with:
- Clear section comments for router, hooks, and derived values
- Proper initialization of translation and asset path hooks
- Clean separation of concerns
139-157
: Great implementation of dynamic empty states!The empty state handling is now more robust with:
- Conditional rendering based on filters and current tab
- Proper internationalization support
- Clean separation of content and presentation
web/core/constants/profile.ts (1)
13-13
: Verify consistent key naming pattern across translations.The change from
"profile"
to"profile.label"
appears to be part of a broader localization effort. Let's verify that this naming pattern is consistently applied across translation files and other profile-related components.✅ Verification successful
Translation key pattern is consistent across the codebase.
The change from
"profile"
to"profile.label"
follows the established i18n pattern where translation keys are properly namespaced. This structure is consistently implemented across all language files and correctly referenced throughout the codebase.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for consistency in profile-related translation keys and their usage # Check for profile-related translation keys in translation files echo "Checking translation files for profile keys:" rg -g "*.json" '"profile\.' # Check for any remaining direct "profile" keys that might need updating echo "Checking for potentially inconsistent profile keys:" rg -g "*.{ts,tsx}" 'key: "profile"'Length of output: 279
Script:
#!/bin/bash # Find translation files first echo "Locating translation files:" fd -e json -e yaml -e yml echo -e "\nChecking for profile-related keys in all formats:" rg -g "*.{json,yaml,yml}" -i "profile" echo -e "\nChecking TypeScript/React files for profile key usage:" rg -g "*.{ts,tsx}" "profile" -A 1 -B 1Length of output: 59368
web/core/components/stickies/layout/stickies-list.tsx (4)
10-21
: LGTM! Well-organized imports.The new imports are properly categorized and align with the added functionality for empty states, translations, and user permissions.
53-60
: LGTM! Proper permission checks and asset path resolution.The implementation correctly:
- Checks for appropriate workspace-level permissions
- Resolves asset paths for both general and search empty states
104-127
: LGTM! Well-implemented empty state refactor.The empty state implementation is well-structured with:
- Proper conditional rendering based on search context
- Internationalization support
- Permission-based controls
- Clear user feedback
Line range hint
139-140
: Address the TypeScript error in Masonry component.The
@ts-expect-error
comment suggests a type mismatch. Consider:
- Updating the
react-masonry-component
types- Creating a proper type definition
- Using an alternative masonry library with better TypeScript support
web/core/components/cycles/active-cycle/productivity.tsx (3)
4-15
: LGTM! Well-organized imports with clear sectioning.The import changes align well with the empty state refactoring objective, properly introducing internationalization and asset resolution capabilities.
25-31
: LGTM! Proper hook initialization and usage.The translation and asset resolution hooks are well-integrated, supporting the internationalization requirements.
101-101
: Verify translation key and asset path existence.The empty state implementation looks good, but let's verify:
- The translation key
active_cycle.empty_state.chart.title
exists in the translation files- The asset exists at the resolved path
/empty-state/active-cycle/chart
✅ Verification successful
Empty state implementation verified successfully
The implementation is correct with:
- Translation key
active_cycle.empty_state.chart.title
exists in the translations- Asset path follows the established pattern in
web/public/empty-state/
- Component usage is consistent with other empty states in the codebase
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the translation key exists rg -g '*.{json,ts,js}' "active_cycle\.empty_state\.chart\.title" # Check if the asset exists (common image extensions) fd -e svg -e png -e jpg -e jpeg -e gif -p "chart" "empty-state/active-cycle"Length of output: 253
Script:
#!/bin/bash # Find translation files fd -e json translations # Find SimpleEmptyState component definition ast-grep --pattern 'export $_ function SimpleEmptyState' # Find any empty state related assets fd -e svg -e png -e jpg -e jpeg -e gif "empty" # Search for parts of the translation key rg -g '*.{json,ts,js}' "active_cycle"Length of output: 3770
Script:
#!/bin/bash # Check complete translation structure under active_cycle rg -g '*.json' -A 10 '"active_cycle": {' packages/i18n/src/locales/en/translations.json # Find SimpleEmptyState with broader pattern ast-grep --pattern 'SimpleEmptyState' # Find any file containing SimpleEmptyState definition rg "SimpleEmptyState.*=.*" -A 5Length of output: 12046
Script:
#!/bin/bash # Search for the specific translation key with proper JSON escaping rg -g '*.json' --json -A 5 '"active_cycle"' packages/i18n/src/locales/en/translations.json | jq -r '.data.lines.text'Length of output: 290
web/app/profile/page.tsx (1)
26-26
: LGTM! Translation key update is consistent with localization strategy.The change from
profile
toprofile.label
aligns with the broader localization improvements across the application.web/core/components/issues/issue-layouts/empty-states/profile-view.tsx (2)
9-9
: Address TODO comment regarding profileViewId stability.The comment indicates potential stability issues with profileViewId changes. This should be investigated and resolved before merging.
Could you provide more details about the specific issues with profileViewId? This would help in proposing a more robust solution.
23-29
: 🛠️ Refactor suggestionVerify translation key existence for all possible profileViewId values.
The dynamic translation keys could fail if
profileViewId
doesn't match the available translations. Consider adding validation or fallback values.+ const fallbackTitle = t("profile.empty_state.default.title"); + const fallbackDescription = t("profile.empty_state.default.description"); + + const title = t(`profile.empty_state.${profileViewId.toString()}.title`, fallbackTitle); + const description = t(`profile.empty_state.${profileViewId.toString()}.description`, fallbackDescription); + return ( <DetailedEmptyState - title={t(`profile.empty_state.${profileViewId.toString()}.title`)} - description={t(`profile.empty_state.${profileViewId.toString()}.description`)} + title={title} + description={description} assetPath={resolvedPath} /> );web/app/profile/notifications/page.tsx (1)
28-28
: LGTM! Translation key update is consistent with localization strategy.The change from
profile
toprofile.label
aligns with the broader localization improvements across the application.web/core/components/empty-state/simple-empty-state-root.tsx (1)
21-21
: Verify the new dimensions value.The dimensions property for "sm" size has been explicitly set to 78. Please confirm this value maintains the desired aspect ratio and visual consistency.
Consider extracting these magic numbers into a constants file for better maintainability and documentation of the design system values.
web/core/components/core/modals/issue-search-modal-empty-state.tsx (1)
32-44
: LGTM! Well-structured conditional rendering.The conditions for showing different empty states are clear and well-organized. The use of translation keys and resolved asset paths improves maintainability.
web/core/components/issues/issue-detail/issue-activity/comments/root.tsx (1)
54-58
: LGTM! Clean empty state implementation.The empty state implementation is well-structured with proper translations and asset handling.
web/app/profile/activity/page.tsx (2)
27-30
: LGTM! Clean hook implementation.The addition of translation and asset path resolution hooks follows best practices.
56-62
: Verify translation key existence.The empty state implementation looks good, but let's verify that the translation keys exist in the localization files.
✅ Verification successful
Translation keys verified successfully
Both translation keys exist in the English locale file with appropriate content:
profile.empty_state.activity.title
: "No activities yet"profile.empty_state.activity.description
: "Get started by creating a new issue! Add details and properties to it. Explore more in Plane to see your activity."🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the translation keys exist in the English locale file rg "profile\.empty_state\.activity\." -A 2 "packages/i18n/src/locales/en/translations.json"Length of output: 91
Script:
#!/bin/bash # Find translation files fd -e json -e yaml -e yml . | grep -i "translation\|locale\|i18n" # Also try finding files containing the specific translation key pattern rg -l "profile\.empty_state\.activity"Length of output: 435
Script:
#!/bin/bash # Check the structure of the English translations file cat packages/i18n/src/locales/en/translations.json # Also look for any similar patterns in case the key format is different rg "profile" -A 5 packages/i18n/src/locales/en/translations.jsonLength of output: 34069
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx (2)
5-7
: LGTM! Well-structured permission handling.The permission checks are properly implemented using the appropriate constants and hooks.
Also applies to: 28-28, 32-32
48-58
: Verify disabled feature navigation.The implementation looks good, but let's verify the settings navigation path.
✅ Verification successful
Navigation path to features settings is correctly implemented
The route
/[workspaceSlug]/projects/[projectId]/settings/features
exists and matches the navigation path used in the code.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the features settings route exists rg "settings/features" -A 2 "web/routes/"Length of output: 91
Script:
#!/bin/bash # Search for settings/features route in the app directory fd . web/app -t d --full-path '.*settings/features.*' # Search for any files containing settings/features path rg "settings/features" -t typescript -t javascriptLength of output: 5008
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/inbox/page.tsx (1)
5-6
: LGTM! Consistent permission implementation.The permission handling follows the same pattern as other files, maintaining consistency.
Also applies to: 29-31
web/core/components/cycles/archived-cycles/root.tsx (1)
21-22
: LGTM! Clean hook implementation.The hooks are properly implemented and follow the established pattern.
Also applies to: 29-29
web/core/components/modules/archived-modules/root.tsx (1)
21-29
: LGTM! Good use of hooks for internationalization and asset path resolution.The hooks are properly initialized and used consistently throughout the component.
web/core/components/views/views-list.tsx (1)
64-77
: Verify translation keys exist.Please ensure that the following translation keys are defined in the translation files:
project_views.empty_state.general.title
project_views.empty_state.general.description
project_views.empty_state.general.primary_button.text
project_views.empty_state.general.primary_button.comic.title
project_views.empty_state.general.primary_button.comic.description
✅ Verification successful
All translation keys are properly defined ✅
All required translation keys exist in the i18n files (
packages/i18n/src/locales/*/translations.json
) with the correct structure and are available in multiple languages including English, Spanish, French, and Japanese.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check if the translation keys exist in the translation files rg -l "project_views\.empty_state\.general\.(title|description|primary_button)" --type yamlLength of output: 93
Script:
#!/bin/bash # First, find all potential translation files echo "=== Potential translation files ===" fd -e yml -e yaml -e json # Then search for any of these keys in those files echo -e "\n=== Translation key search ===" rg "project_views:" --type yaml --type json -A 5Length of output: 1819
Script:
#!/bin/bash # Search for project_views structure in translation files echo "=== English translations ===" cat packages/i18n/src/locales/en/translations.json | jq '.project_views.empty_state.general // empty' # Also check raw content in case the structure is different echo -e "\n=== Raw search in translations ===" rg -A 10 '"project_views"' packages/i18n/src/locales/*/translations.jsonLength of output: 5684
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx (2)
7-8
: LGTM! Well-structured imports.The imports are logically grouped and follow a consistent pattern.
Also applies to: 20-22
65-75
: LGTM! Well-structured empty state implementation.The DetailedEmptyState component is properly configured with:
- Translated title and description
- Resolved asset path
- Permission-based button state
- Clear navigation action
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx (1)
57-67
: LGTM! Consistent empty state implementation.The DetailedEmptyState implementation follows the same pattern as the views page, maintaining consistency across the application.
web/core/components/issues/issue-layouts/empty-states/project-issues.tsx (3)
37-43
: LGTM! Well-structured asset path resolution.The asset path resolution is properly handled for both filter and no-issues scenarios.
58-86
: LGTM! Comprehensive empty state handling.The implementation effectively handles both scenarios:
- When filters are applied but no issues match
- When there are no issues in the project
32-36
: Consider broadening permission scope for issue creation.The current permission check might be too restrictive. Consider allowing viewers to create issues if that aligns with your product requirements.
web/core/components/inbox/root.tsx (1)
4-5
: LGTM! Clean import organization with clear sectioning.The imports are well-organized with clear separation using comments.
Also applies to: 8-8, 16-16
web/ce/components/cycles/active-cycle/root.tsx (1)
51-55
: LGTM! Comprehensive empty state implementation.The DetailedEmptyState implementation provides clear feedback with both title and description.
web/app/[workspaceSlug]/(projects)/settings/webhooks/page.tsx (1)
86-90
: LGTM! Well-implemented empty state with proper translations.The DetailedEmptyState implementation provides clear feedback with both title and description.
web/core/components/project/card-list.tsx (2)
48-52
: LGTM! Well-implemented permission check for empty state actions.The permission check ensures that only users with appropriate permissions can perform empty state actions.
58-73
: LGTM! Comprehensive empty state with interactive elements.The empty state implementation provides clear feedback and includes an interactive button with proper permission handling.
web/core/components/issues/workspace-draft/root.tsx (1)
38-41
: LGTM! Well-structured permission check implementation.The permission check is correctly implemented using
allowPermissions
to verify if the user has admin or member-level workspace permissions.web/core/components/page-views/workspace-dashboard.tsx (1)
73-76
: LGTM! Clear and concise permission check.The permission check is well-implemented using
allowPermissions
to verify if the user has admin or member-level workspace permissions.web/app/[workspaceSlug]/(projects)/analytics/page.tsx (1)
38-41
: LGTM! Well-implemented permission check.The permission check correctly uses
allowPermissions
to verify if the user has admin or member-level workspace permissions.web/core/components/cycles/active-cycle/progress.tsx (1)
7-7
: LGTM! Clean implementation of i18n and asset path resolution.The changes effectively integrate internationalization and asset path resolution while maintaining the component's core functionality.
Also applies to: 11-11, 14-14, 25-26, 44-44, 106-106
web/core/components/pages/pages-list-main-content.tsx (1)
35-38
: LGTM! Well-implemented permission check.The permission check correctly validates user roles against project-level permissions.
web/core/components/labels/project-setting-label-list.tsx (1)
7-7
: LGTM! Clean implementation of i18n and empty state.The changes effectively integrate internationalization and empty state components while maintaining the component's core functionality.
Also applies to: 10-10, 19-19, 32-33, 39-39, 98-102
web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx (1)
42-46
: LGTM! Well-structured permission checks.The code clearly distinguishes between admin and member level permissions, making it easy to understand and maintain.
web/core/layouts/auth-layout/project-wrapper.tsx (3)
11-11
: Good addition of hooks for better maintainability!The introduction of
useResolvedAssetPath
and proper hook organization improves code maintainability and asset management.Also applies to: 27-27, 58-60
157-161
: Well-implemented permission check for empty state actions.Good security practice to verify user permissions before allowing empty state actions.
180-195
: Enhanced empty state with better user guidance.The DetailedEmptyState implementation with ComicBoxButton provides:
- Clear title and description
- Proper asset path resolution
- Permission-aware action button
- Event tracking for analytics
web/core/components/core/modals/bulk-delete-issues-modal.tsx (3)
9-11
: Good organization of imports and dependencies.Clean separation of plane-specific imports and proper addition of i18n and asset resolution capabilities.
Also applies to: 15-15, 20-20
45-53
: Well-structured hook initialization and asset path resolution.Good practice:
- Clear hook organization
- Separate asset paths for search and issues states
- Proper i18n integration
136-140
: Improved empty state handling with localization.The implementation properly handles different scenarios:
- No issues case
- Search with no results case
- Uses translated messages for better user experience
web/core/components/exporter/guide.tsx (3)
11-12
: Good addition of i18n and asset resolution capabilities.Clean integration of:
- Translation hook
- Asset path resolution
- Empty state components
Also applies to: 15-15, 24-24
41-42
: Well-organized hooks and derived values.Good separation of:
- Plane-specific hooks
- Translation hook
- Asset path resolution
Also applies to: 47-48
171-175
: Enhanced empty state with proper localization.The DetailedEmptyState implementation includes:
- Translated title and description
- Proper asset path resolution
web/core/store/user/permissions.store.ts (2)
42-42
: Well-implemented permission check with type safety.The changes:
- Support both old and new role types
- Maintain type safety with explicit casting
- Preserve existing permission logic
Also applies to: 121-121, 146-146
6-6
: Good preparation for permission system migration.The temporary type
ETempUserRole
helps manage the transition to enums while maintaining backward compatibility.Let's verify the usage of these roles across the codebase:
Also applies to: 26-27
✅ Verification successful
Migration preparation is properly implemented and actively used
The enums
EUserProjectRoles
andEUserWorkspaceRoles
are extensively used across the codebase for permission management, with the temporary typeETempUserRole
successfully bridging the transition. The implementation is consistent and well-structured.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for usage of the new role types rg "EUserProjectRoles|EUserWorkspaceRoles" -t typescriptLength of output: 93
Script:
#!/bin/bash # Search for TypeScript files containing the enum references fd '\.(ts|tsx)$' | xargs grep -l "EUserProjectRoles\|EUserWorkspaceRoles" # Search for direct usage of these enums without file type filter rg "EUserProjectRoles|EUserWorkspaceRoles" # Search for import statements using ast-grep ast-grep --pattern 'import { $$$ EUserProjectRoles $$$} from "@plane/constants"' ast-grep --pattern 'import { $$$ EUserWorkspaceRoles $$$} from "@plane/constants"'Length of output: 9428
web/core/components/workspace/sidebar/dropdown.tsx (1)
44-44
: LGTM! Translation key update aligns with structured localization.The change from
t("workspace_settings")
tot("workspace_settings.label")
follows a more structured approach to translations, making them more maintainable and specific.web/core/components/command-palette/command-modal.tsx (2)
5-5
: LGTM! Enhanced imports improve code organization.The imports are well-organized, separating plane-specific imports and adding necessary permission constants.
Also applies to: 11-12
268-268
: LGTM! Empty state implementation follows new pattern.The SimpleEmptyState component is properly implemented with translation and resolved asset path.
web/core/components/issues/workspace-draft/empty-state.tsx (2)
14-14
: LGTM! Enhanced component with proper permissions.The component is now properly reactive with mobx-react observer and includes appropriate permission checks for workspace actions.
Also applies to: 21-24
36-46
: LGTM! Well-structured empty state implementation.The DetailedEmptyState component is properly implemented with translations, resolved asset path, and permission-based button state.
web/core/components/integration/guide.tsx (1)
Line range hint
319-333
: Well-structured empty state translations with good user guidanceThe workspace dashboard empty state translations are well-organized with:
- Clear title and description
- Engaging comic section for user guidance
- Consistent structure across all locales
web/core/components/issues/issue-layouts/empty-states/archived-issues.tsx (3)
4-6
: LGTM! Well-structured permission and internationalization setup.The integration of user permissions and translations is implemented correctly. The component properly imports and uses:
- Permission enums and hooks for access control
- Translation hooks for internationalization
Also applies to: 20-20, 23-23
33-36
: LGTM! Proper permission check implementation.The permission check is well-implemented using the correct roles and permission levels:
const canPerformEmptyStateActions = allowPermissions( [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER], EUserPermissionsLevel.PROJECT );
58-79
: LGTM! Well-structured conditional rendering with proper empty states.The component effectively handles different empty states:
- When filters are applied - shows filter-specific empty state
- When no archived issues exist - shows default empty state
Both states properly use translations and respect user permissions.web/core/components/issues/issue-layouts/empty-states/global-view.tsx (3)
22-25
: LGTM! Proper workspace-level permission check.The permission check is correctly implemented at the workspace level:
const hasMemberLevelPermission = allowPermissions( [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], EUserPermissionsLevel.WORKSPACE );
35-56
: LGTM! Well-implemented empty state for no projects.The component properly handles the case when no projects exist:
- Uses DetailedEmptyState with proper translations
- Implements ComicBoxButton for enhanced UX
- Correctly tracks user interactions
- Properly disables actions based on permissions
59-74
: LGTM! Proper handling of view-specific empty states.The component effectively handles view-specific empty states with:
- Dynamic translation keys based on the current view
- Proper button configuration based on view type
- Correct permission checks for actions
packages/i18n/src/locales/ja/translations.json (2)
317-318
: LGTM! Proper key replacement.The
inbox
key has been correctly replaced withconnections
, maintaining translation consistency.
319-780
: LGTM! Well-structured and comprehensive empty state translations.The translations are well-organized and complete:
- Properly structured by feature/component
- Includes all necessary UI elements (titles, descriptions, buttons)
- Maintains consistent terminology throughout
- Comic tooltips and button texts are properly localized
packages/i18n/src/locales/en/translations.json (2)
316-318
: LGTM! Proper key replacement.The
inbox
key has been correctly replaced withconnections
, establishing the base translation for other languages.
319-780
: LGTM! Well-structured and comprehensive empty state content.The English translations provide an excellent base for other languages:
- Clear and consistent messaging
- Proper organization by feature/component
- Comprehensive coverage of all UI states
- Helpful guidance in descriptions
packages/i18n/src/locales/es/translations.json (3)
316-317
: LGTM! Well-structured key organization.The replacement of "profile" with "connections" and the addition of new sections follows a clear, hierarchical structure that will make maintenance easier.
318-414
: Excellent empty state messaging for workspace sections!The empty states are well-crafted with:
- Clear, action-oriented messages
- Consistent structure across sections
- Engaging comic elements for better user experience
- Helpful descriptions that guide users on next steps
615-779
: Well-crafted utility empty states!The empty states for utility features (command palette, notifications, etc.) are concise and effective, maintaining consistency while being appropriately brief for these UI elements.
packages/i18n/src/locales/fr/translations.json (1)
317-780
: Excellent consistency between Spanish and French translations!The French translations maintain perfect structural parity with the Spanish version while appropriately adapting the content for French users. All sections are present with culturally appropriate translations.
However, verify that the comic references and metaphors (e.g., "10000 feet view") are culturally appropriate for both Spanish and French audiences.
Run this script to find potentially culture-specific metaphors:
✅ Verification successful
French translations show excellent cultural adaptation
The translations demonstrate thoughtful cultural considerations while maintaining consistency with the Spanish version. Notable adaptations include:
- Using "Vue d'ensemble" instead of a direct translation of "10,000 feet view"
- Preserving technical metaphors that are universally understood in both cultures
- Maintaining comic references with culturally appropriate content
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Find potentially culture-specific metaphors in translations # Look for specific phrases that might need cultural adaptation echo "Checking for culture-specific phrases..." rg -i "feet|view|comic|car|fuel" packages/i18n/src/locales/{es,fr}/translations.jsonLength of output: 8527
@@ -6,7 +6,7 @@ import Image from "next/image"; | |||
// utils | |||
import { cn } from "@plane/utils"; | |||
|
|||
type EmptyStateSize = "sm" | "md" | "lg"; | |||
type EmptyStateSize = "sm" | "lg"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Inconsistency found in empty state size types
The removal of "md" size from EmptyStateSize
in simple-empty-state-root.tsx
creates an inconsistency with detailed-empty-state-root.tsx
, which still includes and uses the "md" size option. This needs to be synchronized across all empty state components.
web/core/components/empty-state/detailed-empty-state-root.tsx
: Still defines and uses "md" sizeweb/core/components/empty-state/simple-empty-state-root.tsx
: Removes "md" size
🔗 Analysis chain
Verify impact of removing "md" size option.
The removal of the "md" size option from EmptyStateSize
type could impact existing components using this size. Please ensure all usages have been updated to use either "sm" or "lg".
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any remaining usage of "md" size in empty states
rg -l '"md"' | xargs -I {} ast-grep --pattern 'size="md"' {}
Length of output: 66
Script:
#!/bin/bash
# Find all empty-state related files
fd empty-state
# Search for "md" string in these files
fd empty-state --exec rg -l "md" {}
# Search for any size prop with "md"
ast-grep --pattern 'size=$_("md")'
# Search for EmptyStateSize type usage
ast-grep --pattern 'EmptyStateSize'
Length of output: 1648
Script:
#!/bin/bash
# Check content of empty-state related files for "md" usage
rg "md" web/core/components/empty-state/detailed-empty-state-root.tsx -B 2 -A 2
rg "md" web/core/components/empty-state/section-empty-state-root.tsx -B 2 -A 2
rg "md" web/core/components/common/new-empty-state.tsx -B 2 -A 2
# Search specifically for size prop patterns
ast-grep --pattern 'size={$_}' web/core/components/empty-state/
Length of output: 2451
@@ -37,7 +37,7 @@ export const SidebarUserMenu = observer(() => { | |||
}, | |||
{ | |||
key: "notifications", | |||
labelTranslationKey: "inbox", | |||
labelTranslationKey: "inbox.label", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Translation key structure is incorrect
The change to "inbox.label" will break translations as:
- No translation files contain "inbox.label" key
- Most language files use nested object structure with
inbox.label
- Chinese (zh-CN) uses direct string value for
inbox
🔗 Analysis chain
Verify translation key exists.
The translation key has been changed from "inbox" to "inbox.label". Please ensure this key exists in all language files.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the new translation key exists in all language files
find . -type f -name "translations.json" -exec sh -c 'echo "Checking {}" && jq -e ".\"inbox.label\"" {}' \;
Length of output: 426
Script:
#!/bin/bash
# Check for any keys ending with .label and the existence of "inbox" key
echo "Checking for .label pattern in English translations:"
jq 'to_entries | .[] | select(.key | endswith(".label")) | .key' ./packages/i18n/src/locales/en/translations.json
echo -e "\nChecking for 'inbox' key in all translation files:"
find . -type f -name "translations.json" -exec sh -c 'echo "Checking {}" && jq -e ".inbox" {}' \;
Length of output: 3134
<DetailedEmptyState | ||
title={t("project_module.empty_state.archived.title")} | ||
description={t("project_module.empty_state.archived.description")} | ||
assetPath={resolvedPath} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Translation keys are missing and need to be added
The following translation keys need to be added to all locale files under packages/i18n/src/locales/*/translations.json
:
project_module.empty_state.archived.title
project_module.empty_state.archived.description
They should be added under the existing project_module.empty_state
section, following the same structure as the general
section.
🔗 Analysis chain
Verify translation keys exist.
Please ensure that the following translation keys are defined in the translation files:
project_module.empty_state.archived.title
project_module.empty_state.archived.description
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if the translation keys exist in the translation files
rg -l "project_module\.empty_state\.archived\.(title|description)" --type yaml
Length of output: 80
Script:
#!/bin/bash
# Find potential translation files
echo "=== Looking for translation files ==="
fd -e yaml -e json -e yml . | grep -i "locale\|translation\|lang\|i18n"
echo -e "\n=== Looking for any project_module related translations ==="
rg "project_module" --type yaml --type json -l
Length of output: 821
Script:
#!/bin/bash
# Check English translations file for the specific empty state keys
echo "=== Checking English translations for empty state keys ==="
rg "empty_state.*archived" packages/i18n/src/locales/en/translations.json -C 2
Length of output: 207
Script:
#!/bin/bash
# Extract the project_module section from English translations
echo "=== Examining project_module translations structure ==="
rg -A 10 '"project_module"' packages/i18n/src/locales/en/translations.json
Length of output: 931
const canPerformEmptyStateActions = allowPermissions( | ||
[EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER, EUserProjectRoles.GUEST], | ||
EUserPermissionsLevel.PROJECT | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Remove GUEST role from empty state actions permissions
The GUEST role should be removed from the permission check to maintain consistency with the rest of the codebase. All other similar components (modules, pages, issues) restrict empty state actions to ADMIN and MEMBER roles only.
- In
web/core/components/views/views-list.tsx
, update the roles array to:
[EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER]
🔗 Analysis chain
Review the roles array for empty state actions.
Consider if GUEST role should have access to empty state actions. The current implementation allows guests to perform these actions if they have project-level permissions.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if there are any existing restrictions on guest role actions
rg -A 5 "EUserProjectRoles\.GUEST.*empty.*state"
Length of output: 50
Script:
#!/bin/bash
# Check for guest role usage patterns
rg "EUserProjectRoles\.GUEST" -A 3
# Look for empty state actions implementation
rg "EmptyState.*ctions" -A 5
# Check for allowPermissions usage patterns
ast-grep --pattern 'allowPermissions([
$$$
])'
Length of output: 23337
<SimpleEmptyState | ||
title={t("project_views.empty_state.filter.title")} | ||
description={t("project_views.empty_state.filter.description")} | ||
assetPath={filteredViewResolvedPath} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Translation keys exist but Chinese translation is missing
The translation keys are properly defined in English, Spanish, French, and Japanese locales. However, the Chinese (zh-CN) translation is missing these keys and should be added for completeness.
🔗 Analysis chain
Verify translation keys exist.
Please ensure that the following translation keys are defined in the translation files:
project_views.empty_state.filter.title
project_views.empty_state.filter.description
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if the translation keys exist in the translation files
rg -l "project_views\.empty_state\.filter\.(title|description)" --type yaml
Length of output: 77
Script:
#!/bin/bash
# First, let's find translation-related files
fd -t f "translation|i18n|locale|lang" -E "*.test.*" -E "*.spec.*"
# Then, let's do a broader search for the project_views key
rg "project_views" -A 5 -B 5
Length of output: 7071
Script:
#!/bin/bash
# Search specifically for the filter keys in the translation files
rg -U "\"filter\":\s*{[^}]*\"title\"[^}]*\"description\"" packages/i18n/src/locales/*/translations.json
Length of output: 430
Script:
#!/bin/bash
# Search for filter keys with simpler pattern and context
rg -A 3 -B 1 '"filter":\s*{' packages/i18n/src/locales/*/translations.json
Length of output: 358
Script:
#!/bin/bash
# Use jq to check for the specific keys in the translation files
for f in packages/i18n/src/locales/*/translations.json; do
echo "=== $f ==="
jq '.project_views.empty_state.filter' "$f"
done
Length of output: 1847
{ENotificationTab.ALL ? ( | ||
<SimpleEmptyState | ||
title={t("notification.empty_state.all.title")} | ||
description={t("notification.empty_state.all.description")} | ||
assetPath={resolvedPath} | ||
/> | ||
) : ( | ||
<SimpleEmptyState | ||
title={t("notification.empty_state.mentions.title")} | ||
description={t("notification.empty_state.mentions.description")} | ||
assetPath={resolvedPath} | ||
/> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the notification tab condition.
The condition ENotificationTab.ALL
is used directly without comparison to the current tab state. This will always evaluate to a truthy value as it's comparing against an enum value.
Apply this fix:
- {ENotificationTab.ALL ? (
+ {currentTab === ENotificationTab.ALL ? (
Note: You'll need to add the currentTab
prop to the component and pass it from the parent.
Committable suggestion skipped: line range outside the PR's diff.
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], | ||
EUserPermissionsLevel.WORKSPACE | ||
); | ||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/onboarding/cycles" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect asset base path.
The asset base path references 'cycles' but this is a draft issues component.
Apply this fix:
- const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/onboarding/cycles" });
+ const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/onboarding/draft-issues" });
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/onboarding/cycles" }); | |
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/onboarding/draft-issues" }); |
@@ -137,7 +136,7 @@ const IntegrationGuide = observer(() => { | |||
</div> | |||
) : ( | |||
<div className="flex h-full w-full items-center justify-center"> | |||
<EmptyState type={EmptyStateType.WORKSPACE_SETTINGS_IMPORT} size="sm" /> | |||
{/* <EmptyState type={EmptyStateType.WORKSPACE_SETTINGS_IMPORT} size="sm" /> */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove commented code instead of commenting it out
The EmptyState component is commented out but not removed. This creates unnecessary code clutter. If the code is no longer needed, it should be removed entirely. Git history will preserve the previous implementation if needed.
- {/* <EmptyState type={EmptyStateType.WORKSPACE_SETTINGS_IMPORT} size="sm" /> */}
Description
Type of Change
Screenshots and Media (if applicable)
Test Scenarios
References
Summary by CodeRabbit
Release Notes
Internationalization and Localization Enhancements
Empty State Improvements
EmptyState
component with more detailedDetailedEmptyState
andSimpleEmptyState
User Permissions Management
Asset Management
useResolvedAssetPath
hook for dynamic asset resolutionKey Changes
EmptyState
component and related constants