Dashboard fixes (#10423)

- Fix some of https://github.com/enso-org/cloud-v2/issues/1350
- Move close icon back to right hand side of tabs
- Change icon for Local category to a computer
- Fix rendering of buttons in the delete pop up
- Remove "Versions" and "Sessions" buttons in Local mode from properties sidebar
- Also remove "Shared with" and "Labels" in Local mode
- Show real path of all assets in properties sidebar when in Local mode
- Opening Settings properly closes the user menu again
- "Export" tooltip changed to "Download"
- "Open help chat" button removed for now

Notes:
- re: Download within a folder doesn't work - this is due to the frontend using a PM endpoint that doesn't support specifying the parent directory
This commit is contained in:
somebody1234 2024-07-09 22:54:09 +10:00 committed by GitHub
parent a3dc50fe1e
commit 9229010cb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 162 additions and 89 deletions

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="2" width="12" height="9" rx="1" stroke="black" stroke-width="2" />
<path
d="M7.06963 13.5822L7.43037 11.4178C7.47055 11.1767 7.67914 11 7.92356 11H8.07644C8.32086 11 8.52945 11.1767 8.56963 11.4178L9 14H6.57644C6.82086 14 7.02945 13.8233 7.06963 13.5822Z"
fill="black" />
<path
d="M11.5 14H9M9 14H4.5H6.57644C6.82086 14 7.02945 13.8233 7.06963 13.5822L7.43037 11.4178C7.47055 11.1767 7.67914 11 7.92356 11H8.07644C8.32086 11 8.52945 11.1767 8.56963 11.4178L9 14Z"
stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<rect opacity="0.3" x="3" y="3" width="10" height="7" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 745 B

View File

@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M1.12012 11.776L12.7387 5.06807C12.7403 5.06838 12.742 5.06869 12.7437 5.06901L16.0071 3.18391L16.0071 5.49044L14.828 6.17119C15.5521 6.89508 16 7.89524 16 9C16 11.2091 14.2091 13 12 13H4H3.5V12.9691C3.38338 12.9545 3.26833 12.9349 3.15505 12.9106L0.00883 14.7271L0 12.4312L1.12462 11.7807C1.12312 11.7791 1.12162 11.7776 1.12012 11.776ZM11.5851 4.00268L0.29851 10.519C0.1061 10.0506 0 9.53771 0 9C0 7.51015 0.81451 6.21055 2.02253 5.52219C2.26308 2.9849 4.39974 1 7 1C9.0514 1 10.8142 2.23534 11.5851 4.00268Z"
fill="black" />
</svg>

Before

Width:  |  Height:  |  Size: 699 B

View File

@ -11,6 +11,7 @@ import type * as inputBindings from '#/configurations/inputBindings'
import * as focusHooks from '#/hooks/focusHooks'
import * as inputBindingsProvider from '#/providers/InputBindingsProvider'
import * as modalProvider from '#/providers/ModalProvider'
import * as textProvider from '#/providers/TextProvider'
import * as aria from '#/components/aria'
@ -113,6 +114,7 @@ export default function MenuEntry(props: MenuEntryProps) {
...variantProps
} = props
const { getText } = textProvider.useText()
const { unsetModal } = modalProvider.useSetModal()
const inputBindings = inputBindingsProvider.useInputBindings()
const focusChildProps = focusHooks.useFocusChild()
const info = inputBindings.metadata[action]
@ -146,7 +148,10 @@ export default function MenuEntry(props: MenuEntryProps) {
{...aria.mergeProps<aria.ButtonProps>()(focusChildProps, {
isDisabled,
className: 'group flex w-full rounded-menu-entry',
onPress: doAction,
onPress: () => {
unsetModal()
doAction()
},
})}
>
<div className={MENU_ENTRY_VARIANTS(variantProps)}>

View File

@ -493,7 +493,7 @@ export default function AssetRow(props: AssetRowProps) {
}
case AssetEventType.download:
case AssetEventType.downloadSelected: {
if (event.type === AssetEventType.downloadSelected ? selected : event.ids.has(item.key)) {
if (event.type === AssetEventType.downloadSelected ? selected : event.ids.has(asset.id)) {
if (isCloud) {
switch (asset.type) {
case backendModule.AssetType.project: {

View File

@ -73,37 +73,42 @@ export interface AssetPanelProps extends AssetPanelRequiredProps {
export default function AssetPanel(props: AssetPanelProps) {
const { isVisible, backend, isReadonly = false, item, setItem, category } = props
const { dispatchAssetEvent, dispatchAssetListEvent } = props
const isCloud = backend?.type === backendModule.BackendType.remote
const { getText } = textProvider.useText()
const { localStorage } = localStorageProvider.useLocalStorage()
const [initialized, setInitialized] = React.useState(false)
const initializedRef = React.useRef(initialized)
initializedRef.current = initialized
const [tab, setTab] = React.useState(() => {
const savedTab = localStorage.get('assetPanelTab') ?? AssetPanelTab.properties
if (
const [tabRaw, setTab] = React.useState(
() => localStorage.get('assetPanelTab') ?? AssetPanelTab.properties
)
const tab = (() => {
if (!isCloud) {
return AssetPanelTab.properties
} else if (
(item?.item.type === backendModule.AssetType.secret ||
item?.item.type === backendModule.AssetType.directory) &&
savedTab === AssetPanelTab.versions
tabRaw === AssetPanelTab.versions
) {
return AssetPanelTab.properties
} else if (
item?.item.type !== backendModule.AssetType.project &&
savedTab === AssetPanelTab.projectSessions
tabRaw === AssetPanelTab.projectSessions
) {
return AssetPanelTab.properties
} else {
return savedTab
return tabRaw
}
})
})()
React.useEffect(() => {
// This prevents secrets and directories always setting the tab to `properties`
// (because they do not support the `versions` tab).
if (initializedRef.current) {
localStorage.set('assetPanelTab', tab)
localStorage.set('assetPanelTab', tabRaw)
}
}, [tab, localStorage])
}, [tabRaw, localStorage])
React.useEffect(() => {
setInitialized(true)
@ -113,15 +118,16 @@ export default function AssetPanel(props: AssetPanelProps) {
<div
data-testid="asset-panel"
className={tailwindMerge.twMerge(
'p-top-bar-margin pointer-events-none absolute flex h-full w-asset-panel flex-col gap-asset-panel bg-white pl-asset-panel-l transition-[box-shadow] clip-path-left-shadow',
'pointer-events-none absolute flex h-full w-asset-panel flex-col gap-asset-panel bg-white p-4 pl-asset-panel-l transition-[box-shadow] clip-path-left-shadow',
isVisible ? 'shadow-softer' : ''
)}
onClick={event => {
event.stopPropagation()
}}
>
<ariaComponents.ButtonGroup className="mt-4 grow-0 basis-8">
{item != null &&
<ariaComponents.ButtonGroup className="grow-0 basis-8">
{isCloud &&
item != null &&
item.item.type !== backendModule.AssetType.secret &&
item.item.type !== backendModule.AssetType.directory && (
<ariaComponents.Button
@ -142,7 +148,7 @@ export default function AssetPanel(props: AssetPanelProps) {
{getText('versions')}
</ariaComponents.Button>
)}
{item != null && item.item.type === backendModule.AssetType.project && (
{isCloud && item != null && item.item.type === backendModule.AssetType.project && (
<ariaComponents.Button
size="medium"
variant="bar"

View File

@ -24,6 +24,7 @@ import StatelessSpinner, * as statelessSpinner from '#/components/StatelessSpinn
import * as backendModule from '#/services/Backend'
import type Backend from '#/services/Backend'
import * as localBackend from '#/services/LocalBackend'
import type * as assetTreeNode from '#/utilities/AssetTreeNode'
import * as object from '#/utilities/object'
@ -82,6 +83,12 @@ export default function AssetProperties(props: AssetPropertiesProps) {
self?.permission === permissions.PermissionAction.edit
const isDatalink = item.item.type === backendModule.AssetType.datalink
const isDatalinkDisabled = datalinkValue === editedDatalinkValue || !isDatalinkSubmittable
const isCloud = backend.type === backendModule.BackendType.remote
const path = isCloud
? null
: item.item.type === backendModule.AssetType.project
? item.item.projectState.path ?? null
: localBackend.extractTypeAndId(item.item.id).id
const createDatalinkMutation = backendHooks.useBackendMutation(backend, 'createDatalink')
const getDatalinkMutation = backendHooks.useBackendMutation(backend, 'getDatalink')
@ -194,46 +201,73 @@ export default function AssetProperties(props: AssetPropertiesProps) {
)}
</div>
</div>
<div className="pointer-events-auto flex flex-col items-start gap-side-panel-section">
<aria.Heading
level={2}
className="h-side-panel-heading py-side-panel-heading-y text-lg leading-snug"
>
{getText('settings')}
</aria.Heading>
<table>
<tbody>
<tr data-testid="asset-panel-permissions" className="h-row">
<td className="text my-auto min-w-side-panel-label p">
<aria.Label className="text inline-block">{getText('sharedWith')}</aria.Label>
</td>
<td className="w-full p">
<SharedWithColumn
isReadonly={isReadonly}
item={item}
setItem={setItem}
state={{ backend, category, dispatchAssetEvent, setQuery: () => {} }}
/>
</td>
</tr>
<tr data-testid="asset-panel-labels" className="h-row">
<td className="text my-auto min-w-side-panel-label p">
<aria.Label className="text inline-block">{getText('labels')}</aria.Label>
</td>
<td className="w-full p">
{item.item.labels?.map(value => {
const label = labels.find(otherLabel => otherLabel.value === value)
return label == null ? null : (
<Label key={value} active isDisabled color={label.color} onPress={() => {}}>
{value}
</Label>
)
})}
</td>
</tr>
</tbody>
</table>
</div>
{!isCloud && (
<div className="pointer-events-auto flex flex-col items-start gap-side-panel-section">
<aria.Heading
level={2}
className="h-side-panel-heading py-side-panel-heading-y text-lg leading-snug"
>
{getText('metadata')}
</aria.Heading>
<table>
<tbody>
<tr data-testid="asset-panel-permissions" className="h-row">
<td className="text my-auto min-w-side-panel-label p-0">
<aria.Label className="text inline-block">{getText('path')}</aria.Label>
</td>
<td className="w-full p-0">
<div className="flex gap-2">
<span className="grow">{path}</span>
<ariaComponents.CopyButton copyText={path ?? ''} />
</div>
</td>
</tr>
</tbody>
</table>
</div>
)}
{isCloud && (
<div className="pointer-events-auto flex flex-col items-start gap-side-panel-section">
<aria.Heading
level={2}
className="h-side-panel-heading py-side-panel-heading-y text-lg leading-snug"
>
{getText('settings')}
</aria.Heading>
<table>
<tbody>
<tr data-testid="asset-panel-permissions" className="h-row">
<td className="text my-auto min-w-side-panel-label p-0">
<aria.Label className="text inline-block">{getText('sharedWith')}</aria.Label>
</td>
<td className="w-full p-0">
<SharedWithColumn
isReadonly={isReadonly}
item={item}
setItem={setItem}
state={{ backend, category, dispatchAssetEvent, setQuery: () => {} }}
/>
</td>
</tr>
<tr data-testid="asset-panel-labels" className="h-row">
<td className="text my-auto min-w-side-panel-label p-0">
<aria.Label className="text inline-block">{getText('labels')}</aria.Label>
</td>
<td className="w-full p-0">
{item.item.labels?.map(value => {
const label = labels.find(otherLabel => otherLabel.value === value)
return label == null ? null : (
<Label key={value} active isDisabled color={label.color} onPress={() => {}}>
{value}
</Label>
)
})}
</td>
</tr>
</tbody>
</table>
</div>
)}
{isDatalink && (
<div className="pointer-events-auto flex flex-col items-start gap-side-panel-section">
<aria.Heading

View File

@ -2,7 +2,7 @@
import * as React from 'react'
import CloudIcon from 'enso-assets/cloud.svg'
import NotCloudIcon from 'enso-assets/not_cloud.svg'
import ComputerIcon from 'enso-assets/computer.svg'
import RecentIcon from 'enso-assets/recent.svg'
import Trash2Icon from 'enso-assets/trash2.svg'
@ -75,7 +75,7 @@ const CATEGORY_DATA: readonly CategoryMetadata[] = [
},
{
category: Category.local,
icon: NotCloudIcon,
icon: ComputerIcon,
textId: 'localCategory',
buttonTextId: 'localCategoryButtonLabel',
dropZoneTextId: 'localCategoryDropZoneLabel',

View File

@ -3,9 +3,9 @@ import * as React from 'react'
import isEmail from 'validator/lib/isEmail'
import ComputerIcon from 'enso-assets/computer.svg'
import KeyboardShortcutsIcon from 'enso-assets/keyboard_shortcuts.svg'
import LogIcon from 'enso-assets/log.svg'
import NotCloudIcon from 'enso-assets/not_cloud.svg'
import PeopleSettingsIcon from 'enso-assets/people_settings.svg'
import PeopleIcon from 'enso-assets/people.svg'
import SettingsIcon from 'enso-assets/settings.svg'
@ -230,7 +230,7 @@ export const SETTINGS_TAB_DATA: Readonly<Record<SettingsTabType, SettingsTabData
[SettingsTabType.local]: {
nameId: 'localSettingsTab',
settingsTab: SettingsTabType.organization,
icon: NotCloudIcon,
icon: ComputerIcon,
visible: context => context.localBackend != null,
sections: [
{

View File

@ -213,7 +213,7 @@ export function Tab(props: InternalTabProps) {
<div
ref={ref}
className={tailwindMerge.twMerge(
'group relative h-full',
'group relative flex h-full items-center gap-3',
!isActive && 'hover:enabled:bg-frame'
)}
>
@ -221,25 +221,26 @@ export function Tab(props: InternalTabProps) {
size="custom"
variant="custom"
loaderPosition="icon"
icon={({ isFocusVisible, isHovered }) =>
(isFocusVisible || isHovered) && onClose ? (
<div className="mt-[1px] flex h-4 w-4 items-center justify-center">
<ariaComponents.CloseButton onPress={onClose} />
</div>
) : (
icon
)
}
icon={icon}
isDisabled={false}
isActive={isActive}
loading={isActive ? false : isLoading}
aria-label={getText(labelId)}
className={tailwindMerge.twMerge('h-full', onClose ? 'pl-4' : 'px-4')}
contentClassName="gap-3"
tooltip={false}
className={tailwindMerge.twMerge('relative flex h-full items-center gap-3 px-4')}
onPress={onPress}
>
{children}
<ariaComponents.Text truncate="1" className="max-w-32">
{children}
</ariaComponents.Text>
</ariaComponents.Button>
{onClose && (
<div className="flex pr-4">
<ariaComponents.CloseButton onPress={onClose} />
</div>
)}
</div>
)
}

View File

@ -25,6 +25,13 @@ import ManagePermissionsModal from '#/modals/ManagePermissionsModal'
import * as backendModule from '#/services/Backend'
import type Backend from '#/services/Backend'
// =================
// === Constants ===
// =================
/** Whether the chat button should be visible. Temporarily disabled. */
const SHOULD_SHOW_CHAT_BUTTON: boolean = false
// ===============
// === UserBar ===
// ===============
@ -74,16 +81,18 @@ export default function UserBar(props: UserBarProps) {
className="flex h-[46px] shrink-0 cursor-default items-center gap-user-bar pl-icons-x pr-3"
{...innerProps}
>
<ariaComponents.Button
variant="icon"
size="custom"
className="mr-1"
icon={ChatIcon}
aria-label={getText('openHelpChat')}
onPress={() => {
setIsHelpChatOpen(true)
}}
/>
{SHOULD_SHOW_CHAT_BUTTON && (
<ariaComponents.Button
variant="icon"
size="custom"
className="mr-1"
icon={ChatIcon}
aria-label={getText('openHelpChat')}
onPress={() => {
setIsHelpChatOpen(true)
}}
/>
)}
{shouldShowUpgradeButton && (
<paywall.PaywallDialogButton feature={'inviteUser'} size="medium" variant="tertiary">

View File

@ -68,10 +68,21 @@ export default function ConfirmDeleteModal(props: ConfirmDeleteModalProps) {
>
<aria.Text className="relative">{getText('confirmPrompt', actionText)}</aria.Text>
<ariaComponents.ButtonGroup>
<ariaComponents.Button variant="delete" onPress={doSubmit}>
<ariaComponents.Button
size="medium"
variant="delete"
className="relative"
onPress={doSubmit}
>
{actionButtonLabel}
</ariaComponents.Button>
<ariaComponents.Button autoFocus variant="cancel" onPress={unsetModal}>
<ariaComponents.Button
size="medium"
variant="cancel"
autoFocus
className="relative"
onPress={unsetModal}
>
{getText('cancel')}
</ariaComponents.Button>
</ariaComponents.ButtonGroup>

View File

@ -222,6 +222,8 @@
"resetAll": "Reset All",
"openHelpChat": "Open Help Chat",
"organization": "Organization",
"metadata": "Metadata",
"path": "Path",
"enterSecretPath": "Enter secret path",
"enterText": "Enter text",
@ -268,7 +270,7 @@
"newFolder": "New Folder",
"newProject": "New Project",
"uploadFiles": "Import",
"downloadFiles": "Export",
"downloadFiles": "Download",
"newDatalink": "New Datalink",
"newSecret": "New Secret",
"newLabel": "New Label",