From 9dda473dc69b92b8773f9d72773bc12d5232a2ed Mon Sep 17 00:00:00 2001 From: daledah Date: Tue, 29 Jul 2025 23:47:20 +0700 Subject: [PATCH 1/6] feat: update workspace empty state --- .../Skeletons/WorkspaceRowSkeleton.tsx | 88 +++++++++++++++++++ src/languages/de.ts | 5 +- src/languages/en.ts | 4 +- src/languages/es.ts | 4 +- src/languages/fr.ts | 4 +- src/languages/it.ts | 4 +- src/languages/ja.ts | 4 +- src/languages/nl.ts | 4 +- src/languages/pl.ts | 4 +- src/languages/pt-BR.ts | 4 +- src/languages/zh-hans.ts | 4 +- src/pages/workspace/WorkspacesListPage.tsx | 57 +++++------- 12 files changed, 132 insertions(+), 54 deletions(-) create mode 100644 src/components/Skeletons/WorkspaceRowSkeleton.tsx diff --git a/src/components/Skeletons/WorkspaceRowSkeleton.tsx b/src/components/Skeletons/WorkspaceRowSkeleton.tsx new file mode 100644 index 000000000000..8730906f49eb --- /dev/null +++ b/src/components/Skeletons/WorkspaceRowSkeleton.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import {Rect} from 'react-native-svg'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import variables from '@styles/variables'; +import ItemListSkeletonView from './ItemListSkeletonView'; + +const barHeight = 7; +const longBarWidth = 120; +const shortBarWidth = 60; +const leftPaneWidth = variables.navigationTabBarSize; +const gapWidth = 12; + +type WorkspaceRowSkeletonProps = { + shouldAnimate?: boolean; + fixedNumItems?: number; + gradientOpacityEnabled?: boolean; +}; + +function WorkspaceRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false}: WorkspaceRowSkeletonProps) { + const styles = useThemeStyles(); + const {windowWidth} = useWindowDimensions(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + const partWidth = Math.floor((windowWidth - leftPaneWidth - gapWidth * 2 - 66) / 3); + return ( + ( + <> + + + + {!shouldUseNarrowLayout && ( + <> + + + + + + )} + + )} + /> + ); +} +WorkspaceRowSkeleton.displayName = 'WorkspaceRowSkeleton'; +export default WorkspaceRowSkeleton; diff --git a/src/languages/de.ts b/src/languages/de.ts index 01641b41b076..b6e796723082 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -4818,9 +4818,8 @@ const translations = { updateTaxCodeFailureMessage: 'Beim Aktualisieren des Steuercodes ist ein Fehler aufgetreten, bitte versuchen Sie es erneut.', }, emptyWorkspace: { - title: 'Erstellen Sie einen Arbeitsbereich', - subtitle: - 'Erstellen Sie einen Arbeitsbereich, um Belege zu verfolgen, Ausgaben zu erstatten, Reisen zu verwalten, Rechnungen zu senden und mehr – alles in der Geschwindigkeit eines Chats.', + title: `Sie haben keine Arbeitsbereiche`, + subtitle: `Verfolgen Sie Belege, erstatten Sie Ausgaben, verwalten Sie Reisen, senden Sie Rechnungen und mehr.`, createAWorkspaceCTA: 'Loslegen', features: { trackAndCollect: 'Belege verfolgen und sammeln', diff --git a/src/languages/en.ts b/src/languages/en.ts index 01eac43d6b76..ffd270e7325d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4797,8 +4797,8 @@ const translations = { updateTaxCodeFailureMessage: 'An error occurred while updating the tax code, please try again', }, emptyWorkspace: { - title: 'Create a workspace', - subtitle: 'Create a workspace to track receipts, reimburse expenses, manage travel, send invoices, and more — all at the speed of chat.', + title: 'You have no workspaces', + subtitle: 'Track receipts, reimburse expenses, manage travel, send invoices, and more.', createAWorkspaceCTA: 'Get Started', features: { trackAndCollect: 'Track and collect receipts', diff --git a/src/languages/es.ts b/src/languages/es.ts index 75b29c8082bc..41489bbc0793 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4810,8 +4810,8 @@ const translations = { updateTaxCodeFailureMessage: 'Se produjo un error al actualizar el código tributario, inténtelo nuevamente', }, emptyWorkspace: { - title: 'Crea un espacio de trabajo', - subtitle: 'Crea un espacio de trabajo para organizar recibos, reembolsar gastos, gestionar viajes, enviar facturas y mucho más, todo a la velocidad del chat.', + title: 'No tienes espacios de trabajo', + subtitle: 'Organizar recibos, reembolsar gastos, gestionar viajes, envía facturas y mucho más.', createAWorkspaceCTA: 'Comenzar', features: { trackAndCollect: 'Organiza recibos', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 7ebd9afa5cdc..a2701300dce7 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -4833,8 +4833,8 @@ const translations = { updateTaxCodeFailureMessage: "Une erreur s'est produite lors de la mise à jour du code fiscal, veuillez réessayer.", }, emptyWorkspace: { - title: 'Créer un espace de travail', - subtitle: 'Créez un espace de travail pour suivre les reçus, rembourser les dépenses, gérer les voyages, envoyer des factures, et plus encore — le tout à la vitesse du chat.', + title: `Vous n'avez aucun espace de travail`, + subtitle: `Suivez les reçus, remboursez les dépenses, gérez les déplacements, envoyez des factures, et plus encore.`, createAWorkspaceCTA: 'Commencer', features: { trackAndCollect: 'Suivre et collecter les reçus', diff --git a/src/languages/it.ts b/src/languages/it.ts index 4d142f10e1db..b1209f795d08 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -4833,8 +4833,8 @@ const translations = { updateTaxCodeFailureMessage: "Si è verificato un errore durante l'aggiornamento del codice fiscale, riprova.", }, emptyWorkspace: { - title: "Crea un'area di lavoro", - subtitle: 'Crea uno spazio di lavoro per tracciare le ricevute, rimborsare le spese, gestire i viaggi, inviare fatture e altro ancora, tutto alla velocità della chat.', + title: `Non hai spazi di lavoro`, + subtitle: `Traccia ricevute, rimborsa spese, gestisci viaggi, invia fatture e altro ancora.`, createAWorkspaceCTA: 'Inizia', features: { trackAndCollect: 'Traccia e raccogli ricevute', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index b764681c4d68..e1b5141941c5 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -4812,8 +4812,8 @@ const translations = { updateTaxCodeFailureMessage: '税コードの更新中にエラーが発生しました。もう一度お試しください。', }, emptyWorkspace: { - title: 'ワークスペースを作成', - subtitle: '領収書を追跡し、経費を払い戻し、旅行を管理し、請求書を送信するためのワークスペースを作成し、チャットの速度でこれらすべてを行いましょう。', + title: `ワークスペースがありません`, + subtitle: `領収書の管理、経費精算、出張管理、請求書の送信などができます。`, createAWorkspaceCTA: '開始する', features: { trackAndCollect: '領収書を追跡して収集する', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 575bfe75fa8e..425f0b989960 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -4835,8 +4835,8 @@ const translations = { updateTaxCodeFailureMessage: 'Er is een fout opgetreden bij het bijwerken van de belastingcode, probeer het opnieuw.', }, emptyWorkspace: { - title: 'Maak een werkruimte aan', - subtitle: 'Maak een werkruimte om bonnetjes bij te houden, uitgaven te vergoeden, reizen te beheren, facturen te versturen en meer — allemaal op de snelheid van chat.', + title: `Je hebt geen werkruimtes`, + subtitle: `Beheer bonnetjes, vergoed uitgaven, regel reizen, verstuur facturen en meer.`, createAWorkspaceCTA: 'Aan de slag', features: { trackAndCollect: 'Volg en verzamel bonnetjes', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 390ea10d533e..b79bcfe21292 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -4824,8 +4824,8 @@ const translations = { updateTaxCodeFailureMessage: 'Wystąpił błąd podczas aktualizacji kodu podatkowego, spróbuj ponownie.', }, emptyWorkspace: { - title: 'Utwórz przestrzeń roboczą', - subtitle: 'Utwórz przestrzeń roboczą do śledzenia paragonów, zwracania wydatków, zarządzania podróżami, wysyłania faktur i nie tylko — wszystko z prędkością czatu.', + title: `Nie masz żadnych przestrzeni roboczych`, + subtitle: `Śledź paragony, zwracaj wydatki, zarządzaj podróżami, wysyłaj faktury i nie tylko.`, createAWorkspaceCTA: 'Rozpocznij', features: { trackAndCollect: 'Śledź i zbieraj paragony', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index ee4ecda3ef84..f1cea44564b2 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -4829,8 +4829,8 @@ const translations = { updateTaxCodeFailureMessage: 'Ocorreu um erro ao atualizar o código de imposto, por favor, tente novamente.', }, emptyWorkspace: { - title: 'Criar um espaço de trabalho', - subtitle: 'Crie um espaço de trabalho para rastrear recibos, reembolsar despesas, gerenciar viagens, enviar faturas e muito mais — tudo na velocidade do chat.', + title: `Você não tem espaços de trabalho`, + subtitle: `Acompanhe recibos, reembolse despesas, gerencie viagens, envie faturas e muito mais.`, createAWorkspaceCTA: 'Começar', features: { trackAndCollect: 'Acompanhe e colete recibos', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 3d185cef6e12..81de2178f10c 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -4751,8 +4751,8 @@ const translations = { updateTaxCodeFailureMessage: '更新税码时发生错误,请重试', }, emptyWorkspace: { - title: '创建一个工作区', - subtitle: '创建一个工作区来跟踪收据、报销费用、管理差旅、发送发票等——一切都在聊天的速度下完成。', + title: `您没有任何工作区`, + subtitle: `跟踪收据、报销费用、管理差旅、发送发票等。`, createAWorkspaceCTA: '开始使用', features: { trackAndCollect: '跟踪并收集收据', diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 5b3b657cb924..948ea93ae293 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -4,11 +4,9 @@ import {FlatList, View} from 'react-native'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; -import type {FeatureListItem} from '@components/FeatureList'; -import FeatureList from '@components/FeatureList'; +import EmptyStateComponent from '@components/EmptyStateComponent'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; import type {MenuItemProps} from '@components/MenuItem'; import NavigationTabBar from '@components/Navigation/NavigationTabBar'; @@ -22,6 +20,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import SearchBar from '@components/SearchBar'; import type {ListItem} from '@components/SelectionList/types'; +import WorkspaceRowSkeleton from '@components/Skeletons/WorkspaceRowSkeleton'; import SupportalActionRestrictedModal from '@components/SupportalActionRestrictedModal'; import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; @@ -32,6 +31,7 @@ import useOnyx from '@hooks/useOnyx'; import usePayAndDowngrade from '@hooks/usePayAndDowngrade'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchResults from '@hooks/useSearchResults'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {isConnectionInProgress} from '@libs/actions/connections'; @@ -46,6 +46,7 @@ import {getPolicy, getPolicyBrickRoadIndicatorStatus, isPolicyAdmin, shouldShowP import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import {shouldCalculateBillNewDot as shouldCalculateBillNewDotFn} from '@libs/SubscriptionUtils'; import type {AvatarSource} from '@libs/UserUtils'; +import colors from '@styles/theme/colors'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -72,21 +73,6 @@ type WorkspaceItem = ListItem & // eslint-disable-next-line react/no-unused-prop-types type GetMenuItem = {item: WorkspaceItem; index: number}; -const workspaceFeatures: FeatureListItem[] = [ - { - icon: Illustrations.MoneyReceipts, - translationKey: 'workspace.emptyWorkspace.features.trackAndCollect', - }, - { - icon: Illustrations.CreditCardsNew, - translationKey: 'workspace.emptyWorkspace.features.companyCards', - }, - { - icon: Illustrations.MoneyWings, - translationKey: 'workspace.emptyWorkspace.features.reimbursements', - }, -]; - /** * Dismisses the errors on one item */ @@ -107,6 +93,7 @@ function dismissWorkspaceError(policyID: string, pendingAction: OnyxCommon.Pendi function WorkspacesListPage() { const theme = useTheme(); const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const {translate, localeCompare} = useLocalize(); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); @@ -444,23 +431,27 @@ function WorkspacesListPage() { ) : ( - - interceptAnonymousUser(() => Navigation.navigate(ROUTES.WORKSPACE_CONFIRMATION.getRoute(ROUTES.WORKSPACES_LIST.route)))} - illustration={LottieAnimations.WorkspacePlanet} - // We use this style to vertically center the illustration, as the original illustration is not centered - illustrationStyle={styles.emptyWorkspaceIllustrationStyle} - titleStyles={styles.textHeadlineH1} - /> - + interceptAnonymousUser(() => Navigation.navigate(ROUTES.WORKSPACE_CONFIRMATION.getRoute(ROUTES.WORKSPACES_LIST.route))), + buttonText: translate('workspace.new.newWorkspace'), + }, + ]} + /> )} {shouldDisplayLHB && } From bbd5b1355ee6b7b0a5adf9f0c949f6da17fc6104 Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 30 Jul 2025 00:54:59 +0700 Subject: [PATCH 2/6] fix: animation styles --- src/pages/workspace/WorkspacesListPage.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 948ea93ae293..d0314804be5d 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -441,7 +441,13 @@ function WorkspacesListPage() { title={translate('workspace.emptyWorkspace.title')} subtitle={translate('workspace.emptyWorkspace.subtitle')} titleStyles={styles.pt2} - headerStyles={[styles.emptyWorkspaceIllustrationStyle, StyleUtils.getBackgroundColorStyle(colors.pink800)]} + headerStyles={[ + styles.emptyWorkspaceIllustrationStyle, + styles.alignItemsCenter, + styles.justifyContentCenter, + styles.overflowHidden, + StyleUtils.getBackgroundColorStyle(colors.pink800), + ]} lottieWebViewStyles={styles.emptyStateFolderWebStyles} headerContentStyles={styles.emptyStateFolderWebStyles} buttons={[ From 417cdaaebc9e469f7e52630321bc07a2cc6c498f Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 1 Aug 2025 01:36:40 +0700 Subject: [PATCH 3/6] fix: translations --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 86b28397b031..ee722822177a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4809,7 +4809,7 @@ const translations = { }, emptyWorkspace: { title: 'No tienes espacios de trabajo', - subtitle: 'Organizar recibos, reembolsar gastos, gestionar viajes, envía facturas y mucho más.', + subtitle: 'Organiza recibos, reembolsa gastos, gestiona viajes, envía facturas y mucho más.', createAWorkspaceCTA: 'Comenzar', features: { trackAndCollect: 'Organiza recibos', From 4473e79503575a57a3e0548b8d8253440ee39c9f Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 8 Aug 2025 03:32:33 +0700 Subject: [PATCH 4/6] refactor some suggestions --- src/components/Skeletons/WorkspaceRowSkeleton.tsx | 2 ++ src/languages/de.ts | 4 ++-- src/languages/fr.ts | 4 ++-- src/languages/it.ts | 4 ++-- src/languages/ja.ts | 4 ++-- src/languages/nl.ts | 4 ++-- src/languages/pl.ts | 4 ++-- src/languages/pt-BR.ts | 4 ++-- src/languages/zh-hans.ts | 4 ++-- 9 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/components/Skeletons/WorkspaceRowSkeleton.tsx b/src/components/Skeletons/WorkspaceRowSkeleton.tsx index 8730906f49eb..5c9f18ef4ef3 100644 --- a/src/components/Skeletons/WorkspaceRowSkeleton.tsx +++ b/src/components/Skeletons/WorkspaceRowSkeleton.tsx @@ -22,6 +22,8 @@ function WorkspaceRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpac const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + // We calculate the width of the sections on the skeleton by first calculating the skeleton view width + // Then we subtract the width by 66, which is the x position of the first part. const partWidth = Math.floor((windowWidth - leftPaneWidth - gapWidth * 2 - 66) / 3); return ( Date: Wed, 13 Aug 2025 10:38:34 +0700 Subject: [PATCH 5/6] fix: styles --- src/pages/workspace/WorkspacesListPage.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 4e7fc04387c2..2ffe2c1ee411 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -444,13 +444,7 @@ function WorkspacesListPage() { title={translate('workspace.emptyWorkspace.title')} subtitle={translate('workspace.emptyWorkspace.subtitle')} titleStyles={styles.pt2} - headerStyles={[ - styles.emptyWorkspaceIllustrationStyle, - styles.alignItemsCenter, - styles.justifyContentCenter, - styles.overflowHidden, - StyleUtils.getBackgroundColorStyle(colors.pink800), - ]} + headerStyles={[styles.alignItemsCenter, styles.justifyContentCenter, styles.overflowHidden, StyleUtils.getBackgroundColorStyle(colors.pink800)]} lottieWebViewStyles={styles.emptyStateFolderWebStyles} headerContentStyles={styles.emptyStateFolderWebStyles} buttons={[ From 4780482f82c379db7e4c3afe8a3e564f6f6b6597 Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 20 Aug 2025 09:24:44 +0700 Subject: [PATCH 6/6] fix: resize and correct spacing in empty state illustration --- src/pages/workspace/WorkspacesListPage.tsx | 7 ++++--- src/styles/index.ts | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 7ef99e94159a..30ba4462c6f4 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -47,6 +47,7 @@ import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import {shouldCalculateBillNewDot as shouldCalculateBillNewDotFn} from '@libs/SubscriptionUtils'; import type {AvatarSource} from '@libs/UserUtils'; import colors from '@styles/theme/colors'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -445,9 +446,9 @@ function WorkspacesListPage() { title={translate('workspace.emptyWorkspace.title')} subtitle={translate('workspace.emptyWorkspace.subtitle')} titleStyles={styles.pt2} - headerStyles={[styles.alignItemsCenter, styles.justifyContentCenter, styles.overflowHidden, StyleUtils.getBackgroundColorStyle(colors.pink800)]} - lottieWebViewStyles={styles.emptyStateFolderWebStyles} - headerContentStyles={styles.emptyStateFolderWebStyles} + headerStyles={[styles.overflowHidden, StyleUtils.getBackgroundColorStyle(colors.pink800), StyleUtils.getHeight(variables.sectionIllustrationHeight)]} + lottieWebViewStyles={styles.emptyWorkspaceListIllustrationStyle} + headerContentStyles={styles.emptyWorkspaceListIllustrationStyle} buttons={[ { success: true, diff --git a/src/styles/index.ts b/src/styles/index.ts index b9f6e70eb1e6..8f3f5f4b0ee2 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2018,6 +2018,12 @@ const styles = (theme: ThemeColors) => marginBottom: -20, }, + emptyWorkspaceListIllustrationStyle: { + marginTop: 12, + marginBottom: -20, + height: '100%', + }, + overlayStyles: (current: OverlayStylesParams, isModalOnTheLeft: boolean) => ({ ...positioning.pFixed,