diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index f981474abc7..ba47bbf4573 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -17,6 +17,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Added an index structure for volume annotation segments, in preparation for per-segment statistics. [#7063](https://github.com/scalableminds/webknossos/pull/7063) - Instead of showing all possible action items next to each other, there is now an overflow menu for layer actions. [#7123](https://github.com/scalableminds/webknossos/pull/7123) - In order to facilitate changing the brush size in the brush tool, buttons with preset brush sizes were added. These presets are user configurable by assigning the current brush size to any of the preset buttons. Additionally the preset brush sizes can be set with keyboard shortcuts. [#7101](https://github.com/scalableminds/webknossos/pull/7101) +- Added new graphics and restyled empty dashboards. [#7008](https://github.com/scalableminds/webknossos/pull/7008) ### Changed - Creating bounding boxes can now be done by dragging the left mouse button (when the bounding box tool is selected). To move around in the dataset while this tool is active, keep ALT pressed. [#7118](https://github.com/scalableminds/webknossos/pull/7118) diff --git a/frontend/javascripts/components/pricing_enforcers.tsx b/frontend/javascripts/components/pricing_enforcers.tsx index 85507aa7960..0e9da2e0fc2 100644 --- a/frontend/javascripts/components/pricing_enforcers.tsx +++ b/frontend/javascripts/components/pricing_enforcers.tsx @@ -257,7 +257,7 @@ export function PageUnavailableForYourPlanView({ } + icon={} subTitle={

{getFeatureNotAvailableInPlanMessage( diff --git a/frontend/javascripts/dashboard/dashboard_task_list_view.tsx b/frontend/javascripts/dashboard/dashboard_task_list_view.tsx index c211ddd018e..e4857b22ba0 100644 --- a/frontend/javascripts/dashboard/dashboard_task_list_view.tsx +++ b/frontend/javascripts/dashboard/dashboard_task_list_view.tsx @@ -392,31 +392,46 @@ class DashboardTaskListView extends React.PureComponent { renderPlaceholder() { return this.state.isLoading ? null : ( - <> -

- You have no assigned tasks. Request a new task by clicking on the{" "} - Get a New Task button. -

- {this.props.activeUser.isAdmin && ( - <> -

- Tasks are a powerful way to distribute annotation jobs among groups of users.{" "} - Create new tasks from the admin menu. -

-

- To learn more about the task system in WEBKNOSSOS,{" "} - - check out the documentation - - . -

- - )} - + + + } + > + +

+ You have no tasks assigned to you. Request a new task by clicking on the{" "} + Get a New Task button above. +

+ {this.props.activeUser.isAdmin && ( + <> +

+ Tasks are a powerful way to distribute annotation jobs among groups of users + as part of the WEBKNOSSOS project management.{" "} +

+ + + + + + + + )} + + } + /> +
+ +
); } diff --git a/frontend/javascripts/dashboard/dashboard_view.tsx b/frontend/javascripts/dashboard/dashboard_view.tsx index 1d8a989aba2..dcbae7be83f 100644 --- a/frontend/javascripts/dashboard/dashboard_view.tsx +++ b/frontend/javascripts/dashboard/dashboard_view.tsx @@ -177,26 +177,26 @@ class DashboardView extends PureComponent { } : null, { - label: "Tasks", - key: "tasks", + label: "Annotations", + key: "explorativeAnnotations", children: ( - - + ), }, { - label: "Annotations", - key: "explorativeAnnotations", + label: "Tasks", + key: "tasks", children: ( - - + ), diff --git a/frontend/javascripts/dashboard/dataset_folder_view.tsx b/frontend/javascripts/dashboard/dataset_folder_view.tsx index e7b5df6a58f..87c25c4f6b9 100644 --- a/frontend/javascripts/dashboard/dataset_folder_view.tsx +++ b/frontend/javascripts/dashboard/dataset_folder_view.tsx @@ -4,12 +4,16 @@ import { APIDatasetCompact, APIUser, FolderItem } from "types/api_flow_types"; import DatasetCollectionContextProvider, { useDatasetCollectionContext, } from "./dataset/dataset_collection_context"; - -import DatasetView from "./dataset_view"; +import { Button, Card, Col, Row } from "antd"; +import { Link } from "react-router-dom"; +import * as Utils from "libs/utils"; +import DatasetView, { DatasetAddButton, DatasetRefreshButton } from "./dataset_view"; import { DetailsSidebar } from "./folders/details_sidebar"; import { EditFolderModal } from "./folders/edit_folder_modal"; import { FolderTreeSidebar } from "./folders/folder_tree"; -import { useDatasetsInFolderQuery } from "./dataset/queries"; +import features, { getDemoDatasetUrl } from "features"; +import { RenderToPortal } from "oxalis/view/layouting/portal_utils"; +import { useFolderHierarchyQuery, useDatasetsInFolderQuery } from "./dataset/queries"; type Props = { user: APIUser; @@ -27,6 +31,7 @@ function DatasetFolderViewInner(props: Props) { const context = useDatasetCollectionContext(); const { selectedDatasets, setSelectedDatasets } = context; const [folderIdForEditModal, setFolderIdForEditModal] = useState(null); + const { data: hierarchy } = useFolderHierarchyQuery(); const setSelectedDataset = (ds: APIDatasetCompact | null, multiSelect?: boolean) => { if (!ds) { @@ -89,6 +94,98 @@ function DatasetFolderViewInner(props: Props) { ); }, [context.datasets]); + const renderNoDatasetsPlaceHolder = () => { + const openPublicDatasetCard = ( + + }> + +

Check out a published community dataset to experience WEBKNOSSOS in action.

+ + + + + } + /> +
+ + ); + + const uploadPlaceholderCard = ( + + }> + +

+ WEBKNOSSOS supports a variety of (remote){" "} + + file formats + {" "} + and is also able to convert them when necessary. +

+ + + + , + + } + /> +
+ + ); + + const adminHeader = + Utils.isUserAdminOrDatasetManager(props.user) || Utils.isUserTeamManager(props.user) ? ( +
+ + +
+ ) : null; + + return ( + + {adminHeader} + + {features().isWkorgInstance ? openPublicDatasetCard : null} + {Utils.isUserAdminOrDatasetManager(props.user) ? uploadPlaceholderCard : null} + + + ); + }; + + if ( + hierarchy != null && + hierarchy.flatItems.length === 1 && + context.datasets.length === 0 && + context.activeFolderId != null + ) { + // Show a placeholder if only the root folder exists and no dataset is available yet + // (aka a new, empty organization) + return renderNoDatasetsPlaceHolder(); + } + return (
setJobs(newJobs)); } }, []); + useEffect(() => { let interval: ReturnType | null = null; @@ -129,6 +127,7 @@ function DatasetView(props: Props) { return () => (interval != null ? clearInterval(interval) : undefined); }, []); + useEffect(() => { persistence.persist({ searchQuery: searchQuery || "", @@ -170,10 +169,6 @@ function DatasetView(props: Props) { ); } - const margin = { - marginRight: 5, - }; - const createFilteringModeRadio = (key: DatasetFilteringMode, label: string) => ( { @@ -229,7 +224,6 @@ function DatasetView(props: Props) { ) : ( searchBox ); - const showLoadingIndicator = context.isLoading || context.isChecking; const adminHeader = (
{isUserAdminOrDatasetManagerOrTeamManager ? ( - - context.fetchDatasets()} - disabled={context.isChecking} - > - {showLoadingIndicator ? : } Refresh - - - - - - + + {context.activeFolderId != null && ( } onClick={() => context.activeFolderId != null && @@ -328,6 +297,44 @@ function DatasetView(props: Props) { ); } +export function DatasetRefreshButton({ context }: { context: DatasetCollectionContextValue }) { + const showLoadingIndicator = context.isLoading || context.isChecking; + + return ( + + context.fetchDatasets()} + disabled={context.isChecking} + > + {showLoadingIndicator ? : } Refresh + + + ); +} + +export function DatasetAddButton({ context }: { context: DatasetCollectionContextValue }) { + const { data: folder } = useFolderQuery(context.activeFolderId); + + return ( + + + + ); +} + const SEARCH_OPTIONS = [ { label: "Search everywhere", value: "everywhere" }, { label: "Search current folder", value: "folder" }, @@ -495,74 +502,24 @@ function renderPlaceholder( } if (searchQuery) { - return searchQuery.length >= MINIMUM_SEARCH_QUERY_LENGTH ? "No datasets found." : null; + return searchQuery.length >= MINIMUM_SEARCH_QUERY_LENGTH + ? "No datasets match your search." + : null; } - const openPublicDatasetCard = ( - } - action={ - - - - } - height={350} - > - Have a look at a public dataset to experience WEBKNOSSOS in action. - - ); - - const uploadPlaceholder = ( - } - action={ - - - - } - height={350} - > - WEBKNOSSOS supports a variety of (remote){" "} - - file formats - {" "} - and is also able to convert them when necessary. - - ); - const emptyListHintText = Utils.isUserAdminOrDatasetManager(user) ? "There are no datasets in this folder. Import one or move a dataset from another folder." : "There are no datasets in this folder. Please ask an admin or dataset manager to import a dataset or to grant you permissions to add datasets to this folder."; return ( - - - - {features().isWkorgInstance ? openPublicDatasetCard : null} - {Utils.isUserAdminOrDatasetManager(user) ? uploadPlaceholder : null} - -
- {emptyListHintText} -
- -
+ {emptyListHintText} +
); } diff --git a/frontend/javascripts/dashboard/explorative_annotations_view.tsx b/frontend/javascripts/dashboard/explorative_annotations_view.tsx index e4b9ce06415..c378abc895d 100644 --- a/frontend/javascripts/dashboard/explorative_annotations_view.tsx +++ b/frontend/javascripts/dashboard/explorative_annotations_view.tsx @@ -1,7 +1,7 @@ import { Link } from "react-router-dom"; // @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module '@sca... Remove this comment to see the full error message import { PropTypes } from "@scalableminds/prop-types"; -import { Spin, Input, Table, Button, Modal, Tooltip, Tag } from "antd"; +import { Spin, Input, Table, Button, Modal, Tooltip, Tag, Row, Col, Card } from "antd"; import { DownloadOutlined, FolderOpenOutlined, @@ -439,6 +439,30 @@ class ExplorativeAnnotationsView extends React.PureComponent { }); }; + getEmptyListPlaceholder = () => { + return ( + + + }> + +

Create your first annotation by opening a dataset from the datasets page.

+ + + + + } + /> +
+ +
+ ); + }; + handleOnSearch: SearchProps["onSearch"] = (value, _event) => { if (value !== "") { this.addTagToSearch(value); @@ -551,6 +575,10 @@ class ExplorativeAnnotationsView extends React.PureComponent { }, ]; + if (filteredAndSortedTracings.length === 0) { + return this.getEmptyListPlaceholder(); + } + return ( { this.currentPageData = currentPageData; return null; }} - locale={{ - emptyText: ( -

- Create annotations by opening a dataset from{" "} - the datasets page. -

- ), - }} > } + icon={} status="warning" title={ Sorry, the page you visited does not exist. diff --git a/frontend/stylesheets/_drawings.less b/frontend/stylesheets/_drawings.less index dded2854be5..3a1d83cd310 100644 --- a/frontend/stylesheets/_drawings.less +++ b/frontend/stylesheets/_drawings.less @@ -1,21 +1,45 @@ +.drawing { + background-repeat: no-repeat; + background-size: cover; + display: inline-block; +} + .drawing-404 { width: 302px; height: 360px; - display: inline-block; - background-image: url("/assets/images/drawings/404.svg"); - background-repeat: no-repeat; - background-size: cover; + background-image: url("/assets/images/drawings/404.svg"); } .drawing-paid-feature-not-available { width: 500px; height: 300px; - display: inline-block; background-image: url("/assets/images/drawings/upgrade-plan.svg"); - background-repeat: no-repeat; - background-size: cover; } .drawing-signup:before { background-image: url("/assets/images/drawings/signup.svg") !important; +} + +.drawing-empty-list-tasks { + width: 352px; + height: 392px; + background-image: url("/assets/images/drawings/empty-list-tasks.svg"); +} + +.drawing-empty-list-annotations { + width: 266px; + height: 210px; + background-image: url("/assets/images/drawings/empty-list-annotations.svg"); +} + +.drawing-empty-list-public-gallery { + width: 308px; + height: 242px; + background-image: url("/assets/images/drawings/empty-list-public-gallery.svg"); +} + +.drawing-empty-list-dataset-upload { + width: 300px; + height: 242px; + background-image: url("/assets/images/drawings/empty-list-dataset-upload.svg"); } \ No newline at end of file diff --git a/public/images/drawings/empty-list-annotations.svg b/public/images/drawings/empty-list-annotations.svg new file mode 100644 index 00000000000..a2a2918a102 --- /dev/null +++ b/public/images/drawings/empty-list-annotations.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/drawings/empty-list-dataset-upload.svg b/public/images/drawings/empty-list-dataset-upload.svg new file mode 100644 index 00000000000..df64266c49b --- /dev/null +++ b/public/images/drawings/empty-list-dataset-upload.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/drawings/empty-list-public-gallery.svg b/public/images/drawings/empty-list-public-gallery.svg new file mode 100644 index 00000000000..990f7326760 --- /dev/null +++ b/public/images/drawings/empty-list-public-gallery.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/drawings/empty-list-tasks.svg b/public/images/drawings/empty-list-tasks.svg new file mode 100644 index 00000000000..682393d5b46 --- /dev/null +++ b/public/images/drawings/empty-list-tasks.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +