mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 19:21:54 +03:00
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:
parent
a3dc50fe1e
commit
9229010cb6
10
app/ide-desktop/lib/assets/computer.svg
Normal file
10
app/ide-desktop/lib/assets/computer.svg
Normal 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 |
@ -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 |
@ -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)}>
|
||||
|
@ -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: {
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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: [
|
||||
{
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user