diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs
index ee9dd7e9bc..6b42b9fc7c 100644
--- a/services/web/app/src/Features/Project/ProjectListController.mjs
+++ b/services/web/app/src/Features/Project/ProjectListController.mjs
@@ -577,6 +577,7 @@ async function projectListPage(req, res, next) {
'overleaf-library',
'import-markdown',
'themed-modals',
+ 'shared-workspace',
].filter(Boolean)
await Promise.all(
diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json
index c4b747df09..dc1f435fe9 100644
--- a/services/web/frontend/extracted-translations.json
+++ b/services/web/frontend/extracted-translations.json
@@ -1344,6 +1344,7 @@
"no_actions": "",
"no_add_on_purchase_while_paused": "",
"no_ai_assistant": "",
+ "no_archived_projects": "",
"no_audit_logs_found": "",
"no_borders": "",
"no_caption": "",
@@ -1370,6 +1371,8 @@
"no_preview_available": "",
"no_project_notifications_description": "",
"no_projects": "",
+ "no_projects_match_query": "",
+ "no_projects_yet": "",
"no_references_found": "",
"no_references_found_in_this_file": "",
"no_resolved_comments": "",
@@ -1379,6 +1382,7 @@
"no_selection_select_file": "",
"no_symbols_found": "",
"no_thanks_cancel_now": "",
+ "no_trashed_projects": "",
"no_trashed_references": "",
"non_blinking_cursor": "",
"normal": "",
@@ -1386,6 +1390,7 @@
"normally_x_price_per_year": "",
"not_managed": "",
"not_now": "",
+ "nothing_shared_with_you_yet": "",
"notification": "",
"notification_personal_and_group_subscriptions": "",
"notification_project_invite_accepted_message": "",
@@ -1608,6 +1613,13 @@
"projects": "",
"projects_count": "",
"projects_list": "",
+ "projects_shared_with_you_directly_or_in_shared_workspaces_will_appear_here": "",
+ "projects_shared_with_you_directly_will_appear_here": "",
+ "projects_you_archive_will_appear_here": "",
+ "projects_you_create_across_all_your_workspaces_will_appear_here": "",
+ "projects_you_create_will_appear_here": "",
+ "projects_you_own_or_have_access_to_will_appear_here": "",
+ "projects_you_trash_will_appear_here": "",
"provide_details_of_your_sso_configuration": "",
"provider_rate_limited": "",
"public": "",
@@ -2573,6 +2585,7 @@
"you_can_now_log_in_sso": "",
"you_can_now_search_and_add_references_from_your_rm_library_without_needing_to_import_files": "",
"you_can_only_be_affiliated_with_one_group": "",
+ "you_can_restore_or_permanently_delete_them_at_any_time": "",
"you_can_select_or_invite_collaborator": "",
"you_can_select_or_invite_collaborator_plural": "",
"you_can_still_use_your_premium_features": "",
@@ -2639,6 +2652,7 @@
"your_subscription_will_pause_on": "",
"your_subscription_will_pause_on_short": "",
"your_subscriptions": "",
+ "your_tags": "",
"youre_a_member_of_overleaf_labs": "",
"youre_about_to_enable_single_sign_on": "",
"youre_about_to_enable_single_sign_on_sso_only": "",
diff --git a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2
index 0c1bed5eb3..379bf3bb7b 100644
Binary files a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 and b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 differ
diff --git a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs
index cd75514c59..2aef7d579c 100644
--- a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs
+++ b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs
@@ -5,6 +5,7 @@
export default /** @type {const} */ ([
'account_balance',
+ 'archive',
'arrow_back_ios_new',
'arrow_circle_up',
'auto_delete',
@@ -30,6 +31,7 @@ export default /** @type {const} */ ([
'edit_square',
'error',
'experiment',
+ 'find_in_page',
'find_replace',
'format_strikethrough',
'forum',
diff --git a/services/web/frontend/js/features/project-list/components/no-projects.tsx b/services/web/frontend/js/features/project-list/components/no-projects.tsx
new file mode 100644
index 0000000000..f9ea0addef
--- /dev/null
+++ b/services/web/frontend/js/features/project-list/components/no-projects.tsx
@@ -0,0 +1,83 @@
+import { useTranslation } from 'react-i18next'
+import MaterialIcon from '@/shared/components/material-icon'
+import { Filter } from '@/features/project-list/context/project-list-context'
+import NewProjectButton from '@/features/project-list/components/new-project-button'
+import getMeta from '@/utils/meta'
+
+type NoProjectsCommonProps = {
+ showNewProjectButton?: boolean
+}
+
+type NoProjectsProps = NoProjectsCommonProps & {
+ activeSection: Filter
+}
+
+function NoProjects({ activeSection, showNewProjectButton }: NoProjectsProps) {
+ const { t } = useTranslation()
+ const usersBestSubscription = getMeta('ol-usersBestSubscription')
+ const isGroupPlan = usersBestSubscription?.type === 'group'
+
+ return (
+
+
+
+ {activeSection === 'all' && (
+
+ )}
+ {activeSection === 'owned' && }
+ {activeSection === 'shared' && }
+ {activeSection === 'archived' && (
+
+ )}
+ {activeSection === 'trashed' && (
+
+ )}
+
+
+
+ {(activeSection === 'all' || activeSection === 'owned') &&
+ t('no_projects_yet')}
+ {activeSection === 'shared' && t('nothing_shared_with_you_yet')}
+ {activeSection === 'archived' && t('no_archived_projects')}
+ {activeSection === 'trashed' && t('no_trashed_projects')}
+
+
+ {activeSection === 'all' &&
+ t('projects_you_own_or_have_access_to_will_appear_here')}
+ {activeSection === 'owned' &&
+ (isGroupPlan
+ ? t(
+ 'projects_you_create_across_all_your_workspaces_will_appear_here'
+ )
+ : t('projects_you_create_will_appear_here'))}
+ {activeSection === 'shared' &&
+ (isGroupPlan
+ ? t(
+ 'projects_shared_with_you_directly_or_in_shared_workspaces_will_appear_here'
+ )
+ : t('projects_shared_with_you_directly_will_appear_here'))}
+ {activeSection === 'archived' &&
+ t('projects_you_archive_will_appear_here')}
+ {activeSection === 'trashed' && (
+ <>
+ {t('projects_you_trash_will_appear_here')}
+
+ {t('you_can_restore_or_permanently_delete_them_at_any_time')}
+ >
+ )}
+
+
+ {showNewProjectButton && (
+
+
+
+ )}
+
+
+ )
+}
+
+export default NoProjects
diff --git a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
index fc8d715a4b..c043246381 100644
--- a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
+++ b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
@@ -24,8 +24,10 @@ import SystemMessages from '@/shared/components/system-messages'
import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg'
import overleafLogoDark from '@/shared/svgs/overleaf-a-ds-solution-mallard-dark.svg'
import CookieBanner from '@/shared/components/cookie-banner'
+import NoProjects from '@/features/project-list/components/no-projects'
import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
export function ProjectListDsNav() {
const navbarProps = getMeta('ol-navbar')
@@ -39,9 +41,11 @@ export function ProjectListDsNav() {
filter,
tags,
selectedTagId,
+ currentFilterProjectsCount,
} = useProjectListContext()
const activeOverallTheme = useActiveOverallTheme()
const isLibraryEnabled = isSplitTestEnabled('overleaf-library')
+ const isSharedWorkspaceEnabled = useFeatureFlag('shared-workspace')
const selectedTag = tags.find(tag => tag._id === selectedTagId)
const showTrashHeader = isLibraryEnabled && filter === 'trashed'
@@ -142,49 +146,52 @@ export function ProjectListDsNav() {
{showTrashHeader && }
-
-
-
-
-
- {isLibraryEnabled && showNewProjectButton && (
-
-
+
+
+ >
+ ) : (
+
+
+
+
- )}
-
-
- {/* Omit the survey card in mobile view for now */}
-
-
-
-
-
+ {isLibraryEnabled && showNewProjectButton && (
+
+
+
+ )}
+
+
+ {/* Omit the survey card in mobile view for now */}
+
+
+
+
+
-
-
-
-
-
+ )}
@@ -195,3 +202,20 @@ export function ProjectListDsNav() {
)
}
+
+function ProjectsToolbarMobile() {
+ const { t } = useTranslation()
+
+ return (
+
+ )
+}
diff --git a/services/web/frontend/js/features/project-list/components/sidebar/tags-list.tsx b/services/web/frontend/js/features/project-list/components/sidebar/tags-list.tsx
index bd101e322a..3d0e730e9a 100644
--- a/services/web/frontend/js/features/project-list/components/sidebar/tags-list.tsx
+++ b/services/web/frontend/js/features/project-list/components/sidebar/tags-list.tsx
@@ -13,9 +13,11 @@ import {
OLDropdownMenu,
OLDropdownToggle,
} from '@/shared/components/ol/ol-dropdown-menu'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
export default function TagsList() {
const { t } = useTranslation()
+ const isSharedWorkspaceEnabled = useFeatureFlag('shared-workspace')
const {
tags,
projectsPerTag,
@@ -40,7 +42,7 @@ export default function TagsList() {
aria-hidden="true"
data-testid="organize-projects"
>
- {t('organize_tags')}
+ {isSharedWorkspaceEnabled ? t('your_tags') : t('organize_tags')}